diff --git a/.fossa.yml b/.fossa.yml new file mode 100644 index 00000000000..013accd4498 --- /dev/null +++ b/.fossa.yml @@ -0,0 +1,11 @@ +version: 3 + +project: + id: vitess + name: vitess + +# Exclude the maven based scanning of our java client until we can get it working again. +targets: + exclude: + - type: maven + path: java diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 595995681f9..7306523f000 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -18,9 +18,10 @@ ## Checklist -- [ ] "Backport to:" labels have been added if this change should be back-ported +- [ ] "Backport to:" labels have been added if this change should be back-ported to release branches +- [ ] If this change is to be back-ported to previous releases, a justification is included in the PR description - [ ] Tests were added or are not required -- [ ] Did the new or modified tests pass consistently locally and on the CI +- [ ] Did the new or modified tests pass consistently locally and on CI? - [ ] Documentation was added or is not required ## Deployment Notes diff --git a/.github/workflows/assign_milestone.yml b/.github/workflows/assign_milestone.yml index 7c56f45728f..6529613fa39 100644 --- a/.github/workflows/assign_milestone.yml +++ b/.github/workflows/assign_milestone.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/auto_approve_pr.yml b/.github/workflows/auto_approve_pr.yml index 552f1ec2e68..115e648c3d2 100644 --- a/.github/workflows/auto_approve_pr.yml +++ b/.github/workflows/auto_approve_pr.yml @@ -3,14 +3,20 @@ on: pull_request: types: [opened, reopened] +permissions: + contents: read + jobs: auto_approve: name: Auto Approve Pull Request runs-on: ubuntu-latest + + permissions: + pull-requests: write # only given on local PRs, forks run with `read` access + steps: - name: Checkout code uses: actions/checkout@v3 - - name: Auto Approve Pull Request env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/check_label.yml b/.github/workflows/check_label.yml index c3c89273df8..ec5309c6757 100644 --- a/.github/workflows/check_label.yml +++ b/.github/workflows/check_label.yml @@ -3,10 +3,6 @@ on: pull_request: types: [opened, labeled, unlabeled, synchronize] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Check Pull Request labels') - cancel-in-progress: true - permissions: read-all jobs: @@ -68,6 +64,12 @@ jobs: echo "Expecting PR to not have the NeedsIssue label; please create a linked issue and remove the label." exit 1 fi + if cat ${LABELS_JSON} | jq -r '.[].name ' | grep -q 'NeedsBackportReason' ; then + if cat ${LABELS_JSON} | jq -r '.[].name ' | grep -q 'Backport to:'; then + echo "Expecting PR to not have the NeedsBackportReason label; please add your justification to the PR description and remove the label." + exit 1 + fi + fi - name: Do Not Merge label diff --git a/.github/workflows/check_make_vtadmin_authz_testgen.yml b/.github/workflows/check_make_vtadmin_authz_testgen.yml index 8f9199e7658..8a6bd867db5 100644 --- a/.github/workflows/check_make_vtadmin_authz_testgen.yml +++ b/.github/workflows/check_make_vtadmin_authz_testgen.yml @@ -31,7 +31,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -50,7 +50,7 @@ jobs: uses: actions/setup-go@v4 if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true' with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.vtadmin_changes == 'true' diff --git a/.github/workflows/check_make_vtadmin_web_proto.yml b/.github/workflows/check_make_vtadmin_web_proto.yml index 5f3302fc97c..baa86a53964 100644 --- a/.github/workflows/check_make_vtadmin_web_proto.yml +++ b/.github/workflows/check_make_vtadmin_web_proto.yml @@ -31,7 +31,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -52,7 +52,7 @@ jobs: uses: actions/setup-go@v4 if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.proto_changes == 'true' with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Setup Node if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.proto_changes == 'true' diff --git a/.github/workflows/cluster_endtoend_12.yml b/.github/workflows/cluster_endtoend_12.yml index 5ce650f1ea6..179ee0e0a1a 100644 --- a/.github/workflows/cluster_endtoend_12.yml +++ b/.github/workflows/cluster_endtoend_12.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_13.yml b/.github/workflows/cluster_endtoend_13.yml index fa98916736f..eadfd722f96 100644 --- a/.github/workflows/cluster_endtoend_13.yml +++ b/.github/workflows/cluster_endtoend_13.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_15.yml b/.github/workflows/cluster_endtoend_15.yml index 2501f26ab58..ca9c6c29eac 100644 --- a/.github/workflows/cluster_endtoend_15.yml +++ b/.github/workflows/cluster_endtoend_15.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_18.yml b/.github/workflows/cluster_endtoend_18.yml index 234e672afb0..395fb895497 100644 --- a/.github/workflows/cluster_endtoend_18.yml +++ b/.github/workflows/cluster_endtoend_18.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_21.yml b/.github/workflows/cluster_endtoend_21.yml index feeedcd46b8..92de13f00ee 100644 --- a/.github/workflows/cluster_endtoend_21.yml +++ b/.github/workflows/cluster_endtoend_21.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_22.yml b/.github/workflows/cluster_endtoend_22.yml index f4cee992fb2..486d7019b25 100644 --- a/.github/workflows/cluster_endtoend_22.yml +++ b/.github/workflows/cluster_endtoend_22.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_backup_pitr.yml b/.github/workflows/cluster_endtoend_backup_pitr.yml index b3b6e0d56f6..f96a8a64948 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml b/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml index fb9946fdb0b..42bb39a4b10 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -103,9 +105,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml index 6cad7922321..9457222437c 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup_mysql57.yml b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup_mysql57.yml index b895a19a8d0..402a9485ca0 100644 --- a/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup_mysql57.yml +++ b/.github/workflows/cluster_endtoend_backup_pitr_xtrabackup_mysql57.yml @@ -53,13 +53,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -75,7 +77,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -107,9 +109,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml index f65d2625c28..c46ead2633c 100644 --- a/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml +++ b/.github/workflows/cluster_endtoend_ers_prs_newfeatures_heavy.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_mysql80.yml b/.github/workflows/cluster_endtoend_mysql80.yml index 5c3739aafd0..682727916e0 100644 --- a/.github/workflows/cluster_endtoend_mysql80.yml +++ b/.github/workflows/cluster_endtoend_mysql80.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_mysql_server_vault.yml b/.github/workflows/cluster_endtoend_mysql_server_vault.yml index 793e7372309..35cbebcc09a 100644 --- a/.github/workflows/cluster_endtoend_mysql_server_vault.yml +++ b/.github/workflows/cluster_endtoend_mysql_server_vault.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml index af61a6a5059..aa4c688983e 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_ghost.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_ghost.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml index 43dc184c204..d3658a78370 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_ghost_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert.yml b/.github/workflows/cluster_endtoend_onlineddl_revert.yml index d2c6e23ee86..ef627b456d5 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_revert.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_revert.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml index ac93c1ac532..01b5744df3d 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_revert_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml index 38031f4441e..c41f29f1a5a 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml index 0a205266c4f..e12f8a4fdc7 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_scheduler_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml index d83fb7010b8..c241d29dc97 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml index a941c9faef0..2714520edac 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml index a51cb6c33fe..af41c9a04a1 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml index 77626919a89..ce76de36f82 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml index 1230fcd3518..1cf7c2e74fb 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml index 86ef8eec019..fc4c427794a 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_stress_suite_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml index 34e521d648f..4cd2663d45c 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml index a400ea99677..8e18bd318bd 100644 --- a/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml +++ b/.github/workflows/cluster_endtoend_onlineddl_vrepl_suite_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml index 68a25ee46ec..869a2fb5b1e 100644 --- a/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml +++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -93,9 +95,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml b/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml index ba57948d162..b15ee2076c1 100644 --- a/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml +++ b/.github/workflows/cluster_endtoend_schemadiff_vrepl_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -104,9 +106,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml index 0fe0d4e18da..bb1ba2f6889 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_consul.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_consul.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml index 5af0e2ff852..31af8d767be 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml b/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml index e1ae8eeb69c..a628f79e9b8 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_tablegc_mysql57.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -103,9 +105,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml index 8b6826f257c..18a895983ea 100644 --- a/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml +++ b/.github/workflows/cluster_endtoend_tabletmanager_throttler_topo.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_topo_connection_cache.yml b/.github/workflows/cluster_endtoend_topo_connection_cache.yml index bb59336df48..64c45d51f86 100644 --- a/.github/workflows/cluster_endtoend_topo_connection_cache.yml +++ b/.github/workflows/cluster_endtoend_topo_connection_cache.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml index ec3d101629e..8725ba46582 100644 --- a/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml +++ b/.github/workflows/cluster_endtoend_vreplication_across_db_versions.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vreplication_basic.yml b/.github/workflows/cluster_endtoend_vreplication_basic.yml index ea6219bf869..4a708d134ee 100644 --- a/.github/workflows/cluster_endtoend_vreplication_basic.yml +++ b/.github/workflows/cluster_endtoend_vreplication_basic.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml index 5ef46750668..e5b5296dacf 100644 --- a/.github/workflows/cluster_endtoend_vreplication_cellalias.yml +++ b/.github/workflows/cluster_endtoend_vreplication_cellalias.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vreplication_multicell.yml b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml similarity index 92% rename from .github/workflows/cluster_endtoend_vreplication_multicell.yml rename to .github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml index 328c062e1d0..ca4145674a9 100644 --- a/.github/workflows/cluster_endtoend_vreplication_multicell.yml +++ b/.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml @@ -1,9 +1,9 @@ # DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" -name: Cluster (vreplication_multicell) +name: Cluster (vreplication_foreign_key_stress) on: [push, pull_request] concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_multicell)') + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_foreign_key_stress)') cancel-in-progress: true permissions: read-all @@ -15,7 +15,7 @@ env: jobs: build: - name: Run endtoend tests on Cluster (vreplication_multicell) + name: Run endtoend tests on Cluster (vreplication_foreign_key_stress) runs-on: gh-hosted-runners-4cores-1 steps: @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -65,13 +67,13 @@ jobs: - 'tools/**' - 'config/**' - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vreplication_multicell.yml' + - '.github/workflows/cluster_endtoend_vreplication_foreign_key_stress.yml' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -156,7 +158,7 @@ jobs: EOF # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vreplication_multicell | tee -a output.txt | go-junit-report -set-exit-code > report.xml + eatmydata -- go run test.go -docker=false -follow -shard vreplication_foreign_key_stress | tee -a output.txt | go-junit-report -set-exit-code > report.xml - name: Print test output and Record test result in launchable if PR is not a draft if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() diff --git a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml index d8961314a46..64178904645 100644 --- a/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml +++ b/.github/workflows/cluster_endtoend_vreplication_migrate_vdiff2_convert_tz.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_basic.yml b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml similarity index 93% rename from .github/workflows/cluster_endtoend_vreplication_partial_movetables_basic.yml rename to .github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml index 28dca240332..f50124dd80a 100644 --- a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_basic.yml +++ b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml @@ -1,9 +1,9 @@ # DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" -name: Cluster (vreplication_partial_movetables_basic) +name: Cluster (vreplication_partial_movetables_and_materialize) on: [push, pull_request] concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_partial_movetables_basic)') + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_partial_movetables_and_materialize)') cancel-in-progress: true permissions: read-all @@ -15,7 +15,7 @@ env: jobs: build: - name: Run endtoend tests on Cluster (vreplication_partial_movetables_basic) + name: Run endtoend tests on Cluster (vreplication_partial_movetables_and_materialize) runs-on: gh-hosted-runners-4cores-1 steps: @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -65,13 +67,13 @@ jobs: - 'tools/**' - 'config/**' - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vreplication_partial_movetables_basic.yml' + - '.github/workflows/cluster_endtoend_vreplication_partial_movetables_and_materialize.yml' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -156,7 +158,7 @@ jobs: EOF # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vreplication_partial_movetables_basic | tee -a output.txt | go-junit-report -set-exit-code > report.xml + eatmydata -- go run test.go -docker=false -follow -shard vreplication_partial_movetables_and_materialize | tee -a output.txt | go-junit-report -set-exit-code > report.xml - name: Print test output and Record test result in launchable if PR is not a draft if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() diff --git a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_sequences.yml b/.github/workflows/cluster_endtoend_vreplication_partial_movetables_sequences.yml deleted file mode 100644 index c002a72d1e7..00000000000 --- a/.github/workflows/cluster_endtoend_vreplication_partial_movetables_sequences.yml +++ /dev/null @@ -1,170 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (vreplication_partial_movetables_sequences) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vreplication_partial_movetables_sequences)') - cancel-in-progress: true - -permissions: read-all - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (vreplication_partial_movetables_sequences) - runs-on: gh-hosted-runners-4cores-1 - - steps: - - name: Skip CI - run: | - if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then - echo "skipping CI due to the 'Skip CI' label" - exit 1 - fi - - - name: Check if workflow needs to be skipped - id: skip-workflow - run: | - skip='false' - if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then - skip='true' - fi - echo Skip ${skip} - echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - PR_DATA=$(curl \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") - draft=$(echo "$PR_DATA" | jq .draft -r) - echo "is_draft=${draft}" >> $GITHUB_OUTPUT - - - name: Check out code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - - name: Check for changes in relevant files - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.sum' - - 'go.mod' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vreplication_partial_movetables_sequences.yml' - - - name: Set up Go - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v4 - with: - go-version: 1.21.3 - - - name: Set up python - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v4 - - - name: Tune the OS - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - # Limit local port range to not use ports that overlap with server side - # ports that we listen on. - sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - - # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb - echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get update - # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 - - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/vitessio/go-junit-report@HEAD - - - name: Setup launchable dependencies - if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . - - - name: Run cluster endtoend test - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 45 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -exo pipefail - - # Increase our open file descriptor limit as we could hit this - ulimit -n 65536 - cat <<-EOF>>./config/mycnf/mysql80.cnf - innodb_buffer_pool_dump_at_shutdown=OFF - innodb_buffer_pool_in_core_file=OFF - innodb_buffer_pool_load_at_startup=OFF - innodb_buffer_pool_size=64M - innodb_doublewrite=OFF - innodb_flush_log_at_trx_commit=0 - innodb_flush_method=O_DIRECT - innodb_numa_interleave=ON - innodb_adaptive_hash_index=OFF - sync_binlog=0 - sync_relay_log=0 - performance_schema=OFF - slow-query-log=OFF - EOF - - cat <<-EOF>>./config/mycnf/mysql80.cnf - binlog-transaction-compression=ON - EOF - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vreplication_partial_movetables_sequences | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable if PR is not a draft - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() - run: | - if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - fi - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_vreplication_v2.yml b/.github/workflows/cluster_endtoend_vreplication_v2.yml index 9229b34a5bf..e8117904cf6 100644 --- a/.github/workflows/cluster_endtoend_vreplication_v2.yml +++ b/.github/workflows/cluster_endtoend_vreplication_v2.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vstream_failover.yml b/.github/workflows/cluster_endtoend_vstream.yml similarity index 92% rename from .github/workflows/cluster_endtoend_vstream_failover.yml rename to .github/workflows/cluster_endtoend_vstream.yml index a620b8caad9..c532fde7774 100644 --- a/.github/workflows/cluster_endtoend_vstream_failover.yml +++ b/.github/workflows/cluster_endtoend_vstream.yml @@ -1,9 +1,9 @@ # DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" -name: Cluster (vstream_failover) +name: Cluster (vstream) on: [push, pull_request] concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vstream_failover)') + group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vstream)') cancel-in-progress: true permissions: read-all @@ -15,7 +15,7 @@ env: jobs: build: - name: Run endtoend tests on Cluster (vstream_failover) + name: Run endtoend tests on Cluster (vstream) runs-on: gh-hosted-runners-4cores-1 steps: @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -65,13 +67,13 @@ jobs: - 'tools/**' - 'config/**' - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vstream_failover.yml' + - '.github/workflows/cluster_endtoend_vstream.yml' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -134,7 +136,7 @@ jobs: set -exo pipefail # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vstream_failover | tee -a output.txt | go-junit-report -set-exit-code > report.xml + eatmydata -- go run test.go -docker=false -follow -shard vstream | tee -a output.txt | go-junit-report -set-exit-code > report.xml - name: Print test output and Record test result in launchable if PR is not a draft if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() diff --git a/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml b/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml deleted file mode 100644 index 5db27dad710..00000000000 --- a/.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml +++ /dev/null @@ -1,148 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (vstream_stoponreshard_false) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vstream_stoponreshard_false)') - cancel-in-progress: true - -permissions: read-all - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (vstream_stoponreshard_false) - runs-on: gh-hosted-runners-4cores-1 - - steps: - - name: Skip CI - run: | - if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then - echo "skipping CI due to the 'Skip CI' label" - exit 1 - fi - - - name: Check if workflow needs to be skipped - id: skip-workflow - run: | - skip='false' - if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then - skip='true' - fi - echo Skip ${skip} - echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - PR_DATA=$(curl \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") - draft=$(echo "$PR_DATA" | jq .draft -r) - echo "is_draft=${draft}" >> $GITHUB_OUTPUT - - - name: Check out code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - - name: Check for changes in relevant files - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.sum' - - 'go.mod' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vstream_stoponreshard_false.yml' - - - name: Set up Go - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v4 - with: - go-version: 1.21.3 - - - name: Set up python - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v4 - - - name: Tune the OS - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - # Limit local port range to not use ports that overlap with server side - # ports that we listen on. - sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - - # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb - echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get update - # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 - - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/vitessio/go-junit-report@HEAD - - - name: Setup launchable dependencies - if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . - - - name: Run cluster endtoend test - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 45 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -exo pipefail - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vstream_stoponreshard_false | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable if PR is not a draft - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() - run: | - if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - fi - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml b/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml deleted file mode 100644 index 32e7685bf8f..00000000000 --- a/.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml +++ /dev/null @@ -1,148 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (vstream_stoponreshard_true) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vstream_stoponreshard_true)') - cancel-in-progress: true - -permissions: read-all - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (vstream_stoponreshard_true) - runs-on: gh-hosted-runners-4cores-1 - - steps: - - name: Skip CI - run: | - if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then - echo "skipping CI due to the 'Skip CI' label" - exit 1 - fi - - - name: Check if workflow needs to be skipped - id: skip-workflow - run: | - skip='false' - if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then - skip='true' - fi - echo Skip ${skip} - echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - PR_DATA=$(curl \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") - draft=$(echo "$PR_DATA" | jq .draft -r) - echo "is_draft=${draft}" >> $GITHUB_OUTPUT - - - name: Check out code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - - name: Check for changes in relevant files - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.sum' - - 'go.mod' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vstream_stoponreshard_true.yml' - - - name: Set up Go - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v4 - with: - go-version: 1.21.3 - - - name: Set up python - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v4 - - - name: Tune the OS - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - # Limit local port range to not use ports that overlap with server side - # ports that we listen on. - sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - - # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb - echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get update - # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 - - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/vitessio/go-junit-report@HEAD - - - name: Setup launchable dependencies - if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . - - - name: Run cluster endtoend test - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 45 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -exo pipefail - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vstream_stoponreshard_true | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable if PR is not a draft - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() - run: | - if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - fi - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml b/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml deleted file mode 100644 index 27620919d99..00000000000 --- a/.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml +++ /dev/null @@ -1,148 +0,0 @@ -# DO NOT MODIFY: THIS FILE IS GENERATED USING "make generate_ci_workflows" - -name: Cluster (vstream_with_keyspaces_to_watch) -on: [push, pull_request] -concurrency: - group: format('{0}-{1}', ${{ github.ref }}, 'Cluster (vstream_with_keyspaces_to_watch)') - cancel-in-progress: true - -permissions: read-all - -env: - LAUNCHABLE_ORGANIZATION: "vitess" - LAUNCHABLE_WORKSPACE: "vitess-app" - GITHUB_PR_HEAD_SHA: "${{ github.event.pull_request.head.sha }}" - -jobs: - build: - name: Run endtoend tests on Cluster (vstream_with_keyspaces_to_watch) - runs-on: gh-hosted-runners-4cores-1 - - steps: - - name: Skip CI - run: | - if [[ "${{contains( github.event.pull_request.labels.*.name, 'Skip CI')}}" == "true" ]]; then - echo "skipping CI due to the 'Skip CI' label" - exit 1 - fi - - - name: Check if workflow needs to be skipped - id: skip-workflow - run: | - skip='false' - if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then - skip='true' - fi - echo Skip ${skip} - echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - PR_DATA=$(curl \ - -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}") - draft=$(echo "$PR_DATA" | jq .draft -r) - echo "is_draft=${draft}" >> $GITHUB_OUTPUT - - - name: Check out code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - - name: Check for changes in relevant files - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main - id: changes - with: - token: '' - filters: | - end_to_end: - - 'go/**/*.go' - - 'test.go' - - 'Makefile' - - 'build.env' - - 'go.sum' - - 'go.mod' - - 'proto/*.proto' - - 'tools/**' - - 'config/**' - - 'bootstrap.sh' - - '.github/workflows/cluster_endtoend_vstream_with_keyspaces_to_watch.yml' - - - name: Set up Go - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-go@v4 - with: - go-version: 1.21.3 - - - name: Set up python - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - uses: actions/setup-python@v4 - - - name: Tune the OS - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - # Limit local port range to not use ports that overlap with server side - # ports that we listen on. - sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" - # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio - echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf - sudo sysctl -p /etc/sysctl.conf - - - name: Get dependencies - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - run: | - - # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb - echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* - sudo apt-get update - # Install everything else we need, and configure - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata xz-utils libncurses5 - - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - # install JUnit report formatter - go install github.com/vitessio/go-junit-report@HEAD - - - name: Setup launchable dependencies - if: steps.skip-workflow.outputs.is_draft == 'false' && steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && github.base_ref == 'main' - run: | - # Get Launchable CLI installed. If you can, make it a part of the builder image to speed things up - pip3 install --user launchable~=1.0 > /dev/null - - # verify that launchable setup is all correct. - launchable verify || true - - # Tell Launchable about the build you are producing and testing - launchable record build --name "$GITHUB_RUN_ID" --no-commit-collection --source . - - - name: Run cluster endtoend test - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' - timeout-minutes: 45 - run: | - # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file - # which musn't be more than 107 characters long. - export VTDATAROOT="/tmp/" - source build.env - - set -exo pipefail - - # run the tests however you normally do, then produce a JUnit XML file - eatmydata -- go run test.go -docker=false -follow -shard vstream_with_keyspaces_to_watch | tee -a output.txt | go-junit-report -set-exit-code > report.xml - - - name: Print test output and Record test result in launchable if PR is not a draft - if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' && always() - run: | - if [[ "${{steps.skip-workflow.outputs.is_draft}}" == "false" ]]; then - # send recorded tests to launchable - launchable record tests --build "$GITHUB_RUN_ID" go-test . || true - fi - - # print test output - cat output.txt diff --git a/.github/workflows/cluster_endtoend_vtbackup.yml b/.github/workflows/cluster_endtoend_vtbackup.yml index 8f2dcd3768b..ea5bd88161a 100644 --- a/.github/workflows/cluster_endtoend_vtbackup.yml +++ b/.github/workflows/cluster_endtoend_vtbackup.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml index aad84a910c6..cad638aa376 100644 --- a/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtctlbackup_sharded_clustertest_heavy.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml index 19bb9efe86c..da4fdc95b75 100644 --- a/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml +++ b/.github/workflows/cluster_endtoend_vtgate_concurrentdml.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml index e2824c5844d..b5eaf284b6c 100644 --- a/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml +++ b/.github/workflows/cluster_endtoend_vtgate_foreignkey_stress.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_gen4.yml b/.github/workflows/cluster_endtoend_vtgate_gen4.yml index 205de4b5e68..f446f9ff99d 100644 --- a/.github/workflows/cluster_endtoend_vtgate_gen4.yml +++ b/.github/workflows/cluster_endtoend_vtgate_gen4.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml index 98d59d60aee..2a71a332567 100644 --- a/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_general_heavy.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_godriver.yml b/.github/workflows/cluster_endtoend_vtgate_godriver.yml index 2f4082d10d4..2997cebd919 100644 --- a/.github/workflows/cluster_endtoend_vtgate_godriver.yml +++ b/.github/workflows/cluster_endtoend_vtgate_godriver.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml index 4a9f6e227fb..de139601780 100644 --- a/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml +++ b/.github/workflows/cluster_endtoend_vtgate_partial_keyspace.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_queries.yml b/.github/workflows/cluster_endtoend_vtgate_queries.yml index 6d41d922fc4..dc456d62be9 100644 --- a/.github/workflows/cluster_endtoend_vtgate_queries.yml +++ b/.github/workflows/cluster_endtoend_vtgate_queries.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml index 028e1492029..43276e7a9bb 100644 --- a/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml +++ b/.github/workflows/cluster_endtoend_vtgate_readafterwrite.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml index 5972472402e..f963bd94e90 100644 --- a/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml +++ b/.github/workflows/cluster_endtoend_vtgate_reservedconn.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_schema.yml b/.github/workflows/cluster_endtoend_vtgate_schema.yml index 68a2bd697be..72d0d1f7c5a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml index 1c5d1e675f8..517bcf884f8 100644 --- a/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml +++ b/.github/workflows/cluster_endtoend_vtgate_schema_tracker.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml index 26adb43fd74..1cf1b1412d3 100644 --- a/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml +++ b/.github/workflows/cluster_endtoend_vtgate_tablet_healthcheck_cache.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_topo.yml b/.github/workflows/cluster_endtoend_vtgate_topo.yml index 49945a607d8..e16567209b8 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml index ee72650dcbd..a8c0253012c 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_consul.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml index 4051373d9aa..c8cdefb4e1a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml +++ b/.github/workflows/cluster_endtoend_vtgate_topo_etcd.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_transaction.yml b/.github/workflows/cluster_endtoend_vtgate_transaction.yml index b7cc848692f..a2a3800d88b 100644 --- a/.github/workflows/cluster_endtoend_vtgate_transaction.yml +++ b/.github/workflows/cluster_endtoend_vtgate_transaction.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml index b6359682993..2fe07e70596 100644 --- a/.github/workflows/cluster_endtoend_vtgate_unsharded.yml +++ b/.github/workflows/cluster_endtoend_vtgate_unsharded.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml index 83fb2b2d829..60043a7fc4a 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vindex_heavy.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtgate_vschema.yml b/.github/workflows/cluster_endtoend_vtgate_vschema.yml index 4c2f3b2637d..fea02a620f7 100644 --- a/.github/workflows/cluster_endtoend_vtgate_vschema.yml +++ b/.github/workflows/cluster_endtoend_vtgate_vschema.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtorc.yml b/.github/workflows/cluster_endtoend_vtorc.yml index 872576ab8b5..3dec9b0262d 100644 --- a/.github/workflows/cluster_endtoend_vtorc.yml +++ b/.github/workflows/cluster_endtoend_vtorc.yml @@ -43,19 +43,30 @@ jobs: draft=$(echo "$PR_DATA" | jq .draft -r) echo "is_draft=${draft}" >> $GITHUB_OUTPUT + - name: Check Memory + run: | + totalMem=$(free -g | awk 'NR==2 {print $2}') + echo "total memory $totalMem GB" + if [[ "$totalMem" -lt 15 ]]; then + echo "Less memory than required" + exit 1 + fi + - name: Check out code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +82,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +103,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_vtorc_mysql57.yml b/.github/workflows/cluster_endtoend_vtorc_mysql57.yml index 72baf7940b6..dc9f3581a62 100644 --- a/.github/workflows/cluster_endtoend_vtorc_mysql57.yml +++ b/.github/workflows/cluster_endtoend_vtorc_mysql57.yml @@ -43,19 +43,30 @@ jobs: draft=$(echo "$PR_DATA" | jq .draft -r) echo "is_draft=${draft}" >> $GITHUB_OUTPUT + - name: Check Memory + run: | + totalMem=$(free -g | awk 'NR==2 {print $2}') + echo "total memory $totalMem GB" + if [[ "$totalMem" -lt 15 ]]; then + echo "Less memory than required" + exit 1 + fi + - name: Check out code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +82,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -103,9 +114,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml index b56d4dc61a5..7104452f35f 100644 --- a/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml +++ b/.github/workflows/cluster_endtoend_vttablet_prscomplex.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -92,9 +94,9 @@ jobs: run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/cluster_endtoend_xb_backup.yml b/.github/workflows/cluster_endtoend_xb_backup.yml index f24baaf31af..f48b4d3198b 100644 --- a/.github/workflows/cluster_endtoend_xb_backup.yml +++ b/.github/workflows/cluster_endtoend_xb_backup.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml b/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml index b85628a0dbe..dec8dba5c41 100644 --- a/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml +++ b/.github/workflows/cluster_endtoend_xb_backup_mysql57.yml @@ -53,13 +53,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -75,7 +77,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -107,9 +109,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/cluster_endtoend_xb_recovery.yml b/.github/workflows/cluster_endtoend_xb_recovery.yml index 3fbe34b0569..f00078d92de 100644 --- a/.github/workflows/cluster_endtoend_xb_recovery.yml +++ b/.github/workflows/cluster_endtoend_xb_recovery.yml @@ -49,13 +49,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -71,7 +73,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml b/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml index aaa2b034105..413cfca1622 100644 --- a/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml +++ b/.github/workflows/cluster_endtoend_xb_recovery_mysql57.yml @@ -53,13 +53,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -75,7 +77,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -107,9 +109,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 00000000000..817d9b4135e --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,119 @@ +name: Code Coverage +on: [push, pull_request] +concurrency: + group: format('{0}-{1}', ${{ github.ref }}, 'Code Coverage') + cancel-in-progress: true + +permissions: read-all + +jobs: + test: + name: Code Coverage + runs-on: gh-hosted-runners-16cores-1 + + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Check for changes in files relevant to code coverage + uses: dorny/paths-filter@v3.0.1 + id: changes + with: + token: '' + filters: | + changed_files: + - .github/workflows/codecov.yml + - 'go/**' + - go.mod + - go.sum + - Makefile + + - name: Set up Go + if: steps.changes.outputs.changed_files == 'true' + uses: actions/setup-go@v4 + with: + go-version: 1.22.0 + + - name: Set up python + if: steps.changes.outputs.changed_files == 'true' + uses: actions/setup-python@v4 + + - name: Tune the OS + if: steps.changes.outputs.changed_files == 'true' + run: | + sudo sysctl -w net.ipv4.ip_local_port_range="22768 65535" + # Increase the asynchronous non-blocking I/O. More information at https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_use_native_aio + echo "fs.aio-max-nr = 1048576" | sudo tee -a /etc/sysctl.conf + sudo sysctl -p /etc/sysctl.conf + + - name: Get dependencies + if: steps.changes.outputs.changed_files == 'true' + run: | + export DEBIAN_FRONTEND="noninteractive" + sudo apt-get update + + # Uninstall any previously installed MySQL first + sudo systemctl stop apparmor + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean + sudo deluser mysql + sudo rm -rf /var/lib/mysql + sudo rm -rf /etc/mysql + + # Get key to latest MySQL repo + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + + # mysql80 + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb + echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections + sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* + sudo apt-get update + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client + + sudo apt-get install -y make unzip g++ curl git wget ant openjdk-11-jdk eatmydata + sudo service mysql stop + sudo bash -c "echo '/usr/sbin/mysqld { }' > /etc/apparmor.d/usr.sbin.mysqld" # https://bugs.launchpad.net/ubuntu/+source/mariadb-10.1/+bug/1806263 + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld || echo "could not remove mysqld profile" + + mkdir -p dist bin + curl -L https://github.com/coreos/etcd/releases/download/v3.3.10/etcd-v3.3.10-linux-amd64.tar.gz | tar -zxC dist + mv dist/etcd-v3.3.10-linux-amd64/{etcd,etcdctl} bin/ + + go mod download + go install golang.org/x/tools/cmd/goimports@latest + + - name: Run make tools + if: steps.changes.outputs.changed_files == 'true' + run: | + make tools + + - name: Run unit tests and generate code coverage reports + if: steps.changes.outputs.changed_files == 'true' + timeout-minutes: 45 + run: | + set -exo pipefail + # We set the VTDATAROOT to the /tmp folder to reduce the file path of mysql.sock file + # which musn't be more than 107 characters long. + export VTDATAROOT="/tmp/" + + export NOVTADMINBUILD=1 + + # Exclude endtoend tests from the coverage report. + # TODO: figure out how best to include our endtoend tests in the coverage report. + rm -rf go/test/endtoend go/*/endtoend go/vt/*/endtoend go/cmd/vttestserver + + eatmydata -- make unit_test_cover + + # Restore the files we deleted as codecov tries to fix their paths. + git reset --hard HEAD + + - name: Upload coverage reports to codecov.io + if: steps.changes.outputs.changed_files == 'true' + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + verbose: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql_analysis.yml b/.github/workflows/codeql_analysis.yml index 8bafc62213a..74d9885c7ed 100644 --- a/.github/workflows/codeql_analysis.yml +++ b/.github/workflows/codeql_analysis.yml @@ -29,6 +29,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.22.0 + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -41,11 +46,6 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: 1.21.3 - - name: Get base dependencies run: | sudo DEBIAN_FRONTEND="noninteractive" apt-get update @@ -58,8 +58,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 52c90038680..202af3a1575 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Setup node uses: actions/setup-node@v3 diff --git a/.github/workflows/docker_test_cluster_10.yml b/.github/workflows/docker_test_cluster_10.yml index 3ff9a2a6e74..155057bee3b 100644 --- a/.github/workflows/docker_test_cluster_10.yml +++ b/.github/workflows/docker_test_cluster_10.yml @@ -31,7 +31,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -54,7 +54,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/docker_test_cluster_25.yml b/.github/workflows/docker_test_cluster_25.yml index e01caf200b1..24ba24bd2d7 100644 --- a/.github/workflows/docker_test_cluster_25.yml +++ b/.github/workflows/docker_test_cluster_25.yml @@ -31,7 +31,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -54,7 +54,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/e2e_race.yml b/.github/workflows/e2e_race.yml index 0d773d936e4..bfedef56c80 100644 --- a/.github/workflows/e2e_race.yml +++ b/.github/workflows/e2e_race.yml @@ -30,7 +30,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -52,7 +52,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -63,9 +63,9 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' run: | # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/endtoend.yml b/.github/workflows/endtoend.yml index 1c0b5f00342..7c8ac2bfefa 100644 --- a/.github/workflows/endtoend.yml +++ b/.github/workflows/endtoend.yml @@ -30,7 +30,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -52,7 +52,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/local_example.yml b/.github/workflows/local_example.yml index 348e191abff..76b062c4f22 100644 --- a/.github/workflows/local_example.yml +++ b/.github/workflows/local_example.yml @@ -34,7 +34,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -57,7 +57,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - uses: actions/setup-node@v3 if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' @@ -75,9 +75,9 @@ jobs: run: | if [ ${{matrix.os}} = "ubuntu-22.04" ]; then # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/region_example.yml b/.github/workflows/region_example.yml index 5d58a6bb5bf..f6c009eabfc 100644 --- a/.github/workflows/region_example.yml +++ b/.github/workflows/region_example.yml @@ -34,7 +34,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -57,7 +57,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - uses: actions/setup-node@v3 if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.examples == 'true' @@ -75,9 +75,9 @@ jobs: run: | if [ ${{matrix.os}} = "ubuntu-22.04" ]; then # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/static_checks_etc.yml b/.github/workflows/static_checks_etc.yml index 34714d00256..8d00930cf75 100644 --- a/.github/workflows/static_checks_etc.yml +++ b/.github/workflows/static_checks_etc.yml @@ -33,9 +33,16 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + - name: Run FOSSA scan and upload build data + uses: fossa-contrib/fossa-action@v3 + with: + # This is a push-only API token: https://github.com/fossa-contrib/fossa-action#push-only-api-token + fossa-api-key: f62c11ef0c249fef239947f01279aa0f + github-token: ${{ github.token }} + - name: Check for changes in Go files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -97,12 +104,16 @@ jobs: - 'changelog/**' - './go/tools/releases/**' - '.github/workflows/static_checks_etc.yml' + workflows: + - '.github/**' + - 'Makefile' + - 'test/ci_workflow_gen.go' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && (steps.changes.outputs.go_files == 'true' || steps.changes.outputs.parser_changes == 'true' || steps.changes.outputs.proto_changes == 'true') uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.go_files == 'true' @@ -162,7 +173,7 @@ jobs: - name: Install golangci-lint if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.go_files == 'true' - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.0 - name: Clean Env if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.go_files == 'true' @@ -214,3 +225,18 @@ jobs: echo "$output" echo "" exit 1 + + - name: Check make generate_ci_workflows + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + set -e + make generate_ci_workflows + output=$(git status -s) + if [ -z "${output}" ]; then + exit 0 + fi + echo 'Please run `make generate_ci_workflows`, commit and push again.' + echo 'Running `make generate_ci_workflows` on CI yields the following changes:' + echo "$output" + echo "" + exit 1 diff --git a/.github/workflows/unit_race.yml b/.github/workflows/unit_race.yml index 80121299139..f0059511357 100644 --- a/.github/workflows/unit_race.yml +++ b/.github/workflows/unit_race.yml @@ -35,7 +35,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -57,7 +57,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' diff --git a/.github/workflows/unit_test_mysql57.yml b/.github/workflows/unit_test_mysql57.yml index 5c5b9c2a206..e4567d39085 100644 --- a/.github/workflows/unit_test_mysql57.yml +++ b/.github/workflows/unit_test_mysql57.yml @@ -49,7 +49,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -71,7 +71,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' @@ -101,10 +101,10 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # mysql57 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/.github/workflows/unit_test_mysql80.yml b/.github/workflows/unit_test_mysql80.yml index 0427ef18158..1e752bf431b 100644 --- a/.github/workflows/unit_test_mysql80.yml +++ b/.github/workflows/unit_test_mysql80.yml @@ -49,7 +49,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -71,7 +71,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' @@ -101,10 +101,10 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # mysql80 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/.github/workflows/update_golang_dependencies.yml b/.github/workflows/update_golang_dependencies.yml new file mode 100644 index 00000000000..fbd60614850 --- /dev/null +++ b/.github/workflows/update_golang_dependencies.yml @@ -0,0 +1,56 @@ +name: Update Golang Dependencies + +on: + schedule: + - cron: "0 0 1,15 * *" # Runs every month on the 1st and 15th days at midnight UTC + workflow_dispatch: + +permissions: read-all + +jobs: + update_golang_deps: + if: github.repository == 'vitessio/vitess' + permissions: + contents: write + pull-requests: write + name: Update Golang Dependencies + runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.22.0 + + - name: Check out code + uses: actions/checkout@v3 + with: + ref: main + + - name: Upgrade the Golang Dependencies + id: detect-and-update + run: | + go get -u ./... + + output=$(git status -s) + if [ -z "${output}" ]; then + exit 0 + fi + + go mod tidy + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + branch: "upgrade-go-deps-on-main" + commit-message: "upgrade go deps" + signoff: true + delete-branch: true + title: "Upgrade the Golang Dependencies" + body: | + This Pull Request updates all the Golang dependencies to their latest version using `go get -u ./...`. + base: main + labels: | + go + dependencies + Component: General + Type: Internal Cleanup diff --git a/.github/workflows/update_golang_version.yml b/.github/workflows/update_golang_version.yml index a30ba27a5a7..30320d94c20 100644 --- a/.github/workflows/update_golang_version.yml +++ b/.github/workflows/update_golang_version.yml @@ -15,14 +15,14 @@ jobs: pull-requests: write strategy: matrix: - branch: [ main, release-18.0, release-17.0, release-16.0, release-15.0 ] + branch: [ main, release-19.0, release-18.0, release-17.0, release-16.0 ] name: Update Golang Version runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Check out code uses: actions/checkout@v3 diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml index 9532995d49c..c0fd1cbf829 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing - Backups - E2E +name: Backups - E2E - Upgrade Downgrade Testing on: push: pull_request: @@ -10,33 +10,10 @@ concurrency: permissions: read-all jobs: - get_previous_release: - if: always() - name: Get Previous Release - Backups - E2E - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - upgrade_downgrade_test_e2e: timeout-minutes: 60 - if: always() && needs.get_previous_release.result == 'success' name: Run Upgrade Downgrade Test - Backups - E2E runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -59,10 +36,20 @@ jobs: - name: Check out commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + if: steps.skip-workflow.outputs.skip-workflow == 'false' + id: output-previous-release-ref + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -85,7 +72,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -117,11 +104,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Check out other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Check out other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml index cc8e3afb42a..1ea62e10b09 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing - Backups - E2E - Next Release +name: Backups - E2E - Next Release - Upgrade Downgrade Testing on: push: pull_request: @@ -10,33 +10,11 @@ concurrency: permissions: read-all jobs: - get_next_release: - if: always() - name: Get Latest Release - Backups - E2E - Next Release - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test_e2e: timeout-minutes: 60 - if: always() && needs.get_next_release.result == 'success' name: Run Upgrade Downgrade Test - Backups - E2E - Next Release runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -46,6 +24,18 @@ jobs: exit 1 fi + - name: Check out commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -53,19 +43,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - name: Check out commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -88,7 +74,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -120,11 +106,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Check out other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Check out other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual.yml b/.github/workflows/upgrade_downgrade_test_backups_manual.yml index 6789dda2067..aab3d1aee00 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing - Backups - Manual +name: Backups - Manual - Upgrade Downgrade Testing on: push: pull_request: @@ -10,34 +10,12 @@ concurrency: permissions: read-all jobs: - get_previous_release: - if: always() - name: Get Previous Release - Backups - Manual - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT # This job usually execute in ± 20 minutes upgrade_downgrade_test_manual: timeout-minutes: 40 - if: always() && (needs.get_previous_release.result == 'success') name: Run Upgrade Downgrade Test - Backups - Manual runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -61,10 +39,20 @@ jobs: - name: Checkout to commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-previous-release-ref + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -82,12 +70,13 @@ jobs: - 'config/**' - 'bootstrap.sh' - '.github/workflows/upgrade_downgrade_test_backups_manual.yml' + - 'examples/**' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -112,8 +101,8 @@ jobs: sudo rm -rf /etc/mysql # Install MySQL 8.0 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -137,11 +126,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Checkout to the other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Checkout to the other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml index 0120571a78e..6e8cb842102 100644 --- a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing - Backups - Manual - Next Release +name: Backups - Manual - Next Release - Upgrade Downgrade Testing on: push: pull_request: @@ -10,34 +10,12 @@ concurrency: permissions: read-all jobs: - get_next_release: - if: always() - name: Get Previous Release - Backups - Manual - Next Release - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT # This job usually execute in ± 20 minutes upgrade_downgrade_test_manual: timeout-minutes: 40 - if: always() && (needs.get_next_release.result == 'success') name: Run Upgrade Downgrade Test - Backups - Manual - Next Release runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -47,6 +25,19 @@ jobs: exit 1 fi + # Checkout to this build's commit + - name: Checkout to commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -54,20 +45,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - # Checkout to this build's commit - - name: Checkout to commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -85,12 +71,13 @@ jobs: - 'config/**' - 'bootstrap.sh' - '.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml' + - 'examples/**' - name: Set up Go if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -115,8 +102,8 @@ jobs: sudo rm -rf /etc/mysql # Install MySQL 8.0 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -140,11 +127,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Checkout to the other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Checkout to the other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml index a3dc81f3723..00dab6edccc 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Query Serving (Queries) +name: Query Serving (Queries) - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtgate, vttablet, etc) built on different versions. jobs: - get_previous_release: - if: always() - name: Get Previous Release - Query Serving (Queries) - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_previous_release.result == 'success') name: Run Upgrade Downgrade Test - Query Serving (Queries) runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -61,10 +39,20 @@ jobs: - name: Check out commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-previous-release-ref + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -87,7 +75,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -111,8 +99,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -135,11 +123,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Check out other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Check out other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml index 923c766e377..a29df51f57c 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Query Serving (Queries) Next Release +name: Query Serving (Queries) Next Release - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtgate, vttablet, etc) built on different versions. jobs: - get_next_release: - if: always() - name: Get Latest Release - Query Serving (Queries) Next Release - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_next_release.result == 'success') name: Run Upgrade Downgrade Test - Query Serving (Queries) Next Release runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -48,6 +26,18 @@ jobs: exit 1 fi + - name: Check out commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -55,19 +45,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - name: Check out commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -90,7 +76,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -114,8 +100,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -138,11 +124,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Check out other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Check out other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml index 14c8afaf87f..86a40081d56 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Query Serving (Schema) +name: Query Serving (Schema) - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtgate, vttablet, etc) built on different versions. jobs: - get_previous_release: - if: always() - name: Get Previous Release - Query Serving (Schema) - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_previous_release.result == 'success') name: Run Upgrade Downgrade Test - Query Serving (Schema) runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -61,10 +39,20 @@ jobs: - name: Check out commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-previous-release-ref + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -87,7 +75,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -111,8 +99,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -135,11 +123,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Check out other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Check out other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml index f22ece10010..59fba0a3f9f 100644 --- a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml +++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Query Serving (Schema) Next Release +name: Query Serving (Schema) Next Release - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtgate, vttablet, etc) built on different versions. jobs: - get_next_release: - if: always() - name: Get Latest Release - Query Serving (Schema) Next Release - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_next_release.result == 'success') name: Run Upgrade Downgrade Test - Query Serving (Schema) Next Release runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -48,6 +26,18 @@ jobs: exit 1 fi + - name: Check out commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -55,19 +45,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - name: Check out commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -90,7 +76,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -114,8 +100,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -138,11 +124,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Check out other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Check out other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml index 82d6f267856..c49b0b85fae 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vtctl.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Reparent New Vtctl +name: Reparent New Vtctl - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtctl, vttablet, etc) built on different versions. jobs: - get_next_release: - if: always() - name: Get Latest Release - Reparent New Vtctl - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_next_release.result == 'success') name: Run Upgrade Downgrade Test - Reparent New Vtctl runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -48,6 +26,18 @@ jobs: exit 1 fi + - name: Check out commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -55,19 +45,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - name: Check out commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -90,7 +76,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -114,8 +100,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -138,11 +124,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Check out other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Check out other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml index c5b6c964124..3f6fee94d7e 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Reparent New VTTablet +name: Reparent New VTTablet - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtctl, vttablet, etc) built on different versions. jobs: - get_next_release: - if: always() - name: Get Latest Release - Reparent New VTTablet - runs-on: gh-hosted-runners-16cores-1 - outputs: - next_release: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-next-release-ref - run: | - next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $next_release_ref - echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_next_release.result == 'success') name: Run Upgrade Downgrade Test - Reparent New VTTablet runs-on: gh-hosted-runners-16cores-1 - needs: - - get_next_release steps: - name: Skip CI @@ -48,6 +26,18 @@ jobs: exit 1 fi + - name: Check out commit's code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-next-release-ref + run: | + next_release_ref=$(./tools/get_next_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $next_release_ref + echo "next_release_ref=${next_release_ref}" >> $GITHUB_OUTPUT + - name: Check if workflow needs to be skipped id: skip-workflow run: | @@ -55,19 +45,15 @@ jobs: if [[ "${{github.event.pull_request}}" == "" ]] && [[ "${{github.ref}}" != "refs/heads/main" ]] && [[ ! "${{github.ref}}" =~ ^refs/heads/release-[0-9]+\.[0-9]$ ]] && [[ ! "${{github.ref}}" =~ "refs/tags/.*" ]]; then skip='true' fi - if [[ "${{needs.get_next_release.outputs.next_release}}" == "" ]]; then + if [[ "${{steps.output-next-release-ref.outputs.next_release_ref}}" == "" ]]; then skip='true' fi echo Skip ${skip} echo "skip-workflow=${skip}" >> $GITHUB_OUTPUT - - name: Check out commit's code - if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: actions/checkout@v3 - - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -90,7 +76,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -114,8 +100,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -138,11 +124,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the next release of Vitess - - name: Check out other version's code (${{ needs.get_next_release.outputs.next_release }}) + - name: Check out other version's code (${{ steps.output-next-release-ref.outputs.next_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_next_release.outputs.next_release }} + ref: ${{ steps.output-next-release-ref.outputs.next_release_ref }} - name: Get dependencies for the next release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml index c4391efdef5..c2e0349f3fb 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vtctl.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Reparent Old Vtctl +name: Reparent Old Vtctl - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtctl, vttablet, etc) built on different versions. jobs: - get_previous_release: - if: always() - name: Get Previous Release - Reparent Old Vtctl - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_previous_release.result == 'success') name: Run Upgrade Downgrade Test - Reparent Old Vtctl runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -61,10 +39,20 @@ jobs: - name: Check out commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-previous-release-ref + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -87,7 +75,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -111,8 +99,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -135,11 +123,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Check out other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Check out other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml index f3ffcaa2d17..2392d5842e3 100644 --- a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml +++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml @@ -1,4 +1,4 @@ -name: Upgrade Downgrade Testing Reparent Old VTTablet +name: Reparent Old VTTablet - Upgrade Downgrade Testing on: push: pull_request: @@ -13,32 +13,10 @@ permissions: read-all # (vtctl, vttablet, etc) built on different versions. jobs: - get_previous_release: - if: always() - name: Get Previous Release - Reparent Old VTTablet - runs-on: gh-hosted-runners-16cores-1 - outputs: - previous_release: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - - steps: - - name: Check out to HEAD - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set output with latest release branch - id: output-previous-release-ref - run: | - previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) - echo $previous_release_ref - echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT upgrade_downgrade_test: - if: always() && (needs.get_previous_release.result == 'success') name: Run Upgrade Downgrade Test - Reparent Old VTTablet runs-on: gh-hosted-runners-16cores-1 - needs: - - get_previous_release steps: - name: Skip CI @@ -61,10 +39,20 @@ jobs: - name: Check out commit's code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set output with latest release branch + id: output-previous-release-ref + if: steps.skip-workflow.outputs.skip-workflow == 'false' + run: | + previous_release_ref=$(./tools/get_previous_release.sh ${{github.base_ref}} ${{github.ref}}) + echo $previous_release_ref + echo "previous_release_ref=${previous_release_ref}" >> $GITHUB_OUTPUT - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -87,7 +75,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -111,8 +99,8 @@ jobs: sudo rm -rf /var/lib/mysql sudo rm -rf /etc/mysql # Install mysql80 - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update @@ -135,11 +123,11 @@ jobs: sudo apt-get install -y percona-xtrabackup-24 # Checkout to the last release of Vitess - - name: Check out other version's code (${{ needs.get_previous_release.outputs.previous_release }}) + - name: Check out other version's code (${{ steps.output-previous-release-ref.outputs.previous_release_ref }}) if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/checkout@v3 with: - ref: ${{ needs.get_previous_release.outputs.previous_release }} + ref: ${{ steps.output-previous-release-ref.outputs.previous_release_ref }} - name: Get dependencies for the last release if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/.golangci.yml b/.golangci.yml index 9c674953a76..ce554ad99ba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,6 +11,7 @@ linters-settings: disable: # not supported when using Generics in 1.18 - nilness - unusedwrite + - loopclosure # fixed in go1.22 linters: disable-all: true diff --git a/Makefile b/Makefile index 625a28baac0..beefc35927f 100644 --- a/Makefile +++ b/Makefile @@ -214,10 +214,14 @@ e2e_test: build go test $(VT_GO_PARALLEL) ./go/.../endtoend/... # Run the code coverage tools, compute aggregate. -# If you want to improve in a directory, run: -# go test -coverprofile=coverage.out && go tool cover -html=coverage.out -unit_test_cover: build - go test $(VT_GO_PARALLEL) -cover ./go/... | misc/parse_cover.py +unit_test_cover: build dependency_check demo + source build.env + go test $(VT_GO_PARALLEL) -count=1 -failfast -covermode=atomic -coverpkg=vitess.io/vitess/go/... -coverprofile=coverage.out ./go/... + # Handle go tool cover failures due to not handling `//line` directives, which + # the goyacc compiler adds to the generated parser in sql.go. See: + # https://github.com/golang/go/issues/41222 + sed -i'' -e '/^vitess.io\/vitess\/go\/vt\/sqlparser\/yaccpar/d' coverage.out + go tool $(VT_GO_PARALLEL) cover -html=coverage.out unit_test_race: build dependency_check tools/unit_test_race.sh @@ -278,7 +282,7 @@ $(PROTO_GO_OUTS): minimaltools install_protoc-gen-go proto/*.proto # This rule builds the bootstrap images for all flavors. DOCKER_IMAGES_FOR_TEST = mysql57 mysql80 percona57 percona80 DOCKER_IMAGES = common $(DOCKER_IMAGES_FOR_TEST) -BOOTSTRAP_VERSION=24 +BOOTSTRAP_VERSION=28 ensure_bootstrap_version: find docker/ -type f -exec sed -i "s/^\(ARG bootstrap_version\)=.*/\1=${BOOTSTRAP_VERSION}/" {} \; sed -i 's/\(^.*flag.String(\"bootstrap-version\",\) *\"[^\"]\+\"/\1 \"${BOOTSTRAP_VERSION}\"/' test.go @@ -324,13 +328,6 @@ $(DOCKER_BASE_TARGETS): docker_base_%: docker_base_all: docker_base $(DOCKER_BASE_TARGETS) -DOCKER_MYSQL_VERSIONS = 8.0.30 8.0.34 -docker_mysql: - for i in $(DOCKER_MYSQL_VERSIONS); do echo "building vitess/mysql:$$i"; ${call build_docker_image,docker/mysql/Dockerfile.$$i,vitess/mysql:$$i} || exit 1; done - -docker_mysql_push: - for i in $(DOCKER_MYSQL_VERSIONS); do echo "pushing vitess/mysql:$$i"; docker push vitess/mysql:$$i || exit 1; done - docker_lite: ${call build_docker_image,docker/lite/Dockerfile,vitess/lite} diff --git a/README.md b/README.md index 6f021141aca..adc8cd93c19 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.vitess/vitess-jdbc/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.vitess/vitess-jdbc) -[![codebeat badge](https://codebeat.co/badges/51c9a056-1103-4522-9a9c-dc623821ea87)](https://codebeat.co/projects/github-com-youtube-vitess) +[![Coverage Status](https://codecov.io/gh/vitessio/vitess/branch/main/graph/badge.svg)](https://app.codecov.io/gh/vitessio/vitess/tree/main) [![Go Report Card](https://goreportcard.com/badge/vitess.io/vitess)](https://goreportcard.com/report/vitess.io/vitess) -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvitessio%2Fvitess.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvitessio%2Fvitess?ref=badge_shield) +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B162%2Fvitess.svg?type=shield&issueType=license)](https://app.fossa.com/projects/custom%2B162%2Fvitess?ref=badge_shield&issueType=license) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1724/badge)](https://bestpractices.coreinfrastructure.org/projects/1724) # Vitess @@ -19,11 +19,10 @@ since 2011, and has grown to encompass tens of thousands of MySQL nodes. For more about Vitess, please visit [vitess.io](https://vitess.io). -Vitess has a growing community. You can view the list of adopters -[here](https://github.com/vitessio/vitess/blob/main/ADOPTERS.md). +Vitess has a growing community. [View the list of adopters](https://github.com/vitessio/vitess/blob/main/ADOPTERS.md). ## Reporting a Problem, Issue, or Bug -To report a problem, the best way to get attention is to create a GitHub [issue](.https://github.com/vitessio/vitess/issues ) using proper severity level based on this [guide](https://github.com/vitessio/vitess/blob/main/SEVERITY.md). +To report a problem, create a [GitHub issue](https://github.com/vitessio/vitess/issues). For topics that are better discussed live, please join the [Vitess Slack](https://vitess.io/slack) workspace. You may post any questions on the #general channel or join some of the special-interest channels. @@ -40,11 +39,11 @@ See [Security](SECURITY.md) for a full outline of the security process. ### Security Audit -A third party security audit was performed by Cure53. You can see the full report [here](doc/VIT-01-report.pdf). +A third party security audit was performed by ADA Logics. [Read the full report](doc/VIT-03-report-security-audit.pdf). ## License Unless otherwise noted, the Vitess source files are distributed under the Apache Version 2.0 license found in the LICENSE file. -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvitessio%2Fvitess.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvitessio%2Fvitess?ref=badge_large) +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B162%2Fvitess.svg?type=large&issueType=license)](https://app.fossa.com/projects/custom%2B162%2Fvitess?ref=badge_large&issueType=license) diff --git a/build.env b/build.env index 5986ee247b0..fa8ecb26b43 100755 --- a/build.env +++ b/build.env @@ -17,7 +17,7 @@ source ./tools/shell_functions.inc go version >/dev/null 2>&1 || fail "Go is not installed or is not in \$PATH. See https://vitess.io/contributing/build-from-source for install instructions." -goversion_min 1.21.3 || echo "Go version reported: `go version`. Version 1.21.3+ recommended. See https://vitess.io/contributing/build-from-source for install instructions." +goversion_min 1.22.0 || echo "Go version reported: `go version`. Version 1.22.0+ recommended. See https://vitess.io/contributing/build-from-source for install instructions." mkdir -p dist mkdir -p bin @@ -31,7 +31,7 @@ export PROTOC_VER=21.3 export ZK_VER=${ZK_VERSION:-3.8.0} export ETCD_VER=v3.5.6 export CONSUL_VER=1.11.4 -export TOXIPROXY_VER=v2.5.0 +export TOXIPROXY_VER=v2.7.0 mkdir -p "$VTDATAROOT" diff --git a/changelog/16.0/16.0.6/changelog.md b/changelog/16.0/16.0.6/changelog.md new file mode 100644 index 00000000000..959bf2bd570 --- /dev/null +++ b/changelog/16.0/16.0.6/changelog.md @@ -0,0 +1,43 @@ +# Changelog of Vitess v16.0.6 + +### Bug fixes +#### CLI + * [release-16.0] Fix anonymous paths in cobra code-gen (#14185) [#14236](https://github.com/vitessio/vitess/pull/14236) +#### Examples + * [release-16.0] examples: fix flag syntax for zkctl (#14469) [#14485](https://github.com/vitessio/vitess/pull/14485) +#### Online DDL + * [Release 16.0]: Online DDL: timeouts for all gRPC calls (#14182) [#14191](https://github.com/vitessio/vitess/pull/14191) + * [release-16.0] schemadiff: fix missing `DROP CONSTRAINT` in duplicate/redundant constraints scenario. (#14387) [#14389](https://github.com/vitessio/vitess/pull/14389) +#### Query Serving + * [release-16.0] Rewrite `USING` to `ON` condition for joins (#13931) [#13940](https://github.com/vitessio/vitess/pull/13940) + * [release-16.0] Make column resolution closer to MySQL (#14426) [#14428](https://github.com/vitessio/vitess/pull/14428) + * [release-16.0] vtgate/engine: Fix race condition in join logic (#14435) [#14439](https://github.com/vitessio/vitess/pull/14439) + * [release-16.0] Ensure hexval and int don't share BindVar after Normalization (#14451) [#14477](https://github.com/vitessio/vitess/pull/14477) +#### Throttler + * [release-16.0] Tablet throttler: fix race condition by removing goroutine call (#14179) [#14200](https://github.com/vitessio/vitess/pull/14200) +#### VReplication + * [release-16.0] VDiff: wait for shard streams of one table diff to complete for before starting that of the next table (#14345) [#14380](https://github.com/vitessio/vitess/pull/14380) +### CI/Build +#### General + * [release-16.0] Upgrade the Golang version to `go1.20.9` [#14194](https://github.com/vitessio/vitess/pull/14194) + * [release-16.0] Upgrade the Golang version to `go1.20.10` [#14228](https://github.com/vitessio/vitess/pull/14228) +#### Online DDL + * [release-16.0] OnlineDDL: reduce vrepl_stress workload in forks (#14302) [#14347](https://github.com/vitessio/vitess/pull/14347) +### Dependabot +#### General + * [release-16.0] Bump github.com/cyphar/filepath-securejoin from 0.2.3 to 0.2.4 (#14239) [#14251](https://github.com/vitessio/vitess/pull/14251) + * [release-16.0] Bump golang.org/x/net from 0.14.0 to 0.17.0 (#14260) [#14262](https://github.com/vitessio/vitess/pull/14262) + * [release-16.0] Bump google.golang.org/grpc from 1.55.0-dev to 1.59.0 (#14364) [#14496](https://github.com/vitessio/vitess/pull/14496) +#### VTAdmin + * [release-16.0] Bump postcss from 8.4.21 to 8.4.31 in /web/vtadmin (#14173) [#14256](https://github.com/vitessio/vitess/pull/14256) + * [release-16.0] Bump @babel/traverse from 7.21.4 to 7.23.2 in /web/vtadmin (#14304) [#14306](https://github.com/vitessio/vitess/pull/14306) +### Enhancement +#### Build/CI + * [release-16.0] Automatic approval of `vitess-bot` clean backports (#14352) [#14355](https://github.com/vitessio/vitess/pull/14355) +### Release +#### General + * Code freeze of release-16.0 [#14409](https://github.com/vitessio/vitess/pull/14409) +### Testing +#### Query Serving + * [release-16.0] vtgate: Allow additional errors in warnings test (#14461) [#14463](https://github.com/vitessio/vitess/pull/14463) + diff --git a/changelog/16.0/16.0.6/release_notes.md b/changelog/16.0/16.0.6/release_notes.md new file mode 100644 index 00000000000..881ed26b348 --- /dev/null +++ b/changelog/16.0/16.0.6/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v16.0.6 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/16.0/16.0.6/changelog.md). + +The release includes 21 merged Pull Requests. + +Thanks to all our contributors: @app/github-actions, @app/vitess-bot, @harshit-gangal, @shlomi-noach + diff --git a/changelog/16.0/16.0.7/changelog.md b/changelog/16.0/16.0.7/changelog.md new file mode 100644 index 00000000000..d4565e2f9ba --- /dev/null +++ b/changelog/16.0/16.0.7/changelog.md @@ -0,0 +1,42 @@ +# Changelog of Vitess v16.0.7 + +### Bug fixes +#### Build/CI + * [release-16.0] Update create_release.sh (#14492) [#14514](https://github.com/vitessio/vitess/pull/14514) +#### Cluster management + * [release-16.0] Fix Panic in PRS due to a missing nil check (#14656) [#14674](https://github.com/vitessio/vitess/pull/14674) +#### Query Serving + * [release-16.0] expression rewriting: enable more rewrites and limit CNF rewrites (#14560) [#14574](https://github.com/vitessio/vitess/pull/14574) + * [release-16.0] fix concurrency on stream execute engine primitives (#14586) [#14590](https://github.com/vitessio/vitess/pull/14590) + * [16.0] bug fix: stop all kinds of expressions from cnf-exploding [#14595](https://github.com/vitessio/vitess/pull/14595) + * [release-16.0] tabletserver: do not consolidate streams on primary tablet when consolidator mode is `notOnPrimary` (#14332) [#14683](https://github.com/vitessio/vitess/pull/14683) +#### VReplication + * Revert "[release-16.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612)" [#14743](https://github.com/vitessio/vitess/pull/14743) + * [release-16.0] VReplication: Update singular workflow in traffic switcher (#14826) [#14827](https://github.com/vitessio/vitess/pull/14827) +### CI/Build +#### Build/CI + * [release-16.0] Update MySQL apt package and GPG signature (#14785) [#14790](https://github.com/vitessio/vitess/pull/14790) +#### Docker + * [release-16.0] Build and push Docker Images from GitHub Actions [#14513](https://github.com/vitessio/vitess/pull/14513) +#### General + * [release-16.0] Upgrade the Golang version to `go1.20.12` [#14691](https://github.com/vitessio/vitess/pull/14691) +### Dependabot +#### General + * [release-16.0] build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#14814) [#14818](https://github.com/vitessio/vitess/pull/14818) +### Enhancement +#### Build/CI + * [release-16.0] Add step to static check to ensure consistency of GHA workflows (#14724) [#14725](https://github.com/vitessio/vitess/pull/14725) +### Internal Cleanup +#### TabletManager + * [release-16.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612) [#14620](https://github.com/vitessio/vitess/pull/14620) +### Performance +#### Query Serving + * [release-16.0] vindexes: fix pooled collator buffer memory leak (#14621) [#14622](https://github.com/vitessio/vitess/pull/14622) +### Release +#### General + * [release-16.0] Code Freeze for `v16.0.7` [#14808](https://github.com/vitessio/vitess/pull/14808) +### Testing +#### Backup and Restore + * [release-16.0] Add a retry to remove the vttablet directory during upgrade/downgrade backup tests (#14753) [#14756](https://github.com/vitessio/vitess/pull/14756) + * [release-16.0] Backup flaky test [#14819](https://github.com/vitessio/vitess/pull/14819) + diff --git a/changelog/16.0/16.0.7/release_notes.md b/changelog/16.0/16.0.7/release_notes.md new file mode 100644 index 00000000000..4a2b5703d9d --- /dev/null +++ b/changelog/16.0/16.0.7/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v16.0.7 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/16.0/16.0.7/changelog.md). + +The release includes 18 merged Pull Requests. + +Thanks to all our contributors: @GuptaManan100, @app/github-actions, @app/vitess-bot, @deepthi, @frouioui, @harshit-gangal, @maxenglander, @shlomi-noach, @systay + diff --git a/changelog/16.0/README.md b/changelog/16.0/README.md index d45a817ad48..86fd6e15961 100644 --- a/changelog/16.0/README.md +++ b/changelog/16.0/README.md @@ -1,5 +1,13 @@ ## v16.0 The dedicated team for this release can be found [here](team.md). +* **[16.0.7](16.0.7)** + * [Changelog](16.0.7/changelog.md) + * [Release Notes](16.0.7/release_notes.md) + +* **[16.0.6](16.0.6)** + * [Changelog](16.0.6/changelog.md) + * [Release Notes](16.0.6/release_notes.md) + * **[16.0.5](16.0.5)** * [Changelog](16.0.5/changelog.md) * [Release Notes](16.0.5/release_notes.md) diff --git a/changelog/17.0/17.0.4/changelog.md b/changelog/17.0/17.0.4/changelog.md new file mode 100644 index 00000000000..3aba7b735f9 --- /dev/null +++ b/changelog/17.0/17.0.4/changelog.md @@ -0,0 +1,48 @@ +# Changelog of Vitess v17.0.4 + +### Bug fixes +#### CLI + * [release-17.0] Fix anonymous paths in cobra code-gen (#14185) [#14237](https://github.com/vitessio/vitess/pull/14237) +#### Evalengine + * [release-17.0] evalengine: Misc bugs (#14351) [#14353](https://github.com/vitessio/vitess/pull/14353) +#### Examples + * [release-17.0] examples: fix flag syntax for zkctl (#14469) [#14486](https://github.com/vitessio/vitess/pull/14486) +#### General + * [release-17.0] viper: register dynamic config with both disk and live (#14453) [#14454](https://github.com/vitessio/vitess/pull/14454) +#### Online DDL + * [Release 17.0]: Online DDL: timeouts for all gRPC calls (#14182) [#14190](https://github.com/vitessio/vitess/pull/14190) + * [release-17.0] schemadiff: fix missing `DROP CONSTRAINT` in duplicate/redundant constraints scenario. (#14387) [#14390](https://github.com/vitessio/vitess/pull/14390) +#### Query Serving + * [release-17.0] Make column resolution closer to MySQL (#14426) [#14429](https://github.com/vitessio/vitess/pull/14429) + * [release-17.0] vtgate/engine: Fix race condition in join logic (#14435) [#14440](https://github.com/vitessio/vitess/pull/14440) + * [release-17.0] Ensure hexval and int don't share BindVar after Normalization (#14451) [#14478](https://github.com/vitessio/vitess/pull/14478) +#### Throttler + * [release-17.0] Tablet throttler: fix race condition by removing goroutine call (#14179) [#14199](https://github.com/vitessio/vitess/pull/14199) +#### VReplication + * [release-17.0] VDiff: wait for shard streams of one table diff to complete for before starting that of the next table (#14345) [#14381](https://github.com/vitessio/vitess/pull/14381) +### CI/Build +#### General + * [release-17.0] Upgrade the Golang version to `go1.20.9` [#14196](https://github.com/vitessio/vitess/pull/14196) + * [release-17.0] Upgrade the Golang version to `go1.20.10` [#14229](https://github.com/vitessio/vitess/pull/14229) +#### Online DDL + * [release-17.0] OnlineDDL: reduce vrepl_stress workload in forks (#14302) [#14348](https://github.com/vitessio/vitess/pull/14348) +### Dependabot +#### General + * [release-17.0] Bump github.com/cyphar/filepath-securejoin from 0.2.3 to 0.2.4 (#14239) [#14252](https://github.com/vitessio/vitess/pull/14252) + * [release-17.0] Bump golang.org/x/net from 0.14.0 to 0.17.0 (#14260) [#14263](https://github.com/vitessio/vitess/pull/14263) + * [release-17.0] Bump google.golang.org/grpc from 1.55.0-dev to 1.59.0 (#14364) [#14497](https://github.com/vitessio/vitess/pull/14497) +#### VTAdmin + * [release-17.0] Bump postcss from 8.4.21 to 8.4.31 in /web/vtadmin (#14173) [#14257](https://github.com/vitessio/vitess/pull/14257) + * [release-17.0] Bump @babel/traverse from 7.21.4 to 7.23.2 in /web/vtadmin (#14304) [#14307](https://github.com/vitessio/vitess/pull/14307) +### Enhancement +#### Build/CI + * [release-17.0] Automatic approval of `vitess-bot` clean backports (#14352) [#14356](https://github.com/vitessio/vitess/pull/14356) +### Release +#### General + * Code freeze of release-17.0 [#14407](https://github.com/vitessio/vitess/pull/14407) +### Testing +#### Cluster management + * Fix Upgrade downgrade reparent tests in release-17.0 [#14507](https://github.com/vitessio/vitess/pull/14507) +#### Query Serving + * [release-17.0] vtgate: Allow more errors for the warning check (#14421) [#14422](https://github.com/vitessio/vitess/pull/14422) + diff --git a/changelog/17.0/17.0.4/release_notes.md b/changelog/17.0/17.0.4/release_notes.md new file mode 100644 index 00000000000..30d3c9274e9 --- /dev/null +++ b/changelog/17.0/17.0.4/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v17.0.4 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/17.0/17.0.4/changelog.md). + +The release includes 23 merged Pull Requests. + +Thanks to all our contributors: @GuptaManan100, @app/github-actions, @app/vitess-bot, @mattlord, @shlomi-noach + diff --git a/changelog/17.0/17.0.5/changelog.md b/changelog/17.0/17.0.5/changelog.md new file mode 100644 index 00000000000..91078d04d7e --- /dev/null +++ b/changelog/17.0/17.0.5/changelog.md @@ -0,0 +1,49 @@ +# Changelog of Vitess v17.0.5 + +### Bug fixes +#### Build/CI + * [release-17.0] Update create_release.sh (#14492) [#14515](https://github.com/vitessio/vitess/pull/14515) +#### Cluster management + * [release-17.0] Fix Panic in PRS due to a missing nil check (#14656) [#14675](https://github.com/vitessio/vitess/pull/14675) + * Revert "[release-17.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612)" [#14744](https://github.com/vitessio/vitess/pull/14744) +#### Evalengine + * [release-17.0] Fix nullability checks in evalengine (#14556) [#14563](https://github.com/vitessio/vitess/pull/14563) +#### Query Serving + * [release-17.0] expression rewriting: enable more rewrites and limit CNF rewrites (#14560) [#14575](https://github.com/vitessio/vitess/pull/14575) + * [release-17.0] fix concurrency on stream execute engine primitives (#14586) [#14591](https://github.com/vitessio/vitess/pull/14591) + * [17.0] bug fix: stop all kinds of expressions from cnf-exploding [#14594](https://github.com/vitessio/vitess/pull/14594) + * [release-17.0] tabletserver: do not consolidate streams on primary tablet when consolidator mode is `notOnPrimary` (#14332) [#14678](https://github.com/vitessio/vitess/pull/14678) + * Fix accepting bind variables in time related function calls. [#14763](https://github.com/vitessio/vitess/pull/14763) +#### VReplication + * [release-17.0] VReplication: Update singular workflow in traffic switcher (#14826) [#14828](https://github.com/vitessio/vitess/pull/14828) +### CI/Build +#### Build/CI + * [release-17.0] Update MySQL apt package and GPG signature (#14785) [#14791](https://github.com/vitessio/vitess/pull/14791) +#### Docker + * [release-17.0] Build and push Docker Images from GitHub Actions [#14512](https://github.com/vitessio/vitess/pull/14512) +#### General + * [release-17.0] Upgrade the Golang version to `go1.20.11` [#14489](https://github.com/vitessio/vitess/pull/14489) + * [release-17.0] Upgrade the Golang version to `go1.20.12` [#14692](https://github.com/vitessio/vitess/pull/14692) +### Dependabot +#### General + * [release-17.0] build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#14814) [#14816](https://github.com/vitessio/vitess/pull/14816) +#### VTAdmin + * [release-17.0] Bump @adobe/css-tools from 4.3.1 to 4.3.2 in /web/vtadmin (#14654) [#14667](https://github.com/vitessio/vitess/pull/14667) +### Enhancement +#### Build/CI + * [release-17.0] Add step to static check to ensure consistency of GHA workflows (#14724) [#14726](https://github.com/vitessio/vitess/pull/14726) +### Internal Cleanup +#### TabletManager + * [release-17.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612) [#14619](https://github.com/vitessio/vitess/pull/14619) +#### vtctldclient + * [release-17.0] Fix typo for `--cells` flag help description in `ApplyRoutingRules` (#14721) [#14722](https://github.com/vitessio/vitess/pull/14722) +### Performance +#### Query Serving + * [release-17.0] vindexes: fix pooled collator buffer memory leak (#14621) [#14623](https://github.com/vitessio/vitess/pull/14623) +### Release +#### General + * [release-17.0] Code Freeze for `v17.0.5` [#14806](https://github.com/vitessio/vitess/pull/14806) +### Testing +#### Backup and Restore + * [release-17.0] Add a retry to remove the vttablet directory during upgrade/downgrade backup tests (#14753) [#14757](https://github.com/vitessio/vitess/pull/14757) + diff --git a/changelog/17.0/17.0.5/release_notes.md b/changelog/17.0/17.0.5/release_notes.md new file mode 100644 index 00000000000..5f032a06fb5 --- /dev/null +++ b/changelog/17.0/17.0.5/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v17.0.5 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/17.0/17.0.5/changelog.md). + +The release includes 22 merged Pull Requests. + +Thanks to all our contributors: @GuptaManan100, @app/github-actions, @app/vitess-bot, @deepthi, @frouioui, @harshit-gangal, @shlomi-noach, @systay + diff --git a/changelog/17.0/README.md b/changelog/17.0/README.md index dcbba316bdd..767e3bb0939 100644 --- a/changelog/17.0/README.md +++ b/changelog/17.0/README.md @@ -1,4 +1,12 @@ ## v17.0 +* **[17.0.5](17.0.5)** + * [Changelog](17.0.5/changelog.md) + * [Release Notes](17.0.5/release_notes.md) + +* **[17.0.4](17.0.4)** + * [Changelog](17.0.4/changelog.md) + * [Release Notes](17.0.4/release_notes.md) + * **[17.0.3](17.0.3)** * [Changelog](17.0.3/changelog.md) * [Release Notes](17.0.3/release_notes.md) diff --git a/changelog/18.0/18.0.0/summary.md b/changelog/18.0/18.0.0/summary.md index eb2b6692201..35ad018b7bc 100644 --- a/changelog/18.0/18.0.0/summary.md +++ b/changelog/18.0/18.0.0/summary.md @@ -95,6 +95,8 @@ There are 3 foreign key modes now supported in Vitess - 3. `disallow` - In this mode Vitess explicitly disallows any DDL statements that try to create a foreign key constraint. This mode is equivalent to running VTGate with the flag `--foreign_key_mode=disallow`. +In addition to query support, there is a new flag to `MoveTables` called `--atomic-copy` which should be used to import data into Vitess from databases which have foreign keys defined in the schema. + #### Upgrade process After upgrading from v17 to v18, users should specify the correct foreign key mode for all their keyspaces in the VSchema using the new property. diff --git a/changelog/18.0/18.0.1/changelog.md b/changelog/18.0/18.0.1/changelog.md new file mode 100644 index 00000000000..efae252075f --- /dev/null +++ b/changelog/18.0/18.0.1/changelog.md @@ -0,0 +1,40 @@ +# Changelog of Vitess v18.0.1 + +### Bug fixes +#### Backup and Restore + * [release 18.0]: `ReadBinlogFilesTimestamps` backwards compatibility [#14526](https://github.com/vitessio/vitess/pull/14526) +#### Build/CI + * [release-18.0] Update create_release.sh (#14492) [#14516](https://github.com/vitessio/vitess/pull/14516) +#### Evalengine + * [release-18.0] Fix nullability checks in evalengine (#14556) [#14564](https://github.com/vitessio/vitess/pull/14564) +#### Examples + * [release-18.0] examples: fix flag syntax for zkctl (#14469) [#14487](https://github.com/vitessio/vitess/pull/14487) +#### Observability + * [release-18.0] Fix #14414: resilient_server metrics name/prefix logic is inverted, leading to no metrics being recorded (#14415) [#14527](https://github.com/vitessio/vitess/pull/14527) +#### Query Serving + * [release-18.0] Make column resolution closer to MySQL (#14426) [#14430](https://github.com/vitessio/vitess/pull/14430) + * [release-18.0] Bug fix: Use target tablet from health stats cache when checking replication status (#14436) [#14456](https://github.com/vitessio/vitess/pull/14456) + * [release-18.0] Ensure hexval and int don't share BindVar after Normalization (#14451) [#14479](https://github.com/vitessio/vitess/pull/14479) + * [release-18.0] planbuilder bugfix: expose columns through derived tables (#14501) [#14504](https://github.com/vitessio/vitess/pull/14504) + * [release-18.0] expression rewriting: enable more rewrites and limit CNF rewrites (#14560) [#14576](https://github.com/vitessio/vitess/pull/14576) +#### vtctldclient + * [release-18.0] vtctldclient: Apply tablet type filtering for keyspace+shard in GetTablets (#14467) [#14470](https://github.com/vitessio/vitess/pull/14470) +### CI/Build +#### Docker + * [release-18.0] Build and push Docker Images from GitHub Actions [#14511](https://github.com/vitessio/vitess/pull/14511) +### Dependabot +#### General + * [release-18.0] Bump google.golang.org/grpc from 1.55.0-dev to 1.59.0 (#14364) [#14498](https://github.com/vitessio/vitess/pull/14498) +### Documentation +#### Documentation + * [release-18.0] release notes: add FK import to summary (#14518) [#14519](https://github.com/vitessio/vitess/pull/14519) +### Internal Cleanup +#### Query Serving + * [release-18.0] Remove excessive VTGate logging of default planner selection (#14554) [#14561](https://github.com/vitessio/vitess/pull/14561) +### Release +#### General + * [release-18.0] Code Freeze for `v18.0.1` [#14549](https://github.com/vitessio/vitess/pull/14549) +### Testing +#### Query Serving + * [release-18.0] vtgate: Allow additional errors in warnings test (#14461) [#14465](https://github.com/vitessio/vitess/pull/14465) + diff --git a/changelog/18.0/18.0.1/release_notes.md b/changelog/18.0/18.0.1/release_notes.md new file mode 100644 index 00000000000..f6f07d6e652 --- /dev/null +++ b/changelog/18.0/18.0.1/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v18.0.1 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/18.0/18.0.1/changelog.md). + +The release includes 17 merged Pull Requests. + +Thanks to all our contributors: @app/vitess-bot, @frouioui, @harshit-gangal, @shlomi-noach + diff --git a/changelog/18.0/18.0.2/changelog.md b/changelog/18.0/18.0.2/changelog.md new file mode 100644 index 00000000000..c7866efd06a --- /dev/null +++ b/changelog/18.0/18.0.2/changelog.md @@ -0,0 +1,56 @@ +# Changelog of Vitess v18.0.2 + +### Bug fixes +#### Cluster management + * [release-18.0] Fix Panic in PRS due to a missing nil check (#14656) [#14676](https://github.com/vitessio/vitess/pull/14676) + * Revert "[release-18.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612)" [#14742](https://github.com/vitessio/vitess/pull/14742) +#### Evalengine + * [release-18.0] evalengine: Fix the min / max calculation for decimals (#14614) [#14616](https://github.com/vitessio/vitess/pull/14616) +#### Query Serving + * [release-18.0] fix concurrency on stream execute engine primitives (#14586) [#14592](https://github.com/vitessio/vitess/pull/14592) + * [18.0] bug fix: stop all kinds of expressions from cnf-exploding [#14593](https://github.com/vitessio/vitess/pull/14593) + * [release-18.0] bugfix: do not rewrite an expression twice (#14641) [#14643](https://github.com/vitessio/vitess/pull/14643) + * [release-18.0] tabletserver: do not consolidate streams on primary tablet when consolidator mode is `notOnPrimary` (#14332) [#14679](https://github.com/vitessio/vitess/pull/14679) + * [release-18.0] TabletServer: Handle nil targets properly everywhere (#14734) [#14741](https://github.com/vitessio/vitess/pull/14741) +#### VReplication + * [release-18.0] VReplication TableStreamer: Only stream tables in tablestreamer (ignore views) (#14646) [#14649](https://github.com/vitessio/vitess/pull/14649) + * [release-18.0] VDiff: Fix vtctldclient limit bug (#14778) [#14780](https://github.com/vitessio/vitess/pull/14780) + * [release-18.0] Backport: VReplication SwitchWrites: Properly return errors in SwitchWrites #14800 [#14824](https://github.com/vitessio/vitess/pull/14824) + * [release-18.0] VReplication: Update singular workflow in traffic switcher (#14826) [#14829](https://github.com/vitessio/vitess/pull/14829) +### CI/Build +#### Build/CI + * [release-18.0] Update MySQL apt package and GPG signature (#14785) [#14792](https://github.com/vitessio/vitess/pull/14792) +#### General + * [release-18.0] Upgrade the Golang version to `go1.21.5` [#14690](https://github.com/vitessio/vitess/pull/14690) +### Dependabot +#### General + * [release-18.0] build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#14814) [#14817](https://github.com/vitessio/vitess/pull/14817) +#### VTAdmin + * [release-18.0] Bump @adobe/css-tools from 4.3.1 to 4.3.2 in /web/vtadmin (#14654) [#14668](https://github.com/vitessio/vitess/pull/14668) +### Enhancement +#### Backup and Restore + * [release-18.0] increase vtctlclient backupShard command success rate (#14604) [#14639](https://github.com/vitessio/vitess/pull/14639) +#### Build/CI + * [release-18.0] Add step to static check to ensure consistency of GHA workflows (#14724) [#14727](https://github.com/vitessio/vitess/pull/14727) +#### Query Serving + * [release-18.0] planbuilder: push down ordering through filter (#14583) [#14584](https://github.com/vitessio/vitess/pull/14584) +### Internal Cleanup +#### TabletManager + * [release-18.0] Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` (#14612) [#14617](https://github.com/vitessio/vitess/pull/14617) +#### vtctldclient + * [release-18.0] Fix typo for `--cells` flag help description in `ApplyRoutingRules` (#14721) [#14723](https://github.com/vitessio/vitess/pull/14723) +### Performance +#### Query Serving + * vindexes: fix pooled collator buffer memory leak [#14621](https://github.com/vitessio/vitess/pull/14621) +### Regression +#### Query Serving + * [release-18.0] plabuilder: use OR for not in comparisons (#14607) [#14615](https://github.com/vitessio/vitess/pull/14615) + * [release-18.0] fix: insert on duplicate key update missing BindVars (#14728) [#14755](https://github.com/vitessio/vitess/pull/14755) +### Release +#### General + * Back to dev mode after v18.0.1 [#14580](https://github.com/vitessio/vitess/pull/14580) + * [release-18.0] Code Freeze for `v18.0.2` [#14804](https://github.com/vitessio/vitess/pull/14804) +### Testing +#### Backup and Restore + * [release-18.0] Add a retry to remove the vttablet directory during upgrade/downgrade backup tests (#14753) [#14758](https://github.com/vitessio/vitess/pull/14758) + diff --git a/changelog/18.0/18.0.2/release_notes.md b/changelog/18.0/18.0.2/release_notes.md new file mode 100644 index 00000000000..e431e9be6c5 --- /dev/null +++ b/changelog/18.0/18.0.2/release_notes.md @@ -0,0 +1,7 @@ +# Release of Vitess v18.0.2 +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/18.0/18.0.2/changelog.md). + +The release includes 27 merged Pull Requests. + +Thanks to all our contributors: @app/github-actions, @app/vitess-bot, @brendar, @deepthi, @harshit-gangal, @rohit-nayak-ps, @systay + diff --git a/changelog/18.0/README.md b/changelog/18.0/README.md index 97676dc7e39..625ff947c5c 100644 --- a/changelog/18.0/README.md +++ b/changelog/18.0/README.md @@ -1,4 +1,12 @@ ## v18.0 +* **[18.0.2](18.0.2)** + * [Changelog](18.0.2/changelog.md) + * [Release Notes](18.0.2/release_notes.md) + +* **[18.0.1](18.0.1)** + * [Changelog](18.0.1/changelog.md) + * [Release Notes](18.0.1/release_notes.md) + * **[18.0.0](18.0.0)** * [Changelog](18.0.0/changelog.md) * [Release Notes](18.0.0/release_notes.md) diff --git a/changelog/19.0/19.0.0/changelog.md b/changelog/19.0/19.0.0/changelog.md new file mode 100644 index 00000000000..28f2b1fef0b --- /dev/null +++ b/changelog/19.0/19.0.0/changelog.md @@ -0,0 +1,515 @@ +# Changelog of Vitess v19.0.0 + +### Announcement +#### General + * summary: updated summary with 19.0 changes [#15132](https://github.com/vitessio/vitess/pull/15132) +### Bug fixes +#### Backup and Restore + * MysqlCtl: implement missing `ReadBinlogFilesTimestamps` function [#14525](https://github.com/vitessio/vitess/pull/14525) + * Replication: Have the DB flavor process waiting for a pos [#14745](https://github.com/vitessio/vitess/pull/14745) +#### Build/CI + * Update create_release.sh [#14492](https://github.com/vitessio/vitess/pull/14492) + * Set minimal tokens for auto_approve_pr [#14534](https://github.com/vitessio/vitess/pull/14534) + * Run Go deps upgrade every week [#14910](https://github.com/vitessio/vitess/pull/14910) +#### CLI + * Fix anonymous paths in cobra code-gen [#14185](https://github.com/vitessio/vitess/pull/14185) +#### Cluster management + * Fix Panic in PRS due to a missing nil check [#14656](https://github.com/vitessio/vitess/pull/14656) + * Fix hearbeatWriter Close being stuck if waiting for a semi-sync ACK [#14823](https://github.com/vitessio/vitess/pull/14823) + * Block replication and query RPC calls until wait for dba grants has completed [#14836](https://github.com/vitessio/vitess/pull/14836) + * Fix the parser to allow multiple strings one after the other [#15076](https://github.com/vitessio/vitess/pull/15076) +#### Docker + * [Docker] Fix VTadmin build [#14363](https://github.com/vitessio/vitess/pull/14363) +#### Evalengine + * evalengine: Misc bugs [#14351](https://github.com/vitessio/vitess/pull/14351) + * datetime: obey the evalengine's environment time [#14358](https://github.com/vitessio/vitess/pull/14358) + * Fix nullability checks in evalengine [#14556](https://github.com/vitessio/vitess/pull/14556) + * evalengine: Handle zero dates correctly [#14610](https://github.com/vitessio/vitess/pull/14610) + * evalengine: Fix the min / max calculation for decimals [#14614](https://github.com/vitessio/vitess/pull/14614) + * evalengine: Fix week overflow [#14859](https://github.com/vitessio/vitess/pull/14859) + * evalengine: Return evalTemporal types for current date / time [#15079](https://github.com/vitessio/vitess/pull/15079) +#### Examples + * examples: fix flag syntax for zkctl [#14469](https://github.com/vitessio/vitess/pull/14469) +#### General + * viper: register dynamic config with both disk and live [#14453](https://github.com/vitessio/vitess/pull/14453) + * Protect `ExecuteFetchAsDBA` against multi-statements, excluding a sequence of `CREATE TABLE|VIEW`. [#14954](https://github.com/vitessio/vitess/pull/14954) + * Use the correct parser for truncation [#14985](https://github.com/vitessio/vitess/pull/14985) + * Fix log format error in vttls.go [#15035](https://github.com/vitessio/vitess/pull/15035) +#### Observability + * Fix #14414: resilient_server metrics name/prefix logic is inverted, leading to no metrics being recorded [#14415](https://github.com/vitessio/vitess/pull/14415) +#### Online DDL + * Online DDL: timeouts for all gRPC calls [#14182](https://github.com/vitessio/vitess/pull/14182) + * OnlineDDL: fix scenarios where migration hangs instead of directly failing [#14290](https://github.com/vitessio/vitess/pull/14290) + * schemadiff: fix missing `DROP CONSTRAINT` in duplicate/redundant constraints scenario. [#14387](https://github.com/vitessio/vitess/pull/14387) +#### Query Serving + * bugfix: use the proper interface for comment directives [#14267](https://github.com/vitessio/vitess/pull/14267) + * evalengine: Use the right unknown type to initialize [#14313](https://github.com/vitessio/vitess/pull/14313) + * engine: fix race when reading fields in Concatenate [#14324](https://github.com/vitessio/vitess/pull/14324) + * tabletserver: do not consolidate streams on primary tablet when consolidator mode is `notOnPrimary` [#14332](https://github.com/vitessio/vitess/pull/14332) + * Planner bugfix [#14365](https://github.com/vitessio/vitess/pull/14365) + * semantics: Fix missing union pop from scoper [#14401](https://github.com/vitessio/vitess/pull/14401) + * fix: mismatch in column count and value count [#14417](https://github.com/vitessio/vitess/pull/14417) + * Make column resolution closer to MySQL [#14426](https://github.com/vitessio/vitess/pull/14426) + * vtgate/engine: Fix race condition in join logic [#14435](https://github.com/vitessio/vitess/pull/14435) + * Bug fix: Use target tablet from health stats cache when checking replication status [#14436](https://github.com/vitessio/vitess/pull/14436) + * Ensure hexval and int don't share BindVar after Normalization [#14451](https://github.com/vitessio/vitess/pull/14451) + * planbuilder bugfix: expose columns through derived tables [#14501](https://github.com/vitessio/vitess/pull/14501) + * Fix missing query serving error code [#14520](https://github.com/vitessio/vitess/pull/14520) + * Fix type coercion in cascading non-literal updates [#14524](https://github.com/vitessio/vitess/pull/14524) + * Type Cast all update expressions in verify queries [#14555](https://github.com/vitessio/vitess/pull/14555) + * expression rewriting: enable more rewrites and limit CNF rewrites [#14560](https://github.com/vitessio/vitess/pull/14560) + * bug fix: stop all kinds of expressions from cnf-exploding [#14585](https://github.com/vitessio/vitess/pull/14585) + * fix concurrency on stream execute engine primitives [#14586](https://github.com/vitessio/vitess/pull/14586) + * bugfix: do not rewrite an expression twice [#14641](https://github.com/vitessio/vitess/pull/14641) + * txserializer: change log message based on dry run [#14651](https://github.com/vitessio/vitess/pull/14651) + * Vindexes: Pass context in consistent lookup handleDup [#14653](https://github.com/vitessio/vitess/pull/14653) + * Fail correlated subquery in planning phase instead of a runtime error [#14701](https://github.com/vitessio/vitess/pull/14701) + * bugfix: use the original expression and not the alias [#14704](https://github.com/vitessio/vitess/pull/14704) + * Fix RegisterNotifier to use a copy of the tables to prevent data races [#14716](https://github.com/vitessio/vitess/pull/14716) + * fix: flush tables with read lock to run only with reserved connection [#14720](https://github.com/vitessio/vitess/pull/14720) + * TabletServer: Handle nil targets properly everywhere [#14734](https://github.com/vitessio/vitess/pull/14734) + * bugfix: don't panic when missing schema information [#14787](https://github.com/vitessio/vitess/pull/14787) + * schemadiff: allow char->varchar FK reference type matching [#14849](https://github.com/vitessio/vitess/pull/14849) + * sqlparser: FORCE_CUTOVER is a non-reserved keyword [#14885](https://github.com/vitessio/vitess/pull/14885) + * Improve err extraction logic [#14887](https://github.com/vitessio/vitess/pull/14887) + * Add nil check to prevent panics [#14902](https://github.com/vitessio/vitess/pull/14902) + * evalengine bugfix: handle nil evals correctly when coercing values [#14906](https://github.com/vitessio/vitess/pull/14906) + * Vttablet panic in requests Wait [#14924](https://github.com/vitessio/vitess/pull/14924) + * `schemadiff`: fix diffing of textual columns with implicit charsets [#14930](https://github.com/vitessio/vitess/pull/14930) + * bugfix: Columns alias expanding [#14935](https://github.com/vitessio/vitess/pull/14935) + * Fix panic for unknown columns in foreign key managed mode [#15025](https://github.com/vitessio/vitess/pull/15025) + * Fix subquery cloning and dependencies [#15039](https://github.com/vitessio/vitess/pull/15039) + * Fix `buffer_drain_concurrency` not doing anything [#15042](https://github.com/vitessio/vitess/pull/15042) + * Copy expression types to avoid weight_strings and derived tables [#15069](https://github.com/vitessio/vitess/pull/15069) + * Improve efficiency and accuracy of mysqld.GetVersionString [#15096](https://github.com/vitessio/vitess/pull/15096) + * mysql: Ensure we set up the initial collation correctly [#15115](https://github.com/vitessio/vitess/pull/15115) +#### Schema Tracker + * discovery: fix crash with nil server vschema [#15086](https://github.com/vitessio/vitess/pull/15086) +#### TabletManager + * mysqlctl: Cleanup stale socket lockfile [#14553](https://github.com/vitessio/vitess/pull/14553) + * mysqlctl: Fix cleaning up the stale lock file [#14600](https://github.com/vitessio/vitess/pull/14600) + * tabletserver: Skip wait for DBA grants for external tablets [#14629](https://github.com/vitessio/vitess/pull/14629) + * mysqlctl: Error out on stale socket [#14650](https://github.com/vitessio/vitess/pull/14650) +#### Throttler + * Throttler: set timeouts on gRPC communication and on topo communication [#14165](https://github.com/vitessio/vitess/pull/14165) + * examples: rm heartbeat flags [#14980](https://github.com/vitessio/vitess/pull/14980) +#### Topology + * Ignore non-Shard keys in FindAllShardsInKeyspace List impl [#15117](https://github.com/vitessio/vitess/pull/15117) +#### VReplication + * VReplication: error on vtctldclient commands w/o tablet types [#14294](https://github.com/vitessio/vitess/pull/14294) + * VDiff: wait for shard streams of one table diff to complete for before starting that of the next table [#14345](https://github.com/vitessio/vitess/pull/14345) + * Vtctld SwitchReads: fix bug where writes were also being switched as part of switching reads when all traffic was switched using SwitchTraffic [#14360](https://github.com/vitessio/vitess/pull/14360) + * VDiff tablet selection: pick non-serving tablets in Reshard workflows [#14413](https://github.com/vitessio/vitess/pull/14413) + * VDiff: "show all" should only report vdiffs for the specified keyspace and workflow [#14442](https://github.com/vitessio/vitess/pull/14442) + * VReplication: Properly Handle FK Constraints When Deferring Secondary Keys [#14543](https://github.com/vitessio/vitess/pull/14543) + * Materializer: normalize schema via schemadiff on --atomic-copy [#14636](https://github.com/vitessio/vitess/pull/14636) + * VReplication TableStreamer: Only stream tables in tablestreamer (ignore views) [#14646](https://github.com/vitessio/vitess/pull/14646) + * VDiff: Fix vtctldclient limit bug [#14778](https://github.com/vitessio/vitess/pull/14778) + * VReplication: Guard against unsafe _vt.vreplication writes [#14797](https://github.com/vitessio/vitess/pull/14797) + * VReplication SwitchWrites: Properly return errors in SwitchWrites [#14800](https://github.com/vitessio/vitess/pull/14800) + * VReplication: Update singular workflow in traffic switcher [#14826](https://github.com/vitessio/vitess/pull/14826) + * Flakes: Fix flaky vtctl unit test TestMoveTables [#14886](https://github.com/vitessio/vitess/pull/14886) + * VReplication: send unique key name to `rowstreamer`, which can then use with `FORCE INDEX` [#14916](https://github.com/vitessio/vitess/pull/14916) + * VDiff: Make max diff duration upgrade/downgrade safe [#14995](https://github.com/vitessio/vitess/pull/14995) +#### vtctl + * VReplication: Add missing info to vtctldclient workflow SHOW output [#14225](https://github.com/vitessio/vitess/pull/14225) +#### vtctldclient + * vtctldclient: Apply tablet type filtering for keyspace+shard in GetTablets [#14467](https://github.com/vitessio/vitess/pull/14467) +### CI/Build +#### Backup and Restore + * Incremental backup: fix race condition in reading 'mysqlbinlog' output [#14330](https://github.com/vitessio/vitess/pull/14330) +#### Build/CI + * Enhance PR template + CI workflow for backport labels. [#14779](https://github.com/vitessio/vitess/pull/14779) + * Update MySQL apt package and GPG signature [#14785](https://github.com/vitessio/vitess/pull/14785) + * fix: build on delete operator [#14833](https://github.com/vitessio/vitess/pull/14833) + * CI: Adjust FOSSA API secret name [#14918](https://github.com/vitessio/vitess/pull/14918) + * CI: Tweak our code coverage profile behavior [#14967](https://github.com/vitessio/vitess/pull/14967) + * Fix relevant files listing for `endtoend` CI [#15104](https://github.com/vitessio/vitess/pull/15104) +#### Docker + * Vitess MySQL Docker Image [#14158](https://github.com/vitessio/vitess/pull/14158) + * Build and push Docker `vitess/vttestserver` DockerHub from GitHub Actions [#14314](https://github.com/vitessio/vitess/pull/14314) + * Add `vtexplain` and `vtbackup` to base docker auto-build [#14318](https://github.com/vitessio/vitess/pull/14318) +#### Evalengine + * Fix codegen command with the right type [#14376](https://github.com/vitessio/vitess/pull/14376) +#### General + * [main] Upgrade the Golang version to `go1.21.2` [#14193](https://github.com/vitessio/vitess/pull/14193) + * [main] Upgrade the Golang version to `go1.21.3` [#14231](https://github.com/vitessio/vitess/pull/14231) + * [main] Upgrade the Golang version to `go1.21.4` [#14488](https://github.com/vitessio/vitess/pull/14488) + * [main] Upgrade the Golang version to `go1.21.5` [#14689](https://github.com/vitessio/vitess/pull/14689) + * connpool: fix racy test [#14731](https://github.com/vitessio/vitess/pull/14731) +#### Online DDL + * onlineddl_vrepl_stress: fix flakiness caused by timeouts [#14295](https://github.com/vitessio/vitess/pull/14295) + * OnlineDDL: reduce vrepl_stress workload in forks [#14302](https://github.com/vitessio/vitess/pull/14302) + * Online DDL: fix endtoend test dropping foreign key [#14522](https://github.com/vitessio/vitess/pull/14522) + * VTGate/foreign keys stress test: add tests for 'standard' replica [#14747](https://github.com/vitessio/vitess/pull/14747) +#### VTAdmin + * Update vtadmin dependencies [#14336](https://github.com/vitessio/vitess/pull/14336) + * Fix stray vtadmin package-lock.json content [#14350](https://github.com/vitessio/vitess/pull/14350) +#### VTorc + * docker: add dedicated vtorc container [#14126](https://github.com/vitessio/vitess/pull/14126) +### Dependabot +#### General + * Bump github.com/cyphar/filepath-securejoin from 0.2.3 to 0.2.4 [#14239](https://github.com/vitessio/vitess/pull/14239) + * Bump golang.org/x/net from 0.14.0 to 0.17.0 [#14260](https://github.com/vitessio/vitess/pull/14260) + * Bump google.golang.org/grpc from 1.55.0-dev to 1.59.0 [#14364](https://github.com/vitessio/vitess/pull/14364) + * build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 [#14814](https://github.com/vitessio/vitess/pull/14814) +#### Java + * build(deps): bump com.google.guava:guava from 30.1.1-jre to 32.0.0-jre in /java [#14759](https://github.com/vitessio/vitess/pull/14759) + * build(deps): bump io.netty:netty-handler from 4.1.93.Final to 4.1.94.Final in /java [#14863](https://github.com/vitessio/vitess/pull/14863) +#### VTAdmin + * Bump @cypress/request and cypress in /vitess-mixin/e2e [#14038](https://github.com/vitessio/vitess/pull/14038) + * Bump postcss from 8.4.21 to 8.4.31 in /web/vtadmin [#14173](https://github.com/vitessio/vitess/pull/14173) + * Bump @babel/traverse from 7.21.4 to 7.23.2 in /web/vtadmin [#14304](https://github.com/vitessio/vitess/pull/14304) + * Bump @adobe/css-tools from 4.3.1 to 4.3.2 in /web/vtadmin [#14654](https://github.com/vitessio/vitess/pull/14654) + * build(deps-dev): bump vite from 4.2.3 to 4.5.2 in /web/vtadmin [#15001](https://github.com/vitessio/vitess/pull/15001) +### Documentation +#### CLI + * Bypass cobra completion commands so they still function [#14217](https://github.com/vitessio/vitess/pull/14217) +#### Documentation + * release notes: edit summary for consistency [#14319](https://github.com/vitessio/vitess/pull/14319) + * release notes: add FK import to summary [#14518](https://github.com/vitessio/vitess/pull/14518) + * 19.0 release notes: ExecuteFetchAsDBA breaking change [#15021](https://github.com/vitessio/vitess/pull/15021) +#### General + * Add summary changes for recent PRs [#14598](https://github.com/vitessio/vitess/pull/14598) + * Add summary changes to indicate MySQL 5.7 is EOL and Vitess is dropping support for it in v19 [#14663](https://github.com/vitessio/vitess/pull/14663) +### Enhancement +#### Backup and Restore + * increase vtctlclient backupShard command success rate [#14604](https://github.com/vitessio/vitess/pull/14604) + * Backup: `--incremental-from-pos` supports backup name [#14923](https://github.com/vitessio/vitess/pull/14923) + * Incremental backup: do not error on empty backup [#15022](https://github.com/vitessio/vitess/pull/15022) +#### Build/CI + * CI: Re-enable FOSSA scan and add Codecov [#14333](https://github.com/vitessio/vitess/pull/14333) + * Automatic approval of `vitess-bot` clean backports [#14352](https://github.com/vitessio/vitess/pull/14352) + * Tell shellcheck to follow sourced files [#14377](https://github.com/vitessio/vitess/pull/14377) + * Add step to static check to ensure consistency of GHA workflows [#14724](https://github.com/vitessio/vitess/pull/14724) +#### CLI + * VReplication: Add traffic state to vtctldclient workflow status output [#14280](https://github.com/vitessio/vitess/pull/14280) + * vtctldclient,grpcvtctldserver ApplySchema: return unknown params from grpcvtctldserver.ApplySchema, log them in vtctldclient.ApplySchema [#14672](https://github.com/vitessio/vitess/pull/14672) +#### Cluster management + * Add HealthCheck's `healthy` map to the VTGate UI [#14521](https://github.com/vitessio/vitess/pull/14521) + * Make vttablet wait for vt_dba user to be granted privileges [#14565](https://github.com/vitessio/vitess/pull/14565) + * Add wait for reading mycnf to prevent race [#14626](https://github.com/vitessio/vitess/pull/14626) + * Add log for error to help debug [#14632](https://github.com/vitessio/vitess/pull/14632) + * Take replication lag into account while selecting primary candidate [#14634](https://github.com/vitessio/vitess/pull/14634) + * Postpone waiting for dba grants after restore has succeeded [#14680](https://github.com/vitessio/vitess/pull/14680) + * vtctldclient: --strict rejects unknown vindex params in ApplyVSchema [#14862](https://github.com/vitessio/vitess/pull/14862) + * Respect tolerable replication lag even when the new primary has been provided in PRS [#15090](https://github.com/vitessio/vitess/pull/15090) +#### Docker + * Build and push Docker `vitess/lite` to DockerHub from GitHub Actions [#14243](https://github.com/vitessio/vitess/pull/14243) + * Build and push Docker `vitess/base` and component images to DockerHub from GitHub Actions [#14271](https://github.com/vitessio/vitess/pull/14271) + * Be more explicit in release notes regarding the deprecation of certain `vitess/lite` tags [#15040](https://github.com/vitessio/vitess/pull/15040) +#### Evalengine + * evalengine: Improve the typing situation for functions [#14533](https://github.com/vitessio/vitess/pull/14533) + * evalengine: Implement SUBSTRING [#14899](https://github.com/vitessio/vitess/pull/14899) + * evalengine: Implement FROM_DAYS [#15058](https://github.com/vitessio/vitess/pull/15058) + * evalengine: Implement TO_DAYS [#15065](https://github.com/vitessio/vitess/pull/15065) + * evalengine: Add MID alias [#15066](https://github.com/vitessio/vitess/pull/15066) + * evalEngine: Implement TIME_TO_SEC [#15094](https://github.com/vitessio/vitess/pull/15094) +#### Examples + * Tools: Remove dependencies installed by `make tools` [#14309](https://github.com/vitessio/vitess/pull/14309) + * Deprecate `mysqld` in `vitess/lite` and use `mysql:8.0.30` image for the operator [#14990](https://github.com/vitessio/vitess/pull/14990) +#### General + * build: Allow compilation on Windows [#14718](https://github.com/vitessio/vitess/pull/14718) +#### Observability + * Debug vars: Expose build version in `/debug/vars` [#14713](https://github.com/vitessio/vitess/pull/14713) + * [servenv] optional pprof endpoints [#14796](https://github.com/vitessio/vitess/pull/14796) + * vtgate: increment vtgate_warnings counter for non atomic commits [#15010](https://github.com/vitessio/vitess/pull/15010) + * query_executor: Record `WaitingForConnection` stat in all cases [#15073](https://github.com/vitessio/vitess/pull/15073) +#### Online DDL + * Online DDL: support DROP FOREIGN KEY statement [#14338](https://github.com/vitessio/vitess/pull/14338) + * Online DDL: revert considerations for migrations with foreign key constraints [#14368](https://github.com/vitessio/vitess/pull/14368) + * Enable Online DDL foreign key support (also in vtgate stress tests) when backing MySQL includes appropriate patch [#14370](https://github.com/vitessio/vitess/pull/14370) + * Online DDL: lint DDL strategy flags [#14373](https://github.com/vitessio/vitess/pull/14373) + * schemadiff: remove table name from auto-generated FK constraint name [#14385](https://github.com/vitessio/vitess/pull/14385) + * TableGC: speed up GC process via `RequestChecks()`. Utilized by Online DDL for artifact cleanup [#14431](https://github.com/vitessio/vitess/pull/14431) + * Support `fast_analyze_table` variable, introduced in public MySQL fork [#14494](https://github.com/vitessio/vitess/pull/14494) + * Online DDL: edit CONSTRAINT names in CREATE TABLE [#14517](https://github.com/vitessio/vitess/pull/14517) + * Online DDL: support migration cut-over backoff and forced cut-over [#14546](https://github.com/vitessio/vitess/pull/14546) + * ApplySchema: log selected flags [#14798](https://github.com/vitessio/vitess/pull/14798) + * schemadiff: using MySQL capabilities to analyze a SchemaDiff and whether changes are applicable instantly/immediately. [#14878](https://github.com/vitessio/vitess/pull/14878) + * OnlineDDL to use schemadiff version capabilities; refactor some `flavor` code. [#14883](https://github.com/vitessio/vitess/pull/14883) + * `schemadiff`: formalize `InstantDDLCapability` [#14900](https://github.com/vitessio/vitess/pull/14900) +#### Query Serving + * add option for warming reads to mirror primary read queries onto replicas from vtgates to warm bufferpools [#13206](https://github.com/vitessio/vitess/pull/13206) + * Add support for new lock syntax in MySQL8 [#13965](https://github.com/vitessio/vitess/pull/13965) + * gen4: Support explicit column aliases on derived tables [#14129](https://github.com/vitessio/vitess/pull/14129) + * Gracefully shutdown VTGate instances [#14219](https://github.com/vitessio/vitess/pull/14219) + * Mark non-unique lookup vindex as backfill to ignore vindex selection [#14227](https://github.com/vitessio/vitess/pull/14227) + * UNION column type coercion [#14245](https://github.com/vitessio/vitess/pull/14245) + * Add support for common table expressions [#14321](https://github.com/vitessio/vitess/pull/14321) + * Add cycle detection for foreign keys [#14339](https://github.com/vitessio/vitess/pull/14339) + * feat: support invisible columns [#14366](https://github.com/vitessio/vitess/pull/14366) + * Add support for more queries [#14369](https://github.com/vitessio/vitess/pull/14369) + * schemadiff: identify a FK sequential execution scenario, and more [#14397](https://github.com/vitessio/vitess/pull/14397) + * Add support for AVG on sharded queries [#14419](https://github.com/vitessio/vitess/pull/14419) + * Use hash joins when nested loop joins are not feasible [#14448](https://github.com/vitessio/vitess/pull/14448) + * Make `Foreign_key_checks` a Vitess Aware variable [#14484](https://github.com/vitessio/vitess/pull/14484) + * Add `SHOW VSCHEMA KEYSPACES` query [#14505](https://github.com/vitessio/vitess/pull/14505) + * Support unlimited number of ORs in `ExtractINFromOR` [#14566](https://github.com/vitessio/vitess/pull/14566) + * planbuilder: push down ordering through filter [#14583](https://github.com/vitessio/vitess/pull/14583) + * refactor the INSERT engine primitive [#14606](https://github.com/vitessio/vitess/pull/14606) + * Optimise hash joins [#14644](https://github.com/vitessio/vitess/pull/14644) + * schemadiff: granular foreign key reference errors [#14682](https://github.com/vitessio/vitess/pull/14682) + * schemadiff: pursue foreign key errors and proceed to build schema [#14705](https://github.com/vitessio/vitess/pull/14705) + * schemadiff: additional FK column type matching rules [#14751](https://github.com/vitessio/vitess/pull/14751) + * Fix order by and group by normalization [#14764](https://github.com/vitessio/vitess/pull/14764) + * reduce NOWAIT usage to tables with unique keys for foreign key plans [#14772](https://github.com/vitessio/vitess/pull/14772) + * vtgate: record warning for partially successful cross-shard commits [#14848](https://github.com/vitessio/vitess/pull/14848) + * Added support for group_concat and count distinct with multiple expressions [#14851](https://github.com/vitessio/vitess/pull/14851) + * Multi Table Delete Planner Support [#14855](https://github.com/vitessio/vitess/pull/14855) + * Improve sharded query routing for tuple list [#14892](https://github.com/vitessio/vitess/pull/14892) + * Make Schema Tracking case-sensitive [#14904](https://github.com/vitessio/vitess/pull/14904) + * Explain Statement plan improvement [#14928](https://github.com/vitessio/vitess/pull/14928) + * planner: support cross shard DELETE with LIMIT/ORDER BY [#14959](https://github.com/vitessio/vitess/pull/14959) + * `transaction_mode` variable to return flag default if unset [#15032](https://github.com/vitessio/vitess/pull/15032) + * evalengine: Implement LAST_DAY [#15038](https://github.com/vitessio/vitess/pull/15038) + * `schemadiff`: analyze and report foreign key loops/cycles [#15062](https://github.com/vitessio/vitess/pull/15062) + * Add support for multi table deletes with foreign keys [#15081](https://github.com/vitessio/vitess/pull/15081) + * Add support for delete planning with limits in presence of foreign keys [#15097](https://github.com/vitessio/vitess/pull/15097) +#### TabletManager + * Allow for passing in the MySQL shutdown timeout [#14568](https://github.com/vitessio/vitess/pull/14568) +#### Throttler + * Tablet throttler: post 18 refactoring, race condition fixes, unit & race testing, deprecation of HTTP checks [#14181](https://github.com/vitessio/vitess/pull/14181) +#### VReplication + * vreplication timeout query optimizer hints [#13840](https://github.com/vitessio/vitess/pull/13840) + * VReplication: Ensure that RowStreamer uses optimal index when possible [#13893](https://github.com/vitessio/vitess/pull/13893) + * go/vt/wrangler: add len(qr.Rows) check to no streams found log msg [#14062](https://github.com/vitessio/vitess/pull/14062) + * Migrate CreateLookupVindex and ExternalizeVindex to vtctldclient [#14086](https://github.com/vitessio/vitess/pull/14086) + * set vreplication net read and net write timeout session vars to high values [#14203](https://github.com/vitessio/vitess/pull/14203) + * allow tablet picker to exclude specified tablets from its candidate list [#14224](https://github.com/vitessio/vitess/pull/14224) + * VReplication: Add --all-cells flag to create sub-commands [#14341](https://github.com/vitessio/vitess/pull/14341) + * go/vt/wrangler: reduce VReplicationExec calls when getting copy state [#14375](https://github.com/vitessio/vitess/pull/14375) + * VStream: Skip vindex keyrange filtering when we can [#14384](https://github.com/vitessio/vitess/pull/14384) + * implement `--max-report-sample-rows` for VDiff [#14437](https://github.com/vitessio/vitess/pull/14437) + * VReplication VPlayer: support statement and transaction batching [#14502](https://github.com/vitessio/vitess/pull/14502) + * Snapshot connection: revert to explicit table locks when `FTWRL` is unavailable [#14578](https://github.com/vitessio/vitess/pull/14578) + * VReplication: Improve replication plan error messages [#14752](https://github.com/vitessio/vitess/pull/14752) + * VDiff: Support a max diff time for tables [#14786](https://github.com/vitessio/vitess/pull/14786) + * VDiff: Support diffing tables without a defined Primary Key [#14794](https://github.com/vitessio/vitess/pull/14794) +#### VTAdmin + * Optimize the GetWorkflows RPC [#14212](https://github.com/vitessio/vitess/pull/14212) +#### vtctldclient + * Support cluster bootstrapping in vtctldclient [#14315](https://github.com/vitessio/vitess/pull/14315) +#### vttestserver + * Make vttestserver docker image work with vtctldclient [#14665](https://github.com/vitessio/vitess/pull/14665) +### Feature Request +#### Build/CI + * Automatically update the Golang dependencies using a CRON [#14891](https://github.com/vitessio/vitess/pull/14891) +#### Evalengine + * evalengine: implement AggregateEvalTypes [#15085](https://github.com/vitessio/vitess/pull/15085) +#### Query Serving + * Cache stream query plans in vttablet [#13264](https://github.com/vitessio/vitess/pull/13264) + * Foreign key on update action with non literal values [#14278](https://github.com/vitessio/vitess/pull/14278) + * `Replace into` statement plan with foreign keys : unsharded [#14396](https://github.com/vitessio/vitess/pull/14396) + * Enable REPLACE INTO engine and Fix Foreign key locking issue [#14532](https://github.com/vitessio/vitess/pull/14532) + * Add foreign key support for insert on duplicate key update [#14638](https://github.com/vitessio/vitess/pull/14638) + * Multi Table Delete Support: join with reference table [#14784](https://github.com/vitessio/vitess/pull/14784) + * `schemadiff`: `EnumReorderStrategy`, checking if enum or set values change ordinal [#15106](https://github.com/vitessio/vitess/pull/15106) +#### VReplication + * Provide subset of shards for certain VReplication Commands [#14873](https://github.com/vitessio/vitess/pull/14873) +### Internal Cleanup +#### Backup and Restore + * vtbackup: Fix copy pasta typo in option description [#14664](https://github.com/vitessio/vitess/pull/14664) +#### Build/CI + * Typo fix and remove unsupported branch for go version upgrade matrix [#14896](https://github.com/vitessio/vitess/pull/14896) + * Reduce the frequency of the golang dependency upgrade CRON [#15008](https://github.com/vitessio/vitess/pull/15008) + * Remove codebeat badge [#15116](https://github.com/vitessio/vitess/pull/15116) +#### CLI + * Make vtctldclient mount command more standard [#14281](https://github.com/vitessio/vitess/pull/14281) + * remove deprecated flags from the codebase [#14544](https://github.com/vitessio/vitess/pull/14544) + * cleanup deprecated flag types in tabletenv [#14733](https://github.com/vitessio/vitess/pull/14733) +#### Cluster management + * Enable verbose logging for some more RPCs [#14770](https://github.com/vitessio/vitess/pull/14770) + * go/vt/topo: add error value to GetTablet logs [#14846](https://github.com/vitessio/vitess/pull/14846) +#### Docker + * Remove `MYSQL_FLAVOR` from all Docker images [#14159](https://github.com/vitessio/vitess/pull/14159) +#### Documentation + * Fix broken link in docker readme [#14222](https://github.com/vitessio/vitess/pull/14222) + * Mention roadmap planning/modification in the release process [#14254](https://github.com/vitessio/vitess/pull/14254) +#### Evalengine + * refactor: introduce evalengine type and use it [#14292](https://github.com/vitessio/vitess/pull/14292) + * sqlparser: export all Expr interfaces [#14371](https://github.com/vitessio/vitess/pull/14371) + * evalengine: Proper support for bit literals [#14374](https://github.com/vitessio/vitess/pull/14374) + * evalengine: fix numeric coercibility [#14473](https://github.com/vitessio/vitess/pull/14473) + * evalengine: Internal cleanup and consistency fixes [#14854](https://github.com/vitessio/vitess/pull/14854) +#### General + * chore: unnecessary use of fmt.Sprintf [#14328](https://github.com/vitessio/vitess/pull/14328) + * Miscellaneous typo fixes to comments [#14472](https://github.com/vitessio/vitess/pull/14472) + * Refactor: use NonEmpty() instead of !IsEmpty() [#14499](https://github.com/vitessio/vitess/pull/14499) + * Fix license header typo [#14630](https://github.com/vitessio/vitess/pull/14630) + * go/vt/vtgate: fix nilness issues [#14685](https://github.com/vitessio/vitess/pull/14685) + * go/vt/vttablet: fix nilness issues [#14686](https://github.com/vitessio/vitess/pull/14686) + * go/vt/vtadmin: fix nilness issues [#14687](https://github.com/vitessio/vitess/pull/14687) + * go/cache: fix nilness issues and unused code [#14688](https://github.com/vitessio/vitess/pull/14688) + * Keyspace ServedFrom: remove this deprecated attribute and related code [#14694](https://github.com/vitessio/vitess/pull/14694) + * go/vt/topo: fix nilness issues and unused variables [#14709](https://github.com/vitessio/vitess/pull/14709) + * go/vt/wrangler: fix nilness issues and unused variable [#14710](https://github.com/vitessio/vitess/pull/14710) + * go/vt/vtctl: fix nilness issues and error scopes [#14711](https://github.com/vitessio/vitess/pull/14711) + * mysql: Refactor out usage of servenv [#14732](https://github.com/vitessio/vitess/pull/14732) + * Remove servenv usage and config flags from collations [#14781](https://github.com/vitessio/vitess/pull/14781) + * Remove unused EventStreamer [#14783](https://github.com/vitessio/vitess/pull/14783) + * Cleanup of dead code [#14799](https://github.com/vitessio/vitess/pull/14799) + * go: resolve various nilness issues [#14803](https://github.com/vitessio/vitess/pull/14803) + * sqlparser: Refactor out servenv and inject everywhere [#14822](https://github.com/vitessio/vitess/pull/14822) + * Allow for building 32 bit libraries for subparts [#14841](https://github.com/vitessio/vitess/pull/14841) + * Improve links in README [#14867](https://github.com/vitessio/vitess/pull/14867) + * Use one canonical style for unlimited queries [#14870](https://github.com/vitessio/vitess/pull/14870) + * Fix a number of CodeQL warnings [#14882](https://github.com/vitessio/vitess/pull/14882) + * Update Go dependencies [#14888](https://github.com/vitessio/vitess/pull/14888) + * Modify the release instructions to properly clone Vitess when using the vtop examples [#14889](https://github.com/vitessio/vitess/pull/14889) + * Dead code cleanup [#14894](https://github.com/vitessio/vitess/pull/14894) + * Refactor out more usage of servenv for mysql version [#14938](https://github.com/vitessio/vitess/pull/14938) + * refac: deprecate `vitess/go/maps2` for `golang.org/x/exp/maps` [#14960](https://github.com/vitessio/vitess/pull/14960) + * vtenv: Introduce vtenv for passing in collation & parser information [#14994](https://github.com/vitessio/vitess/pull/14994) +#### Observability + * Remove some logs that are logging excessively on large clusters [#14825](https://github.com/vitessio/vitess/pull/14825) + * vstreamer: rm excessive logging [#14856](https://github.com/vitessio/vitess/pull/14856) +#### Online DDL + * New unified internal table names format, part 1: identifying and accepting new format tables [#14613](https://github.com/vitessio/vitess/pull/14613) +#### Query Serving + * Use panic instead of errors inside the operator package [#14085](https://github.com/vitessio/vitess/pull/14085) + * sqlparser: normalize IndexInfo [#14177](https://github.com/vitessio/vitess/pull/14177) + * refactor plan test cases [#14192](https://github.com/vitessio/vitess/pull/14192) + * Rename `BinaryIsAtVersion` to `BinaryIsAtLeastAtVersion` [#14269](https://github.com/vitessio/vitess/pull/14269) + * Refactor: foreign key in semantic analysis phase [#14273](https://github.com/vitessio/vitess/pull/14273) + * Rename Foreign Key enum values in VSchema and drop `FK_` prefix [#14274](https://github.com/vitessio/vitess/pull/14274) + * Refactor: New operator InsertionSelection to adhere to the operator model [#14286](https://github.com/vitessio/vitess/pull/14286) + * refactor: move more code from logical plans to ops [#14287](https://github.com/vitessio/vitess/pull/14287) + * evalengine: serialize to SQL [#14337](https://github.com/vitessio/vitess/pull/14337) + * vindexes: Efficient unicode hashing [#14395](https://github.com/vitessio/vitess/pull/14395) + * tx throttler: remove unused topology watchers [#14412](https://github.com/vitessio/vitess/pull/14412) + * sqlparser: Use KEY instead of INDEX for normalized form [#14416](https://github.com/vitessio/vitess/pull/14416) + * tx_throttler: delete topo watcher metric instead of deprecating [#14445](https://github.com/vitessio/vitess/pull/14445) + * Remove excessive VTGate logging of default planner selection [#14554](https://github.com/vitessio/vitess/pull/14554) + * refactor: minor cleanups in planner code [#14642](https://github.com/vitessio/vitess/pull/14642) + * planbuilder: clean up code [#14657](https://github.com/vitessio/vitess/pull/14657) + * Pass on vindex errors with wrap than overriding them [#14737](https://github.com/vitessio/vitess/pull/14737) + * refactor: remove more errors from operator planning [#14767](https://github.com/vitessio/vitess/pull/14767) + * Change variable name for better readability [#14771](https://github.com/vitessio/vitess/pull/14771) + * go/cache: use generics and remove unused API [#14850](https://github.com/vitessio/vitess/pull/14850) + * Export `convertMySQLVersionToCommentVersion` to use it in vitess-operator [#14988](https://github.com/vitessio/vitess/pull/14988) +#### TabletManager + * logging: log time taken for tablet initialization only once [#14597](https://github.com/vitessio/vitess/pull/14597) + * Replace use of `WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS` with `WAIT_FOR_EXECUTED_GTID_SET` [#14612](https://github.com/vitessio/vitess/pull/14612) +#### Throttler + * MaxReplicationLagModule.recalculateRate no longer fills the log [#14875](https://github.com/vitessio/vitess/pull/14875) +#### VReplication + * VReplication: VTTablet flag cleanup [#14297](https://github.com/vitessio/vitess/pull/14297) +#### vtctl + * Move all examples to vtctldclient [#14226](https://github.com/vitessio/vitess/pull/14226) +#### vtctldclient + * Fix typo for `--cells` flag help description in `ApplyRoutingRules` [#14721](https://github.com/vitessio/vitess/pull/14721) +### Performance +#### Evalengine + * Tiny Weights [#14402](https://github.com/vitessio/vitess/pull/14402) +#### General + * Replace usages of bytes.Buffer with strings.Builder [#14539](https://github.com/vitessio/vitess/pull/14539) +#### Query Serving + * Improved Connection Pooling [#14034](https://github.com/vitessio/vitess/pull/14034) + * schemadiff: improved heuristic for dependent migration permutation evaluation time [#14249](https://github.com/vitessio/vitess/pull/14249) + * mysql/conn: do not allocate during writes [#14482](https://github.com/vitessio/vitess/pull/14482) + * Use GetTabletsByCell in healthcheck [#14693](https://github.com/vitessio/vitess/pull/14693) + * mysql: do not allocate in parseOKPacket [#15067](https://github.com/vitessio/vitess/pull/15067) + * mysql: remove more allocations from parseOKPacket [#15082](https://github.com/vitessio/vitess/pull/15082) +#### Throttler + * Throttler: Use tmclient pool for CheckThrottler tabletmanager RPC [#14979](https://github.com/vitessio/vitess/pull/14979) +#### Topology + * go/vt/topo: enable concurrency for FindAllShardsInKeyspace [#14670](https://github.com/vitessio/vitess/pull/14670) + * Improve TopoServer Performance and Efficiency For Keyspace Shards [#15047](https://github.com/vitessio/vitess/pull/15047) +### Regression +#### Query Serving + * use aggregation engine over distinct engine when overlapping order by [#14359](https://github.com/vitessio/vitess/pull/14359) + * Performance Fixes for Vitess 18 [#14383](https://github.com/vitessio/vitess/pull/14383) + * tuple: serialized form [#14392](https://github.com/vitessio/vitess/pull/14392) + * planbuilder: use OR for not in comparisons [#14607](https://github.com/vitessio/vitess/pull/14607) + * add foreign key as part of set statement when reserved connection is needed [#14696](https://github.com/vitessio/vitess/pull/14696) + * fix: insert on duplicate key update missing BindVars [#14728](https://github.com/vitessio/vitess/pull/14728) + * Subquery inside aggregration function [#14844](https://github.com/vitessio/vitess/pull/14844) +### Release +#### CLI + * [main] Add vtctldclient info to the 18.0 summary (#14259) [#14265](https://github.com/vitessio/vitess/pull/14265) +#### Documentation + * Add release instructions for Milestones [#14175](https://github.com/vitessio/vitess/pull/14175) + * Update v17 Release notes with a Breaking Change [#14215](https://github.com/vitessio/vitess/pull/14215) +#### General + * add release-18.0 to golang upgrade [#14133](https://github.com/vitessio/vitess/pull/14133) + * moving main to 19.0 snapshot [#14137](https://github.com/vitessio/vitess/pull/14137) + * update release notes on `main` after releases [#14171](https://github.com/vitessio/vitess/pull/14171) + * tooling: don't add bots to authors list [#14411](https://github.com/vitessio/vitess/pull/14411) + * move release notes of 18 to main [#14474](https://github.com/vitessio/vitess/pull/14474) + * v18.0.1 release notes to main [#14579](https://github.com/vitessio/vitess/pull/14579) + * port release notes of v18.0.2, v17.0.5 and v16.0.7 to main [#14840](https://github.com/vitessio/vitess/pull/14840) + * [release-19.0] Code Freeze for `v19.0.0-RC1` [#15137](https://github.com/vitessio/vitess/pull/15137) +### Testing +#### Backup and Restore + * Add a retry to remove the vttablet directory during upgrade/downgrade backup tests [#14753](https://github.com/vitessio/vitess/pull/14753) +#### Build/CI + * Flakes: Shutdown vttablet before mysqld in backup tests [#14647](https://github.com/vitessio/vitess/pull/14647) + * Tests: Fix vdiff test breakage from concurrent merge [#14865](https://github.com/vitessio/vitess/pull/14865) + * Flakes: De-flake TestGatewayBufferingWhenPrimarySwitchesServingState [#14968](https://github.com/vitessio/vitess/pull/14968) + * Remove usage of additional test package [#15007](https://github.com/vitessio/vitess/pull/15007) + * Revert "exclude test from race" [#15014](https://github.com/vitessio/vitess/pull/15014) + * Added missing tests for the sqltypes package [#15056](https://github.com/vitessio/vitess/pull/15056) +#### CLI + * Fixed bug in flagutil package and added tests [#15046](https://github.com/vitessio/vitess/pull/15046) +#### General + * Reduce wait time in test helpers [#14476](https://github.com/vitessio/vitess/pull/14476) + * go/vt/tlstest: fix nilness issues [#14812](https://github.com/vitessio/vitess/pull/14812) + * Add logging for failing tests in CI [#14821](https://github.com/vitessio/vitess/pull/14821) + * bytes2: Add tests for StringUnsafe and Reset methods [#14940](https://github.com/vitessio/vitess/pull/14940) + * Tests: Add test in syslogger for `LOG_EMERG` level [#14942](https://github.com/vitessio/vitess/pull/14942) + * Add required tests for `go/acl` [#14943](https://github.com/vitessio/vitess/pull/14943) + * Add missing test for `go/bucketpool` [#14944](https://github.com/vitessio/vitess/pull/14944) + * test: adds test for acl [#14956](https://github.com/vitessio/vitess/pull/14956) + * tests: improve coverage for `go/bytes2/buffer.go` [#14958](https://github.com/vitessio/vitess/pull/14958) + * tests: add tests for `go/list` [#14962](https://github.com/vitessio/vitess/pull/14962) + * tests: add tests for `go/json2` [#14964](https://github.com/vitessio/vitess/pull/14964) + * tests: add tests for `go/protoutil/duration` [#14965](https://github.com/vitessio/vitess/pull/14965) + * tests: add tests to `go/mathutil` [#14969](https://github.com/vitessio/vitess/pull/14969) + * tests: add tests for `vitess/go/cmd/zk/internal/zkfilepath` [#14970](https://github.com/vitessio/vitess/pull/14970) + * unit test for go/sets/set.go [#14973](https://github.com/vitessio/vitess/pull/14973) + * Add required tests for `go/mysql/hex` [#14976](https://github.com/vitessio/vitess/pull/14976) + * Remove `AppendFloat` from `go/mysql/format` and add required tests [#14986](https://github.com/vitessio/vitess/pull/14986) + * tests: add tests for `go/sqlescape` [#14987](https://github.com/vitessio/vitess/pull/14987) + * tests: add tests for `go/slice` [#14989](https://github.com/vitessio/vitess/pull/14989) + * tests: Add tests for `go/textutil` [#14991](https://github.com/vitessio/vitess/pull/14991) + * tests: increase coverage for multiple files in `vitess/go/stats` to 100% [#14997](https://github.com/vitessio/vitess/pull/14997) + * tests: add tests for `zkfs` utilities [#15002](https://github.com/vitessio/vitess/pull/15002) + * Add missing tests for `go/event/syslogger` [#15005](https://github.com/vitessio/vitess/pull/15005) + * fix: `Unescape(Escape(str))` now returns the original string [#15009](https://github.com/vitessio/vitess/pull/15009) + * tests: add tests for `go/vt/hook` [#15015](https://github.com/vitessio/vitess/pull/15015) + * Added unit tests for the tools/codegen package [#15016](https://github.com/vitessio/vitess/pull/15016) + * Added unit tests for the tools/releases package [#15017](https://github.com/vitessio/vitess/pull/15017) + * tests: Add tests for `go/vt/external` [#15023](https://github.com/vitessio/vitess/pull/15023) + * Move test files to regular names again [#15037](https://github.com/vitessio/vitess/pull/15037) + * Add required tests for `go/unicode2` [#15051](https://github.com/vitessio/vitess/pull/15051) + * tests: add tests for `go/mathstats` [#15054](https://github.com/vitessio/vitess/pull/15054) + * Added tests for the go/vt/callinfo package [#15059](https://github.com/vitessio/vitess/pull/15059) + * Added tests for the vt/logz package [#15060](https://github.com/vitessio/vitess/pull/15060) + * Add required tests for `go/tb` [#15063](https://github.com/vitessio/vitess/pull/15063) +#### Query Serving + * Fix data race in `TestWarmingReads` [#14187](https://github.com/vitessio/vitess/pull/14187) + * vtgate: Allow more errors for the warning check [#14421](https://github.com/vitessio/vitess/pull/14421) + * vtgate: Allow additional errors in warnings test [#14461](https://github.com/vitessio/vitess/pull/14461) + * Foreign Key Fuzzer Benchmark [#14542](https://github.com/vitessio/vitess/pull/14542) + * test: enable test in downgrade testing [#14625](https://github.com/vitessio/vitess/pull/14625) + * Refactor Upgrade downgrade tests [#14782](https://github.com/vitessio/vitess/pull/14782) + * Add check to avoid runtime error and add tests for `go/mysql/fastparse` [#15000](https://github.com/vitessio/vitess/pull/15000) + * tests: add tests for `vt/vtgate/engine/opcode` [#15045](https://github.com/vitessio/vitess/pull/15045) + * tests: add tests to `go/vt/vtgate/semantics/bitset` [#15049](https://github.com/vitessio/vitess/pull/15049) + * Added test for AnalyzeStrict [#15126](https://github.com/vitessio/vitess/pull/15126) +#### Throttler + * Throttler: refactor global configuration setting as throttler member [#14853](https://github.com/vitessio/vitess/pull/14853) + * Throttler: fix race conditions in Operate() termination and in tests [#14971](https://github.com/vitessio/vitess/pull/14971) +#### Topology + * FlakyFix: `TestZk2Topo` [#14162](https://github.com/vitessio/vitess/pull/14162) +#### VReplication + * VReplication: extended e2e test for workflows with tables containing foreign key constraints [#14327](https://github.com/vitessio/vitess/pull/14327) + * TestStreamMigrateMainflow: fix panic in test [#14420](https://github.com/vitessio/vitess/pull/14420) + * Flaky TestFKExtWorkflow: fix Foreign Key stress test flakiness [#14714](https://github.com/vitessio/vitess/pull/14714) + * Some VReplication e2e Refactoring [#14735](https://github.com/vitessio/vitess/pull/14735) + * Test: Take test host/runner specs into account for VDiff diff duration test [#14868](https://github.com/vitessio/vitess/pull/14868) + * vtctldclient CLI validation: Add e2e test to check that options to the vtctldclient commands are supported [#14957](https://github.com/vitessio/vitess/pull/14957) +#### vtctl + * Reduce flakiness in TestShardReplicationPositions [#14708](https://github.com/vitessio/vitess/pull/14708) + diff --git a/changelog/19.0/19.0.0/release_notes.md b/changelog/19.0/19.0.0/release_notes.md new file mode 100644 index 00000000000..404deaf6bc0 --- /dev/null +++ b/changelog/19.0/19.0.0/release_notes.md @@ -0,0 +1,229 @@ +# Release of Vitess v19.0.0 +## Summary + +### Table of Contents + +- **[Major Changes](#major-changes)** + - **[Dropping Support for MySQL 5.7](#drop-support-mysql57)** + - **[Deprecations and Deletions](#deprecations-and-deletions)** + - [VTTablet Flags](#vttablet-flags) + - [Docker Image vitess/lite](#deprecation-vitess-lite-mysqld) + - [Explain Statement Format](#explain-stmt-format) + - **[Breaking Changes](#breaking-changes)** + - [ExecuteFetchAsDBA rejects multi-statement SQL](#execute-fetch-as-dba-reject-multi) + - **[New Stats](#new-stats)** + - [Stream Consolidations](#stream-consolidations) + - [Build Version in `/debug/vars`](#build-version-in-debug-vars) + - **[Planned Reparent Shard](#planned-reparent-shard)** + - [`--tolerable-replication-lag` Sub-flag](#tolerable-repl-lag) + - **[Query Compatibility](#query-compatibility)** + - [Multi Table Delete Support](#multi-table-delete) + - [`SHOW VSCHEMA KEYSPACES` Query](#show-vschema-keyspaces) + - [`FOREIGN_KEY_CHECKS` is now a Vitess Aware Variable](#fk-checks-vitess-aware) + - [Explain Statement](#explain-statement) + - [Partial Multi-shard Commit Warnings](#partial-multi-shard-commit-warnings) + - [New Lock Syntax](#lock-syntax) + - [Support for AVG()](#avg-support) + - [Support for non-recursive CTEs](#cte-support) + - **[Vttestserver](#vttestserver)** + - [`--vtcombo-bind-host` flag](#vtcombo-bind-host) +- **[Minor Changes](#minor-changes)** + - **[Apply VSchema](#apply-vschema)** + - [`--strict` sub-flag and `strict` gRPC field](#strict-flag-and-field) + +## Major Changes + +### Dropping Support for MySQL 5.7 + +Oracle has marked MySQL 5.7 end of life as of October 2023. Vitess is also dropping support for MySQL 5.7 from v19 onwards. Users are advised to upgrade to MySQL 8.0 while on v18 version of Vitess before +upgrading to v19. + +Vitess will however, continue to support importing from MySQL 5.7 into Vitess even in v19. + + +### Deprecations and Deletions + +- The `MYSQL_FLAVOR` environment variable is now removed from all Docker Images. + +#### VTTablet Flags + +- The following flags — which were deprecated in Vitess 7.0 — have been removed: + `--vreplication_healthcheck_topology_refresh`, `--vreplication_healthcheck_retry_delay`, and `--vreplication_healthcheck_timeout`. +- The `--vreplication_tablet_type` flag is now deprecated and ignored. + +#### Docker Image vitess/lite + +The `mysqld` binary is now deprecated in the `vitess/lite` Docker image and will be removed in a future release. +This means that the MySQL/Percona version specific image tags for the `vitess/lite` image are deprecated. + +Below is a full list of available tags for `v19.0.0` and their deprecation status: + +| Image | Deprecated | +|---------------------------------|------------| +| `vitess/lite:v19.0.0` | NO | +| `vitess/lite:v19.0.0-mysql57` | YES | +| `vitess/lite:v19.0.0-mysql80` | YES | +| `vitess/lite:v19.0.0-percona57` | YES | +| `vitess/lite:v19.0.0-percona80` | YES | + +If you are currently using `vitess/lite` as your `mysqld` image in your vitess-operator deployment we invite you to use an official MySQL image, such as `mysql:8.0.30`. + +Below is an example of a kubernetes yaml file before and after upgrading to an official MySQL image: + +```yaml +# before: + +# the image used here includes MySQL 8.0.30 and its binaries + + mysqld: + mysql80Compatible: vitess/lite:v19.0.0-mysql80 +``` +```yaml +# after: + +# if we still want to use MySQL 8.0.30, we now have to use the +# official MySQL image with the 8.0.30 tag as shown below + + mysqld: + mysql80Compatible: mysql:8.0.30 # or even mysql:8.0.34 for instance +``` + +#### Explain Statement Format + +Explain statement format `vitess` and `vexplain` were deprecated in v16 and removed in v19 version. +Use [VExplain Statement](https://vitess.io/docs/19.0/user-guides/sql/vexplain/) for understanding Vitess plans. + +### Breaking Changes + +#### ExecuteFetchAsDBA rejects multi-statement SQL + +`vtctldclient ExecuteFetchAsDBA` (and similarly the `vtctl` and `vtctlclient` commands) now reject multi-statement SQL with error. + +For example, `vtctldclient ExecuteFetchAsDBA my-tablet "stop replica; change replication source to auto_position=1; start replica` will return an error, without attempting to execute any of these queries. + +Previously, `ExecuteFetchAsDBA` silently accepted multi statement SQL. It would (attempt to) execute all of them, but: + +- It would only indicate error for the first statement. Errors on 2nd, 3rd, ... statements were silently ignored. +- It would not consume the result sets of the 2nd, 3rd, ... statements. It would then return the used connection to the pool in a dirty state. Any further query that happens to take that connection out of the pool could get unexpected results. +- As another side effect, multi-statement schema changes would cause schema to be reloaded with only the first change, leaving the cached schema inconsistent with the underlying database. + +`ExecuteFetchAsDBA` does allow a specific use case of multi-statement SQL, which is where all statements are in the form of `CREATE TABLE` or `CREATE VIEW`. This is to support a common pattern of schema initialization, formalized in `ApplySchema --batch-size` which uses `ExecuteFetchAsDBA` under the hood. + +### New Stats + +#### Stream Consolidations + +Prior to 19.0 VTTablet reported how much time non-streaming executions spend waiting for consolidations to occur. In 19.0, VTTablet reports a similar stat for streaming executions in `/debug/vars` stat `Waits.Histograms.StreamConsolidations`. + +#### Build Version in `/debug/vars` + +The build version (e.g., `19.0.0-SNAPSHOT`) has been added to `/debug/vars`, allowing users to programmatically inspect Vitess components' build version at runtime. + +### Planned Reparent Shard + +#### `--tolerable-replication-lag` Sub-flag + +A new sub-flag `--tolerable-replication-lag` has been added to the command `PlannedReparentShard` that allows users to specify the amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary. +This feature is opt-in and not specifying this sub-flag makes Vitess ignore the replication lag entirely. + +A new flag in VTOrc with the same name has been added to control the behaviour of the PlannedReparentShard calls that VTOrc issues. + +### Query Compatibility + +#### Multi Table Delete Support + +Support is added for sharded multi-table delete with target on single table using multiple table join. + +Example: `Delete t1 from t1 join t2 on t1.id = t2.id join t3 on t1.col = t3.col where t3.foo = 5 and t2.bar = 7` + +More details about how it works is available in [MySQL Docs](https://dev.mysql.com/doc/refman/8.0/en/delete.html) + +#### `SHOW VSCHEMA KEYSPACES` Query + +A SQL query, `SHOW VSCHEMA KEYSPACES` is now supported in Vitess. This query prints the vschema information +for all the keyspaces. It is useful for seeing the foreign key mode, whether the keyspace is sharded, and if there is an +error in the VSchema for the keyspace. + +An example output of the query looks like - +```sql +mysql> show vschema keyspaces; ++----------+---------+-------------+---------+ +| Keyspace | Sharded | Foreign Key | Comment | ++----------+---------+-------------+---------+ +| ks | true | managed | | +| uks | false | managed | | ++----------+---------+-------------+---------+ +2 rows in set (0.01 sec) +``` + +#### `FOREIGN_KEY_CHECKS` is now a Vitess Aware Variable + +When VTGate receives a query to change the `FOREIGN_KEY_CHECKS` value for a session, instead of sending the value down to MySQL, VTGate now keeps track of the value and changes the queries by adding `SET_VAR(FOREIGN_KEY_CHECKS=On/Off)` style query optimizer hints wherever required. + +#### Explain Statement + +`Explain` statement can handle routed table queries now. `Explain` is unsupported when the tables involved in the query refers more than one keyspace. Users should use [VExplain Statement](https://vitess.io/docs/19.0/user-guides/sql/vexplain/) in those cases. + +#### Partial Multi-shard Commit Warnings + +When using `multi` transaction mode (the default), it is possible for Vitess to successfully commit to one shard, but fail to commit to a subsequent shard, thus breaking the atomicity of a multi-shard transaction. + +In `v19.0`, VTGate reports partial-success commits in warnings, e.g.: + +```mysql +mysql> commit; +ERROR 1317 (70100): target: customer.-80.primary: vttablet: rpc error: code = Aborted desc = transaction 1703182545849001001: ended at 2023-12-21 14:07:41.515 EST (exceeded timeout: 30s) (CallerID: userData1) +mysql> show warnings; ++---------+------+----------------------------------------------------------+ +| Level | Code | Message | ++---------+------+----------------------------------------------------------+ +| Warning | 301 | multi-db commit failed after committing to 1 shards: 80- | ++---------+------+----------------------------------------------------------+ +1 row in set, 1 warning (0.00 sec) +``` + +### Vttestserver + +#### `--vtcombo-bind-host` flag + +A new flag `--vtcombo-bind-host` has been added to vttestserver that allows the users to configure the bind host that vtcombo uses. This is especially useful when running vttestserver as a docker image and you want to run vtctld commands and look at the vtcombo `/debug/status` dashboard. + +### New lock syntax + +Vitess now supports the following LOCK syntax + +```sql +SELECT .. FOR SHARE (NOWAIT|SKIP LOCKED) +SELECT .. FOR UPDATE (NOWAIT|SKIP LOCKED) +``` + +### Support for AVG() aggregation function + +Vtgate can now evaluate `AVG` on sharded keyspaces, by using a combination of `SUM/COUNT` + +### Support for non-recursive CTEs + +Common table expressions that are not recursive can now be used. + +```sql +with userCount as ( + select id, count(*) as nr from user group by id) +select ref.col, userCount.nr +from ref join userCount on ref.user_id = userCount.id +``` + +## Minor Changes + +### Apply VSchema + +#### `--strict` sub-flag and `strict` gRPC field + +A new sub-flag `--strict` has been added to the command `ApplyVSchema` `vtctl` command that produces an error if unknown params are found in any Vindexes. An equivalent `strict` field has been added to the `ApplyVSchema` gRPC `vtctld` command. + + +The entire changelog for this release can be found [here](https://github.com/vitessio/vitess/blob/main/changelog/19.0/19.0.0/changelog.md). + +The release includes 412 merged Pull Requests. + +Thanks to all our contributors: @ChaitanyaD48, @EshaanAgg, @FirePing32, @GuptaManan100, @Its-Maniaco, @Maniktherana, @Manni-99, @MrFabio, @VaibhavMalik4187, @ajm188, @aparajon, @app/dependabot, @app/github-actions, @app/vitess-bot, @aquarapid, @arthurschreiber, @austenLacy, @beingnoble03, @brendar, @davidpiegza, @dbussink, @deepthi, @derekperkins, @ejortegau, @frouioui, @gerayking, @glokta1, @harshit-gangal, @iheanyi, @jwangace, @lixin963, @mattlord, @mattrobenolt, @maxenglander, @mcrauwel, @mdlayher, @olyazavr, @pbibra, @pnacht, @rajivharlalka, @ravicodelabs, @rbranson, @rohit-nayak-ps, @samanthadrago, @shlomi-noach, @skullface, @systay, @testwill, @tycol7, @vmg, @wangweicugw, @williammartin, @wlx5575 + diff --git a/changelog/19.0/19.0.0/summary.md b/changelog/19.0/19.0.0/summary.md index 5d413c25cae..498457e0404 100644 --- a/changelog/19.0/19.0.0/summary.md +++ b/changelog/19.0/19.0.0/summary.md @@ -3,21 +3,218 @@ ### Table of Contents - **[Major Changes](#major-changes)** + - **[Dropping Support for MySQL 5.7](#drop-support-mysql57)** - **[Deprecations and Deletions](#deprecations-and-deletions)** - - **[Docker](#docker)** - - [New MySQL Image](#mysql-image) + - [VTTablet Flags](#vttablet-flags) + - [Docker Image vitess/lite](#deprecation-vitess-lite-mysqld) + - [Explain Statement Format](#explain-stmt-format) + - **[Breaking Changes](#breaking-changes)** + - [ExecuteFetchAsDBA rejects multi-statement SQL](#execute-fetch-as-dba-reject-multi) + - **[New Stats](#new-stats)** + - [Stream Consolidations](#stream-consolidations) + - [Build Version in `/debug/vars`](#build-version-in-debug-vars) + - **[Planned Reparent Shard](#planned-reparent-shard)** + - [`--tolerable-replication-lag` Sub-flag](#tolerable-repl-lag) + - **[Query Compatibility](#query-compatibility)** + - [Multi Table Delete Support](#multi-table-delete) + - [`SHOW VSCHEMA KEYSPACES` Query](#show-vschema-keyspaces) + - [`FOREIGN_KEY_CHECKS` is now a Vitess Aware Variable](#fk-checks-vitess-aware) + - [Explain Statement](#explain-statement) + - [Partial Multi-shard Commit Warnings](#partial-multi-shard-commit-warnings) + - [New Lock Syntax](#lock-syntax) + - [Support for AVG()](#avg-support) + - [Support for non-recursive CTEs](#cte-support) + - **[Vttestserver](#vttestserver)** + - [`--vtcombo-bind-host` flag](#vtcombo-bind-host) +- **[Minor Changes](#minor-changes)** + - **[Apply VSchema](#apply-vschema)** + - [`--strict` sub-flag and `strict` gRPC field](#strict-flag-and-field) ## Major Changes +### Dropping Support for MySQL 5.7 + +Oracle has marked MySQL 5.7 end of life as of October 2023. Vitess is also dropping support for MySQL 5.7 from v19 onwards. Users are advised to upgrade to MySQL 8.0 while on v18 version of Vitess before +upgrading to v19. + +Vitess will however, continue to support importing from MySQL 5.7 into Vitess even in v19. + + ### Deprecations and Deletions - The `MYSQL_FLAVOR` environment variable is now removed from all Docker Images. -### Docker +#### VTTablet Flags + +- The following flags — which were deprecated in Vitess 7.0 — have been removed: +`--vreplication_healthcheck_topology_refresh`, `--vreplication_healthcheck_retry_delay`, and `--vreplication_healthcheck_timeout`. +- The `--vreplication_tablet_type` flag is now deprecated and ignored. + +#### Docker Image vitess/lite + +The `mysqld` binary is now deprecated in the `vitess/lite` Docker image and will be removed in a future release. +This means that the MySQL/Percona version specific image tags for the `vitess/lite` image are deprecated. + +Below is a full list of available tags for `v19.0.0` and their deprecation status: + +| Image | Deprecated | +|---------------------------------|------------| +| `vitess/lite:v19.0.0` | NO | +| `vitess/lite:v19.0.0-mysql57` | YES | +| `vitess/lite:v19.0.0-mysql80` | YES | +| `vitess/lite:v19.0.0-percona57` | YES | +| `vitess/lite:v19.0.0-percona80` | YES | + +If you are currently using `vitess/lite` as your `mysqld` image in your vitess-operator deployment we invite you to use an official MySQL image, such as `mysql:8.0.30`. + +Below is an example of a kubernetes yaml file before and after upgrading to an official MySQL image: + +```yaml +# before: + +# the image used here includes MySQL 8.0.30 and its binaries + + mysqld: + mysql80Compatible: vitess/lite:v19.0.0-mysql80 +``` +```yaml +# after: + +# if we still want to use MySQL 8.0.30, we now have to use the +# official MySQL image with the 8.0.30 tag as shown below + + mysqld: + mysql80Compatible: mysql:8.0.30 # or even mysql:8.0.34 for instance +``` + +#### Explain Statement Format + +Explain statement format `vitess` and `vexplain` were deprecated in v16 and removed in v19 version. +Use [VExplain Statement](https://vitess.io/docs/19.0/user-guides/sql/vexplain/) for understanding Vitess plans. + +### Breaking Changes + +#### ExecuteFetchAsDBA rejects multi-statement SQL + +`vtctldclient ExecuteFetchAsDBA` (and similarly the `vtctl` and `vtctlclient` commands) now reject multi-statement SQL with error. + +For example, `vtctldclient ExecuteFetchAsDBA my-tablet "stop replica; change replication source to auto_position=1; start replica` will return an error, without attempting to execute any of these queries. + +Previously, `ExecuteFetchAsDBA` silently accepted multi statement SQL. It would (attempt to) execute all of them, but: + +- It would only indicate error for the first statement. Errors on 2nd, 3rd, ... statements were silently ignored. +- It would not consume the result sets of the 2nd, 3rd, ... statements. It would then return the used connection to the pool in a dirty state. Any further query that happens to take that connection out of the pool could get unexpected results. +- As another side effect, multi-statement schema changes would cause schema to be reloaded with only the first change, leaving the cached schema inconsistent with the underlying database. + +`ExecuteFetchAsDBA` does allow a specific use case of multi-statement SQL, which is where all statements are in the form of `CREATE TABLE` or `CREATE VIEW`. This is to support a common pattern of schema initialization, formalized in `ApplySchema --batch-size` which uses `ExecuteFetchAsDBA` under the hood. + +### New Stats + +#### Stream Consolidations + +Prior to 19.0 VTTablet reported how much time non-streaming executions spend waiting for consolidations to occur. In 19.0, VTTablet reports a similar stat for streaming executions in `/debug/vars` stat `Waits.Histograms.StreamConsolidations`. + +#### Build Version in `/debug/vars` + +The build version (e.g., `19.0.0-SNAPSHOT`) has been added to `/debug/vars`, allowing users to programmatically inspect Vitess components' build version at runtime. + +### Planned Reparent Shard + +#### `--tolerable-replication-lag` Sub-flag + +A new sub-flag `--tolerable-replication-lag` has been added to the command `PlannedReparentShard` that allows users to specify the amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary. +This feature is opt-in and not specifying this sub-flag makes Vitess ignore the replication lag entirely. + +A new flag in VTOrc with the same name has been added to control the behaviour of the PlannedReparentShard calls that VTOrc issues. + +### Query Compatibility + +#### Multi Table Delete Support + +Support is added for sharded multi-table delete with target on single table using multiple table join. + +Example: `Delete t1 from t1 join t2 on t1.id = t2.id join t3 on t1.col = t3.col where t3.foo = 5 and t2.bar = 7` + +More details about how it works is available in [MySQL Docs](https://dev.mysql.com/doc/refman/8.0/en/delete.html) + +#### `SHOW VSCHEMA KEYSPACES` Query + +A SQL query, `SHOW VSCHEMA KEYSPACES` is now supported in Vitess. This query prints the vschema information +for all the keyspaces. It is useful for seeing the foreign key mode, whether the keyspace is sharded, and if there is an +error in the VSchema for the keyspace. + +An example output of the query looks like - +```sql +mysql> show vschema keyspaces; ++----------+---------+-------------+---------+ +| Keyspace | Sharded | Foreign Key | Comment | ++----------+---------+-------------+---------+ +| ks | true | managed | | +| uks | false | managed | | ++----------+---------+-------------+---------+ +2 rows in set (0.01 sec) +``` + +#### `FOREIGN_KEY_CHECKS` is now a Vitess Aware Variable + +When VTGate receives a query to change the `FOREIGN_KEY_CHECKS` value for a session, instead of sending the value down to MySQL, VTGate now keeps track of the value and changes the queries by adding `SET_VAR(FOREIGN_KEY_CHECKS=On/Off)` style query optimizer hints wherever required. + +#### Explain Statement + +`Explain` statement can handle routed table queries now. `Explain` is unsupported when the tables involved in the query refers more than one keyspace. Users should use [VExplain Statement](https://vitess.io/docs/19.0/user-guides/sql/vexplain/) in those cases. + +#### Partial Multi-shard Commit Warnings + +When using `multi` transaction mode (the default), it is possible for Vitess to successfully commit to one shard, but fail to commit to a subsequent shard, thus breaking the atomicity of a multi-shard transaction. + +In `v19.0`, VTGate reports partial-success commits in warnings, e.g.: + +```mysql +mysql> commit; +ERROR 1317 (70100): target: customer.-80.primary: vttablet: rpc error: code = Aborted desc = transaction 1703182545849001001: ended at 2023-12-21 14:07:41.515 EST (exceeded timeout: 30s) (CallerID: userData1) +mysql> show warnings; ++---------+------+----------------------------------------------------------+ +| Level | Code | Message | ++---------+------+----------------------------------------------------------+ +| Warning | 301 | multi-db commit failed after committing to 1 shards: 80- | ++---------+------+----------------------------------------------------------+ +1 row in set, 1 warning (0.00 sec) +``` + +### Vttestserver + +#### `--vtcombo-bind-host` flag + +A new flag `--vtcombo-bind-host` has been added to vttestserver that allows the users to configure the bind host that vtcombo uses. This is especially useful when running vttestserver as a docker image and you want to run vtctld commands and look at the vtcombo `/debug/status` dashboard. + +### New lock syntax + +Vitess now supports the following LOCK syntax + +```sql +SELECT .. FOR SHARE (NOWAIT|SKIP LOCKED) +SELECT .. FOR UPDATE (NOWAIT|SKIP LOCKED) +``` + +### Support for AVG() aggregation function + +Vtgate can now evaluate `AVG` on sharded keyspaces, by using a combination of `SUM/COUNT` + +### Support for non-recursive CTEs + +Common table expressions that are not recursive can now be used. + +```sql +with userCount as ( + select id, count(*) as nr from user group by id) +select ref.col, userCount.nr +from ref join userCount on ref.user_id = userCount.id +``` + +## Minor Changes -#### New MySQL Image +### Apply VSchema -In `v19.0` the Vitess team is shipping a new image: `vitess/mysql`. -This lightweight image is a replacement of `vitess/lite` to only run `mysqld`. +#### `--strict` sub-flag and `strict` gRPC field -Several tags are available to let you choose what version of MySQL you want to use: `vitess/mysql:8.0.30`, `vitess/mysql:8.0.34`. +A new sub-flag `--strict` has been added to the command `ApplyVSchema` `vtctl` command that produces an error if unknown params are found in any Vindexes. An equivalent `strict` field has been added to the `ApplyVSchema` gRPC `vtctld` command. diff --git a/changelog/19.0/README.md b/changelog/19.0/README.md index aecff732ce1..b86c2311488 100644 --- a/changelog/19.0/README.md +++ b/changelog/19.0/README.md @@ -1,2 +1,4 @@ ## v19.0 * **[19.0.0](19.0.0)** + * [Changelog](19.0.0/changelog.md) + * [Release Notes](19.0.0/release_notes.md) diff --git a/changelog/20.0/20.0.0/summary.md b/changelog/20.0/20.0.0/summary.md new file mode 100644 index 00000000000..bb376c6e721 --- /dev/null +++ b/changelog/20.0/20.0.0/summary.md @@ -0,0 +1,74 @@ +## Summary + +### Table of Contents + +- **[Major Changes](#major-changes)** + - **[Query Compatibility](#query-compatibility)** + - [Vindex Hints](#vindex-hints) + - [Update with Limit Support](#update-limit) + - [Update with Multi Table Support](#multi-table-update) + - [Delete with Subquery Support](#delete-subquery) + - **[Flag changes](#flag-changes)** + - [`pprof-http` default change](#pprof-http-default) +- **[Minor Changes](#minor-changes)** + - **[New Stats](#new-stats)** + - [VTTablet Query Cache Hits and Misses](#vttablet-query-cache-hits-and-misses) + +## Major Changes + +### Query Compatibility + +#### Vindex Hints + +Vitess now supports Vindex hints that provide a way for users to influence the shard routing of queries in Vitess by specifying, which vindexes should be considered or ignored by the query planner. This feature enhances the control over query execution, allowing for potentially more efficient data access patterns in sharded databases. + +Example: + ```sql + SELECT * FROM user USE VINDEX (hash_user_id, secondary_vindex) WHERE user_id = 123; + SELECT * FROM order IGNORE VINDEX (range_order_id) WHERE order_date = '2021-01-01'; + ``` + +For more information about Vindex hints and its usage, please consult the documentation. + +#### Update with Limit Support + +Support is added for sharded update with limit. + +Example: `update t1 set t1.foo = 'abc', t1.bar = 23 where t1.baz > 5 limit 1` + +More details about how it works is available in [MySQL Docs](https://dev.mysql.com/doc/refman/8.0/en/update.html) + +#### Update with Multi Table Support + +Support is added for sharded multi-table update with column update on single target table using multiple table join. + +Example: `update t1 join t2 on t1.id = t2.id join t3 on t1.col = t3.col set t1.baz = 'abc', t1.apa = 23 where t3.foo = 5 and t2.bar = 7` + +More details about how it works is available in [MySQL Docs](https://dev.mysql.com/doc/refman/8.0/en/update.html) + +#### Delete with Subquery Support + +Support is added for sharded table delete with subquery + +Example: `delete from t1 where id in (select col from t2 where foo = 32 and bar = 43)` + + +### Flag Changes + +#### `pprof-http` Default Change + +The `--pprof-http` flag, which was introduced in v19 with a default of `true`, has now been changed to default to `false`. +This makes HTTP `pprof` endpoints now an *opt-in* feature, rather than opt-out. +To continue enabling these endpoints, explicitly set `--pprof-http` when starting up Vitess components. + + +## Minor Changes + +### New Stats + +#### VTTablet Query Cache Hits and Misses + +VTTablet exposes two new counter stats: + + * `QueryCacheHits`: Query engine query cache hits + * `QueryCacheMisses`: Query engine query cache misses diff --git a/changelog/20.0/README.md b/changelog/20.0/README.md new file mode 100644 index 00000000000..4fb70ae78c1 --- /dev/null +++ b/changelog/20.0/README.md @@ -0,0 +1,2 @@ +## v20.0 +* **[20.0.0](20.0.0)** diff --git a/changelog/README.md b/changelog/README.md index 66ed9543e5d..3a55d986643 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -1,4 +1,5 @@ ## Releases +* [20.0](20.0) * [19.0](19.0) * [18.0](18.0) * [17.0](17.0) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000000..978fc499df1 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,53 @@ +# https://docs.codecov.com/docs/ +# https://docs.codecov.com/docs/codecov-yaml + +codecov: + branch: main # Set the default branch + +parsers: + go: + partials_as_hits: true + +ignore: + # Ignore our end-to-end test code + - "go/flags/endtoend/**" + - "go/mysql/endtoend/**" + - "go/test/endtoend/**" + - "go/vt/vtctl/endtoend/**" + - "go/vt/vtctl/grpcvtctldserver/endtoend/**" + - "go/vt/vtgate/endtoend/**" + - "go/vt/vttablet/endtoend/**" + - "go/cmd/vttestserver/**" # This relies on end-to-end test packages + # Ignore generated code + - "go/**/cached_size.go" # Code generated by Sizegen + - "go/vt/sqlparser/ast_clone.go" # Code generated by ASTHelperGen + - "go/vt/sqlparser/ast_copy_on_rewrite.go" # Code generated by ASTHelperGen + - "go/vt/sqlparser/ast_equals.go" # Code generated by ASTHelperGen + - "go/vt/sqlparser/ast_format_fast.go" # Code generated by ASTFmtGen + - "go/vt/sqlparser/ast_rewrite.go" # Code generated by ASTHelperGen + - "go/vt/sqlparser/ast_visit.go" # Code generated by ASTHelperGen + - "go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go" # Code generated by MockGen + - "go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go" # Code generated by MockGen + - "go/vt/sqlparser/sql.go" # Code generated by goyacc + - "go/mysql/collations/charset/korean/tables.go" # Code generated by go run maketables.go + - "go/mysql/collations/charset/simplifiedchinese/tables.go" # Code generated by go run maketables.go + - "go/mysql/collations/colldata/mysqldata.go" # Code generated by makecolldata + - "go/mysql/collations/colldata/mysqlucadata.go" # Code generated by makecolldata + - "go/mysql/collations/internal/uca/fasttables.go" # Code generated by makecolldata + - "go/mysql/collations/mysqlversion.go" # Code generated by makecolldata + - "go/mysql/collations/supported.go" # Code generated by makecolldata + # Ignore proto files + - "go/vt/proto/**" + +comment: # https://docs.codecov.com/docs/pull-request-comments + hide_project_coverage: false + +coverage: + status: # https://docs.codecov.com/docs/commit-status + patch: + default: + informational: true # Don't ever fail the codecov/patch test + project: + default: + informational: true # Don't ever fail the codecov/project test + diff --git a/config/mycnf/test-suite.cnf b/config/mycnf/test-suite.cnf index e6d0992f6e6..28f4ac16e0d 100644 --- a/config/mycnf/test-suite.cnf +++ b/config/mycnf/test-suite.cnf @@ -1,5 +1,5 @@ # This sets some unsafe settings specifically for -# the test-suite which is currently MySQL 5.7 based +# the test-suite which is currently MySQL 8.0 based # In future it should be renamed testsuite.cnf innodb_buffer_pool_size = 32M @@ -14,13 +14,6 @@ key_buffer_size = 2M sync_binlog=0 innodb_doublewrite=0 -# These two settings are required for the testsuite to pass, -# but enabling them does not spark joy. They should be removed -# in the future. See: -# https://github.com/vitessio/vitess/issues/5396 - -sql_mode = STRICT_TRANS_TABLES - # set a short heartbeat interval in order to detect failures quickly slave_net_timeout = 4 # Disabling `super-read-only`. `test-suite` is mainly used for `vttestserver`. Since `vttestserver` uses a single MySQL for primary and replicas, diff --git a/doc/internal/release/how-to-release.md b/doc/internal/release/how-to-release.md index fd5caa81e03..08411f9c0ac 100644 --- a/doc/internal/release/how-to-release.md +++ b/doc/internal/release/how-to-release.md @@ -83,6 +83,7 @@ That includes: > - There are several pages we want to update: > - [The releases page](https://vitess.io/docs/releases/): we must add the new release to the list with all its information and link. The links can be broken (404 error) while we are preparing for the release, this is fine. > - [The local install page](https://vitess.io/docs/get-started/local/): we must use the proper version increment for this guide and the proper SHA. The SHA will have to be modified once the Release Pull Request and the release is tagged is merged. + > - [The Vitess Operator for Kubernetes page](https://vitess.io/docs/get-started/operator/#install-the-operator), [the Local Install via source for Mac page](https://vitess.io/docs/get-started/local-mac/#install-vitess), [the Local Install via Docker page](https://vitess.io/docs/get-started/local-docker/#check-out-the-vitessiovitess-repository), and [the Vttestserver Docker Image page](https://vitess.io/docs/get-started/vttestserver-docker-image/#check-out-the-vitessiovitess-repository): we must checkout to the proper release branch after cloning Vitess. > - If we are doing a GA or RC release follow the instructions below: > - There are two scripts in the website repository in `./tools/{ga|rc}_release.sh`, use them to update the website documentation. The scripts automate: > - For an RC, we need to create a new entry in the sidebar which represents the next version on `main` and mark the version we are releasing as RC. diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index 75c51b4ad1b..31e9a7c0872 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" diff --git a/docker/base/Dockerfile.mysql57 b/docker/base/Dockerfile.mysql57 index 586cf1d94da..1b21fc1e7e8 100644 --- a/docker/base/Dockerfile.mysql57 +++ b/docker/base/Dockerfile.mysql57 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57" FROM "${image}" diff --git a/docker/base/Dockerfile.percona57 b/docker/base/Dockerfile.percona57 index fce0412250b..06ac1786390 100644 --- a/docker/base/Dockerfile.percona57 +++ b/docker/base/Dockerfile.percona57 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona57" FROM "${image}" diff --git a/docker/base/Dockerfile.percona80 b/docker/base/Dockerfile.percona80 index a236035c511..e70f2dbed42 100644 --- a/docker/base/Dockerfile.percona80 +++ b/docker/base/Dockerfile.percona80 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona80" FROM "${image}" diff --git a/docker/bootstrap/CHANGELOG.md b/docker/bootstrap/CHANGELOG.md index e363dfc0ded..fe57579243c 100644 --- a/docker/bootstrap/CHANGELOG.md +++ b/docker/bootstrap/CHANGELOG.md @@ -92,4 +92,20 @@ List of changes between bootstrap image versions. ## [24] - 2023-10-10 ### Changes -- Update build to golang 1.21.3 \ No newline at end of file +- Update build to golang 1.21.3 + +## [25] - 2023-11-08 +### Changes +- Update build to golang 1.21.4 + +## [26] - 2023-12-06 +### Changes +- Update build to golang 1.21.5 + +## [27] - 2024-01-10 +### Changes +- Update build to golang 1.21.6 + +## [28] - 2024-02-07 +### Changes +- Update build to golang 1.22.0 \ No newline at end of file diff --git a/docker/bootstrap/Dockerfile.common b/docker/bootstrap/Dockerfile.common index 39b0c16566a..d3a99328f8f 100644 --- a/docker/bootstrap/Dockerfile.common +++ b/docker/bootstrap/Dockerfile.common @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 golang:1.21.3-bullseye +FROM --platform=linux/amd64 golang:1.22.0-bullseye # Install Vitess build dependencies RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ diff --git a/docker/bootstrap/Dockerfile.mysql57 b/docker/bootstrap/Dockerfile.mysql57 index 4e9b335ddac..4d79be9d3ec 100644 --- a/docker/bootstrap/Dockerfile.mysql57 +++ b/docker/bootstrap/Dockerfile.mysql57 @@ -5,7 +5,7 @@ FROM --platform=linux/amd64 "${image}" # Install MySQL 5.7 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gnupg dirmngr ca-certificates && \ - for i in $(seq 1 10); do apt-key adv --no-tty --recv-keys --keyserver keyserver.ubuntu.com 467B942D3A79BD29 && break; done && \ + for i in $(seq 1 10); do apt-key adv --no-tty --recv-keys --keyserver keyserver.ubuntu.com A8D3785C && break; done && \ add-apt-repository 'deb http://repo.mysql.com/apt/debian/ buster mysql-5.7' && \ for i in $(seq 1 10); do apt-key adv --no-tty --keyserver keyserver.ubuntu.com --recv-keys 9334A25F8507EFA5 && break; done && \ echo 'deb http://repo.percona.com/apt buster main' > /etc/apt/sources.list.d/percona.list && \ diff --git a/docker/bootstrap/Dockerfile.mysql80 b/docker/bootstrap/Dockerfile.mysql80 index 46dec046411..ff125eb7c83 100644 --- a/docker/bootstrap/Dockerfile.mysql80 +++ b/docker/bootstrap/Dockerfile.mysql80 @@ -5,7 +5,7 @@ FROM --platform=linux/amd64 "${image}" # Install MySQL 8.0 RUN for i in $(seq 1 10); do apt-key adv --no-tty --recv-keys --keyserver keyserver.ubuntu.com 8C718D3B5072E1F5 && break; done && \ - for i in $(seq 1 10); do apt-key adv --no-tty --recv-keys --keyserver keyserver.ubuntu.com 467B942D3A79BD29 && break; done && \ + for i in $(seq 1 10); do apt-key adv --no-tty --recv-keys --keyserver keyserver.ubuntu.com A8D3785C && break; done && \ add-apt-repository 'deb http://repo.mysql.com/apt/debian/ bullseye mysql-8.0' && \ for i in $(seq 1 10); do apt-key adv --no-tty --keyserver keyserver.ubuntu.com --recv-keys 9334A25F8507EFA5 && break; done && \ echo 'deb http://repo.percona.com/apt bullseye main' > /etc/apt/sources.list.d/percona.list && \ diff --git a/docker/lite/Dockerfile.mysql57 b/docker/lite/Dockerfile.mysql57 index 8c07b1a4411..cec9852ed2a 100644 --- a/docker/lite/Dockerfile.mysql57 +++ b/docker/lite/Dockerfile.mysql57 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.mysql80 b/docker/lite/Dockerfile.mysql80 index bc4ad7861c8..74abb9b4f71 100644 --- a/docker/lite/Dockerfile.mysql80 +++ b/docker/lite/Dockerfile.mysql80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.percona57 b/docker/lite/Dockerfile.percona57 index 39d31542fe8..8c95363f7a8 100644 --- a/docker/lite/Dockerfile.percona57 +++ b/docker/lite/Dockerfile.percona57 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona57" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.percona80 b/docker/lite/Dockerfile.percona80 index e20359ea300..e7b5bb9c09b 100644 --- a/docker/lite/Dockerfile.percona80 +++ b/docker/lite/Dockerfile.percona80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona80" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.testing b/docker/lite/Dockerfile.testing index 118db24699b..40353b3a48d 100644 --- a/docker/lite/Dockerfile.testing +++ b/docker/lite/Dockerfile.testing @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.ubi7.mysql57 b/docker/lite/Dockerfile.ubi7.mysql57 index 08ae84cfeb0..8ae4777e87f 100644 --- a/docker/lite/Dockerfile.ubi7.mysql57 +++ b/docker/lite/Dockerfile.ubi7.mysql57 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.ubi7.mysql80 b/docker/lite/Dockerfile.ubi7.mysql80 index c11ac4a6ed4..7f1772b1749 100644 --- a/docker/lite/Dockerfile.ubi7.mysql80 +++ b/docker/lite/Dockerfile.ubi7.mysql80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.ubi7.percona57 b/docker/lite/Dockerfile.ubi7.percona57 index ef55a6b527a..79c9377c63f 100644 --- a/docker/lite/Dockerfile.ubi7.percona57 +++ b/docker/lite/Dockerfile.ubi7.percona57 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona57" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.ubi7.percona80 b/docker/lite/Dockerfile.ubi7.percona80 index 61092685177..6aed00787d4 100644 --- a/docker/lite/Dockerfile.ubi7.percona80 +++ b/docker/lite/Dockerfile.ubi7.percona80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-percona80" FROM "${image}" AS builder diff --git a/docker/lite/Dockerfile.ubi8.arm64.mysql80 b/docker/lite/Dockerfile.ubi8.arm64.mysql80 index 5c2e99c3b51..95ac16afde3 100644 --- a/docker/lite/Dockerfile.ubi8.arm64.mysql80 +++ b/docker/lite/Dockerfile.ubi8.arm64.mysql80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" AS builder @@ -39,13 +39,13 @@ FROM registry.access.redhat.com/ubi8/ubi:latest RUN rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 RUN mkdir /tmp/gpg && chmod 700 /tmp/gpg && export GNUPGHOME=/tmp/gpg \ && yum install -y --setopt=alwaysprompt=no gnupg \ - && ( gpg --keyserver keyserver.ubuntu.com --recv-keys 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A 4D1BB29D63D98E422B2113B19334A25F8507EFA5 99DB70FAE1D7CE227FB6488205B555B38483C65D 3A79BD29 A4A9406876FCBD3C456770C88C718D3B5072E1F5 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 ) \ + && ( gpg --keyserver keyserver.ubuntu.com --recv-keys 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A 4D1BB29D63D98E422B2113B19334A25F8507EFA5 99DB70FAE1D7CE227FB6488205B555B38483C65D A8D3785C A4A9406876FCBD3C456770C88C718D3B5072E1F5 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 ) \ # No xtrabackup packages for aarch64 yet, but still keeping this here && gpg --export --armor 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A > ${GNUPGHOME}/RPM-GPG-KEY-Percona.1 \ && gpg --export --armor 4D1BB29D63D98E422B2113B19334A25F8507EFA5 > ${GNUPGHOME}/RPM-GPG-KEY-Percona.2 \ && gpg --export --armor 99DB70FAE1D7CE227FB6488205B555B38483C65D > ${GNUPGHOME}/RPM-GPG-KEY-CentOS-8 \ && gpg --export --armor 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 > ${GNUPGHOME}/RPM-GPG-KEY-EPEL-8 \ - && gpg --export --armor 3A79BD29 > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 \ + && gpg --export --armor A8D3785C > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 \ && gpg --export --armor A4A9406876FCBD3C456770C88C718D3B5072E1F5 > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.2 \ && rpmkeys --import ${GNUPGHOME}/RPM-GPG-KEY-Percona.1 ${GNUPGHOME}/RPM-GPG-KEY-Percona.2 ${GNUPGHOME}/RPM-GPG-KEY-CentOS-8 ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 ${GNUPGHOME}/RPM-GPG-KEY-MySQL.2 /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release \ && curl -L --retry-delay 10 --retry 3 -o /tmp/mysqlrepo.rpm https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm \ diff --git a/docker/lite/Dockerfile.ubi8.mysql80 b/docker/lite/Dockerfile.ubi8.mysql80 index 094dc8fa712..f7b0f27dd95 100644 --- a/docker/lite/Dockerfile.ubi8.mysql80 +++ b/docker/lite/Dockerfile.ubi8.mysql80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" AS builder @@ -39,12 +39,12 @@ FROM registry.access.redhat.com/ubi8/ubi:latest RUN rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 RUN mkdir /tmp/gpg && chmod 700 /tmp/gpg && export GNUPGHOME=/tmp/gpg \ && yum install -y --setopt=alwaysprompt=no gnupg \ - && ( gpg --keyserver keyserver.ubuntu.com --recv-keys 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A 4D1BB29D63D98E422B2113B19334A25F8507EFA5 99DB70FAE1D7CE227FB6488205B555B38483C65D 3A79BD29 A4A9406876FCBD3C456770C88C718D3B5072E1F5 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 ) \ + && ( gpg --keyserver keyserver.ubuntu.com --recv-keys 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A 4D1BB29D63D98E422B2113B19334A25F8507EFA5 99DB70FAE1D7CE227FB6488205B555B38483C65D A8D3785C A4A9406876FCBD3C456770C88C718D3B5072E1F5 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 ) \ && gpg --export --armor 430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A > ${GNUPGHOME}/RPM-GPG-KEY-Percona.1 \ && gpg --export --armor 4D1BB29D63D98E422B2113B19334A25F8507EFA5 > ${GNUPGHOME}/RPM-GPG-KEY-Percona.2 \ && gpg --export --armor 99DB70FAE1D7CE227FB6488205B555B38483C65D > ${GNUPGHOME}/RPM-GPG-KEY-CentOS-8 \ && gpg --export --armor 94E279EB8D8F25B21810ADF121EA45AB2F86D6A1 > ${GNUPGHOME}/RPM-GPG-KEY-EPEL-8 \ - && gpg --export --armor 3A79BD29 > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 \ + && gpg --export --armor A8D3785C > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 \ && gpg --export --armor A4A9406876FCBD3C456770C88C718D3B5072E1F5 > ${GNUPGHOME}/RPM-GPG-KEY-MySQL.2 \ && rpmkeys --import ${GNUPGHOME}/RPM-GPG-KEY-Percona.1 ${GNUPGHOME}/RPM-GPG-KEY-Percona.2 ${GNUPGHOME}/RPM-GPG-KEY-CentOS-8 ${GNUPGHOME}/RPM-GPG-KEY-MySQL.1 ${GNUPGHOME}/RPM-GPG-KEY-MySQL.2 /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release ${GNUPGHOME}/RPM-GPG-KEY-EPEL-8 \ && curl -L --retry-delay 10 --retry 3 -o /tmp/mysqlrepo.rpm https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm \ diff --git a/docker/lite/install_dependencies.sh b/docker/lite/install_dependencies.sh index 0cbc47fd9cf..b686c2418bf 100755 --- a/docker/lite/install_dependencies.sh +++ b/docker/lite/install_dependencies.sh @@ -146,7 +146,7 @@ esac # Get GPG keys for extra apt repositories. # repo.mysql.com add_apt_key 8C718D3B5072E1F5 -add_apt_key 467B942D3A79BD29 +add_apt_key A8D3785C # All flavors include Percona XtraBackup (from repo.percona.com). add_apt_key 9334A25F8507EFA5 diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 6643799842d..991ccb19d93 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -1,4 +1,4 @@ -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-common" FROM "${image}" diff --git a/docker/mysql/Dockerfile b/docker/mysql/Dockerfile deleted file mode 120000 index 6671907eaf2..00000000000 --- a/docker/mysql/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -Dockerfile.8.0.30 \ No newline at end of file diff --git a/docker/mysql/Dockerfile.8.0.30 b/docker/mysql/Dockerfile.8.0.30 deleted file mode 100644 index 5b5e68263fe..00000000000 --- a/docker/mysql/Dockerfile.8.0.30 +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2023 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM debian:bullseye-slim - -RUN mkdir -p /vt/dist - -# Install dependencies -COPY docker/lite/install_dependencies.sh /vt/dist/install_dependencies.sh -RUN /vt/dist/install_dependencies.sh mysql80 8.0.30 - -# Set up Vitess user and directory tree. -RUN groupadd -r vitess && useradd -r -g vitess vitess -RUN mkdir -p /vt/vtdataroot && chown -R vitess:vitess /vt - -VOLUME /vt/vtdataroot -USER vitess \ No newline at end of file diff --git a/docker/mysql/Dockerfile.8.0.34 b/docker/mysql/Dockerfile.8.0.34 deleted file mode 100644 index 5bd81d9802c..00000000000 --- a/docker/mysql/Dockerfile.8.0.34 +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2023 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM debian:bullseye-slim - -RUN mkdir -p /vt/dist - -# Install dependencies -COPY docker/lite/install_dependencies.sh /vt/dist/install_dependencies.sh -RUN /vt/dist/install_dependencies.sh mysql80 8.0.34 - -# Set up Vitess user and directory tree. -RUN groupadd -r vitess && useradd -r -g vitess vitess -RUN mkdir -p /vt/vtdataroot && chown -R vitess:vitess /vt - -VOLUME /vt/vtdataroot -USER vitess \ No newline at end of file diff --git a/docker/vttestserver/Dockerfile.mysql57 b/docker/vttestserver/Dockerfile.mysql57 index 195f0cd62e4..a4485cdb56b 100644 --- a/docker/vttestserver/Dockerfile.mysql57 +++ b/docker/vttestserver/Dockerfile.mysql57 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql57" FROM "${image}" AS builder @@ -58,4 +58,4 @@ USER vitess COPY docker/vttestserver/setup_vschema_folder.sh /vt/setup_vschema_folder.sh COPY docker/vttestserver/run.sh /vt/run.sh -CMD /vt/run.sh "5.7.9-vitess" +CMD /vt/run.sh "5.7.31-vitess" diff --git a/docker/vttestserver/Dockerfile.mysql80 b/docker/vttestserver/Dockerfile.mysql80 index 2dcc190a957..229cef16e51 100644 --- a/docker/vttestserver/Dockerfile.mysql80 +++ b/docker/vttestserver/Dockerfile.mysql80 @@ -17,7 +17,7 @@ # ensure images contain the right binaries. # Use a temporary layer for the build stage. -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-mysql80" FROM "${image}" AS builder @@ -58,4 +58,4 @@ USER vitess COPY docker/vttestserver/setup_vschema_folder.sh /vt/setup_vschema_folder.sh COPY docker/vttestserver/run.sh /vt/run.sh -CMD /vt/run.sh "8.0.21-vitess" +CMD /vt/run.sh "8.0.30-Vitess" diff --git a/docker/vttestserver/run.sh b/docker/vttestserver/run.sh index 1ff79153af5..e3a99ab38f4 100755 --- a/docker/vttestserver/run.sh +++ b/docker/vttestserver/run.sh @@ -35,6 +35,7 @@ rm -vf "$VTDATAROOT"/"$tablet_dir"/{mysql.sock,mysql.sock.lock} --keyspaces "$KEYSPACES" \ --num_shards "$NUM_SHARDS" \ --mysql_bind_host "${MYSQL_BIND_HOST:-127.0.0.1}" \ + --vtcombo-bind-host "${VTCOMBO_BIND_HOST:-127.0.0.1}" \ --mysql_server_version "${MYSQL_SERVER_VERSION:-$1}" \ --charset "${CHARSET:-utf8mb4}" \ --foreign_key_mode "${FOREIGN_KEY_MODE:-allow}" \ diff --git a/examples/backups/stop_tablets.sh b/examples/backups/stop_tablets.sh index 6a3ced6ab74..d387128309c 100755 --- a/examples/backups/stop_tablets.sh +++ b/examples/backups/stop_tablets.sh @@ -30,7 +30,25 @@ for tablet in 100 200 300; do CELL=zone1 TABLET_UID=$uid ../common/scripts/mysqlctl-down.sh echo "Removing tablet directory zone1-$uid" vtctldclient DeleteTablets --allow-primary zone1-$uid - rm -Rf $VTDATAROOT/vt_0000000$uid + + for ((i=0; i<30; i++)); do + # Redirect stderr to a temporary file + temp_file=$(mktemp) + rm -Rf $VTDATAROOT/vt_0000000$uid 2>"$temp_file" + + if grep -q 'Directory not empty' "$temp_file"; then + echo "Directory not empty, retrying..." + elif [ ! -s "$temp_file" ]; then + echo "Deletion succeeded." + rm -f "$temp_file" + break + else + echo "An error occurred." + cat "$temp_file" + fi + rm -f "$temp_file" + sleep 1 + done done fi done diff --git a/examples/common/scripts/consul-up.sh b/examples/common/scripts/consul-up.sh index 584a25f437a..fb75495b278 100755 --- a/examples/common/scripts/consul-up.sh +++ b/examples/common/scripts/consul-up.sh @@ -40,13 +40,13 @@ sleep 5 # Add the CellInfo description for the cell. # If the node already exists, it's fine, means we used existing data. -echo "add $cell CellInfo" +echo "add ${cell} CellInfo" set +e # shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS VtctldCommand AddCellInfo \ - --root "vitess/$cell" \ +command vtctldclient --server internal --topo-implementation consul --topo-global-server "${CONSUL_SERVER}:${consul_http_port}" AddCellInfo \ + --root "/vitess/${cell}" \ --server-address "${CONSUL_SERVER}:${consul_http_port}" \ - "$cell" + "${cell}" set -e echo "consul start done..." diff --git a/examples/common/scripts/etcd-up.sh b/examples/common/scripts/etcd-up.sh index ac81c1fbd28..1ed22ffce2e 100755 --- a/examples/common/scripts/etcd-up.sh +++ b/examples/common/scripts/etcd-up.sh @@ -32,13 +32,12 @@ sleep 5 # And also add the CellInfo description for the cell. # If the node already exists, it's fine, means we used existing data. -echo "add $cell CellInfo" +echo "add ${cell} CellInfo" set +e -# shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS VtctldCommand AddCellInfo \ - --root /vitess/$cell \ +command vtctldclient --server internal AddCellInfo \ + --root "/vitess/${cell}" \ --server-address "${ETCD_SERVER}" \ - $cell + "${cell}" set -e echo "etcd is running!" diff --git a/examples/common/scripts/vttablet-up.sh b/examples/common/scripts/vttablet-up.sh index 56d212af218..daa40aee894 100755 --- a/examples/common/scripts/vttablet-up.sh +++ b/examples/common/scripts/vttablet-up.sh @@ -53,8 +53,6 @@ vttablet \ --grpc_port $grpc_port \ --service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ --pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ - --heartbeat_enable \ - --heartbeat_interval=250ms \ --heartbeat_on_demand_duration=5s \ > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & diff --git a/examples/common/scripts/zk-up.sh b/examples/common/scripts/zk-up.sh index 3137ed724cc..2b79053d2f6 100755 --- a/examples/common/scripts/zk-up.sh +++ b/examples/common/scripts/zk-up.sh @@ -52,10 +52,10 @@ echo "Started zk servers." # If the node already exists, it's fine, means we used existing data. set +e # shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS VtctldCommand AddCellInfo \ - --root /vitess/$cell \ - --server-address $ZK_SERVER \ - $cell +command vtctldclient --server internal --topo-implementation zk2 --topo-global-server "${ZK_SERVER}" AddCellInfo \ + --root "/vitess/${cell}" \ + --server-address "${ZK_SERVER}" \ + "${cell}" set -e echo "Configured zk servers." diff --git a/examples/compose/client.go b/examples/compose/client.go index 8beaef683cd..95d6b4f4815 100644 --- a/examples/compose/client.go +++ b/examples/compose/client.go @@ -53,7 +53,7 @@ func main() { // Insert some messages on random pages. fmt.Println("Inserting into primary...") - for i := 0; i < 3; i++ { + for range 3 { tx, err := db.Begin() if err != nil { fmt.Printf("begin failed: %v\n", err) diff --git a/examples/compose/vtcompose/vtcompose.go b/examples/compose/vtcompose/vtcompose.go index c6df1d72e48..3bcfd8315e5 100644 --- a/examples/compose/vtcompose/vtcompose.go +++ b/examples/compose/vtcompose/vtcompose.go @@ -218,24 +218,6 @@ func main() { writeFile(dockerComposeFile, "docker-compose.yml") } -func applyFilePatch(dockerYaml []byte, patchFile string) []byte { - yamlPatch, err := os.ReadFile(patchFile) - if err != nil { - log.Fatalf("reading yaml patch file %s: %s", patchFile, err) - } - - patch, err := yamlpatch.DecodePatch(yamlPatch) - if err != nil { - log.Fatalf("decoding patch failed: %s", err) - } - - bs, err := patch.Apply(dockerYaml) - if err != nil { - log.Fatalf("applying patch failed: %s", err) - } - return bs -} - func applyJsonInMemoryPatch(vSchemaFile []byte, patchString string) []byte { patch, err := jsonpatch.DecodePatch([]byte(patchString)) if err != nil { @@ -446,7 +428,7 @@ func applyKeyspaceDependentPatches( dockerComposeFile = applyShardPatches(dockerComposeFile, tabAlias, shard, keyspaceData, externalDbInfoMap, opts) } else { // Determine shard range - for i := 0; i < keyspaceData.shards; i++ { + for i := range keyspaceData.shards { if i == 0 { shard = fmt.Sprintf("-%x", interval) } else if i == (keyspaceData.shards - 1) { @@ -517,28 +499,6 @@ func applyShardPatches( return dockerComposeFile } -func generateDefaultShard(tabAlias int, shard string, keyspaceData keyspaceInfo, opts vtOptions) string { - aliases := []int{tabAlias + 1} // primary alias, e.g. 201 - for i := 0; i < keyspaceData.replicaTablets; i++ { - aliases = append(aliases, tabAlias+2+i) // replica aliases, e.g. 202, 203, ... - } - tabletDepends := make([]string, len(aliases)) - for i, tabletId := range aliases { - tabletDepends[i] = fmt.Sprintf("vttablet%d: {condition : service_healthy}", tabletId) - } - // Wait on all shard tablets to be healthy - dependsOn := "depends_on: {" + strings.Join(tabletDepends, ", ") + "}" - - return fmt.Sprintf(` -- op: add - path: /services/init_shard_primary%[2]d - value: - image: vitess/lite:${VITESS_TAG:-latest} - command: ["sh", "-c", "/vt/bin/vtctldclient %[5]s InitShardPrimary --force %[4]s/%[3]s %[6]s-%[2]d "] - %[1]s -`, dependsOn, aliases[0], shard, keyspaceData.keyspace, opts.topologyFlags, opts.cell) -} - func generateExternalPrimary( tabAlias int, shard string, @@ -548,7 +508,7 @@ func generateExternalPrimary( ) string { aliases := []int{tabAlias + 1} // primary alias, e.g. 201 - for i := 0; i < keyspaceData.replicaTablets; i++ { + for i := range keyspaceData.replicaTablets { aliases = append(aliases, tabAlias+2+i) // replica aliases, e.g. 202, 203, ... } @@ -611,7 +571,7 @@ func applyTabletPatches( dbInfo = val } dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateDefaultTablet(tabAlias+1, shard, "primary", keyspaceData.keyspace, dbInfo, opts)) - for i := 0; i < keyspaceData.replicaTablets; i++ { + for i := range keyspaceData.replicaTablets { dockerComposeFile = applyInMemoryPatch(dockerComposeFile, generateDefaultTablet(tabAlias+2+i, shard, "replica", keyspaceData.keyspace, dbInfo, opts)) } return dockerComposeFile diff --git a/examples/operator/101_initial_cluster.yaml b/examples/operator/101_initial_cluster.yaml index 22cdc6f686d..2b956344659 100644 --- a/examples/operator/101_initial_cluster.yaml +++ b/examples/operator/101_initial_cluster.yaml @@ -15,7 +15,7 @@ spec: vtbackup: vitess/lite:latest vtorc: vitess/lite:latest mysqld: - mysql80Compatible: vitess/lite:latest + mysql80Compatible: mysql:8.0.30 mysqldExporter: prom/mysqld-exporter:v0.11.0 cells: - name: zone1 diff --git a/examples/operator/201_customer_tablets.yaml b/examples/operator/201_customer_tablets.yaml index 5800a5e05df..25c9d26d892 100644 --- a/examples/operator/201_customer_tablets.yaml +++ b/examples/operator/201_customer_tablets.yaml @@ -11,7 +11,7 @@ spec: vtbackup: vitess/lite:latest vtorc: vitess/lite:latest mysqld: - mysql80Compatible: vitess/lite:latest + mysql80Compatible: mysql:8.0.30 mysqldExporter: prom/mysqld-exporter:v0.11.0 cells: - name: zone1 diff --git a/examples/operator/302_new_shards.yaml b/examples/operator/302_new_shards.yaml index 2e15bc40d28..4caf35ed856 100644 --- a/examples/operator/302_new_shards.yaml +++ b/examples/operator/302_new_shards.yaml @@ -11,7 +11,7 @@ spec: vtbackup: vitess/lite:latest vtorc: vitess/lite:latest mysqld: - mysql80Compatible: vitess/lite:latest + mysql80Compatible: mysql:8.0.30 mysqldExporter: prom/mysqld-exporter:v0.11.0 cells: - name: zone1 diff --git a/examples/operator/306_down_shard_0.yaml b/examples/operator/306_down_shard_0.yaml index 4bdb694d678..adc22280490 100644 --- a/examples/operator/306_down_shard_0.yaml +++ b/examples/operator/306_down_shard_0.yaml @@ -11,7 +11,7 @@ spec: vtbackup: vitess/lite:latest vtorc: vitess/lite:latest mysqld: - mysql80Compatible: vitess/lite:latest + mysql80Compatible: mysql:8.0.30 mysqldExporter: prom/mysqld-exporter:v0.11.0 cells: - name: zone1 diff --git a/go.mod b/go.mod index 19fec748de8..f0fc8237ed8 100644 --- a/go.mod +++ b/go.mod @@ -1,100 +1,98 @@ module vitess.io/vitess -go 1.21 +go 1.22.0 require ( - cloud.google.com/go/storage v1.29.0 - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 + cloud.google.com/go/storage v1.38.0 + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 github.com/Azure/azure-pipeline-go v0.2.3 github.com/Azure/azure-storage-blob-go v0.15.0 - github.com/DataDog/datadog-go v4.8.3+incompatible github.com/HdrHistogram/hdrhistogram-go v0.9.0 // indirect github.com/aquarapid/vaultlib v0.5.1 github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.44.258 + github.com/aws/aws-sdk-go v1.50.18 github.com/buger/jsonparser v1.1.1 github.com/cespare/xxhash/v2 v2.2.0 github.com/corpix/uarand v0.1.1 // indirect - github.com/dave/jennifer v1.6.0 - github.com/evanphx/json-patch v5.6.0+incompatible - github.com/fsnotify/fsnotify v1.6.0 - github.com/go-sql-driver/mysql v1.7.0 - github.com/golang/glog v1.0.0 + github.com/dave/jennifer v1.7.0 + github.com/evanphx/json-patch v5.9.0+incompatible + github.com/fsnotify/fsnotify v1.7.0 + github.com/go-sql-driver/mysql v1.7.1 + github.com/golang/glog v1.2.0 github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.4 - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/google/uuid v1.3.0 - github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/google/uuid v1.6.0 + github.com/gorilla/handlers v1.5.2 + github.com/gorilla/mux v1.8.1 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/api v1.27.0 github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/compress v1.16.5 - github.com/klauspost/pgzip v1.2.5 + github.com/klauspost/compress v1.17.6 + github.com/klauspost/pgzip v1.2.6 github.com/krishicks/yaml-patch v0.0.10 - github.com/magiconair/properties v1.8.7 - github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 - github.com/montanaflynn/stats v0.7.0 + github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philhofer/fwd v1.1.2 // indirect github.com/pierrec/lz4 v2.6.1+incompatible - github.com/pires/go-proxyproto v0.6.2 + github.com/pires/go-proxyproto v0.7.0 github.com/pkg/errors v0.9.1 github.com/planetscale/pargzip v0.0.0-20201116224723-90c7fc03ea8a github.com/planetscale/vtprotobuf v0.5.0 - github.com/prometheus/client_golang v1.15.1 - github.com/prometheus/common v0.43.0 // indirect + github.com/prometheus/client_golang v1.18.0 + github.com/prometheus/common v0.46.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/sjmudd/stopwatch v0.1.1 github.com/soheilhy/cmux v0.1.5 - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 + github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.8.4 github.com/tchap/go-patricia v2.3.0+incompatible - github.com/tidwall/gjson v1.12.1 - github.com/tinylib/msgp v1.1.8 // indirect + github.com/tidwall/gjson v1.17.1 + github.com/tinylib/msgp v1.1.9 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 github.com/z-division/go-zookeeper v1.0.0 - go.etcd.io/etcd/api/v3 v3.5.8 - go.etcd.io/etcd/client/pkg/v3 v3.5.8 - go.etcd.io/etcd/client/v3 v3.5.8 + go.etcd.io/etcd/api/v3 v3.5.12 + go.etcd.io/etcd/client/pkg/v3 v3.5.12 + go.etcd.io/etcd/client/v3 v3.5.12 go.uber.org/mock v0.2.0 - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 - golang.org/x/oauth2 v0.7.0 - golang.org/x/sys v0.13.0 - golang.org/x/term v0.13.0 - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 - google.golang.org/api v0.121.0 - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.55.0-dev + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 + golang.org/x/oauth2 v0.17.0 + golang.org/x/sys v0.17.0 + golang.org/x/term v0.17.0 + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 + golang.org/x/tools v0.18.0 + google.golang.org/api v0.165.0 + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/grpc v1.61.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/grpc/examples v0.0.0-20210430044426-28078834f35b - google.golang.org/protobuf v1.30.0 - gopkg.in/DataDog/dd-trace-go.v1 v1.50.1 + google.golang.org/protobuf v1.32.0 + gopkg.in/DataDog/dd-trace-go.v1 v1.60.3 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/ldap.v2 v2.5.1 - gotest.tools v2.2.0+incompatible - sigs.k8s.io/yaml v1.3.0 + sigs.k8s.io/yaml v1.4.0 ) require ( - github.com/Shopify/toxiproxy/v2 v2.5.0 + github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/Shopify/toxiproxy/v2 v2.7.0 github.com/bndr/gotabulate v1.1.2 github.com/gammazero/deque v0.2.1 github.com/google/safehtml v0.1.0 @@ -103,91 +101,101 @@ require ( github.com/kr/text v0.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 - github.com/spf13/afero v1.9.3 + github.com/spf13/afero v1.11.0 github.com/spf13/jwalterweatherman v1.1.0 github.com/xlab/treeprint v1.2.0 - go.uber.org/goleak v1.2.1 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 - golang.org/x/sync v0.3.0 + go.uber.org/goleak v1.3.0 + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/sync v0.6.0 gonum.org/v1/gonum v0.14.0 - modernc.org/sqlite v1.20.3 + modernc.org/sqlite v1.29.1 ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - github.com/DataDog/appsec-internal-go v1.0.0 // indirect - github.com/DataDog/datadog-agent/pkg/obfuscate v0.43.1 // indirect - github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.45.0-rc.1 // indirect - github.com/DataDog/datadog-go/v5 v5.2.0 // indirect - github.com/DataDog/go-libddwaf v1.1.0 // indirect - github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork // indirect - github.com/DataDog/sketches-go v1.4.1 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + github.com/DataDog/appsec-internal-go v1.4.1 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.50.2 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.50.2 // indirect + github.com/DataDog/go-libddwaf/v2 v2.3.2 // indirect + github.com/DataDog/go-sqllexer v0.0.10 // indirect + github.com/DataDog/go-tuf v1.0.4-0.5.2 // indirect + github.com/DataDog/sketches-go v1.4.4 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgrijalva/lfu-go v0.0.0-20141010002404-f174e76c5138 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/ebitengine/purego v0.6.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/s2a-go v0.1.3 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.8.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-ieproxy v0.0.10 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mattn/go-ieproxy v0.0.11 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.23.0 // indirect - github.com/outcaste-io/ristretto v0.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/outcaste-io/ristretto v0.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.5.0 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect + go.opentelemetry.io/otel v1.23.1 // indirect + go.opentelemetry.io/otel/metric v1.23.1 // indirect + go.opentelemetry.io/otel/trace v1.23.1 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect - go4.org/intern v0.0.0-20230205224052-192e9f60865c // indirect - go4.org/unsafe/assume-no-moving-gc v0.0.0-20230426161633-7e06285ff160 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - inet.af/netaddr v0.0.0-20220811202034-502d2d690317 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.5 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.5.0 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.1.3 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index f0b6cc35a35..89064a955f8 100644 --- a/go.sum +++ b/go.sum @@ -1,55 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk= @@ -66,43 +28,39 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/appsec-internal-go v1.0.0 h1:2u5IkF4DBj3KVeQn5Vg2vjPUtt513zxEYglcqnd500U= -github.com/DataDog/appsec-internal-go v1.0.0/go.mod h1:+Y+4klVWKPOnZx6XESG7QHydOaUGEXyH2j/vSg9JiNM= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.43.1 h1:HG4dOM6Ou+zZsaKC++4kpM9VGJ/TYo9X61LPz2mmjDE= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.43.1/go.mod h1:o+rJy3B2o+Zb+wCgLSkMlkD7EiUEA5Q63cid53fZkQY= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.45.0-rc.1 h1:0OK84DbAucLUwoDYoBFve1cuhDWtoquruVVDjgucYlI= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.45.0-rc.1/go.mod h1:VVMDDibJxYEkwcLdZBT2g8EHKpbMT4JdOhRbQ9GdjbM= +github.com/DataDog/appsec-internal-go v1.4.1 h1:xpAS/hBo429pVh7rngquAK2DezUaJjfsX7Wd8cw0aIk= +github.com/DataDog/appsec-internal-go v1.4.1/go.mod h1:rmZ+tpq5ZPKmeOUMYjWFg+q1mRd13mxZwSLBG+xa1ik= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.50.2 h1:y08IzbpFM/HBaKfgayFZe1FpcbZn6bVPXoZ++93vxv8= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.50.2/go.mod h1:A4nLJvxlg6BO/8/zg81til9yT0uRPuXDFMAzDMpmgn4= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.50.2 h1:7jn5EOu84uph4sd+pB3vF8LnsdTjhh+1/NnCvfNpG4A= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.50.2/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= -github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go/v5 v5.1.1/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= -github.com/DataDog/datadog-go/v5 v5.2.0 h1:kSptqUGSNK67DgA+By3rwtFnAh6pTBxJ7Hn8JCLZcKY= -github.com/DataDog/datadog-go/v5 v5.2.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q= -github.com/DataDog/go-libddwaf v1.1.0 h1:PhlI/31yxu88JEgTYqxffhd8oM4KQMfNWUVyICqIDMY= -github.com/DataDog/go-libddwaf v1.1.0/go.mod h1:DI5y8obPajk+Tvy2o+nZc2g/5Ria/Rfq5/624k7pHpE= -github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork h1:yBq5PrAtrM4yVeSzQ+bn050+Ysp++RKF1QmtkL4VqvU= -github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork/go.mod h1:yA5JwkZsHTLuqq3zaRgUQf35DfDkpOZqgtBqHKpwrBs= -github.com/DataDog/gostackparse v0.5.0 h1:jb72P6GFHPHz2W0onsN51cS3FkaMDcjb0QzgxxA4gDk= -github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= -github.com/DataDog/sketches-go v1.4.1 h1:j5G6as+9FASM2qC36lvpvQAj9qsv/jUs3FtO8CwZNAY= -github.com/DataDog/sketches-go v1.4.1/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= +github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= +github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/go-libddwaf/v2 v2.3.2 h1:pdi9xjWW57IpOpTeOyPuNveEDFLmmInsHDeuZk3TY34= +github.com/DataDog/go-libddwaf/v2 v2.3.2/go.mod h1:gsCdoijYQfj8ce/T2bEDNPZFIYnmHluAgVDpuQOWMZE= +github.com/DataDog/go-sqllexer v0.0.10 h1:u07DuRfdlPPmOX/dclb1gcn/zaqWxUiURRRVenKILxc= +github.com/DataDog/go-sqllexer v0.0.10/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= +github.com/DataDog/go-tuf v1.0.4-0.5.2 h1:p7Owb1bMhV9CuGpXv2r3gYxcfcKP+lwRi33MLMIEFuo= +github.com/DataDog/go-tuf v1.0.4-0.5.2/go.mod h1:eaweB+HCRl3YBlX3X4/a5WpxErQMPpHw6HRAx4NOWGQ= +github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= +github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= +github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= +github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= github.com/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3UzkWdp5tH1WMcg= github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= -github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Shopify/toxiproxy/v2 v2.7.0 h1:Zz2jdyqtYw1SpihfMWzLFGpOO92p9effjAkURG57ifc= +github.com/Shopify/toxiproxy/v2 v2.7.0/go.mod h1:k0V84e/dLQmVNuI6S0G7TpXCl611OSRYdptoxm0XTzA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aquarapid/vaultlib v0.5.1 h1:vuLWR6bZzLHybjJBSUYPgZlIp6KZ+SXeHLRRYTuk6d4= github.com/aquarapid/vaultlib v0.5.1/go.mod h1:yT7AlEXtuabkxylOc/+Ulyp18tff1+QjgNLTnFWTlOs= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -111,9 +69,8 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.44.258 h1:JVk1lgpsTnb1kvUw3eGhPLcTpEBp6HeSf1fxcYDs2Ho= -github.com/aws/aws-sdk-go v1.44.258/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/aws/aws-sdk-go v1.50.18 h1:h+FQjxp5sSDqFKScTUXHVahBlqduKtiR0qM18evcvag= +github.com/aws/aws-sdk-go v1.50.18/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -128,20 +85,12 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= @@ -149,88 +98,82 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U= github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/dave/jennifer v1.6.0 h1:MQ/6emI2xM7wt0tJzJzyUik2Q3Tcn2eE0vtYgh4GPVI= -github.com/dave/jennifer v1.6.0/go.mod h1:AxTG893FiZKqxy3FP1kL80VMshSMuz2G+EgvszgGRnk= +github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= +github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/lfu-go v0.0.0-20141010002404-f174e76c5138 h1:dnHgW/+2AbIFKEIhb3mXw6OuPNPrMmrJ6GZylDT556s= +github.com/dgrijalva/lfu-go v0.0.0-20141010002404-f174e76c5138/go.mod h1:NBHEfzv9sqF3bQdv4L3+buvhN5JtVkuO1EhpELjZxXk= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/ebitengine/purego v0.6.0 h1:Yo9uBc1x+ETQbfEaf6wcBsjrQfCEnh/gaGUg7lguEJY= +github.com/ebitengine/purego v0.6.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= +github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -246,90 +189,67 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8= github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM= +github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= -github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= -github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= -github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= +github.com/hashicorp/consul/api v1.27.0 h1:gmJ6DPKQog1426xsdmgk5iqDyoRiNc+ipBdJOqKQFjc= +github.com/hashicorp/consul/api v1.27.0/go.mod h1:JkekNRSou9lANFdt+4IKx3Za7XY0JzzpQjEb4Ivo1c8= +github.com/hashicorp/consul/sdk v0.15.1 h1:kKIGxc7CZtflcF5DLfHeq7rOQmRq3vk7kwISN9bif8Q= +github.com/hashicorp/consul/sdk v0.15.1/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -343,16 +263,17 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= +github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= @@ -360,11 +281,8 @@ github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4 github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -373,20 +291,15 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -408,27 +321,26 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-ieproxy v0.0.10 h1:P+2QihaKCLgbs/32dhFLbxXlqsy8tIG1LUXHIoPaQPo= -github.com/mattn/go-ieproxy v0.0.10/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0= +github.com/mattn/go-ieproxy v0.0.11 h1:MQ/5BuGSgDAHZOJe6YY80IF2UVCfGkwfo6AeD7HtHYo= +github.com/mattn/go-ieproxy v0.0.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 h1:jw16EimP5oAEM/2wt+SiEUov/YDyTCTDuPtIKgQIvk0= github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1/go.mod h1:vuvdOZLJuf5HmJAJrKV64MmozrSsk+or0PB5dzdfspg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -441,9 +353,11 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= -github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= @@ -454,14 +368,10 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= @@ -469,79 +379,78 @@ github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/outcaste-io/ristretto v0.2.0/go.mod h1:iBZA7RCt6jaOr0z6hiBQ6t662/oZ6Gx/yauuPvIWHAI= -github.com/outcaste-io/ristretto v0.2.1 h1:KCItuNIGJZcursqHr3ghO7fc5ddZLEHspL9UR0cQM64= -github.com/outcaste-io/ristretto v0.2.1/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= +github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= +github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= -github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= +github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= +github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/planetscale/pargzip v0.0.0-20201116224723-90c7fc03ea8a h1:y0OpQ4+5tKxeh9+H+2cVgASl9yMZYV9CILinKOiKafA= github.com/planetscale/pargzip v0.0.0-20201116224723-90c7fc03ea8a/go.mod h1:GJFUzQuXIoB2Kjn1ZfDhJr/42D5nWOqRcIQVgCxTuIE= github.com/planetscale/vtprotobuf v0.5.0 h1:l8PXm6Colok5z6qQLNhAj2Jq5BfoMTIHxLER5a6nDqM= github.com/planetscale/vtprotobuf v0.5.0/go.mod h1:wm1N3qk9G/4+VM1WhpkLbvY/d8+0PbwYYpP5P5VhTks= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= -github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 h1:Qp27Idfgi6ACvFQat5+VJvlYToylpM/hcyLBI3WaKPA= github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U= -github.com/secure-systems-lab/go-securesystemslib v0.5.0 h1:oTiNu0QnulMQgN/hLK124wJD/r2f9ZhIUuKIeBsCBT8= -github.com/secure-systems-lab/go-securesystemslib v0.5.0/go.mod h1:uoCqUC0Ap7jrBSEanxT+SdACYJTVplRXWLkGMuDjXqk= +github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA= +github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -551,25 +460,28 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -580,21 +492,21 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU= +github.com/tinylib/msgp v1.1.9/go.mod h1:BCXGB54lDD8qUEPmiG0cQQUANC4IUQyB2ItS2UDlO/k= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= @@ -604,109 +516,71 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/z-division/go-zookeeper v1.0.0 h1:ULsCj0nP6+U1liDFWe+2oEF6o4amixoDcDlwEUghVUY= github.com/z-division/go-zookeeper v1.0.0/go.mod h1:6X4UioQXpvyezJJl4J9NHAJKsoffCwy5wCaaTktXjOA= -go.etcd.io/etcd/api/v3 v3.5.8 h1:Zf44zJszoU7zRV0X/nStPenegNXoFDWcB/MwrJbA+L4= -go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= -go.etcd.io/etcd/client/pkg/v3 v3.5.8 h1:tPp9YRn/UBFAHdhOQUII9eUs7aOK35eulpMhX4YBd+M= -go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= -go.etcd.io/etcd/client/v3 v3.5.8 h1:B6ngTKZSWWowHEoaucOKHQR/AtZKaoHLiUpWxOLG4l4= -go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= +go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= +go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= +go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= +go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg= +go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= +go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= +go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= +go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= +go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= +go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= -go4.org/intern v0.0.0-20230205224052-192e9f60865c h1:b8WZ7Ja8nKegYxfwDLLwT00ZKv4lXAQrw8LYPK+cHSI= -go4.org/intern v0.0.0-20230205224052-192e9f60865c/go.mod h1:RJ0SVrOMpxLhgb5noIV+09zI1RsRlMsbUcSxpWHqbrE= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20230204201903-c31fa085b70e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20230426161633-7e06285ff160 h1:LrTREdITdNDW/JRlUuG3fhXvCK3ZcKXTCf1BbxE8sT4= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20230426161633-7e06285ff160/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -716,76 +590,37 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -794,250 +629,104 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.121.0 h1:8Oopoo8Vavxx6gt+sgs8s8/X60WBAtKQq6JqnkF+xow= -google.golang.org/api v0.121.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.165.0 h1:zd5d4JIIIaYYsfVy1HzoXYZ9rWCSBxxAglbczzo7Bgc= +google.golang.org/api v0.165.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 h1:4++qSzdWBUy9/2x8L5KZgwZw+mjJZ2yDSCGMVM0YzRs= +google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.55.0-dev h1:b3WG8LoyS+X/C5ZbIWsJGjt8Hhqq0wUVX8+rPF/BHZo= -google.golang.org/grpc v1.55.0-dev/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/grpc/examples v0.0.0-20210430044426-28078834f35b h1:D/GTYPo6I1oEo08Bfpuj3xl5XE+UGHj7//5fVyKxhsQ= @@ -1055,10 +744,10 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/DataDog/dd-trace-go.v1 v1.50.1 h1:DUpHhh+MHtpYnUyGr5rpfvKUXkRg93TSEHii/LZVF6g= -gopkg.in/DataDog/dd-trace-go.v1 v1.50.1/go.mod h1:sw4gV8LIXseC5ISMbDJmm79OJDdl8I2Hhtelb6lpHuQ= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/DataDog/dd-trace-go.v1 v1.60.3 h1:BbAk9qEUKTJcxDqwn7OGlTWTfKPNzt6jbhzmx4m33dw= +gopkg.in/DataDog/dd-trace-go.v1 v1.60.3/go.mod h1:XF/Y0lFGnmgedNXnltCm6hXkt9iwyeVVVFbKhJvVwtY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= @@ -1067,7 +756,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -1078,7 +766,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1086,49 +773,26 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/gotraceui v0.2.0 h1:dmNsfQ9Vl3GwbiVD7Z8d/osC6WtGGrasyrC2suc4ZIQ= +honnef.co/go/gotraceui v0.2.0/go.mod h1:qHo4/W75cA3bX0QQoSvDjbJa4R8mAyyFjbWAj63XElc= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU= -inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= -modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= -modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= -modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.29.1 h1:19GY2qvWB4VPw0HppFlZCPAbmxFU41r+qjKZQdQ1ryA= +modernc.org/sqlite v1.29.1/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= -modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/go/README.md b/go/README.md index fc6efdde602..6f9ca0421e6 100644 --- a/go/README.md +++ b/go/README.md @@ -1,3 +1,5 @@ +# README + This directory contains all the Go code for Vitess. Most of the packages at the top level are general-purpose and are suitable @@ -16,4 +18,3 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) ``` - diff --git a/go/acl/acl_test.go b/go/acl/acl_test.go index 680044c1461..65f6ed2b96d 100644 --- a/go/acl/acl_test.go +++ b/go/acl/acl_test.go @@ -18,8 +18,15 @@ package acl import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "os" + "os/exec" "testing" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" ) type TestPolicy struct{} @@ -50,41 +57,103 @@ func TestSimplePolicy(t *testing.T) { currentPolicy = policies["test"] err := CheckAccessActor("", ADMIN) want := "not allowed" - if err == nil || err.Error() != want { - t.Errorf("got %v, want %s", err, want) - } + assert.Equalf(t, err.Error(), want, "got %v, want %s", err, want) + err = CheckAccessActor("", DEBUGGING) - if err != nil { - t.Errorf("got %v, want no error", err) - } + assert.Equalf(t, err, nil, "got %v, want no error", err) + + err = CheckAccessActor("", MONITORING) + assert.Equalf(t, err, nil, "got %v, want no error", err) err = CheckAccessHTTP(nil, ADMIN) - if err == nil || err.Error() != want { - t.Errorf("got %v, want %s", err, want) - } + assert.Equalf(t, err.Error(), want, "got %v, want %s", err, want) + err = CheckAccessHTTP(nil, DEBUGGING) - if err != nil { - t.Errorf("got %v, want no error", err) - } + assert.Equalf(t, err, nil, "got %v, want no error", err) + + err = CheckAccessHTTP(nil, MONITORING) + assert.Equalf(t, err, nil, "got %v, want no error", err) } func TestEmptyPolicy(t *testing.T) { currentPolicy = nil err := CheckAccessActor("", ADMIN) - if err != nil { - t.Errorf("got %v, want no error", err) - } + assert.Equalf(t, err, nil, "got %v, want no error", err) + err = CheckAccessActor("", DEBUGGING) - if err != nil { - t.Errorf("got %v, want no error", err) - } + assert.Equalf(t, err, nil, "got %v, want no error", err) + + err = CheckAccessActor("", MONITORING) + assert.Equalf(t, err, nil, "got %v, want no error", err) err = CheckAccessHTTP(nil, ADMIN) - if err != nil { - t.Errorf("got %v, want no error", err) - } + assert.Equalf(t, err, nil, "got %v, want no error", err) + err = CheckAccessHTTP(nil, DEBUGGING) - if err != nil { - t.Errorf("got %v, want no error", err) + assert.Equalf(t, err, nil, "got %v, want no error", err) + + err = CheckAccessHTTP(nil, MONITORING) + assert.Equalf(t, err, nil, "got %v, want no error", err) +} + +func TestValidSecurityPolicy(t *testing.T) { + securityPolicy = "test" + savePolicy() + + assert.Equalf(t, TestPolicy{}, currentPolicy, "got %v, expected %v", currentPolicy, TestPolicy{}) +} + +func TestInvalidSecurityPolicy(t *testing.T) { + securityPolicy = "invalidSecurityPolicy" + savePolicy() + + assert.Equalf(t, denyAllPolicy{}, currentPolicy, "got %v, expected %v", currentPolicy, denyAllPolicy{}) +} + +func TestSendError(t *testing.T) { + testW := httptest.NewRecorder() + + testErr := errors.New("Testing error message") + SendError(testW, testErr) + + // Check the status code + assert.Equalf(t, testW.Code, http.StatusForbidden, "got %v; want %v", testW.Code, http.StatusForbidden) + + // Check the writer body + want := fmt.Sprintf("Access denied: %v\n", testErr) + got := testW.Body.String() + assert.Equalf(t, got, want, "got %v; want %v", got, want) +} + +func TestRegisterFlags(t *testing.T) { + testFs := pflag.NewFlagSet("test", pflag.ExitOnError) + securityPolicy = "test" + + RegisterFlags(testFs) + + securityPolicyFlag := testFs.Lookup("security_policy") + assert.NotNil(t, securityPolicyFlag, "no security_policy flag is registered") + + // Check the default value of the flag + want := "test" + got := securityPolicyFlag.DefValue + assert.Equalf(t, got, want, "got %v; want %v", got, want) +} + +func TestAlreadyRegisteredPolicy(t *testing.T) { + if os.Getenv("TEST_ACL") == "1" { + RegisterPolicy("test", nil) + return + } + + // Run subprocess to test os.Exit which is called by log.fatalf + // os.Exit should be called if we try to re-register a policy + cmd := exec.Command(os.Args[0], "-test.run=TestAlreadyRegisteredPolicy") + cmd.Env = append(os.Environ(), "TEST_ACL=1") + err := cmd.Run() + if e, ok := err.(*exec.ExitError); ok && !e.Success() { + return } + + t.Errorf("process ran with err %v, want exit status 1", err) } diff --git a/go/acl/deny_all_policy_test.go b/go/acl/deny_all_policy_test.go new file mode 100644 index 00000000000..b66a344af2e --- /dev/null +++ b/go/acl/deny_all_policy_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package acl + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDenyAllPolicy(t *testing.T) { + testDenyAllPolicy := denyAllPolicy{} + + want := errDenyAll + err := testDenyAllPolicy.CheckAccessActor("", ADMIN) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testDenyAllPolicy.CheckAccessActor("", DEBUGGING) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testDenyAllPolicy.CheckAccessActor("", MONITORING) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testDenyAllPolicy.CheckAccessHTTP(nil, ADMIN) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testDenyAllPolicy.CheckAccessHTTP(nil, DEBUGGING) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testDenyAllPolicy.CheckAccessHTTP(nil, MONITORING) + assert.Equalf(t, err, want, "got %v; want %v", err, want) +} diff --git a/go/acl/read_only_policy_test.go b/go/acl/read_only_policy_test.go new file mode 100644 index 00000000000..c5e988a6734 --- /dev/null +++ b/go/acl/read_only_policy_test.go @@ -0,0 +1,46 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package acl + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadOnlyPolicy(t *testing.T) { + testReadOnlyPolicy := readOnlyPolicy{} + + want := errReadOnly + err := testReadOnlyPolicy.CheckAccessActor("", ADMIN) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testReadOnlyPolicy.CheckAccessActor("", DEBUGGING) + assert.Equalf(t, err, nil, "got %v; want no error", err) + + err = testReadOnlyPolicy.CheckAccessActor("", MONITORING) + assert.Equalf(t, err, nil, "got %v; want no error", err) + + err = testReadOnlyPolicy.CheckAccessHTTP(nil, ADMIN) + assert.Equalf(t, err, want, "got %v; want %v", err, want) + + err = testReadOnlyPolicy.CheckAccessHTTP(nil, DEBUGGING) + assert.Equalf(t, err, nil, "got %v; want no error", err) + + err = testReadOnlyPolicy.CheckAccessHTTP(nil, MONITORING) + assert.Equalf(t, err, nil, "got %v; want no error", err) +} diff --git a/go/bucketpool/bucketpool_test.go b/go/bucketpool/bucketpool_test.go index 7649f9b6278..af9693a523f 100644 --- a/go/bucketpool/bucketpool_test.go +++ b/go/bucketpool/bucketpool_test.go @@ -19,156 +19,105 @@ package bucketpool import ( "math/rand" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestPool(t *testing.T) { maxSize := 16384 pool := New(1024, maxSize) - if pool.maxSize != maxSize { - t.Fatalf("Invalid max pool size: %d, expected %d", pool.maxSize, maxSize) - } - if len(pool.pools) != 5 { - t.Fatalf("Invalid number of pools: %d, expected %d", len(pool.pools), 5) - } + require.Equal(t, maxSize, pool.maxSize, "Invalid max pool size") + require.Len(t, pool.pools, 5, "Invalid number of pools") buf := pool.Get(64) - if len(*buf) != 64 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1024 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 64, "unexpected buf length") + require.Equal(t, 1024, cap(*buf), "unexpected buf cap") // get from same pool, check that length is right buf = pool.Get(128) - if len(*buf) != 128 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1024 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 128, "unexpected buf length") + require.Equal(t, 1024, cap(*buf), "unexpected buf cap") pool.Put(buf) // get boundary size buf = pool.Get(1024) - if len(*buf) != 1024 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1024 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 1024, "unexpected buf length") + require.Equal(t, 1024, cap(*buf), "unexpected buf cap") pool.Put(buf) // get from the middle buf = pool.Get(5000) - if len(*buf) != 5000 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 8192 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 5000, "unexpected buf length") + require.Equal(t, 8192, cap(*buf), "unexpected buf cap") pool.Put(buf) // check last pool buf = pool.Get(16383) - if len(*buf) != 16383 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 16384 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 16383, "unexpected buf length") + require.Equal(t, 16384, cap(*buf), "unexpected buf cap") pool.Put(buf) // get big buffer buf = pool.Get(16385) - if len(*buf) != 16385 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 16385 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 16385, "unexpected buf length") + require.Equal(t, 16385, cap(*buf), "unexpected buf cap") pool.Put(buf) } func TestPoolOneSize(t *testing.T) { maxSize := 1024 pool := New(1024, maxSize) - if pool.maxSize != maxSize { - t.Fatalf("Invalid max pool size: %d, expected %d", pool.maxSize, maxSize) - } + require.Equal(t, maxSize, pool.maxSize, "Invalid max pool size") buf := pool.Get(64) - if len(*buf) != 64 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1024 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 64, "unexpected buf length") + require.Equal(t, 1024, cap(*buf), "unexpected buf cap") pool.Put(buf) buf = pool.Get(1025) - if len(*buf) != 1025 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1025 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 1025, "unexpected buf length") + require.Equal(t, 1025, cap(*buf), "unexpected buf cap") pool.Put(buf) } func TestPoolTwoSizeNotMultiplier(t *testing.T) { maxSize := 2000 pool := New(1024, maxSize) - if pool.maxSize != maxSize { - t.Fatalf("Invalid max pool size: %d, expected %d", pool.maxSize, maxSize) - } + require.Equal(t, maxSize, pool.maxSize, "Invalid max pool size") buf := pool.Get(64) - if len(*buf) != 64 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 1024 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 64, "unexpected buf length") + require.Equal(t, 1024, cap(*buf), "unexpected buf cap") pool.Put(buf) buf = pool.Get(2001) - if len(*buf) != 2001 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 2001 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 2001, "unexpected buf length") + require.Equal(t, 2001, cap(*buf), "unexpected buf cap") pool.Put(buf) } +func TestPoolMaxSizeLessThanMinSize(t *testing.T) { + assert.Panics(t, func() { New(15000, 1024) }) +} + func TestPoolWeirdMaxSize(t *testing.T) { maxSize := 15000 pool := New(1024, maxSize) - if pool.maxSize != maxSize { - t.Fatalf("Invalid max pool size: %d, expected %d", pool.maxSize, maxSize) - } + require.Equal(t, maxSize, pool.maxSize, "Invalid max pool size") buf := pool.Get(14000) - if len(*buf) != 14000 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 15000 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 14000, "unexpected buf length") + require.Equal(t, 15000, cap(*buf), "unexpected buf cap") pool.Put(buf) buf = pool.Get(16383) - if len(*buf) != 16383 { - t.Fatalf("unexpected buf length: %d", len(*buf)) - } - if cap(*buf) != 16383 { - t.Fatalf("unexpected buf cap: %d", cap(*buf)) - } + require.Len(t, *buf, 16383, "unexpected buf length") + require.Equal(t, 16383, cap(*buf), "unexpected buf cap") pool.Put(buf) } func TestFuzz(t *testing.T) { maxTestSize := 16384 - for i := 0; i < 20000; i++ { + for range 20000 { minSize := rand.Intn(maxTestSize) if minSize == 0 { minSize = 1 @@ -177,18 +126,12 @@ func TestFuzz(t *testing.T) { p := New(minSize, maxSize) bufSize := rand.Intn(maxTestSize) buf := p.Get(bufSize) - if len(*buf) != bufSize { - t.Fatalf("Invalid length %d, expected %d", len(*buf), bufSize) - } + require.Len(t, *buf, bufSize, "unexpected buf length") sPool := p.findPool(bufSize) if sPool == nil { - if cap(*buf) != len(*buf) { - t.Fatalf("Invalid cap %d, expected %d", cap(*buf), len(*buf)) - } + require.Equal(t, len(*buf), cap(*buf), "unexpected buf cap") } else { - if cap(*buf) != sPool.size { - t.Fatalf("Invalid cap %d, expected %d", cap(*buf), sPool.size) - } + require.Equal(t, sPool.size, cap(*buf), "unexpected buf cap") } p.Put(buf) } diff --git a/go/bytes2/buffer.go b/go/bytes2/buffer.go index 1725274c43c..48561c5e493 100644 --- a/go/bytes2/buffer.go +++ b/go/bytes2/buffer.go @@ -65,7 +65,7 @@ func (buf *Buffer) String() string { // is _not_ allocated, so modifying this buffer after calling StringUnsafe will lead // to undefined behavior. func (buf *Buffer) StringUnsafe() string { - return *(*string)(unsafe.Pointer(&buf.bytes)) + return unsafe.String(unsafe.SliceData(buf.bytes), len(buf.bytes)) } // Reset is equivalent to bytes.Buffer.Reset. diff --git a/go/bytes2/buffer_test.go b/go/bytes2/buffer_test.go index 83cdb346ec9..1652f176df4 100644 --- a/go/bytes2/buffer_test.go +++ b/go/bytes2/buffer_test.go @@ -18,21 +18,39 @@ package bytes2 import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestBuffer(t *testing.T) { b := NewBuffer(nil) + + // Test Write function b.Write([]byte("ab")) + assert.Equal(t, "ab", string(b.Bytes()), "Write()") + + // Test WriteString function b.WriteString("cd") + assert.Equal(t, "abcd", string(b.Bytes()), "WriteString()") + + // Test WriteByte function b.WriteByte('e') - want := "abcde" - if got := string(b.Bytes()); got != want { - t.Errorf("b.Bytes(): %s, want %s", got, want) - } - if got := b.String(); got != want { - t.Errorf("b.String(): %s, want %s", got, want) - } - if got := b.Len(); got != 5 { - t.Errorf("b.Len(): %d, want 5", got) - } + assert.Equal(t, "abcde", string(b.Bytes()), "WriteByte()") + + // Test Bytes function + assert.Equal(t, "abcde", string(b.Bytes())) + + // Test String function + assert.Equal(t, "abcde", b.String()) + + // Test StringUnsafe function + assert.Equal(t, "abcde", b.StringUnsafe()) + + // Test Len function + assert.Equal(t, 5, b.Len()) + + // Test Reset function + b.Reset() + assert.Equal(t, "", string(b.Bytes())) + assert.Equal(t, 0, b.Len()) } diff --git a/go/cache/lru_cache.go b/go/cache/lru_cache.go index d845265b77b..beee6cc3d26 100644 --- a/go/cache/lru_cache.go +++ b/go/cache/lru_cache.go @@ -31,15 +31,13 @@ import ( // LRUCache is a typical LRU cache implementation. If the cache // reaches the capacity, the least recently used item is deleted from -// the cache. Note the capacity is not the number of items, but the -// total sum of the CachedSize() of each item. -type LRUCache struct { +// the cache. +type LRUCache[T any] struct { mu sync.Mutex // list & table contain *entry objects. list *list.List table map[string]*list.Element - cost func(any) int64 size int64 capacity int64 @@ -49,46 +47,44 @@ type LRUCache struct { } // Item is what is stored in the cache -type Item struct { +type Item[T any] struct { Key string - Value any + Value T } -type entry struct { +type entry[T any] struct { key string - value any - size int64 + value T timeAccessed time.Time } // NewLRUCache creates a new empty cache with the given capacity. -func NewLRUCache(capacity int64, cost func(any) int64) *LRUCache { - return &LRUCache{ +func NewLRUCache[T any](capacity int64) *LRUCache[T] { + return &LRUCache[T]{ list: list.New(), table: make(map[string]*list.Element), capacity: capacity, - cost: cost, } } // Get returns a value from the cache, and marks the entry as most // recently used. -func (lru *LRUCache) Get(key string) (v any, ok bool) { +func (lru *LRUCache[T]) Get(key string) (v T, ok bool) { lru.mu.Lock() defer lru.mu.Unlock() element := lru.table[key] if element == nil { lru.misses++ - return nil, false + return *new(T), false } lru.moveToFront(element) lru.hits++ - return element.Value.(*entry).value, true + return element.Value.(*entry[T]).value, true } // Set sets a value in the cache. -func (lru *LRUCache) Set(key string, value any) bool { +func (lru *LRUCache[T]) Set(key string, value T) bool { lru.mu.Lock() defer lru.mu.Unlock() @@ -102,7 +98,7 @@ func (lru *LRUCache) Set(key string, value any) bool { } // Delete removes an entry from the cache, and returns if the entry existed. -func (lru *LRUCache) delete(key string) bool { +func (lru *LRUCache[T]) delete(key string) bool { lru.mu.Lock() defer lru.mu.Unlock() @@ -113,27 +109,17 @@ func (lru *LRUCache) delete(key string) bool { lru.list.Remove(element) delete(lru.table, key) - lru.size -= element.Value.(*entry).size + lru.size-- return true } // Delete removes an entry from the cache -func (lru *LRUCache) Delete(key string) { +func (lru *LRUCache[T]) Delete(key string) { lru.delete(key) } -// Clear will clear the entire cache. -func (lru *LRUCache) Clear() { - lru.mu.Lock() - defer lru.mu.Unlock() - - lru.list.Init() - lru.table = make(map[string]*list.Element) - lru.size = 0 -} - // Len returns the size of the cache (in entries) -func (lru *LRUCache) Len() int { +func (lru *LRUCache[T]) Len() int { lru.mu.Lock() defer lru.mu.Unlock() return lru.list.Len() @@ -142,7 +128,7 @@ func (lru *LRUCache) Len() int { // SetCapacity will set the capacity of the cache. If the capacity is // smaller, and the current cache size exceed that capacity, the cache // will be shrank. -func (lru *LRUCache) SetCapacity(capacity int64) { +func (lru *LRUCache[T]) SetCapacity(capacity int64) { lru.mu.Lock() defer lru.mu.Unlock() @@ -150,105 +136,80 @@ func (lru *LRUCache) SetCapacity(capacity int64) { lru.checkCapacity() } -// Wait is a no-op in the LRU cache -func (lru *LRUCache) Wait() {} - // UsedCapacity returns the size of the cache (in bytes) -func (lru *LRUCache) UsedCapacity() int64 { +func (lru *LRUCache[T]) UsedCapacity() int64 { return lru.size } // MaxCapacity returns the cache maximum capacity. -func (lru *LRUCache) MaxCapacity() int64 { +func (lru *LRUCache[T]) MaxCapacity() int64 { lru.mu.Lock() defer lru.mu.Unlock() return lru.capacity } // Evictions returns the number of evictions -func (lru *LRUCache) Evictions() int64 { +func (lru *LRUCache[T]) Evictions() int64 { lru.mu.Lock() defer lru.mu.Unlock() return lru.evictions } // Hits returns number of cache hits since creation -func (lru *LRUCache) Hits() int64 { +func (lru *LRUCache[T]) Hits() int64 { lru.mu.Lock() defer lru.mu.Unlock() return lru.hits } // Misses returns number of cache misses since creation -func (lru *LRUCache) Misses() int64 { +func (lru *LRUCache[T]) Misses() int64 { lru.mu.Lock() defer lru.mu.Unlock() return lru.misses } -// ForEach yields all the values for the cache, ordered from most recently -// used to least recently used. -func (lru *LRUCache) ForEach(callback func(value any) bool) { - lru.mu.Lock() - defer lru.mu.Unlock() - - for e := lru.list.Front(); e != nil; e = e.Next() { - v := e.Value.(*entry) - if !callback(v.value) { - break - } - } -} - // Items returns all the values for the cache, ordered from most recently // used to least recently used. -func (lru *LRUCache) Items() []Item { +func (lru *LRUCache[T]) Items() []Item[T] { lru.mu.Lock() defer lru.mu.Unlock() - items := make([]Item, 0, lru.list.Len()) + items := make([]Item[T], 0, lru.list.Len()) for e := lru.list.Front(); e != nil; e = e.Next() { - v := e.Value.(*entry) - items = append(items, Item{Key: v.key, Value: v.value}) + v := e.Value.(*entry[T]) + items = append(items, Item[T]{Key: v.key, Value: v.value}) } return items } -func (lru *LRUCache) updateInplace(element *list.Element, value any) { - valueSize := lru.cost(value) - sizeDiff := valueSize - element.Value.(*entry).size - element.Value.(*entry).value = value - element.Value.(*entry).size = valueSize - lru.size += sizeDiff +func (lru *LRUCache[T]) updateInplace(element *list.Element, value T) { + element.Value.(*entry[T]).value = value lru.moveToFront(element) lru.checkCapacity() } -func (lru *LRUCache) moveToFront(element *list.Element) { +func (lru *LRUCache[T]) moveToFront(element *list.Element) { lru.list.MoveToFront(element) - element.Value.(*entry).timeAccessed = time.Now() + element.Value.(*entry[T]).timeAccessed = time.Now() } -func (lru *LRUCache) addNew(key string, value any) { - newEntry := &entry{key, value, lru.cost(value), time.Now()} +func (lru *LRUCache[T]) addNew(key string, value T) { + newEntry := &entry[T]{key, value, time.Now()} element := lru.list.PushFront(newEntry) lru.table[key] = element - lru.size += newEntry.size + lru.size++ lru.checkCapacity() } -func (lru *LRUCache) checkCapacity() { +func (lru *LRUCache[T]) checkCapacity() { // Partially duplicated from Delete for lru.size > lru.capacity { delElem := lru.list.Back() - delValue := delElem.Value.(*entry) + delValue := delElem.Value.(*entry[T]) lru.list.Remove(delElem) delete(lru.table, delValue.key) - lru.size -= delValue.size + lru.size-- lru.evictions++ } } - -func (lru *LRUCache) Close() { - lru.Clear() -} diff --git a/go/cache/lru_cache_test.go b/go/cache/lru_cache_test.go index 3faea669d3f..e0dbfcda117 100644 --- a/go/cache/lru_cache_test.go +++ b/go/cache/lru_cache_test.go @@ -24,12 +24,8 @@ type CacheValue struct { size int64 } -func cacheValueSize(val any) int64 { - return val.(*CacheValue).size -} - func TestInitialState(t *testing.T) { - cache := NewLRUCache(5, cacheValueSize) + cache := NewLRUCache[*CacheValue](5) l, sz, c, e, h, m := cache.Len(), cache.UsedCapacity(), cache.MaxCapacity(), cache.Evictions(), cache.Hits(), cache.Misses() if l != 0 { t.Errorf("length = %v, want 0", l) @@ -52,13 +48,13 @@ func TestInitialState(t *testing.T) { } func TestSetInsertsValue(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) data := &CacheValue{0} key := "key" cache.Set(key, data) v, ok := cache.Get(key) - if !ok || v.(*CacheValue) != data { + if !ok || v != data { t.Errorf("Cache has incorrect value: %v != %v", data, v) } @@ -69,40 +65,24 @@ func TestSetInsertsValue(t *testing.T) { } func TestGetValueWithMultipleTypes(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) data := &CacheValue{0} key := "key" cache.Set(key, data) v, ok := cache.Get("key") - if !ok || v.(*CacheValue) != data { + if !ok || v != data { t.Errorf("Cache has incorrect value for \"key\": %v != %v", data, v) } v, ok = cache.Get(string([]byte{'k', 'e', 'y'})) - if !ok || v.(*CacheValue) != data { + if !ok || v != data { t.Errorf("Cache has incorrect value for []byte {'k','e','y'}: %v != %v", data, v) } } -func TestSetUpdatesSize(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) - emptyValue := &CacheValue{0} - key := "key1" - cache.Set(key, emptyValue) - if sz := cache.UsedCapacity(); sz != 0 { - t.Errorf("cache.UsedCapacity() = %v, expected 0", sz) - } - someValue := &CacheValue{20} - key = "key2" - cache.Set(key, someValue) - if sz := cache.UsedCapacity(); sz != 20 { - t.Errorf("cache.UsedCapacity() = %v, expected 20", sz) - } -} - func TestSetWithOldKeyUpdatesValue(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) emptyValue := &CacheValue{0} key := "key1" cache.Set(key, emptyValue) @@ -110,31 +90,13 @@ func TestSetWithOldKeyUpdatesValue(t *testing.T) { cache.Set(key, someValue) v, ok := cache.Get(key) - if !ok || v.(*CacheValue) != someValue { + if !ok || v != someValue { t.Errorf("Cache has incorrect value: %v != %v", someValue, v) } } -func TestSetWithOldKeyUpdatesSize(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) - emptyValue := &CacheValue{0} - key := "key1" - cache.Set(key, emptyValue) - - if sz := cache.UsedCapacity(); sz != 0 { - t.Errorf("cache.UsedCapacity() = %v, expected %v", sz, 0) - } - - someValue := &CacheValue{20} - cache.Set(key, someValue) - expected := int64(someValue.size) - if sz := cache.UsedCapacity(); sz != expected { - t.Errorf("cache.UsedCapacity() = %v, expected %v", sz, expected) - } -} - func TestGetNonExistent(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) if _, ok := cache.Get("notthere"); ok { t.Error("Cache returned a notthere value after no inserts.") @@ -142,7 +104,7 @@ func TestGetNonExistent(t *testing.T) { } func TestDelete(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) value := &CacheValue{1} key := "key" @@ -159,22 +121,9 @@ func TestDelete(t *testing.T) { } } -func TestClear(t *testing.T) { - cache := NewLRUCache(100, cacheValueSize) - value := &CacheValue{1} - key := "key" - - cache.Set(key, value) - cache.Clear() - - if sz := cache.UsedCapacity(); sz != 0 { - t.Errorf("cache.UsedCapacity() = %v, expected 0 after Clear()", sz) - } -} - func TestCapacityIsObeyed(t *testing.T) { size := int64(3) - cache := NewLRUCache(100, cacheValueSize) + cache := NewLRUCache[*CacheValue](100) cache.SetCapacity(size) value := &CacheValue{1} @@ -215,7 +164,7 @@ func TestCapacityIsObeyed(t *testing.T) { func TestLRUIsEvicted(t *testing.T) { size := int64(3) - cache := NewLRUCache(size, cacheValueSize) + cache := NewLRUCache[*CacheValue](size) cache.Set("key1", &CacheValue{1}) cache.Set("key2", &CacheValue{1}) diff --git a/go/cache/theine/bf/bf.go b/go/cache/theine/bf/bf.go index f68e34d81e3..97b27a5c217 100644 --- a/go/cache/theine/bf/bf.go +++ b/go/cache/theine/bf/bf.go @@ -54,7 +54,7 @@ func (d *Bloomfilter) EnsureCapacity(capacity int) { func (d *Bloomfilter) Exist(h uint64) bool { h1, h2 := uint32(h), uint32(h>>32) var o uint = 1 - for i := uint32(0); i < d.K; i++ { + for i := range d.K { o &= d.Filter.get((h1 + (i * h2)) & (d.M - 1)) } return o == 1 @@ -65,7 +65,7 @@ func (d *Bloomfilter) Exist(h uint64) bool { func (d *Bloomfilter) Insert(h uint64) bool { h1, h2 := uint32(h), uint32(h>>32) var o uint = 1 - for i := uint32(0); i < d.K; i++ { + for i := range d.K { o &= d.Filter.getset((h1 + (i * h2)) & (d.M - 1)) } return o == 1 diff --git a/go/cache/theine/list_test.go b/go/cache/theine/list_test.go index aad68f5c142..a0b607338dd 100644 --- a/go/cache/theine/list_test.go +++ b/go/cache/theine/list_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { l := NewList[StringKey, string](5, LIST_PROBATION) require.Equal(t, uint(5), l.capacity) require.Equal(t, LIST_PROBATION, l.listType) - for i := 0; i < 5; i++ { + for i := range 5 { evicted := l.PushFront(NewEntry(StringKey(fmt.Sprintf("%d", i)), "", 1)) require.Nil(t, evicted) } @@ -42,7 +42,7 @@ func TestList(t *testing.T) { require.Equal(t, "5/4/3/2/1", l.display()) require.Equal(t, "1/2/3/4/5", l.displayReverse()) - for i := 0; i < 5; i++ { + for i := range 5 { entry := l.PopTail() require.Equal(t, StringKey(fmt.Sprintf("%d", i+1)), entry.key) } @@ -50,7 +50,7 @@ func TestList(t *testing.T) { require.Nil(t, entry) var entries []*Entry[StringKey, string] - for i := 0; i < 5; i++ { + for i := range 5 { new := NewEntry(StringKey(fmt.Sprintf("%d", i)), "", 1) evicted := l.PushFront(new) entries = append(entries, new) @@ -76,13 +76,13 @@ func TestListCountCost(t *testing.T) { l := NewList[StringKey, string](100, LIST_PROBATION) require.Equal(t, uint(100), l.capacity) require.Equal(t, LIST_PROBATION, l.listType) - for i := 0; i < 5; i++ { + for i := range 5 { evicted := l.PushFront(NewEntry(StringKey(fmt.Sprintf("%d", i)), "", 20)) require.Nil(t, evicted) } require.Equal(t, 100, l.len) require.Equal(t, 5, l.count) - for i := 0; i < 3; i++ { + for range 3 { entry := l.PopTail() require.NotNil(t, entry) } diff --git a/go/cache/theine/singleflight_test.go b/go/cache/theine/singleflight_test.go index 60b28e69b4e..4ae235f97e2 100644 --- a/go/cache/theine/singleflight_test.go +++ b/go/cache/theine/singleflight_test.go @@ -32,6 +32,9 @@ import ( "sync/atomic" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDo(t *testing.T) { @@ -39,12 +42,9 @@ func TestDo(t *testing.T) { v, err, _ := g.Do("key", func() (string, error) { return "bar", nil }) - if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want { - t.Errorf("Do = %v; want %v", got, want) - } - if err != nil { - t.Errorf("Do error = %v", err) - } + + assert.Equal(t, "bar (string)", fmt.Sprintf("%v (%T)", v, v), "incorrect Do value") + assert.NoError(t, err, "got Do error") } func TestDoErr(t *testing.T) { @@ -53,12 +53,9 @@ func TestDoErr(t *testing.T) { v, err, _ := g.Do("key", func() (string, error) { return "", someErr }) - if err != someErr { - t.Errorf("Do error = %v; want someErr %v", err, someErr) - } - if v != "" { - t.Errorf("unexpected non-nil value %#v", v) - } + + assert.ErrorIs(t, err, someErr, "incorrect Do error") + assert.Empty(t, v, "unexpected non-empty value") } func TestDoDupSuppress(t *testing.T) { @@ -81,20 +78,18 @@ func TestDoDupSuppress(t *testing.T) { const n = 10 wg1.Add(1) - for i := 0; i < n; i++ { + for range n { wg1.Add(1) wg2.Add(1) go func() { defer wg2.Done() wg1.Done() v, err, _ := g.Do("key", fn) - if err != nil { - t.Errorf("Do error: %v", err) + if !assert.NoError(t, err, "unexpected Do error") { return } - if s := v; s != "bar" { - t.Errorf("Do = %T %v; want %q", v, v, "bar") - } + + assert.Equal(t, "bar", v, "unexpected Do value") }() } wg1.Wait() @@ -102,9 +97,8 @@ func TestDoDupSuppress(t *testing.T) { // least reached the line before the Do. c <- "bar" wg2.Wait() - if got := atomic.LoadInt32(&calls); got <= 0 || got >= n { - t.Errorf("number of calls = %d; want over 0 and less than %d", got, n) - } + got := atomic.LoadInt32(&calls) + assert.True(t, got > 0 && got < n, "number of calls not between 0 and %d", n) } // Test singleflight behaves correctly after Do panic. @@ -119,7 +113,7 @@ func TestPanicDo(t *testing.T) { waited := int32(n) panicCount := int32(0) done := make(chan struct{}) - for i := 0; i < n; i++ { + for range n { go func() { defer func() { if err := recover(); err != nil { @@ -137,11 +131,9 @@ func TestPanicDo(t *testing.T) { select { case <-done: - if panicCount != n { - t.Errorf("Expect %d panic, but got %d", n, panicCount) - } + assert.Equal(t, int32(n), panicCount, "unexpected number of panics") case <-time.After(time.Second): - t.Fatalf("Do hangs") + require.Fail(t, "Do hangs") } } @@ -155,13 +147,11 @@ func TestGoexitDo(t *testing.T) { const n = 5 waited := int32(n) done := make(chan struct{}) - for i := 0; i < n; i++ { + for range n { go func() { var err error defer func() { - if err != nil { - t.Errorf("Error should be nil, but got: %v", err) - } + assert.NoError(t, err) if atomic.AddInt32(&waited, -1) == 0 { close(done) } @@ -173,7 +163,7 @@ func TestGoexitDo(t *testing.T) { select { case <-done: case <-time.After(time.Second): - t.Fatalf("Do hangs") + require.Fail(t, "Do hangs") } } @@ -201,10 +191,9 @@ func randKeys(b *testing.B, count, length uint) []string { keys := make([]string, 0, count) key := make([]byte, length) - for i := uint(0); i < count; i++ { - if _, err := io.ReadFull(rand.Reader, key); err != nil { - b.Fatalf("Failed to generate random key %d of %d of length %d: %s", i+1, count, length, err) - } + for i := range uint(count) { + _, err := io.ReadFull(rand.Reader, key) + require.NoError(b, err, "Failed to generate random key %d of %d length %d", i+1, count, length) keys = append(keys, string(key)) } return keys diff --git a/go/cache/theine/sketch_test.go b/go/cache/theine/sketch_test.go index 3437f0cac3c..fb53fa8e5fb 100644 --- a/go/cache/theine/sketch_test.go +++ b/go/cache/theine/sketch_test.go @@ -23,7 +23,7 @@ func TestSketch(t *testing.T) { sketch.SampleSize = 5120 failed := 0 - for i := 0; i < 500; i++ { + for i := range 500 { key := fmt.Sprintf("key:%d", i) keyh := xxhash.Sum64String(key) sketch.Add(keyh) diff --git a/go/cache/theine/store.go b/go/cache/theine/store.go index 3d86e549867..cef5a89c8b7 100644 --- a/go/cache/theine/store.go +++ b/go/cache/theine/store.go @@ -44,17 +44,17 @@ const ( ) type Shard[K cachekey, V any] struct { - hashmap map[K]*Entry[K, V] - dookeeper *bf.Bloomfilter - deque *deque.Deque[*Entry[K, V]] - group *Group[K, V] - qsize uint - qlen int - counter uint - mu sync.RWMutex + hashmap map[K]*Entry[K, V] + doorkeeper *bf.Bloomfilter + deque *deque.Deque[*Entry[K, V]] + group *Group[K, V] + qsize uint + qlen int + counter uint + mu sync.RWMutex } -func NewShard[K cachekey, V any](size uint, qsize uint, doorkeeper bool) *Shard[K, V] { +func NewShard[K cachekey, V any](qsize uint, doorkeeper bool) *Shard[K, V] { s := &Shard[K, V]{ hashmap: make(map[K]*Entry[K, V]), qsize: qsize, @@ -62,17 +62,17 @@ func NewShard[K cachekey, V any](size uint, qsize uint, doorkeeper bool) *Shard[ group: NewGroup[K, V](), } if doorkeeper { - s.dookeeper = bf.New(0.01) + s.doorkeeper = bf.New(0.01) } return s } func (s *Shard[K, V]) set(key K, entry *Entry[K, V]) { s.hashmap[key] = entry - if s.dookeeper != nil { + if s.doorkeeper != nil { ds := 20 * len(s.hashmap) - if ds > s.dookeeper.Capacity { - s.dookeeper.EnsureCapacity(ds) + if ds > s.doorkeeper.Capacity { + s.doorkeeper.EnsureCapacity(ds) } } } @@ -195,10 +195,6 @@ func NewStore[K cachekey, V cacheval](maxsize int64, doorkeeper bool) *Store[K, shardCount = 128 } dequeSize := int(maxsize) / 100 / shardCount - shardSize := int(maxsize) / shardCount - if shardSize < 50 { - shardSize = 50 - } policySize := int(maxsize) - (dequeSize * shardCount) s := &Store[K, V]{ @@ -212,8 +208,8 @@ func NewStore[K cachekey, V cacheval](maxsize int64, doorkeeper bool) *Store[K, writebufsize: writeBufSize, } s.shards = make([]*Shard[K, V], 0, s.shardCount) - for i := 0; i < int(s.shardCount); i++ { - s.shards = append(s.shards, NewShard[K, V](uint(shardSize), uint(dequeSize), doorkeeper)) + for range s.shardCount { + s.shards = append(s.shards, NewShard[K, V](uint(dequeSize), doorkeeper)) } go s.maintenance() @@ -329,11 +325,11 @@ func (s *Store[K, V]) setInternal(key K, value V, cost int64, epoch uint32) (*Sh return shard, exist, true } if s.doorkeeper { - if shard.counter > uint(shard.dookeeper.Capacity) { - shard.dookeeper.Reset() + if shard.counter > uint(shard.doorkeeper.Capacity) { + shard.doorkeeper.Reset() shard.counter = 0 } - hit := shard.dookeeper.Insert(h) + hit := shard.doorkeeper.Insert(h) if !hit { shard.counter += 1 shard.mu.Unlock() @@ -373,7 +369,6 @@ func (s *Store[K, V]) processDeque(shard *Shard[K, V], epoch uint32) { return } var evictedkv []dequeKV[K, V] - var expiredkv []dequeKV[K, V] // send to slru send := make([]*Entry[K, V], 0, 2) @@ -422,9 +417,6 @@ func (s *Store[K, V]) processDeque(shard *Shard[K, V], epoch uint32) { for _, kv := range evictedkv { s.OnRemoval(kv.k, kv.v, EVICTED) } - for _, kv := range expiredkv { - s.OnRemoval(kv.k, kv.v, EXPIRED) - } } } diff --git a/go/cache/theine/store_test.go b/go/cache/theine/store_test.go index 880acf30193..e6a2f9d5679 100644 --- a/go/cache/theine/store_test.go +++ b/go/cache/theine/store_test.go @@ -52,7 +52,7 @@ func TestProcessDeque(t *testing.T) { shard := store.shards[index] shard.qsize = 10 - for i := keyint(0); i < 5; i++ { + for i := range keyint(5) { entry := &Entry[keyint, cachedint]{key: i} entry.cost.Store(1) store.shards[index].deque.PushFront(entry) @@ -74,9 +74,9 @@ func TestProcessDeque(t *testing.T) { func TestDoorKeeperDynamicSize(t *testing.T) { store := NewStore[keyint, cachedint](200000, true) shard := store.shards[0] - require.True(t, shard.dookeeper.Capacity == 512) - for i := keyint(0); i < 5000; i++ { + require.True(t, shard.doorkeeper.Capacity == 512) + for i := range keyint(5000) { shard.set(i, &Entry[keyint, cachedint]{}) } - require.True(t, shard.dookeeper.Capacity > 100000) + require.True(t, shard.doorkeeper.Capacity > 100000) } diff --git a/go/cache/theine/tlfu_test.go b/go/cache/theine/tlfu_test.go index ac6ddaabdb6..f798f89549f 100644 --- a/go/cache/theine/tlfu_test.go +++ b/go/cache/theine/tlfu_test.go @@ -33,7 +33,7 @@ func TestTlfu(t *testing.T) { require.Equal(t, 0, tlfu.slru.protected.len) var entries []*Entry[StringKey, string] - for i := 0; i < 200; i++ { + for i := range 200 { e := NewEntry(StringKey(fmt.Sprintf("%d", i)), "", 1) evicted := tlfu.Set(e) entries = append(entries, e) @@ -78,7 +78,7 @@ func TestTlfu(t *testing.T) { require.Equal(t, 998, tlfu.slru.probation.len) var entries2 []*Entry[StringKey, string] - for i := 0; i < 1000; i++ { + for i := range 1000 { e := NewEntry(StringKey(fmt.Sprintf("%d*", i)), "", 1) tlfu.Set(e) entries2 = append(entries2, e) @@ -103,7 +103,7 @@ func TestEvictEntries(t *testing.T) { require.Equal(t, 0, tlfu.slru.probation.len) require.Equal(t, 0, tlfu.slru.protected.len) - for i := 0; i < 500; i++ { + for i := range 500 { tlfu.Set(NewEntry(StringKey(fmt.Sprintf("%d:1", i)), "", 1)) } require.Equal(t, 500, tlfu.slru.probation.len) diff --git a/go/cmd/mysqlctl/command/init.go b/go/cmd/mysqlctl/command/init.go index 71a9661aa80..14d8e5f6d29 100644 --- a/go/cmd/mysqlctl/command/init.go +++ b/go/cmd/mysqlctl/command/init.go @@ -49,7 +49,7 @@ var initArgs = struct { func commandInit(cmd *cobra.Command, args []string) error { // Generate my.cnf from scratch and use it to find mysqld. - mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort) + mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort, collationEnv) if err != nil { return fmt.Errorf("failed to initialize mysql config: %v", err) } diff --git a/go/cmd/mysqlctl/command/init_config.go b/go/cmd/mysqlctl/command/init_config.go index 70e751e02cb..36687482e08 100644 --- a/go/cmd/mysqlctl/command/init_config.go +++ b/go/cmd/mysqlctl/command/init_config.go @@ -40,7 +40,7 @@ var InitConfig = &cobra.Command{ func commandInitConfig(cmd *cobra.Command, args []string) error { // Generate my.cnf from scratch and use it to find mysqld. - mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort) + mysqld, cnf, err := mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort, collationEnv) if err != nil { return fmt.Errorf("failed to initialize mysql config: %v", err) } diff --git a/go/cmd/mysqlctl/command/reinit_config.go b/go/cmd/mysqlctl/command/reinit_config.go index b06642c8203..fd7523c0411 100644 --- a/go/cmd/mysqlctl/command/reinit_config.go +++ b/go/cmd/mysqlctl/command/reinit_config.go @@ -41,7 +41,7 @@ var ReinitConfig = &cobra.Command{ func commandReinitConfig(cmd *cobra.Command, args []string) error { // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID, collationEnv) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } diff --git a/go/cmd/mysqlctl/command/root.go b/go/cmd/mysqlctl/command/root.go index 4f5626ef7e6..78b3a623666 100644 --- a/go/cmd/mysqlctl/command/root.go +++ b/go/cmd/mysqlctl/command/root.go @@ -23,21 +23,22 @@ import ( "vitess.io/vitess/go/acl" vtcmd "vitess.io/vitess/go/cmd" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" ) var ( - mysqlPort = 3306 - tabletUID = uint32(41983) - mysqlSocket string + mysqlPort = 3306 + tabletUID = uint32(41983) + mysqlSocket string + collationEnv *collations.Environment Root = &cobra.Command{ Use: "mysqlctl", Short: "mysqlctl initializes and controls mysqld with Vitess-specific configuration.", Long: "`mysqlctl` is a command-line client used for managing `mysqld` instances.\n\n" + - "It is responsible for bootstrapping tasks such as generating a configuration file for `mysqld` and initializing the instance and its data directory.\n" + "The `mysqld_safe` watchdog is utilized when present.\n" + "This helps ensure that `mysqld` is automatically restarted after failures.", @@ -74,4 +75,6 @@ func init() { Root.PersistentFlags().StringVar(&mysqlSocket, "mysql_socket", mysqlSocket, "Path to the mysqld socket file.") acl.RegisterFlags(Root.PersistentFlags()) + + collationEnv = collations.NewEnvironment(servenv.MySQLServerVersion()) } diff --git a/go/cmd/mysqlctl/command/shutdown.go b/go/cmd/mysqlctl/command/shutdown.go index 41c804856eb..321d4a9b35f 100644 --- a/go/cmd/mysqlctl/command/shutdown.go +++ b/go/cmd/mysqlctl/command/shutdown.go @@ -30,7 +30,6 @@ var Shutdown = &cobra.Command{ Use: "shutdown", Short: "Shuts down mysqld, without removing any files.", Long: "Stop a `mysqld` instance that was previously started with `init` or `start`.\n\n" + - "For large `mysqld` instances, you may need to extend the `wait_time` to shutdown cleanly.", Example: `mysqlctl --tablet_uid 101 --alsologtostderr shutdown`, Args: cobra.NoArgs, @@ -45,15 +44,15 @@ var shutdownArgs = struct { func commandShutdown(cmd *cobra.Command, args []string) error { // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID, collationEnv) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } defer mysqld.Close() - ctx, cancel := context.WithTimeout(context.Background(), shutdownArgs.WaitTime) + ctx, cancel := context.WithTimeout(context.Background(), shutdownArgs.WaitTime+10*time.Second) defer cancel() - if err := mysqld.Shutdown(ctx, cnf, true); err != nil { + if err := mysqld.Shutdown(ctx, cnf, true, shutdownArgs.WaitTime); err != nil { return fmt.Errorf("failed shutdown mysql: %v", err) } return nil diff --git a/go/cmd/mysqlctl/command/start.go b/go/cmd/mysqlctl/command/start.go index 397909e0966..aef404d0a8e 100644 --- a/go/cmd/mysqlctl/command/start.go +++ b/go/cmd/mysqlctl/command/start.go @@ -45,7 +45,7 @@ var startArgs = struct { func commandStart(cmd *cobra.Command, args []string) error { // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID, collationEnv) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } diff --git a/go/cmd/mysqlctl/command/teardown.go b/go/cmd/mysqlctl/command/teardown.go index 0d37a15cfdc..89d7b3b5f6d 100644 --- a/go/cmd/mysqlctl/command/teardown.go +++ b/go/cmd/mysqlctl/command/teardown.go @@ -32,7 +32,6 @@ var Teardown = &cobra.Command{ Long: "{{< warning >}}\n" + "This is a destructive operation.\n" + "{{}}\n\n" + - "Shuts down a `mysqld` instance and removes its data directory.", Example: `mysqlctl --tablet_uid 101 --alsologtostderr teardown`, Args: cobra.NoArgs, @@ -48,15 +47,15 @@ var teardownArgs = struct { func commandTeardown(cmd *cobra.Command, args []string) error { // There ought to be an existing my.cnf, so use it to find mysqld. - mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID) + mysqld, cnf, err := mysqlctl.OpenMysqldAndMycnf(tabletUID, collationEnv) if err != nil { return fmt.Errorf("failed to find mysql config: %v", err) } defer mysqld.Close() - ctx, cancel := context.WithTimeout(context.Background(), teardownArgs.WaitTime) + ctx, cancel := context.WithTimeout(context.Background(), teardownArgs.WaitTime+10*time.Second) defer cancel() - if err := mysqld.Teardown(ctx, cnf, teardownArgs.Force); err != nil { + if err := mysqld.Teardown(ctx, cnf, teardownArgs.Force, teardownArgs.WaitTime); err != nil { return fmt.Errorf("failed teardown mysql (forced? %v): %v", teardownArgs.Force, err) } return nil diff --git a/go/cmd/mysqlctld/cli/mysqlctld.go b/go/cmd/mysqlctld/cli/mysqlctld.go index 6ebaa5dc422..2dff7da0f7f 100644 --- a/go/cmd/mysqlctld/cli/mysqlctld.go +++ b/go/cmd/mysqlctld/cli/mysqlctld.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/cobra" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" @@ -40,13 +41,15 @@ var ( mysqld *mysqlctl.Mysqld cnf *mysqlctl.Mycnf - mysqlPort = 3306 - tabletUID = uint32(41983) - mysqlSocket string + mysqlPort = 3306 + tabletUID = uint32(41983) + mysqlSocket string + collationEnv *collations.Environment // mysqlctl init flags - waitTime = 5 * time.Minute - initDBSQLFile string + waitTime = 5 * time.Minute + shutdownWaitTime = 5 * time.Minute + initDBSQLFile string Main = &cobra.Command{ Use: "mysqlctld", @@ -64,6 +67,7 @@ var ( --mysql_port=17100 \ --socket_file=/path/to/socket_file`, Args: cobra.NoArgs, + Version: servenv.AppVersion.String(), PreRunE: servenv.CobraPreRunE, RunE: run, } @@ -84,10 +88,13 @@ func init() { Main.Flags().IntVar(&mysqlPort, "mysql_port", mysqlPort, "MySQL port") Main.Flags().Uint32Var(&tabletUID, "tablet_uid", tabletUID, "Tablet UID") Main.Flags().StringVar(&mysqlSocket, "mysql_socket", mysqlSocket, "Path to the mysqld socket file") - Main.Flags().DurationVar(&waitTime, "wait_time", waitTime, "How long to wait for mysqld startup or shutdown") + Main.Flags().DurationVar(&waitTime, "wait_time", waitTime, "How long to wait for mysqld startup") Main.Flags().StringVar(&initDBSQLFile, "init_db_sql_file", initDBSQLFile, "Path to .sql file to run after mysqld initialization") + Main.Flags().DurationVar(&shutdownWaitTime, "shutdown-wait-time", shutdownWaitTime, "How long to wait for mysqld shutdown") acl.RegisterFlags(Main.Flags()) + + collationEnv = collations.NewEnvironment(servenv.MySQLServerVersion()) } func run(cmd *cobra.Command, args []string) error { @@ -108,7 +115,7 @@ func run(cmd *cobra.Command, args []string) error { log.Infof("mycnf file (%s) doesn't exist, initializing", mycnfFile) var err error - mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort) + mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(tabletUID, mysqlSocket, mysqlPort, collationEnv) if err != nil { cancel() return fmt.Errorf("failed to initialize mysql config: %w", err) @@ -124,7 +131,7 @@ func run(cmd *cobra.Command, args []string) error { log.Infof("mycnf file (%s) already exists, starting without init", mycnfFile) var err error - mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(tabletUID) + mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(tabletUID, collationEnv) if err != nil { cancel() return fmt.Errorf("failed to find mysql config: %w", err) @@ -154,8 +161,9 @@ func run(cmd *cobra.Command, args []string) error { // Take mysqld down with us on SIGTERM before entering lame duck. servenv.OnTermSync(func() { log.Infof("mysqlctl received SIGTERM, shutting down mysqld first") - ctx := context.Background() - if err := mysqld.Shutdown(ctx, cnf, true); err != nil { + ctx, cancel := context.WithTimeout(context.Background(), shutdownWaitTime+10*time.Second) + defer cancel() + if err := mysqld.Shutdown(ctx, cnf, true, shutdownWaitTime); err != nil { log.Errorf("failed to shutdown mysqld: %v", err) } }) diff --git a/go/cmd/topo2topo/cli/plugin_consultopo.go b/go/cmd/topo2topo/cli/plugin_consultopo.go index a128f294a42..56d178e2975 100644 --- a/go/cmd/topo2topo/cli/plugin_consultopo.go +++ b/go/cmd/topo2topo/cli/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/topo2topo/cli/topo2topo.go b/go/cmd/topo2topo/cli/topo2topo.go index 6e7e173872b..f6f69e08eda 100644 --- a/go/cmd/topo2topo/cli/topo2topo.go +++ b/go/cmd/topo2topo/cli/topo2topo.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/vt/grpccommon" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/helpers" ) @@ -51,6 +52,7 @@ var ( It can also be used to compare data between two topologies.`, Args: cobra.NoArgs, PreRunE: servenv.CobraPreRunE, + Version: servenv.AppVersion.String(), RunE: run, } ) @@ -94,12 +96,21 @@ func run(cmd *cobra.Command, args []string) error { return compareTopos(ctx, fromTS, toTS) } - return copyTopos(ctx, fromTS, toTS) + parser, err := sqlparser.New(sqlparser.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + return fmt.Errorf("cannot create sqlparser: %w", err) + } + + return copyTopos(ctx, fromTS, toTS, parser) } -func copyTopos(ctx context.Context, fromTS, toTS *topo.Server) error { +func copyTopos(ctx context.Context, fromTS, toTS *topo.Server, parser *sqlparser.Parser) error { if doKeyspaces { - if err := helpers.CopyKeyspaces(ctx, fromTS, toTS); err != nil { + if err := helpers.CopyKeyspaces(ctx, fromTS, toTS, parser); err != nil { return err } } diff --git a/go/cmd/vtadmin/main.go b/go/cmd/vtadmin/main.go index 210e2edb918..6cc3b9065b5 100644 --- a/go/cmd/vtadmin/main.go +++ b/go/cmd/vtadmin/main.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" + _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" @@ -35,8 +36,7 @@ import ( vtadminhttp "vitess.io/vitess/go/vt/vtadmin/http" "vitess.io/vitess/go/vt/vtadmin/http/debug" "vitess.io/vitess/go/vt/vtadmin/rbac" - - _flag "vitess.io/vitess/go/internal/flag" + "vitess.io/vitess/go/vt/vtenv" ) var ( @@ -139,7 +139,15 @@ func run(cmd *cobra.Command, args []string) { } cache.SetCacheRefreshKey(cacheRefreshKey) - s := vtadmin.NewAPI(clusters, vtadmin.Options{ + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + fatal(err) + } + s := vtadmin.NewAPI(env, clusters, vtadmin.Options{ GRPCOpts: opts, HTTPOpts: httpOpts, RBAC: rbacConfig, @@ -208,11 +216,11 @@ func main() { rootCmd.Flags().AddGoFlag(flag.Lookup("stderrthreshold")) rootCmd.Flags().AddGoFlag(flag.Lookup("log_dir")) + servenv.RegisterMySQLServerFlags(rootCmd.Flags()) + if err := rootCmd.Execute(); err != nil { log.Fatal(err) } - - log.Flush() } type noopCloser struct{} diff --git a/go/cmd/vtbackup/cli/vtbackup.go b/go/cmd/vtbackup/cli/vtbackup.go index 121ba39b8c5..1f6f62f7ad1 100644 --- a/go/cmd/vtbackup/cli/vtbackup.go +++ b/go/cmd/vtbackup/cli/vtbackup.go @@ -29,11 +29,11 @@ import ( "github.com/spf13/cobra" - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/acl" "vitess.io/vitess/go/cmd" "vitess.io/vitess/go/exit" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" @@ -84,13 +84,16 @@ var ( incrementalFromPos string // mysqlctld-like flags - mysqlPort = 3306 - mysqlSocket string - mysqlTimeout = 5 * time.Minute - initDBSQLFile string - detachedMode bool - keepAliveTimeout time.Duration - disableRedoLog bool + mysqlPort = 3306 + mysqlSocket string + mysqlTimeout = 5 * time.Minute + mysqlShutdownTimeout = 5 * time.Minute + initDBSQLFile string + detachedMode bool + keepAliveTimeout time.Duration + disableRedoLog bool + + collationEnv *collations.Environment // Deprecated, use "Phase" instead. deprecatedDurationByPhase = stats.NewGaugesWithSingleLabel( @@ -201,18 +204,21 @@ func init() { Main.Flags().StringVar(&initKeyspace, "init_keyspace", initKeyspace, "(init parameter) keyspace to use for this tablet") Main.Flags().StringVar(&initShard, "init_shard", initShard, "(init parameter) shard to use for this tablet") Main.Flags().IntVar(&concurrency, "concurrency", concurrency, "(init restore parameter) how many concurrent files to restore at once") - Main.Flags().StringVar(&incrementalFromPos, "incremental_from_pos", incrementalFromPos, "Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position") + Main.Flags().StringVar(&incrementalFromPos, "incremental_from_pos", incrementalFromPos, "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.") // mysqlctld-like flags Main.Flags().IntVar(&mysqlPort, "mysql_port", mysqlPort, "mysql port") Main.Flags().StringVar(&mysqlSocket, "mysql_socket", mysqlSocket, "path to the mysql socket") Main.Flags().DurationVar(&mysqlTimeout, "mysql_timeout", mysqlTimeout, "how long to wait for mysqld startup") + Main.Flags().DurationVar(&mysqlShutdownTimeout, "mysql-shutdown-timeout", mysqlShutdownTimeout, "how long to wait for mysqld shutdown") Main.Flags().StringVar(&initDBSQLFile, "init_db_sql_file", initDBSQLFile, "path to .sql file to run after mysql_install_db") Main.Flags().BoolVar(&detachedMode, "detach", detachedMode, "detached mode - run backups detached from the terminal") Main.Flags().DurationVar(&keepAliveTimeout, "keep-alive-timeout", keepAliveTimeout, "Wait until timeout elapses after a successful backup before shutting down.") Main.Flags().BoolVar(&disableRedoLog, "disable-redo-log", disableRedoLog, "Disable InnoDB redo log during replication-from-primary phase of backup.") acl.RegisterFlags(Main.Flags()) + + collationEnv = collations.NewEnvironment(servenv.MySQLServerVersion()) } func run(_ *cobra.Command, args []string) error { @@ -325,7 +331,7 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back }() // Start up mysqld as if we are mysqlctld provisioning a fresh tablet. - mysqld, mycnf, err := mysqlctl.CreateMysqldAndMycnf(tabletAlias.Uid, mysqlSocket, mysqlPort) + mysqld, mycnf, err := mysqlctl.CreateMysqldAndMycnf(tabletAlias.Uid, mysqlSocket, mysqlPort, collationEnv) if err != nil { return fmt.Errorf("failed to initialize mysql config: %v", err) } @@ -340,9 +346,9 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back defer func() { // Be careful not to use the original context, because we don't want to // skip shutdown just because we timed out waiting for other things. - mysqlShutdownCtx, mysqlShutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) + mysqlShutdownCtx, mysqlShutdownCancel := context.WithTimeout(context.Background(), mysqlShutdownTimeout+10*time.Second) defer mysqlShutdownCancel() - if err := mysqld.Shutdown(mysqlShutdownCtx, mycnf, false); err != nil { + if err := mysqld.Shutdown(mysqlShutdownCtx, mycnf, false, mysqlShutdownTimeout); err != nil { log.Errorf("failed to shutdown mysqld: %v", err) } }() @@ -356,18 +362,19 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back } backupParams := mysqlctl.BackupParams{ - Cnf: mycnf, - Mysqld: mysqld, - Logger: logutil.NewConsoleLogger(), - Concurrency: concurrency, - IncrementalFromPos: incrementalFromPos, - HookExtraEnv: extraEnv, - TopoServer: topoServer, - Keyspace: initKeyspace, - Shard: initShard, - TabletAlias: topoproto.TabletAliasString(tabletAlias), - Stats: backupstats.BackupStats(), - UpgradeSafe: upgradeSafe, + Cnf: mycnf, + Mysqld: mysqld, + Logger: logutil.NewConsoleLogger(), + Concurrency: concurrency, + IncrementalFromPos: incrementalFromPos, + HookExtraEnv: extraEnv, + TopoServer: topoServer, + Keyspace: initKeyspace, + Shard: initShard, + TabletAlias: topoproto.TabletAliasString(tabletAlias), + Stats: backupstats.BackupStats(), + UpgradeSafe: upgradeSafe, + MysqlShutdownTimeout: mysqlShutdownTimeout, } // In initial_backup mode, just take a backup of this empty database. if initialBackup { @@ -416,16 +423,17 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back log.Infof("Restoring latest backup from directory %v", backupDir) restoreAt := time.Now() params := mysqlctl.RestoreParams{ - Cnf: mycnf, - Mysqld: mysqld, - Logger: logutil.NewConsoleLogger(), - Concurrency: concurrency, - HookExtraEnv: extraEnv, - DeleteBeforeRestore: true, - DbName: dbName, - Keyspace: initKeyspace, - Shard: initShard, - Stats: backupstats.RestoreStats(), + Cnf: mycnf, + Mysqld: mysqld, + Logger: logutil.NewConsoleLogger(), + Concurrency: concurrency, + HookExtraEnv: extraEnv, + DeleteBeforeRestore: true, + DbName: dbName, + Keyspace: initKeyspace, + Shard: initShard, + Stats: backupstats.RestoreStats(), + MysqlShutdownTimeout: mysqlShutdownTimeout, } backupManifest, err := mysqlctl.Restore(ctx, params) var restorePos replication.Position @@ -583,7 +591,7 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back return fmt.Errorf("Could not prep for full shutdown: %v", err) } // Shutdown, waiting for it to finish - if err := mysqld.Shutdown(ctx, mycnf, true); err != nil { + if err := mysqld.Shutdown(ctx, mycnf, true, mysqlShutdownTimeout); err != nil { return fmt.Errorf("Something went wrong during full MySQL shutdown: %v", err) } // Start MySQL, waiting for it to come up diff --git a/go/cmd/vtclient/cli/vtclient_test.go b/go/cmd/vtclient/cli/vtclient_test.go index a5ee571cd0b..a323ab5c63a 100644 --- a/go/cmd/vtclient/cli/vtclient_test.go +++ b/go/cmd/vtclient/cli/vtclient_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtcombo/cli/main.go b/go/cmd/vtcombo/cli/main.go index bfc0ad894fe..a86b98cc250 100644 --- a/go/cmd/vtcombo/cli/main.go +++ b/go/cmd/vtcombo/cli/main.go @@ -40,9 +40,11 @@ import ( "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtcombo" "vitess.io/vitess/go/vt/vtctld" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -76,9 +78,12 @@ In particular, it contains: plannerName string vschemaPersistenceDir string - tpb vttestpb.VTTestTopology - ts *topo.Server - resilientServer *srvtopo.ResilientServer + tpb vttestpb.VTTestTopology + ts *topo.Server + resilientServer *srvtopo.ResilientServer + tabletTypesToWait []topodatapb.TabletType + + env *vtenv.Environment ) func init() { @@ -111,9 +116,21 @@ func init() { Main.Flags().Var(vttest.TextTopoData(&tpb), "proto_topo", "vttest proto definition of the topology, encoded in compact text format. See vttest.proto for more information.") Main.Flags().Var(vttest.JSONTopoData(&tpb), "json_topo", "vttest proto definition of the topology, encoded in json format. See vttest.proto for more information.") + Main.Flags().Var((*topoproto.TabletTypeListFlag)(&tabletTypesToWait), "tablet_types_to_wait", "Wait till connected for specified tablet types during Gateway initialization. Should be provided as a comma-separated set of tablet types.") + // We're going to force the value later, so don't even bother letting the // user know about this flag. Main.Flags().MarkHidden("tablet_protocol") + + var err error + env, err = vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + log.Fatalf("unable to initialize env: %v", err) + } } func startMysqld(uid uint32) (mysqld *mysqlctl.Mysqld, cnf *mysqlctl.Mycnf, err error) { @@ -123,7 +140,7 @@ func startMysqld(uid uint32) (mysqld *mysqlctl.Mysqld, cnf *mysqlctl.Mycnf, err mycnfFile := mysqlctl.MycnfFile(uid) if _, statErr := os.Stat(mycnfFile); os.IsNotExist(statErr) { - mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(uid, "", mysqlPort) + mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(uid, "", mysqlPort, env.CollationEnv()) if err != nil { return nil, nil, fmt.Errorf("failed to initialize mysql config :%w", err) } @@ -131,7 +148,7 @@ func startMysqld(uid uint32) (mysqld *mysqlctl.Mysqld, cnf *mysqlctl.Mycnf, err return nil, nil, fmt.Errorf("failed to initialize mysql :%w", err) } } else { - mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(uid) + mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(uid, env.CollationEnv()) if err != nil { return nil, nil, fmt.Errorf("failed to find mysql config: %w", err) } @@ -147,6 +164,8 @@ func startMysqld(uid uint32) (mysqld *mysqlctl.Mysqld, cnf *mysqlctl.Mycnf, err return mysqld, cnf, nil } +const mysqlShutdownTimeout = 5 * time.Minute + func run(cmd *cobra.Command, args []string) (err error) { // Stash away a copy of the topology that vtcombo was started with. // @@ -163,7 +182,7 @@ func run(cmd *cobra.Command, args []string) (err error) { // vtctld UI requires the cell flag cmd.Flags().Set("cell", tpb.Cells[0]) - if cmd.Flags().Lookup("log_dir") == nil { + if f := cmd.Flags().Lookup("log_dir"); f != nil && !f.Changed { cmd.Flags().Set("log_dir", "$VTDATAROOT/tmp") } @@ -195,13 +214,15 @@ func run(cmd *cobra.Command, args []string) (err error) { return err } servenv.OnClose(func() { - mysqld.Shutdown(context.TODO(), cnf, true) + ctx, cancel := context.WithTimeout(context.Background(), mysqlShutdownTimeout+10*time.Second) + defer cancel() + mysqld.Shutdown(ctx, cnf, true, mysqlShutdownTimeout) }) // We want to ensure we can write to this database mysqld.SetReadOnly(false) } else { - dbconfigs.GlobalDBConfigs.InitWithSocket("") + dbconfigs.GlobalDBConfigs.InitWithSocket("", env.CollationEnv()) mysqld.Mysqld = mysqlctl.NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) } @@ -213,11 +234,13 @@ func run(cmd *cobra.Command, args []string) (err error) { // to be the "internal" protocol that InitTabletMap registers. cmd.Flags().Set("tablet_manager_protocol", "internal") cmd.Flags().Set("tablet_protocol", "internal") - uid, err := vtcombo.InitTabletMap(ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, schemaDir, startMysql) + uid, err := vtcombo.InitTabletMap(env, ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, schemaDir, startMysql) if err != nil { // ensure we start mysql in the event we fail here if startMysql { - mysqld.Shutdown(context.TODO(), cnf, true) + ctx, cancel := context.WithTimeout(context.Background(), mysqlShutdownTimeout+10*time.Second) + defer cancel() + mysqld.Shutdown(ctx, cnf, true, mysqlShutdownTimeout) } return fmt.Errorf("initTabletMapProto failed: %w", err) @@ -236,8 +259,8 @@ func run(cmd *cobra.Command, args []string) (err error) { } } - wr := wrangler.New(logutil.NewConsoleLogger(), ts, nil) - newUID, err := vtcombo.CreateKs(ctx, ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, schemaDir, ks, true, uid, wr) + wr := wrangler.New(env, logutil.NewConsoleLogger(), ts, nil) + newUID, err := vtcombo.CreateKs(ctx, env, ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, schemaDir, ks, true, uid, wr) if err != nil { return err } @@ -264,7 +287,9 @@ func run(cmd *cobra.Command, args []string) (err error) { err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, ks.GetName(), tpb.Cells, false) if err != nil { if startMysql { - mysqld.Shutdown(context.TODO(), cnf, true) + ctx, cancel := context.WithTimeout(context.Background(), mysqlShutdownTimeout+10*time.Second) + defer cancel() + mysqld.Shutdown(ctx, cnf, true, mysqlShutdownTimeout) } return fmt.Errorf("Couldn't build srv keyspace for (%v: %v). Got error: %w", ks, tpb.Cells, err) @@ -273,21 +298,33 @@ func run(cmd *cobra.Command, args []string) (err error) { // vtgate configuration and init resilientServer = srvtopo.NewResilientServer(context.Background(), ts, "ResilientSrvTopoServer") - tabletTypesToWait := []topodatapb.TabletType{ - topodatapb.TabletType_PRIMARY, - topodatapb.TabletType_REPLICA, - topodatapb.TabletType_RDONLY, + + tabletTypes := make([]topodatapb.TabletType, 0, 1) + if len(tabletTypesToWait) != 0 { + for _, tt := range tabletTypesToWait { + if topoproto.IsServingType(tt) { + tabletTypes = append(tabletTypes, tt) + } + } + + if len(tabletTypes) == 0 { + log.Exitf("tablet_types_to_wait should contain at least one serving tablet type") + } + } else { + tabletTypes = append(tabletTypes, topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY) } + plannerVersion, _ := plancontext.PlannerNameToVersion(plannerName) vtgate.QueryLogHandler = "/debug/vtgate/querylog" vtgate.QueryLogzHandler = "/debug/vtgate/querylogz" vtgate.QueryzHandler = "/debug/vtgate/queryz" + // pass nil for healthcheck, it will get created - vtg := vtgate.Init(context.Background(), nil, resilientServer, tpb.Cells[0], tabletTypesToWait, plannerVersion) + vtg := vtgate.Init(context.Background(), env, nil, resilientServer, tpb.Cells[0], tabletTypes, plannerVersion) // vtctld configuration and init - err = vtctld.InitVtctld(ts) + err = vtctld.InitVtctld(env, ts) if err != nil { return err } diff --git a/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go b/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go index 2cf8eed8368..7f6000f5af9 100644 --- a/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go +++ b/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctld") { - grpcvtctldserver.StartServer(servenv.GRPCServer, ts) + grpcvtctldserver.StartServer(servenv.GRPCServer, env, ts) } }) } diff --git a/go/cmd/vtcombo/cli/plugin_grpcvtctlserver.go b/go/cmd/vtcombo/cli/plugin_grpcvtctlserver.go index 8b7f918bc58..236b83e3d28 100644 --- a/go/cmd/vtcombo/cli/plugin_grpcvtctlserver.go +++ b/go/cmd/vtcombo/cli/plugin_grpcvtctlserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctl") { - grpcvtctlserver.StartServer(servenv.GRPCServer, ts) + grpcvtctlserver.StartServer(servenv.GRPCServer, env, ts) } }) } diff --git a/go/cmd/vtcombo/cli/status.go b/go/cmd/vtcombo/cli/status.go index 8069fc72606..80176d4a11a 100644 --- a/go/cmd/vtcombo/cli/status.go +++ b/go/cmd/vtcombo/cli/status.go @@ -41,7 +41,10 @@ func addStatusParts(vtg *vtgate.VTGate) { servenv.AddStatusPart("Gateway Status", vtgate.StatusTemplate, func() any { return vtg.GetGatewayCacheStatus() }) - servenv.AddStatusPart("Health Check Cache", discovery.HealthCheckTemplate, func() any { + servenv.AddStatusPart("Health Check - Cache", discovery.HealthCheckCacheTemplate, func() any { return vtg.Gateway().TabletsCacheStatus() }) + servenv.AddStatusPart("Health Check - Healthy Tablets", discovery.HealthCheckHealthyTemplate, func() any { + return vtg.Gateway().TabletsHealthyStatus() + }) } diff --git a/go/cmd/vtcombo/cli/vschema_watcher.go b/go/cmd/vtcombo/cli/vschema_watcher.go index c1c9f120b96..112a2a0730f 100644 --- a/go/cmd/vtcombo/cli/vschema_watcher.go +++ b/go/cmd/vtcombo/cli/vschema_watcher.go @@ -63,7 +63,7 @@ func loadKeyspacesFromDir(dir string, keyspaces []*vttestpb.Keyspace, ts *topo.S log.Fatalf("Unable to parse keyspace file %v: %v", ksFile, err) } - _, err = vindexes.BuildKeyspace(keyspace) + _, err = vindexes.BuildKeyspace(keyspace, env.Parser()) if err != nil { log.Fatalf("Invalid keyspace definition: %v", err) } diff --git a/go/cmd/vtctl/plugin_cephbackupstorage.go b/go/cmd/vtctl/plugin_cephbackupstorage.go index 6cd2d5619d0..0d4710ec982 100644 --- a/go/cmd/vtctl/plugin_cephbackupstorage.go +++ b/go/cmd/vtctl/plugin_cephbackupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctl/plugin_s3backupstorage.go b/go/cmd/vtctl/plugin_s3backupstorage.go index a5b5c671ebb..f1d1a454041 100644 --- a/go/cmd/vtctl/plugin_s3backupstorage.go +++ b/go/cmd/vtctl/plugin_s3backupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctl/vtctl.go b/go/cmd/vtctl/vtctl.go index 175e49c6831..ba84369620a 100644 --- a/go/cmd/vtctl/vtctl.go +++ b/go/cmd/vtctl/vtctl.go @@ -19,7 +19,6 @@ package main import ( "context" "fmt" - "log/syslog" "os" "os/signal" "strings" @@ -40,6 +39,7 @@ import ( "vitess.io/vitess/go/vt/vtctl" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vtctl/localvtctldclient" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" ) @@ -118,11 +118,7 @@ func main() { startMsg := fmt.Sprintf("USER=%v SUDO_USER=%v %v", os.Getenv("USER"), os.Getenv("SUDO_USER"), strings.Join(os.Args, " ")) - if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil { - syslogger.Info(startMsg) // nolint:errcheck - } else { - log.Warningf("cannot connect to syslog: %v", err) - } + logSyslog(startMsg) closer := trace.StartTracing("vtctl") defer trace.LogErrorsWhenClosing(closer) @@ -131,10 +127,18 @@ func main() { ts := topo.Open() defer ts.Close() - ctx, cancel := context.WithTimeout(context.Background(), waitTime) installSignalHandlers(cancel) + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + log.Fatalf("cannot initialize sql parser: %v", err) + } + // (TODO:ajm188) . // // For v12, we are going to support new commands by prefixing as: @@ -159,7 +163,7 @@ func main() { // New behavior. Strip off the prefix, and set things up to run through // the vtctldclient command tree, using the localvtctldclient (in-process) // client. - vtctld := grpcvtctldserver.NewVtctldServer(ts) + vtctld := grpcvtctldserver.NewVtctldServer(env, ts) localvtctldclient.SetServer(vtctld) command.VtctldClientProtocol = "local" @@ -175,8 +179,7 @@ func main() { fallthrough default: log.Warningf("WARNING: vtctl should only be used for VDiff v1 workflows. Please use VDiff v2 and consider using vtctldclient for all other commands.") - - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(env, logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) if args[0] == "--" { vtctl.PrintDoubleDashDeprecationNotice(wr) @@ -184,7 +187,7 @@ func main() { } action = args[0] - err := vtctl.RunCommand(ctx, wr, args) + err = vtctl.RunCommand(ctx, wr, args) cancel() switch err { case vtctl.ErrUnknownCommand: diff --git a/go/vt/sidecardb/schema/schematracker/schemacopy.sql b/go/cmd/vtctl/vtctl_unix.go similarity index 51% rename from go/vt/sidecardb/schema/schematracker/schemacopy.sql rename to go/cmd/vtctl/vtctl_unix.go index 296bb34df14..bee0be238d7 100644 --- a/go/vt/sidecardb/schema/schematracker/schemacopy.sql +++ b/go/cmd/vtctl/vtctl_unix.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2023 The Vitess Authors. @@ -14,15 +16,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -CREATE TABLE IF NOT EXISTS schemacopy -( - `table_schema` varchar(64) NOT NULL, - `table_name` varchar(64) NOT NULL, - `column_name` varchar(64) NOT NULL, - `ordinal_position` bigint unsigned NOT NULL, - `character_set_name` varchar(32) DEFAULT NULL, - `collation_name` varchar(32) DEFAULT NULL, - `data_type` varchar(64) NOT NULL, - `column_key` varchar(3) NOT NULL, - PRIMARY KEY (`table_schema`, `table_name`, `ordinal_position`) -) ENGINE = InnoDB +package main + +import ( + "log/syslog" + + "vitess.io/vitess/go/vt/log" +) + +func logSyslog(msg string) { + if syslogger, err := syslog.New(syslog.LOG_INFO, "vtctl "); err == nil { + syslogger.Info(msg) // nolint:errcheck + } else { + log.Warningf("cannot connect to syslog: %v", err) + } +} diff --git a/go/cmd/vtctl/vtctl_windows.go b/go/cmd/vtctl/vtctl_windows.go new file mode 100644 index 00000000000..63c5cceb63b --- /dev/null +++ b/go/cmd/vtctl/vtctl_windows.go @@ -0,0 +1,27 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "vitess.io/vitess/go/vt/log" +) + +func logSyslog(msg string) { + log.Warningf("windows does not have syslog support") +} diff --git a/go/cmd/vtctld/cli/cli.go b/go/cmd/vtctld/cli/cli.go index e5124133adb..4f8c57b6b2f 100644 --- a/go/cmd/vtctld/cli/cli.go +++ b/go/cmd/vtctld/cli/cli.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -23,10 +23,12 @@ import ( "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtctld" + "vitess.io/vitess/go/vt/vtenv" ) var ( ts *topo.Server + env *vtenv.Environment Main = &cobra.Command{ Use: "vtctld", Short: "The Vitess cluster management daemon.", @@ -59,8 +61,17 @@ func run(cmd *cobra.Command, args []string) error { ts = topo.Open() defer ts.Close() + var err error + env, err = vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + return err + } // Init the vtctld core - if err := vtctld.InitVtctld(ts); err != nil { + if err := vtctld.InitVtctld(env, ts); err != nil { return err } diff --git a/go/cmd/vtctld/cli/plugin_cephbackupstorage.go b/go/cmd/vtctld/cli/plugin_cephbackupstorage.go index 171198f5e29..7755e1cae2d 100644 --- a/go/cmd/vtctld/cli/plugin_cephbackupstorage.go +++ b/go/cmd/vtctld/cli/plugin_cephbackupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctld/cli/plugin_consultopo.go b/go/cmd/vtctld/cli/plugin_consultopo.go index 4617d753953..b8f8f2e8cdc 100644 --- a/go/cmd/vtctld/cli/plugin_consultopo.go +++ b/go/cmd/vtctld/cli/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go b/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go index ff283d91336..b712d9c0fd0 100644 --- a/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go +++ b/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctld") { - grpcvtctldserver.StartServer(servenv.GRPCServer, ts) + grpcvtctldserver.StartServer(servenv.GRPCServer, env, ts) } }) } diff --git a/go/cmd/vtctld/cli/plugin_grpcvtctlserver.go b/go/cmd/vtctld/cli/plugin_grpcvtctlserver.go index 8b7f918bc58..236b83e3d28 100644 --- a/go/cmd/vtctld/cli/plugin_grpcvtctlserver.go +++ b/go/cmd/vtctld/cli/plugin_grpcvtctlserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctl") { - grpcvtctlserver.StartServer(servenv.GRPCServer, ts) + grpcvtctlserver.StartServer(servenv.GRPCServer, env, ts) } }) } diff --git a/go/cmd/vtctld/cli/plugin_s3backupstorage.go b/go/cmd/vtctld/cli/plugin_s3backupstorage.go index 4b3ecb33edb..e09f6060809 100644 --- a/go/cmd/vtctld/cli/plugin_s3backupstorage.go +++ b/go/cmd/vtctld/cli/plugin_s3backupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctld/cli/plugin_zk2topo.go b/go/cmd/vtctld/cli/plugin_zk2topo.go index 77f86d98d52..f1e5f27ea1b 100644 --- a/go/cmd/vtctld/cli/plugin_zk2topo.go +++ b/go/cmd/vtctld/cli/plugin_zk2topo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctld/cli/schema.go b/go/cmd/vtctld/cli/schema.go index 480679a09e6..9f1f9d06072 100644 --- a/go/cmd/vtctld/cli/schema.go +++ b/go/cmd/vtctld/cli/schema.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -71,11 +71,11 @@ func initSchema() { return } ctx := context.Background() - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(env, logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) _, err = schemamanager.Run( ctx, controller, - schemamanager.NewTabletExecutor("vtctld/schema", wr.TopoServer(), wr.TabletManagerClient(), wr.Logger(), schemaChangeReplicasTimeout, 0), + schemamanager.NewTabletExecutor("vtctld/schema", wr.TopoServer(), wr.TabletManagerClient(), wr.Logger(), schemaChangeReplicasTimeout, 0, env.Parser()), ) if err != nil { log.Errorf("Schema change failed, error: %v", err) diff --git a/go/cmd/vtctld/main.go b/go/cmd/vtctld/main.go index 6f9ab7384fc..46ce01e409f 100644 --- a/go/cmd/vtctld/main.go +++ b/go/cmd/vtctld/main.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtctldclient/cli/pflag.go b/go/cmd/vtctldclient/cli/pflag.go index 04d202cd644..f985e74901e 100644 --- a/go/cmd/vtctldclient/cli/pflag.go +++ b/go/cmd/vtctldclient/cli/pflag.go @@ -19,23 +19,11 @@ package cli import ( "github.com/spf13/pflag" - "vitess.io/vitess/go/flagutil" "vitess.io/vitess/go/vt/topo/topoproto" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) -// StringMapValue augments flagutil.StringMapValue so it can be used as a -// pflag.Value. -type StringMapValue struct { - flagutil.StringMapValue -} - -// Type is part of the pflag.Value interface. -func (v *StringMapValue) Type() string { - return "cli.StringMapValue" -} - // KeyspaceTypeFlag adds the pflag.Value interface to a topodatapb.KeyspaceType. type KeyspaceTypeFlag topodatapb.KeyspaceType diff --git a/go/cmd/vtctldclient/command/backups.go b/go/cmd/vtctldclient/command/backups.go index e6314ed7d6e..ea439ded70e 100644 --- a/go/cmd/vtctldclient/command/backups.go +++ b/go/cmd/vtctldclient/command/backups.go @@ -35,7 +35,7 @@ import ( var ( // Backup makes a Backup gRPC call to a vtctld. Backup = &cobra.Command{ - Use: "Backup [--concurrency ] [--allow-primary] [--incremental-from-pos=|auto] [--upgrade-safe] ", + Use: "Backup [--concurrency ] [--allow-primary] [--incremental-from-pos=||auto] [--upgrade-safe] ", Short: "Uses the BackupStorage service on the given tablet to create and store a new backup.", DisableFlagsInUseLine: true, Args: cobra.ExactArgs(1), @@ -43,7 +43,7 @@ var ( } // BackupShard makes a BackupShard gRPC call to a vtctld. BackupShard = &cobra.Command{ - Use: "BackupShard [--concurrency ] [--allow-primary] [--incremental-from-pos=|auto] [--upgrade-safe] ", + Use: "BackupShard [--concurrency ] [--allow-primary] [--incremental-from-pos=||auto] [--upgrade-safe] ", Short: "Finds the most up-to-date REPLICA, RDONLY, or SPARE tablet in the given shard and uses the BackupStorage service on that tablet to create and store a new backup.", Long: `Finds the most up-to-date REPLICA, RDONLY, or SPARE tablet in the given shard and uses the BackupStorage service on that tablet to create and store a new backup. @@ -80,7 +80,7 @@ If no replica-type tablet can be found, the backup can be taken on the primary i var backupOptions = struct { AllowPrimary bool - Concurrency uint64 + Concurrency int32 IncrementalFromPos string UpgradeSafe bool }{} @@ -119,7 +119,7 @@ func commandBackup(cmd *cobra.Command, args []string) error { var backupShardOptions = struct { AllowPrimary bool - Concurrency uint64 + Concurrency int32 IncrementalFromPos string UpgradeSafe bool }{} @@ -280,15 +280,15 @@ func commandRestoreFromBackup(cmd *cobra.Command, args []string) error { func init() { Backup.Flags().BoolVar(&backupOptions.AllowPrimary, "allow-primary", false, "Allow the primary of a shard to be used for the backup. WARNING: If using the builtin backup engine, this will shutdown mysqld on the primary and stop writes for the duration of the backup.") - Backup.Flags().Uint64Var(&backupOptions.Concurrency, "concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously.") - Backup.Flags().StringVar(&backupOptions.IncrementalFromPos, "incremental-from-pos", "", "Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position") + Backup.Flags().Int32Var(&backupOptions.Concurrency, "concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously.") + Backup.Flags().StringVar(&backupOptions.IncrementalFromPos, "incremental-from-pos", "", "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.") Backup.Flags().BoolVar(&backupOptions.UpgradeSafe, "upgrade-safe", false, "Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades.") Root.AddCommand(Backup) BackupShard.Flags().BoolVar(&backupShardOptions.AllowPrimary, "allow-primary", false, "Allow the primary of a shard to be used for the backup. WARNING: If using the builtin backup engine, this will shutdown mysqld on the primary and stop writes for the duration of the backup.") - BackupShard.Flags().Uint64Var(&backupShardOptions.Concurrency, "concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously.") - BackupShard.Flags().StringVar(&backupShardOptions.IncrementalFromPos, "incremental-from-pos", "", "Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position") + BackupShard.Flags().Int32Var(&backupShardOptions.Concurrency, "concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously.") + BackupShard.Flags().StringVar(&backupShardOptions.IncrementalFromPos, "incremental-from-pos", "", "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.") BackupShard.Flags().BoolVar(&backupOptions.UpgradeSafe, "upgrade-safe", false, "Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades.") Root.AddCommand(BackupShard) diff --git a/go/cmd/vtctldclient/command/keyspaces.go b/go/cmd/vtctldclient/command/keyspaces.go index 420c274ddd5..6330220d773 100644 --- a/go/cmd/vtctldclient/command/keyspaces.go +++ b/go/cmd/vtctldclient/command/keyspaces.go @@ -30,8 +30,6 @@ import ( "vitess.io/vitess/go/cmd/vtctldclient/cli" "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/vt/topo" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" "vitess.io/vitess/go/vt/proto/vttime" @@ -135,8 +133,6 @@ var createKeyspaceOptions = struct { Force bool AllowEmptyVSchema bool - ServedFromsMap cli.StringMapValue - KeyspaceType cli.KeyspaceTypeFlag BaseKeyspace string SnapshotTimestamp string @@ -203,18 +199,6 @@ func commandCreateKeyspace(cmd *cobra.Command, args []string) error { SidecarDbName: createKeyspaceOptions.SidecarDBName, } - for n, v := range createKeyspaceOptions.ServedFromsMap.StringMapValue { - tt, err := topo.ParseServingTabletType(n) - if err != nil { - return err - } - - req.ServedFroms = append(req.ServedFroms, &topodatapb.Keyspace_ServedFrom{ - TabletType: tt, - Keyspace: v, - }) - } - resp, err := client.CreateKeyspace(commandCtx, req) if err != nil { return err @@ -422,7 +406,6 @@ func commandValidateVersionKeyspace(cmd *cobra.Command, args []string) error { func init() { CreateKeyspace.Flags().BoolVarP(&createKeyspaceOptions.Force, "force", "f", false, "Proceeds even if the keyspace already exists. Does not overwrite the existing keyspace record.") CreateKeyspace.Flags().BoolVarP(&createKeyspaceOptions.AllowEmptyVSchema, "allow-empty-vschema", "e", false, "Allows a new keyspace to have no vschema.") - CreateKeyspace.Flags().Var(&createKeyspaceOptions.ServedFromsMap, "served-from", "Specifies a set of db_type:keyspace pairs used to serve traffic for the keyspace.") CreateKeyspace.Flags().Var(&createKeyspaceOptions.KeyspaceType, "type", "The type of the keyspace.") CreateKeyspace.Flags().StringVar(&createKeyspaceOptions.BaseKeyspace, "base-keyspace", "", "The base keyspace for a snapshot keyspace.") CreateKeyspace.Flags().StringVar(&createKeyspaceOptions.SnapshotTimestamp, "snapshot-timestamp", "", "The snapshot time for a snapshot keyspace, as a timestamp in RFC3339 format.") diff --git a/go/cmd/vtctldclient/command/onlineddl.go b/go/cmd/vtctldclient/command/onlineddl.go index dbe927de2bf..6193de9b2af 100644 --- a/go/cmd/vtctldclient/command/onlineddl.go +++ b/go/cmd/vtctldclient/command/onlineddl.go @@ -102,6 +102,14 @@ var ( Args: cobra.ExactArgs(2), RunE: commandOnlineDDLUnthrottle, } + OnlineDDLForceCutOver = &cobra.Command{ + Use: "force-cutover ", + Short: "Mark a given schema migration, or all pending migrations, for forced cut over.", + Example: "OnlineDDL force-cutover test_keyspace 82fa54ac_e83e_11ea_96b7_f875a4d24e90", + DisableFlagsInUseLine: true, + Args: cobra.ExactArgs(2), + RunE: commandOnlineDDLForceCutOver, + } OnlineDDLShow = &cobra.Command{ Use: "show", Short: "Display information about online DDL operations.", @@ -184,6 +192,30 @@ func commandOnlineDDLCleanup(cmd *cobra.Command, args []string) error { return nil } +func commandOnlineDDLForceCutOver(cmd *cobra.Command, args []string) error { + keyspace, uuid, err := analyzeOnlineDDLCommandWithUuidOrAllArgument(cmd) + if err != nil { + return err + } + cli.FinishedParsing(cmd) + + resp, err := client.ForceCutOverSchemaMigration(commandCtx, &vtctldatapb.ForceCutOverSchemaMigrationRequest{ + Keyspace: keyspace, + Uuid: uuid, + }) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + return nil +} + func commandOnlineDDLComplete(cmd *cobra.Command, args []string) error { keyspace, uuid, err := analyzeOnlineDDLCommandWithUuidOrAllArgument(cmd) if err != nil { @@ -393,6 +425,7 @@ func init() { OnlineDDL.AddCommand(OnlineDDLRetry) OnlineDDL.AddCommand(OnlineDDLThrottle) OnlineDDL.AddCommand(OnlineDDLUnthrottle) + OnlineDDL.AddCommand(OnlineDDLForceCutOver) OnlineDDLShow.Flags().BoolVar(&onlineDDLShowArgs.JSON, "json", false, "Output JSON instead of human-readable table.") OnlineDDLShow.Flags().StringVar(&onlineDDLShowArgs.OrderStr, "order", "asc", "Sort the results by `id` property of the Schema migration.") diff --git a/go/cmd/vtctldclient/command/reparents.go b/go/cmd/vtctldclient/command/reparents.go index 5c83016701a..17b87eaba4f 100644 --- a/go/cmd/vtctldclient/command/reparents.go +++ b/go/cmd/vtctldclient/command/reparents.go @@ -183,9 +183,10 @@ func commandInitShardPrimary(cmd *cobra.Command, args []string) error { } var plannedReparentShardOptions = struct { - NewPrimaryAliasStr string - AvoidPrimaryAliasStr string - WaitReplicasTimeout time.Duration + NewPrimaryAliasStr string + AvoidPrimaryAliasStr string + WaitReplicasTimeout time.Duration + TolerableReplicationLag time.Duration }{} func commandPlannedReparentShard(cmd *cobra.Command, args []string) error { @@ -216,11 +217,12 @@ func commandPlannedReparentShard(cmd *cobra.Command, args []string) error { cli.FinishedParsing(cmd) resp, err := client.PlannedReparentShard(commandCtx, &vtctldatapb.PlannedReparentShardRequest{ - Keyspace: keyspace, - Shard: shard, - NewPrimary: newPrimaryAlias, - AvoidPrimary: avoidPrimaryAlias, - WaitReplicasTimeout: protoutil.DurationToProto(plannedReparentShardOptions.WaitReplicasTimeout), + Keyspace: keyspace, + Shard: shard, + NewPrimary: newPrimaryAlias, + AvoidPrimary: avoidPrimaryAlias, + WaitReplicasTimeout: protoutil.DurationToProto(plannedReparentShardOptions.WaitReplicasTimeout), + TolerableReplicationLag: protoutil.DurationToProto(plannedReparentShardOptions.TolerableReplicationLag), }) if err != nil { return err @@ -292,6 +294,7 @@ func init() { Root.AddCommand(InitShardPrimary) PlannedReparentShard.Flags().DurationVar(&plannedReparentShardOptions.WaitReplicasTimeout, "wait-replicas-timeout", topo.RemoteOperationTimeout, "Time to wait for replicas to catch up on replication both before and after reparenting.") + PlannedReparentShard.Flags().DurationVar(&plannedReparentShardOptions.TolerableReplicationLag, "tolerable-replication-lag", 0, "Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary.") PlannedReparentShard.Flags().StringVar(&plannedReparentShardOptions.NewPrimaryAliasStr, "new-primary", "", "Alias of a tablet that should be the new primary.") PlannedReparentShard.Flags().StringVar(&plannedReparentShardOptions.AvoidPrimaryAliasStr, "avoid-primary", "", "Alias of a tablet that should not be the primary; i.e. \"reparent to any other tablet if this one is the primary\".") Root.AddCommand(PlannedReparentShard) diff --git a/go/cmd/vtctldclient/command/root.go b/go/cmd/vtctldclient/command/root.go index 1194b49ec8f..0fc39423bc7 100644 --- a/go/cmd/vtctldclient/command/root.go +++ b/go/cmd/vtctldclient/command/root.go @@ -22,14 +22,22 @@ import ( "fmt" "io" "strconv" + "strings" + "sync" "time" "github.com/spf13/cobra" "vitess.io/vitess/go/trace" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" + "vitess.io/vitess/go/vt/vtctl/localvtctldclient" "vitess.io/vitess/go/vt/vtctl/vtctldclient" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vttablet/tmclient" // These imports ensure init()s within them get called and they register their commands/subcommands. "vitess.io/vitess/go/cmd/vtctldclient/cli" @@ -42,8 +50,16 @@ import ( _ "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/reshard" _ "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/vdiff" _ "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/workflow" + + // These imports register the topo factories to use when --server=internal. + _ "vitess.io/vitess/go/vt/topo/consultopo" + _ "vitess.io/vitess/go/vt/topo/etcd2topo" + _ "vitess.io/vitess/go/vt/topo/zk2topo" ) +// The --server value if you want to use a "local" vtctld server. +const useInternalVtctld = "internal" + var ( // VtctldClientProtocol is the protocol to use when creating the vtctldclient.VtctldClient. VtctldClientProtocol = "grpc" @@ -54,14 +70,39 @@ var ( commandCtx context.Context commandCancel func() + // Register functions to be called when the command completes. + onTerm = []func(){} + + // Register our nil tmclient grpc handler only one time. + // This is primarily for tests where we execute the root + // command multiple times. + once = sync.Once{} + server string actionTimeout time.Duration compactOutput bool + env *vtenv.Environment + + topoOptions = struct { + implementation string + globalServerAddresses []string + globalRoot string + }{ // Set defaults + implementation: "etcd2", + globalServerAddresses: []string{"localhost:2379"}, + globalRoot: "/vitess/global", + } + // Root is the main entrypoint to the vtctldclient CLI. Root = &cobra.Command{ Use: "vtctldclient", Short: "Executes a cluster management command on the remote vtctld server.", + Long: fmt.Sprintf(`Executes a cluster management command on the remote vtctld server. +If there are no running vtctld servers -- for example when bootstrapping +a new Vitess cluster -- you can specify a --server value of '%s'. +When doing so, you would use the --topo* flags so that the client can +connect directly to the topo server(s).`, useInternalVtctld), // We use PersistentPreRun to set up the tracer, grpc client, and // command context for every command. PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { @@ -87,6 +128,10 @@ var ( if client != nil { err = client.Close() } + // Execute any registered onTerm functions. + for _, f := range onTerm { + f() + } trace.LogErrorsWhenClosing(traceCloser) return err }, @@ -152,6 +197,27 @@ func getClientForCommand(cmd *cobra.Command) (vtctldclient.VtctldClient, error) return nil, errNoServer } + if server == useInternalVtctld { + ts, err := topo.OpenServer(topoOptions.implementation, strings.Join(topoOptions.globalServerAddresses, ","), topoOptions.globalRoot) + if err != nil { + return nil, fmt.Errorf("failed to connect to the topology server: %v", err) + } + onTerm = append(onTerm, ts.Close) + + // Use internal vtctld server implementation. + // Register a nil grpc handler -- we will not use tmclient at all but + // a factory still needs to be registered. + once.Do(func() { + tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { + return nil + }) + }) + vtctld := grpcvtctldserver.NewVtctldServer(env, ts) + localvtctldclient.SetServer(vtctld) + VtctldClientProtocol = "local" + server = "" + } + return vtctldclient.New(VtctldClientProtocol, server) } @@ -159,5 +225,18 @@ func init() { Root.PersistentFlags().StringVar(&server, "server", "", "server to use for the connection (required)") Root.PersistentFlags().DurationVar(&actionTimeout, "action_timeout", time.Hour, "timeout to use for the command") Root.PersistentFlags().BoolVar(&compactOutput, "compact", false, "use compact format for otherwise verbose outputs") + Root.PersistentFlags().StringVar(&topoOptions.implementation, "topo-implementation", topoOptions.implementation, "the topology implementation to use") + Root.PersistentFlags().StringSliceVar(&topoOptions.globalServerAddresses, "topo-global-server-address", topoOptions.globalServerAddresses, "the address of the global topology server(s)") + Root.PersistentFlags().StringVar(&topoOptions.globalRoot, "topo-global-root", topoOptions.globalRoot, "the path of the global topology data in the global topology server") vreplcommon.RegisterCommands(Root) + + var err error + env, err = vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + log.Fatalf("failed to initialize vtenv: %v", err) + } } diff --git a/go/cmd/vtctldclient/command/root_test.go b/go/cmd/vtctldclient/command/root_test.go index 155fac78705..5efe844e1a1 100644 --- a/go/cmd/vtctldclient/command/root_test.go +++ b/go/cmd/vtctldclient/command/root_test.go @@ -17,13 +17,19 @@ limitations under the License. package command_test import ( + "context" + "fmt" "os" + "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/cmd/vtctldclient/command" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vtctl/localvtctldclient" vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" @@ -52,3 +58,64 @@ func TestRoot(t *testing.T) { assert.Contains(t, err.Error(), "unknown command") }) } + +// TestRootWithInternalVtctld tests that the internal VtctldServer +// implementation -- used with --server=internal -- works for +// commands as expected. +func TestRootWithInternalVtctld(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + cell := "zone1" + ts, factory := memorytopo.NewServerAndFactory(ctx, cell) + topo.RegisterFactory("test", factory) + command.VtctldClientProtocol = "local" + baseArgs := []string{"vtctldclient", "--server", "internal", "--topo-implementation", "test"} + + args := append([]string{}, os.Args...) + protocol := command.VtctldClientProtocol + t.Cleanup(func() { + ts.Close() + os.Args = append([]string{}, args...) + command.VtctldClientProtocol = protocol + }) + + testCases := []struct { + command string + args []string + expectErr string + }{ + { + command: "AddCellInfo", + args: []string{"--root", fmt.Sprintf("/vitess/%s", cell), "--server-address", "", cell}, + expectErr: "node already exists", // Cell already exists + }, + { + command: "GetTablets", + }, + { + command: "NoCommandDrJones", + expectErr: "unknown command", // Invalid command + }, + } + + for _, tc := range testCases { + t.Run(tc.command, func(t *testing.T) { + defer func() { + // Reset the OS args. + os.Args = append([]string{}, args...) + }() + + os.Args = append(baseArgs, tc.command) + os.Args = append(os.Args, tc.args...) + + err := command.Root.Execute() + if tc.expectErr != "" { + if !strings.Contains(err.Error(), tc.expectErr) { + t.Errorf(fmt.Sprintf("%s error = %v, expectErr = %v", tc.command, err, tc.expectErr)) + } + } else { + require.NoError(t, err, "unexpected error: %v", err) + } + }) + } +} diff --git a/go/cmd/vtctldclient/command/routing_rules.go b/go/cmd/vtctldclient/command/routing_rules.go index 5f16ad7ab07..0ffee0c2c24 100644 --- a/go/cmd/vtctldclient/command/routing_rules.go +++ b/go/cmd/vtctldclient/command/routing_rules.go @@ -148,7 +148,7 @@ func commandGetRoutingRules(cmd *cobra.Command, args []string) error { func init() { ApplyRoutingRules.Flags().StringVarP(&applyRoutingRulesOptions.Rules, "rules", "r", "", "Routing rules, specified as a string.") ApplyRoutingRules.Flags().StringVarP(&applyRoutingRulesOptions.RulesFilePath, "rules-file", "f", "", "Path to a file containing routing rules specified as JSON.") - ApplyRoutingRules.Flags().StringSliceVarP(&applyRoutingRulesOptions.Cells, "cells", "c", nil, "Limit the VSchema graph rebuildingg to the specified cells. Ignored if --skip-rebuild is specified.") + ApplyRoutingRules.Flags().StringSliceVarP(&applyRoutingRulesOptions.Cells, "cells", "c", nil, "Limit the VSchema graph rebuilding to the specified cells. Ignored if --skip-rebuild is specified.") ApplyRoutingRules.Flags().BoolVar(&applyRoutingRulesOptions.SkipRebuild, "skip-rebuild", false, "Skip rebuilding the SrvVSchema objects.") ApplyRoutingRules.Flags().BoolVarP(&applyRoutingRulesOptions.DryRun, "dry-run", "d", false, "Load the specified routing rules as a validation step, but do not actually apply the rules to the topo.") Root.AddCommand(ApplyRoutingRules) diff --git a/go/cmd/vtctldclient/command/schema.go b/go/cmd/vtctldclient/command/schema.go index 795b1315e89..db34bd2588f 100644 --- a/go/cmd/vtctldclient/command/schema.go +++ b/go/cmd/vtctldclient/command/schema.go @@ -29,7 +29,6 @@ import ( "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/schema" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" @@ -123,7 +122,7 @@ func commandApplySchema(cmd *cobra.Command, args []string) error { allSQL = strings.Join(applySchemaOptions.SQL, ";") } - parts, err := sqlparser.SplitStatementToPieces(allSQL) + parts, err := env.Parser().SplitStatementToPieces(allSQL) if err != nil { return err } @@ -230,7 +229,7 @@ func commandReloadSchema(cmd *cobra.Command, args []string) error { } var reloadSchemaKeyspaceOptions = struct { - Concurrency uint32 + Concurrency int32 IncludePrimary bool }{ Concurrency: 10, @@ -255,7 +254,7 @@ func commandReloadSchemaKeyspace(cmd *cobra.Command, args []string) error { } var reloadSchemaShardOptions = struct { - Concurrency uint32 + Concurrency int32 IncludePrimary bool }{ Concurrency: 10, @@ -286,8 +285,6 @@ func commandReloadSchemaShard(cmd *cobra.Command, args []string) error { } func init() { - ApplySchema.Flags().Bool("allow-long-unavailability", false, "Deprecated and has no effect.") - ApplySchema.Flags().MarkDeprecated("--allow-long-unavailability", "") ApplySchema.Flags().StringVar(&applySchemaOptions.DDLStrategy, "ddl-strategy", string(schema.DDLStrategyDirect), "Online DDL strategy, compatible with @@ddl_strategy session variable (examples: 'gh-ost', 'pt-osc', 'gh-ost --max-load=Threads_running=100'.") ApplySchema.Flags().StringSliceVar(&applySchemaOptions.UUIDList, "uuid", nil, "Optional, comma-delimited, repeatable, explicit UUIDs for migration. If given, must match number of DDL changes.") ApplySchema.Flags().StringVar(&applySchemaOptions.MigrationContext, "migration-context", "", "For Online DDL, optionally supply a custom unique string used as context for the migration(s) in this command. By default a unique context is auto-generated by Vitess.") @@ -310,11 +307,11 @@ func init() { Root.AddCommand(ReloadSchema) - ReloadSchemaKeyspace.Flags().Uint32Var(&reloadSchemaKeyspaceOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") + ReloadSchemaKeyspace.Flags().Int32Var(&reloadSchemaKeyspaceOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") ReloadSchemaKeyspace.Flags().BoolVar(&reloadSchemaKeyspaceOptions.IncludePrimary, "include-primary", false, "Also reload the primary tablets.") Root.AddCommand(ReloadSchemaKeyspace) - ReloadSchemaShard.Flags().Uint32Var(&reloadSchemaShardOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") + ReloadSchemaShard.Flags().Int32Var(&reloadSchemaShardOptions.Concurrency, "concurrency", 10, "Number of tablets to reload in parallel. Set to zero for unbounded concurrency.") ReloadSchemaShard.Flags().BoolVar(&reloadSchemaShardOptions.IncludePrimary, "include-primary", false, "Also reload the primary tablet.") Root.AddCommand(ReloadSchemaShard) } diff --git a/go/cmd/vtctldclient/command/shards.go b/go/cmd/vtctldclient/command/shards.go index 231a44b3949..a33ea187f62 100644 --- a/go/cmd/vtctldclient/command/shards.go +++ b/go/cmd/vtctldclient/command/shards.go @@ -558,7 +558,7 @@ func commandSourceShardDelete(cmd *cobra.Command, args []string) error { return err } - uid, err := strconv.ParseUint(cmd.Flags().Arg(1), 10, 32) + uid, err := strconv.ParseInt(cmd.Flags().Arg(1), 10, 32) if err != nil { return fmt.Errorf("Failed to parse SourceShard uid: %w", err) // nolint } diff --git a/go/cmd/vtctldclient/command/vreplication/common/cancel.go b/go/cmd/vtctldclient/command/vreplication/common/cancel.go index 48abcc89584..838a95faad9 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/cancel.go +++ b/go/cmd/vtctldclient/command/vreplication/common/cancel.go @@ -30,6 +30,7 @@ import ( var CancelOptions = struct { KeepData bool KeepRoutingRules bool + Shards []string }{} func GetCancelCommand(opts *SubCommandsOpts) *cobra.Command { @@ -58,6 +59,7 @@ func commandCancel(cmd *cobra.Command, args []string) error { Workflow: BaseOptions.Workflow, KeepData: CancelOptions.KeepData, KeepRoutingRules: CancelOptions.KeepRoutingRules, + Shards: CancelOptions.Shards, } resp, err := GetClient().WorkflowDelete(GetCommandCtx(), req) if err != nil { diff --git a/go/cmd/vtctldclient/command/vreplication/common/complete.go b/go/cmd/vtctldclient/command/vreplication/common/complete.go index 6e210b188fe..19f82548af7 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/complete.go +++ b/go/cmd/vtctldclient/command/vreplication/common/complete.go @@ -16,6 +16,7 @@ var CompleteOptions = struct { KeepRoutingRules bool RenameTables bool DryRun bool + Shards []string }{} func GetCompleteCommand(opts *SubCommandsOpts) *cobra.Command { diff --git a/go/cmd/vtctldclient/command/vreplication/common/show.go b/go/cmd/vtctldclient/command/vreplication/common/show.go index 71e6675f690..8022296153b 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/show.go +++ b/go/cmd/vtctldclient/command/vreplication/common/show.go @@ -26,8 +26,9 @@ import ( vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) -var showOptions = struct { +var ShowOptions = struct { IncludeLogs bool + Shards []string }{} func GetShowCommand(opts *SubCommandsOpts) *cobra.Command { @@ -40,7 +41,7 @@ func GetShowCommand(opts *SubCommandsOpts) *cobra.Command { Args: cobra.NoArgs, RunE: commandShow, } - cmd.Flags().BoolVar(&showOptions.IncludeLogs, "include-logs", true, "Include recent logs for the workflow.") + cmd.Flags().BoolVar(&ShowOptions.IncludeLogs, "include-logs", true, "Include recent logs for the workflow.") return cmd } @@ -50,7 +51,8 @@ func commandShow(cmd *cobra.Command, args []string) error { req := &vtctldatapb.GetWorkflowsRequest{ Keyspace: BaseOptions.TargetKeyspace, Workflow: BaseOptions.Workflow, - IncludeLogs: showOptions.IncludeLogs, + IncludeLogs: ShowOptions.IncludeLogs, + Shards: ShowOptions.Shards, } resp, err := GetClient().GetWorkflows(GetCommandCtx(), req) if err != nil { diff --git a/go/cmd/vtctldclient/command/vreplication/common/status.go b/go/cmd/vtctldclient/command/vreplication/common/status.go index ad038c42536..54a2b45ce2c 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/status.go +++ b/go/cmd/vtctldclient/command/vreplication/common/status.go @@ -26,6 +26,10 @@ import ( vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) +var StatusOptions = struct { + Shards []string +}{} + func GetStatusCommand(opts *SubCommandsOpts) *cobra.Command { cmd := &cobra.Command{ Use: "status", @@ -49,6 +53,7 @@ func commandStatus(cmd *cobra.Command, args []string) error { req := &vtctldatapb.WorkflowStatusRequest{ Keyspace: BaseOptions.TargetKeyspace, Workflow: BaseOptions.Workflow, + Shards: StatusOptions.Shards, } resp, err := GetClient().WorkflowStatus(GetCommandCtx(), req) if err != nil { diff --git a/go/cmd/vtctldclient/command/vreplication/common/utils.go b/go/cmd/vtctldclient/command/vreplication/common/utils.go index da6e3329579..a742f31a9ff 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/utils.go +++ b/go/cmd/vtctldclient/command/vreplication/common/utils.go @@ -64,6 +64,9 @@ var ( DeferSecondaryKeys bool AutoStart bool StopAfterCopy bool + MySQLServerVersion string + TruncateUILen int + TruncateErrLen int }{} ) @@ -230,6 +233,7 @@ var SwitchTrafficOptions = struct { DryRun bool Direction workflow.TrafficSwitchDirection InitializeTargetSequences bool + Shards []string }{} func AddCommonSwitchTrafficFlags(cmd *cobra.Command, initializeTargetSequences bool) { @@ -243,3 +247,7 @@ func AddCommonSwitchTrafficFlags(cmd *cobra.Command, initializeTargetSequences b cmd.Flags().BoolVar(&SwitchTrafficOptions.InitializeTargetSequences, "initialize-target-sequences", false, "When moving tables from an unsharded keyspace to a sharded keyspace, initialize any sequences that are being used on the target when switching writes.") } } + +func AddShardSubsetFlag(cmd *cobra.Command, shardsOption *[]string) { + cmd.Flags().StringSliceVar(shardsOption, "shards", nil, "(Optional) Specifies a comma-separated list of shards to operate on.") +} diff --git a/go/cmd/vtctldclient/command/vreplication/common/utils_test.go b/go/cmd/vtctldclient/command/vreplication/common/utils_test.go index 0dc179060d6..a8a0df2e9b2 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/utils_test.go +++ b/go/cmd/vtctldclient/command/vreplication/common/utils_test.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vtctl/localvtctldclient" "vitess.io/vitess/go/vt/vtctl/vtctldclient" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" ) @@ -144,7 +145,7 @@ func SetupLocalVtctldClient(t *testing.T, ctx context.Context, cells ...string) tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { return nil }) - vtctld := grpcvtctldserver.NewVtctldServer(ts) + vtctld := grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) localvtctldclient.SetServer(vtctld) command.VtctldClientProtocol = "local" client, err := vtctldclient.New(command.VtctldClientProtocol, "") diff --git a/go/cmd/vtctldclient/command/vreplication/materialize/create.go b/go/cmd/vtctldclient/command/vreplication/materialize/create.go index d835b0f3426..88aed1c664c 100644 --- a/go/cmd/vtctldclient/command/vreplication/materialize/create.go +++ b/go/cmd/vtctldclient/command/vreplication/materialize/create.go @@ -102,6 +102,15 @@ func commandCreate(cmd *cobra.Command, args []string) error { TabletSelectionPreference: tsp, } + createOptions.TableSettings.parser, err = sqlparser.New(sqlparser.Options{ + MySQLServerVersion: common.CreateOptions.MySQLServerVersion, + TruncateUILen: common.CreateOptions.TruncateUILen, + TruncateErrLen: common.CreateOptions.TruncateErrLen, + }) + if err != nil { + return err + } + req := &vtctldatapb.MaterializeCreateRequest{ Settings: ms, } @@ -132,7 +141,8 @@ func commandCreate(cmd *cobra.Command, args []string) error { // tableSettings is a wrapper around a slice of TableMaterializeSettings // proto messages that implements the pflag.Value interface. type tableSettings struct { - val []*vtctldatapb.TableMaterializeSettings + val []*vtctldatapb.TableMaterializeSettings + parser *sqlparser.Parser } func (ts *tableSettings) String() string { @@ -157,7 +167,7 @@ func (ts *tableSettings) Set(v string) error { return fmt.Errorf("missing target_table or source_expression") } // Validate that the query is valid. - stmt, err := sqlparser.Parse(tms.SourceExpression) + stmt, err := ts.parser.Parse(tms.SourceExpression) if err != nil { return fmt.Errorf("invalid source_expression: %q", tms.SourceExpression) } @@ -167,7 +177,7 @@ func (ts *tableSettings) Set(v string) error { err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { switch node := node.(type) { case sqlparser.TableName: - if !node.Name.IsEmpty() { + if node.Name.NotEmpty() { if seenSourceTables[node.Name.String()] { return false, fmt.Errorf("multiple source_expression queries use the same table: %q", node.Name.String()) } diff --git a/go/cmd/vtctldclient/command/vreplication/materialize/materialize.go b/go/cmd/vtctldclient/command/vreplication/materialize/materialize.go index 58be1ec4433..5845504af3f 100644 --- a/go/cmd/vtctldclient/command/vreplication/materialize/materialize.go +++ b/go/cmd/vtctldclient/command/vreplication/materialize/materialize.go @@ -17,9 +17,12 @@ limitations under the License. package materialize import ( + "fmt" + "github.com/spf13/cobra" "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/common" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/vt/topo/topoproto" ) @@ -46,6 +49,9 @@ func registerCommands(root *cobra.Command) { create.Flags().Var(&createOptions.TableSettings, "table-settings", "A JSON array defining what tables to materialize using what select statements. See the --help output for more details.") create.MarkFlagRequired("table-settings") create.Flags().BoolVar(&common.CreateOptions.StopAfterCopy, "stop-after-copy", false, "Stop the workflow after it's finished copying the existing rows and before it starts replicating changes.") + create.Flags().StringVar(&common.CreateOptions.MySQLServerVersion, "mysql_server_version", fmt.Sprintf("%s-Vitess", config.DefaultMySQLVersion), "Configure the MySQL version to use for example for the parser.") + create.Flags().IntVar(&common.CreateOptions.TruncateUILen, "sql-max-length-ui", 512, "truncate queries in debug UIs to the given length (default 512)") + create.Flags().IntVar(&common.CreateOptions.TruncateErrLen, "sql-max-length-errors", 0, "truncate queries in error logs to the given length (default unlimited)") base.AddCommand(create) // Generic workflow commands. diff --git a/go/cmd/vtctldclient/command/vreplication/mount/mount.go b/go/cmd/vtctldclient/command/vreplication/mount/mount.go index 95ce3961e71..33bc69e5626 100644 --- a/go/cmd/vtctldclient/command/vreplication/mount/mount.go +++ b/go/cmd/vtctldclient/command/vreplication/mount/mount.go @@ -143,9 +143,7 @@ func commandList(cmd *cobra.Command, args []string) error { if err != nil { return err } - if err != nil { - return err - } + data, err := json.Marshal(resp) if err != nil { return err diff --git a/go/cmd/vtctldclient/command/vreplication/movetables/movetables.go b/go/cmd/vtctldclient/command/vreplication/movetables/movetables.go index e2c7daed223..0757fc542ac 100644 --- a/go/cmd/vtctldclient/command/vreplication/movetables/movetables.go +++ b/go/cmd/vtctldclient/command/vreplication/movetables/movetables.go @@ -53,18 +53,25 @@ func registerCommands(root *cobra.Command) { SubCommand: "MoveTables", Workflow: "commerce2customer", } - base.AddCommand(common.GetShowCommand(opts)) - base.AddCommand(common.GetStatusCommand(opts)) + showCommand := common.GetShowCommand(opts) + common.AddShardSubsetFlag(showCommand, &common.ShowOptions.Shards) + base.AddCommand(showCommand) + + statusCommand := common.GetStatusCommand(opts) + common.AddShardSubsetFlag(statusCommand, &common.StatusOptions.Shards) + base.AddCommand(statusCommand) base.AddCommand(common.GetStartCommand(opts)) base.AddCommand(common.GetStopCommand(opts)) switchTrafficCommand := common.GetSwitchTrafficCommand(opts) common.AddCommonSwitchTrafficFlags(switchTrafficCommand, true) + common.AddShardSubsetFlag(switchTrafficCommand, &common.SwitchTrafficOptions.Shards) base.AddCommand(switchTrafficCommand) reverseTrafficCommand := common.GetReverseTrafficCommand(opts) common.AddCommonSwitchTrafficFlags(reverseTrafficCommand, false) + common.AddShardSubsetFlag(reverseTrafficCommand, &common.SwitchTrafficOptions.Shards) base.AddCommand(reverseTrafficCommand) complete := common.GetCompleteCommand(opts) @@ -72,11 +79,13 @@ func registerCommands(root *cobra.Command) { complete.Flags().BoolVar(&common.CompleteOptions.KeepRoutingRules, "keep-routing-rules", false, "Keep the routing rules in place that direct table traffic from the source keyspace to the target keyspace of the MoveTables workflow.") complete.Flags().BoolVar(&common.CompleteOptions.RenameTables, "rename-tables", false, "Keep the original source table data that was copied by the MoveTables workflow, but rename each table to '__old'.") complete.Flags().BoolVar(&common.CompleteOptions.DryRun, "dry-run", false, "Print the actions that would be taken and report any known errors that would have occurred.") + common.AddShardSubsetFlag(complete, &common.CompleteOptions.Shards) base.AddCommand(complete) cancel := common.GetCancelCommand(opts) cancel.Flags().BoolVar(&common.CancelOptions.KeepData, "keep-data", false, "Keep the partially copied table data from the MoveTables workflow in the target keyspace.") cancel.Flags().BoolVar(&common.CancelOptions.KeepRoutingRules, "keep-routing-rules", false, "Keep the routing rules created for the MoveTables workflow.") + common.AddShardSubsetFlag(cancel, &common.CancelOptions.Shards) base.AddCommand(cancel) } diff --git a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff.go b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff.go index a98cf3ad743..bce6e18ddbb 100644 --- a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff.go +++ b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff.go @@ -57,15 +57,17 @@ var ( TargetCells []string TabletTypes []topodatapb.TabletType Tables []string - Limit uint32 // We only accept positive values but pass on an int64 + Limit int64 FilteredReplicationWaitTime time.Duration DebugQuery bool + MaxReportSampleRows int64 OnlyPKs bool UpdateTableStats bool - MaxExtraRowsToCompare uint32 // We only accept positive values but pass on an int64 + MaxExtraRowsToCompare int64 Wait bool WaitUpdateInterval time.Duration AutoRetry bool + MaxDiffDuration time.Duration }{} deleteOptions = struct { @@ -112,6 +114,16 @@ var ( createOptions.Tables[i] = strings.TrimSpace(table) } } + // Enforce non-negative values for limits and max options. + if createOptions.Limit < 1 { + return fmt.Errorf("--limit must be a positive value") + } + if createOptions.MaxReportSampleRows < 0 { + return fmt.Errorf("--max-report-sample-rows must not be a negative value") + } + if createOptions.MaxExtraRowsToCompare < 0 { + return fmt.Errorf("--max-extra-rows-to-compare must not be a negative value") + } return nil } @@ -270,15 +282,17 @@ func commandCreate(cmd *cobra.Command, args []string) error { TabletTypes: createOptions.TabletTypes, TabletSelectionPreference: tsp, Tables: createOptions.Tables, - Limit: int64(createOptions.Limit), + Limit: createOptions.Limit, FilteredReplicationWaitTime: protoutil.DurationToProto(createOptions.FilteredReplicationWaitTime), DebugQuery: createOptions.DebugQuery, OnlyPKs: createOptions.OnlyPKs, UpdateTableStats: createOptions.UpdateTableStats, - MaxExtraRowsToCompare: int64(createOptions.MaxExtraRowsToCompare), + MaxExtraRowsToCompare: createOptions.MaxExtraRowsToCompare, Wait: createOptions.Wait, WaitUpdateInterval: protoutil.DurationToProto(createOptions.WaitUpdateInterval), AutoRetry: createOptions.AutoRetry, + MaxReportSampleRows: createOptions.MaxReportSampleRows, + MaxDiffDuration: protoutil.DurationToProto(createOptions.MaxDiffDuration), }) if err != nil { @@ -861,15 +875,17 @@ func registerCommands(root *cobra.Command) { create.Flags().Var((*topoprotopb.TabletTypeListFlag)(&createOptions.TabletTypes), "tablet-types", "Tablet types to use on the source and target.") create.Flags().BoolVar(&common.CreateOptions.TabletTypesInPreferenceOrder, "tablet-types-in-preference-order", true, "When performing source tablet selection, look for candidates in the type order as they are listed in the tablet-types flag.") create.Flags().DurationVar(&createOptions.FilteredReplicationWaitTime, "filtered-replication-wait-time", 30*time.Second, "Specifies the maximum time to wait, in seconds, for replication to catch up when syncing tablet streams.") - create.Flags().Uint32Var(&createOptions.Limit, "limit", math.MaxUint32, "Max rows to stop comparing after.") + create.Flags().Int64Var(&createOptions.Limit, "limit", math.MaxInt64, "Max rows to stop comparing after.") create.Flags().BoolVar(&createOptions.DebugQuery, "debug-query", false, "Adds a mysql query to the report that can be used for further debugging.") + create.Flags().Int64Var(&createOptions.MaxReportSampleRows, "max-report-sample-rows", 10, "Maximum number of row differences to report (0 for all differences). NOTE: when increasing this value it is highly recommended to also specify --only-pks") create.Flags().BoolVar(&createOptions.OnlyPKs, "only-pks", false, "When reporting missing rows, only show primary keys in the report.") create.Flags().StringSliceVar(&createOptions.Tables, "tables", nil, "Only run vdiff for these tables in the workflow.") - create.Flags().Uint32Var(&createOptions.MaxExtraRowsToCompare, "max-extra-rows-to-compare", 1000, "If there are collation differences between the source and target, you can have rows that are identical but simply returned in a different order from MySQL. We will do a second pass to compare the rows for any actual differences in this case and this flag allows you to control the resources used for this operation.") + create.Flags().Int64Var(&createOptions.MaxExtraRowsToCompare, "max-extra-rows-to-compare", 1000, "If there are collation differences between the source and target, you can have rows that are identical but simply returned in a different order from MySQL. We will do a second pass to compare the rows for any actual differences in this case and this flag allows you to control the resources used for this operation.") create.Flags().BoolVar(&createOptions.Wait, "wait", false, "When creating or resuming a vdiff, wait for it to finish before exiting.") create.Flags().DurationVar(&createOptions.WaitUpdateInterval, "wait-update-interval", time.Duration(1*time.Minute), "When waiting on a vdiff to finish, check and display the current status this often.") create.Flags().BoolVar(&createOptions.AutoRetry, "auto-retry", true, "Should this vdiff automatically retry and continue in case of recoverable errors.") create.Flags().BoolVar(&createOptions.UpdateTableStats, "update-table-stats", false, "Update the table statistics, using ANALYZE TABLE, on each table involved in the VDiff during initialization. This will ensure that progress estimates are as accurate as possible -- but it does involve locks and can potentially impact query processing on the target keyspace.") + create.Flags().DurationVar(&createOptions.MaxDiffDuration, "max-diff-duration", 0, "How long should an individual table diff run before being stopped and restarted in order to lessen the impact on tablets due to holding open database snapshots for long periods of time (0 is the default and means no time limit).") base.AddCommand(create) base.AddCommand(delete) diff --git a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go index 1a2a374cf81..10d7762acde 100644 --- a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go +++ b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go @@ -30,6 +30,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vtctl/workflow" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -83,7 +84,7 @@ func newTestVDiffEnv(t testing.TB, ctx context.Context, sourceShards, targetShar tabletType: topodatapb.TabletType_REPLICA, tmc: newTestVDiffTMClient(), } - env.ws = workflow.NewServer(env.topoServ, env.tmc) + env.ws = workflow.NewServer(vtenv.NewTestEnv(), env.topoServ, env.tmc) env.tmc.testEnv = env // Generate a unique dialer name. diff --git a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_test.go b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_test.go index fd535bb2aad..8742d22abd0 100644 --- a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_test.go +++ b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_test.go @@ -269,6 +269,277 @@ func TestVDiffUnsharded(t *testing.T) { } } ]`), + }, { + id: "9", // --max-vdiff-report-rows=20 --only-pks + result: sqltypes.MakeTestResult(fields, + "completed||t1|"+UUID+"|completed|30|"+starttime+"|30|"+comptime+"|1|"+ + `{"TableName": "t1", "MatchingRows": 10, "ProcessedRows": 30, "MismatchedRows": 20, "ExtraRowsSource": 0, `+ + `"ExtraRowsTarget": 0, "MismatchedRowsSample": [`+ + `{"Source": {"Row": {"c1": "2"}}, "Target": {"Row": {"c1": "2"}}},`+ + `{"Source": {"Row": {"c1": "3"}}, "Target": {"Row": {"c1": "3"}}},`+ + `{"Source": {"Row": {"c1": "4"}}, "Target": {"Row": {"c1": "4"}}},`+ + `{"Source": {"Row": {"c1": "5"}}, "Target": {"Row": {"c1": "5"}}},`+ + `{"Source": {"Row": {"c1": "6"}}, "Target": {"Row": {"c1": "6"}}},`+ + `{"Source": {"Row": {"c1": "7"}}, "Target": {"Row": {"c1": "7"}}},`+ + `{"Source": {"Row": {"c1": "8"}}, "Target": {"Row": {"c1": "8"}}},`+ + `{"Source": {"Row": {"c1": "9"}}, "Target": {"Row": {"c1": "9"}}},`+ + `{"Source": {"Row": {"c1": "10"}}, "Target": {"Row": {"c1": "10"}}},`+ + `{"Source": {"Row": {"c1": "11"}}, "Target": {"Row": {"c1": "11"}}},`+ + `{"Source": {"Row": {"c1": "12"}}, "Target": {"Row": {"c1": "12"}}},`+ + `{"Source": {"Row": {"c1": "13"}}, "Target": {"Row": {"c1": "13"}}},`+ + `{"Source": {"Row": {"c1": "14"}}, "Target": {"Row": {"c1": "14"}}},`+ + `{"Source": {"Row": {"c1": "15"}}, "Target": {"Row": {"c1": "15"}}},`+ + `{"Source": {"Row": {"c1": "16"}}, "Target": {"Row": {"c1": "16"}}},`+ + `{"Source": {"Row": {"c1": "17"}}, "Target": {"Row": {"c1": "17"}}},`+ + `{"Source": {"Row": {"c1": "18"}}, "Target": {"Row": {"c1": "18"}}},`+ + `{"Source": {"Row": {"c1": "19"}}, "Target": {"Row": {"c1": "19"}}},`+ + `{"Source": {"Row": {"c1": "20"}}, "Target": {"Row": {"c1": "20"}}},`+ + `{"Source": {"Row": {"c1": "21"}}, "Target": {"Row": {"c1": "21"}}}`+ + `]}`), + report: fmt.Sprintf(badReportfmt, + env.targetKeyspace, UUID, 30, true, starttime, comptime, 30, 10, 20, 0, 0, 30, 10, 20, 0, 0, + `"MismatchedRowsSample": [ + { + "Source": { + "Row": { + "c1": "2" + } + }, + "Target": { + "Row": { + "c1": "2" + } + } + }, + { + "Source": { + "Row": { + "c1": "3" + } + }, + "Target": { + "Row": { + "c1": "3" + } + } + }, + { + "Source": { + "Row": { + "c1": "4" + } + }, + "Target": { + "Row": { + "c1": "4" + } + } + }, + { + "Source": { + "Row": { + "c1": "5" + } + }, + "Target": { + "Row": { + "c1": "5" + } + } + }, + { + "Source": { + "Row": { + "c1": "6" + } + }, + "Target": { + "Row": { + "c1": "6" + } + } + }, + { + "Source": { + "Row": { + "c1": "7" + } + }, + "Target": { + "Row": { + "c1": "7" + } + } + }, + { + "Source": { + "Row": { + "c1": "8" + } + }, + "Target": { + "Row": { + "c1": "8" + } + } + }, + { + "Source": { + "Row": { + "c1": "9" + } + }, + "Target": { + "Row": { + "c1": "9" + } + } + }, + { + "Source": { + "Row": { + "c1": "10" + } + }, + "Target": { + "Row": { + "c1": "10" + } + } + }, + { + "Source": { + "Row": { + "c1": "11" + } + }, + "Target": { + "Row": { + "c1": "11" + } + } + }, + { + "Source": { + "Row": { + "c1": "12" + } + }, + "Target": { + "Row": { + "c1": "12" + } + } + }, + { + "Source": { + "Row": { + "c1": "13" + } + }, + "Target": { + "Row": { + "c1": "13" + } + } + }, + { + "Source": { + "Row": { + "c1": "14" + } + }, + "Target": { + "Row": { + "c1": "14" + } + } + }, + { + "Source": { + "Row": { + "c1": "15" + } + }, + "Target": { + "Row": { + "c1": "15" + } + } + }, + { + "Source": { + "Row": { + "c1": "16" + } + }, + "Target": { + "Row": { + "c1": "16" + } + } + }, + { + "Source": { + "Row": { + "c1": "17" + } + }, + "Target": { + "Row": { + "c1": "17" + } + } + }, + { + "Source": { + "Row": { + "c1": "18" + } + }, + "Target": { + "Row": { + "c1": "18" + } + } + }, + { + "Source": { + "Row": { + "c1": "19" + } + }, + "Target": { + "Row": { + "c1": "19" + } + } + }, + { + "Source": { + "Row": { + "c1": "20" + } + }, + "Target": { + "Row": { + "c1": "20" + } + } + }, + { + "Source": { + "Row": { + "c1": "21" + } + }, + "Target": { + "Row": { + "c1": "21" + } + } + } + ]`), }, } diff --git a/go/cmd/vtctldclient/command/vreplication/workflow/delete.go b/go/cmd/vtctldclient/command/vreplication/workflow/delete.go index 4eae8076fec..3739979ff5f 100644 --- a/go/cmd/vtctldclient/command/vreplication/workflow/delete.go +++ b/go/cmd/vtctldclient/command/vreplication/workflow/delete.go @@ -54,6 +54,7 @@ func commandDelete(cmd *cobra.Command, args []string) error { Workflow: baseOptions.Workflow, KeepData: deleteOptions.KeepData, KeepRoutingRules: deleteOptions.KeepRoutingRules, + Shards: baseOptions.Shards, } resp, err := common.GetClient().WorkflowDelete(common.GetCommandCtx(), req) if err != nil { diff --git a/go/cmd/vtctldclient/command/vreplication/workflow/show.go b/go/cmd/vtctldclient/command/vreplication/workflow/show.go index ebc18ea250d..cbb1d01ba10 100644 --- a/go/cmd/vtctldclient/command/vreplication/workflow/show.go +++ b/go/cmd/vtctldclient/command/vreplication/workflow/show.go @@ -59,6 +59,7 @@ func commandShow(cmd *cobra.Command, args []string) error { Keyspace: baseOptions.Keyspace, Workflow: baseOptions.Workflow, IncludeLogs: workflowShowOptions.IncludeLogs, + Shards: baseOptions.Shards, } resp, err := common.GetClient().GetWorkflows(common.GetCommandCtx(), req) if err != nil { diff --git a/go/cmd/vtctldclient/command/vreplication/workflow/state.go b/go/cmd/vtctldclient/command/vreplication/workflow/state.go index 89e75312ab2..a5555d78b0e 100644 --- a/go/cmd/vtctldclient/command/vreplication/workflow/state.go +++ b/go/cmd/vtctldclient/command/vreplication/workflow/state.go @@ -82,6 +82,7 @@ func commandUpdateState(cmd *cobra.Command, args []string) error { TabletTypes: []topodatapb.TabletType{topodatapb.TabletType(textutil.SimulatedNullInt)}, OnDdl: binlogdatapb.OnDDLAction(textutil.SimulatedNullInt), State: state, + Shards: baseOptions.Shards, }, } diff --git a/go/cmd/vtctldclient/command/vreplication/workflow/update.go b/go/cmd/vtctldclient/command/vreplication/workflow/update.go index 466d81e8be4..52df87acc8b 100644 --- a/go/cmd/vtctldclient/command/vreplication/workflow/update.go +++ b/go/cmd/vtctldclient/command/vreplication/workflow/update.go @@ -111,6 +111,7 @@ func commandUpdate(cmd *cobra.Command, args []string) error { TabletTypes: updateOptions.TabletTypes, TabletSelectionPreference: tsp, OnDdl: binlogdatapb.OnDDLAction(onddl), + Shards: baseOptions.Shards, }, } diff --git a/go/cmd/vtctldclient/command/vreplication/workflow/workflow.go b/go/cmd/vtctldclient/command/vreplication/workflow/workflow.go index e552b61d476..a4fbb37d4bd 100644 --- a/go/cmd/vtctldclient/command/vreplication/workflow/workflow.go +++ b/go/cmd/vtctldclient/command/vreplication/workflow/workflow.go @@ -39,6 +39,7 @@ var ( baseOptions = struct { Keyspace string Workflow string + Shards []string }{} workflowShowOptions = struct { @@ -59,21 +60,26 @@ func registerCommands(root *cobra.Command) { delete.MarkFlagRequired("workflow") delete.Flags().BoolVar(&deleteOptions.KeepData, "keep-data", false, "Keep the partially copied table data from the workflow in the target keyspace.") delete.Flags().BoolVar(&deleteOptions.KeepRoutingRules, "keep-routing-rules", false, "Keep the routing rules created for the workflow.") + common.AddShardSubsetFlag(delete, &baseOptions.Shards) base.AddCommand(delete) + common.AddShardSubsetFlag(workflowList, &baseOptions.Shards) base.AddCommand(workflowList) show.Flags().StringVarP(&baseOptions.Workflow, "workflow", "w", "", "The workflow you want the details for.") show.MarkFlagRequired("workflow") show.Flags().BoolVar(&workflowShowOptions.IncludeLogs, "include-logs", true, "Include recent logs for the workflow.") + common.AddShardSubsetFlag(show, &baseOptions.Shards) base.AddCommand(show) start.Flags().StringVarP(&baseOptions.Workflow, "workflow", "w", "", "The workflow you want to start.") start.MarkFlagRequired("workflow") + common.AddShardSubsetFlag(start, &baseOptions.Shards) base.AddCommand(start) stop.Flags().StringVarP(&baseOptions.Workflow, "workflow", "w", "", "The workflow you want to stop.") stop.MarkFlagRequired("workflow") + common.AddShardSubsetFlag(stop, &baseOptions.Shards) base.AddCommand(stop) update.Flags().StringVarP(&baseOptions.Workflow, "workflow", "w", "", "The workflow you want to update.") @@ -82,6 +88,7 @@ func registerCommands(root *cobra.Command) { update.Flags().VarP((*topoproto.TabletTypeListFlag)(&updateOptions.TabletTypes), "tablet-types", "t", "New source tablet types to replicate from (e.g. PRIMARY,REPLICA,RDONLY).") update.Flags().BoolVar(&updateOptions.TabletTypesInPreferenceOrder, "tablet-types-in-order", true, "When performing source tablet selection, look for candidates in the type order as they are listed in the tablet-types flag.") update.Flags().StringVar(&updateOptions.OnDDL, "on-ddl", "", "New instruction on what to do when DDL is encountered in the VReplication stream. Possible values are IGNORE, STOP, EXEC, and EXEC_IGNORE.") + common.AddShardSubsetFlag(update, &baseOptions.Shards) base.AddCommand(update) } diff --git a/go/cmd/vtctldclient/command/vschemas.go b/go/cmd/vtctldclient/command/vschemas.go index c7faf6765f4..37ad00ccb6b 100644 --- a/go/cmd/vtctldclient/command/vschemas.go +++ b/go/cmd/vtctldclient/command/vschemas.go @@ -40,7 +40,7 @@ var ( } // ApplyVSchema makes an ApplyVSchema gRPC call to a vtctld. ApplyVSchema = &cobra.Command{ - Use: "ApplyVSchema {--vschema= || --vschema-file= || --sql= || --sql-file=} [--cells=c1,c2,...] [--skip-rebuild] [--dry-run] ", + Use: "ApplyVSchema {--vschema= || --vschema-file= || --sql= || --sql-file=} [--cells=c1,c2,...] [--skip-rebuild] [--dry-run] [--strict] ", Short: "Applies the VTGate routing schema to the provided keyspace. Shows the result after application.", DisableFlagsInUseLine: true, Args: cobra.ExactArgs(1), @@ -56,6 +56,7 @@ var applyVSchemaOptions = struct { DryRun bool SkipRebuild bool Cells []string + Strict bool }{} func commandApplyVSchema(cmd *cobra.Command, args []string) error { @@ -75,6 +76,7 @@ func commandApplyVSchema(cmd *cobra.Command, args []string) error { SkipRebuild: applyVSchemaOptions.SkipRebuild, Cells: applyVSchemaOptions.Cells, DryRun: applyVSchemaOptions.DryRun, + Strict: applyVSchemaOptions.Strict, } var err error @@ -113,11 +115,16 @@ func commandApplyVSchema(cmd *cobra.Command, args []string) error { if err != nil { return err } - data, err := cli.MarshalJSON(res.VSchema) + vsData, err := cli.MarshalJSON(res.VSchema) if err != nil { return err } - fmt.Printf("New VSchema object:\n%s\nIf this is not what you expected, check the input data (as JSON parsing will skip unexpected fields).\n", data) + fmt.Printf("New VSchema object:\n%s\nIf this is not what you expected, check the input data (as JSON parsing will skip unexpected fields).\n", vsData) + for vdxName, ups := range res.UnknownVindexParams { + for _, param := range ups.Params { + fmt.Printf("Unknown parameter in vindex %s: %s\n", vdxName, param) + } + } return nil } @@ -151,6 +158,7 @@ func init() { ApplyVSchema.Flags().BoolVar(&applyVSchemaOptions.DryRun, "dry-run", false, "If set, do not save the altered vschema, simply echo to console.") ApplyVSchema.Flags().BoolVar(&applyVSchemaOptions.SkipRebuild, "skip-rebuild", false, "Skip rebuilding the SrvSchema objects.") ApplyVSchema.Flags().StringSliceVar(&applyVSchemaOptions.Cells, "cells", nil, "Limits the rebuild to the specified cells, after application. Ignored if --skip-rebuild is set.") + ApplyVSchema.Flags().BoolVar(&applyVSchemaOptions.Strict, "strict", false, "If set, treat unknown vindex params as errors.") Root.AddCommand(ApplyVSchema) Root.AddCommand(GetVSchema) diff --git a/go/cmd/vtexplain/cli/vtexplain.go b/go/cmd/vtexplain/cli/vtexplain.go index 8b0622cf8a3..c8671cdb532 100644 --- a/go/cmd/vtexplain/cli/vtexplain.go +++ b/go/cmd/vtexplain/cli/vtexplain.go @@ -24,6 +24,8 @@ import ( "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtexplain" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -78,12 +80,11 @@ If no keyspace name is present, VTExplain will return the following error: ` + "```\n", Example: "Explain how Vitess will execute the query `SELECT * FROM users` using the VSchema contained in `vschemas.json` and the database schema `schema.sql`:\n\n" + "```\nvtexplain --vschema-file vschema.json --schema-file schema.sql --sql \"SELECT * FROM users\"\n```\n\n" + - "Explain how the example will execute on 128 shards using Row-based replication:\n\n" + - "```\nvtexplain -- -shards 128 --vschema-file vschema.json --schema-file schema.sql --replication-mode \"ROW\" --output-mode text --sql \"INSERT INTO users (user_id, name) VALUES(1, 'john')\"\n```\n", Args: cobra.NoArgs, PreRunE: servenv.CobraPreRunE, + Version: servenv.AppVersion.String(), RunE: run, } ) @@ -175,7 +176,17 @@ func parseAndRun() error { Target: dbName, } - vte, err := vtexplain.Init(context.Background(), vschema, schema, ksShardMap, opts) + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + return err + } + ctx := context.Background() + ts := memorytopo.NewServer(ctx, vtexplain.Cell) + vte, err := vtexplain.Init(ctx, env, ts, vschema, schema, ksShardMap, opts) if err != nil { return err } diff --git a/go/cmd/vtgate/cli/cli.go b/go/cmd/vtgate/cli/cli.go index 9182bfcf9a4..c348c4d8296 100644 --- a/go/cmd/vtgate/cli/cli.go +++ b/go/cmd/vtgate/cli/cli.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -30,6 +30,7 @@ import ( "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -158,8 +159,17 @@ func run(cmd *cobra.Command, args []string) error { plannerVersion, _ := plancontext.PlannerNameToVersion(plannerName) + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + return fmt.Errorf("unable to initialize env: %v", err) + } + // pass nil for HealthCheck and it will be created - vtg := vtgate.Init(context.Background(), nil, resilientServer, cell, tabletTypes, plannerVersion) + vtg := vtgate.Init(context.Background(), env, nil, resilientServer, cell, tabletTypes, plannerVersion) servenv.OnRun(func() { // Flags are parsed now. Parse the template using the actual flag value and overwrite the current template. diff --git a/go/cmd/vtgate/cli/plugin_auth_clientcert.go b/go/cmd/vtgate/cli/plugin_auth_clientcert.go index 1a1334e71ba..d486669847f 100644 --- a/go/cmd/vtgate/cli/plugin_auth_clientcert.go +++ b/go/cmd/vtgate/cli/plugin_auth_clientcert.go @@ -23,6 +23,10 @@ import ( "vitess.io/vitess/go/vt/vtgate" ) +var clientcertAuthMethod string + func init() { - vtgate.RegisterPluginInitializer(func() { mysql.InitAuthServerClientCert() }) + Main.Flags().StringVar(&clientcertAuthMethod, "mysql_clientcert_auth_method", string(mysql.MysqlClearPassword), "client-side authentication method to use. Supported values: mysql_clear_password, dialog.") + + vtgate.RegisterPluginInitializer(func() { mysql.InitAuthServerClientCert(clientcertAuthMethod) }) } diff --git a/go/cmd/vtgate/cli/plugin_auth_ldap.go b/go/cmd/vtgate/cli/plugin_auth_ldap.go index 7dc5b246f72..f8312267504 100644 --- a/go/cmd/vtgate/cli/plugin_auth_ldap.go +++ b/go/cmd/vtgate/cli/plugin_auth_ldap.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -19,10 +19,21 @@ package cli // This plugin imports ldapauthserver to register the LDAP implementation of AuthServer. import ( + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/ldapauthserver" "vitess.io/vitess/go/vt/vtgate" ) +var ( + ldapAuthConfigFile string + ldapAuthConfigString string + ldapAuthMethod string +) + func init() { - vtgate.RegisterPluginInitializer(func() { ldapauthserver.Init() }) + Main.Flags().StringVar(&ldapAuthConfigFile, "mysql_ldap_auth_config_file", "", "JSON File from which to read LDAP server config.") + Main.Flags().StringVar(&ldapAuthConfigString, "mysql_ldap_auth_config_string", "", "JSON representation of LDAP server config.") + Main.Flags().StringVar(&ldapAuthMethod, "mysql_ldap_auth_method", string(mysql.MysqlClearPassword), "client-side authentication method to use. Supported values: mysql_clear_password, dialog.") + + vtgate.RegisterPluginInitializer(func() { ldapauthserver.Init(ldapAuthConfigFile, ldapAuthConfigString, ldapAuthMethod) }) } diff --git a/go/cmd/vtgate/cli/plugin_auth_static.go b/go/cmd/vtgate/cli/plugin_auth_static.go index 9ffd60a79f2..7ed0e7b8f61 100644 --- a/go/cmd/vtgate/cli/plugin_auth_static.go +++ b/go/cmd/vtgate/cli/plugin_auth_static.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -19,10 +19,24 @@ package cli // This plugin imports staticauthserver to register the flat-file implementation of AuthServer. import ( + "time" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/vtgate" ) +var ( + mysqlAuthServerStaticFile string + mysqlAuthServerStaticString string + mysqlAuthServerStaticReloadInterval time.Duration +) + func init() { - vtgate.RegisterPluginInitializer(func() { mysql.InitAuthServerStatic() }) + Main.Flags().StringVar(&mysqlAuthServerStaticFile, "mysql_auth_server_static_file", "", "JSON File to read the users/passwords from.") + Main.Flags().StringVar(&mysqlAuthServerStaticString, "mysql_auth_server_static_string", "", "JSON representation of the users/passwords config.") + Main.Flags().DurationVar(&mysqlAuthServerStaticReloadInterval, "mysql_auth_static_reload_interval", 0, "Ticker to reload credentials") + + vtgate.RegisterPluginInitializer(func() { + mysql.InitAuthServerStatic(mysqlAuthServerStaticFile, mysqlAuthServerStaticString, mysqlAuthServerStaticReloadInterval) + }) } diff --git a/go/cmd/vtgate/cli/plugin_auth_vault.go b/go/cmd/vtgate/cli/plugin_auth_vault.go index 2aee32e3940..a119d2d389b 100644 --- a/go/cmd/vtgate/cli/plugin_auth_vault.go +++ b/go/cmd/vtgate/cli/plugin_auth_vault.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -19,10 +19,36 @@ package cli // This plugin imports InitAuthServerVault to register the HashiCorp Vault implementation of AuthServer. import ( + "time" + "vitess.io/vitess/go/mysql/vault" "vitess.io/vitess/go/vt/vtgate" ) +var ( + vaultAddr string + vaultTimeout time.Duration + vaultCACert string + vaultPath string + vaultCacheTTL time.Duration + vaultTokenFile string + vaultRoleID string + vaultRoleSecretIDFile string + vaultRoleMountPoint string +) + func init() { - vtgate.RegisterPluginInitializer(func() { vault.InitAuthServerVault() }) + Main.Flags().StringVar(&vaultAddr, "mysql_auth_vault_addr", "", "URL to Vault server") + Main.Flags().DurationVar(&vaultTimeout, "mysql_auth_vault_timeout", 10*time.Second, "Timeout for vault API operations") + Main.Flags().StringVar(&vaultCACert, "mysql_auth_vault_tls_ca", "", "Path to CA PEM for validating Vault server certificate") + Main.Flags().StringVar(&vaultPath, "mysql_auth_vault_path", "", "Vault path to vtgate credentials JSON blob, e.g.: secret/data/prod/vtgatecreds") + Main.Flags().DurationVar(&vaultCacheTTL, "mysql_auth_vault_ttl", 30*time.Minute, "How long to cache vtgate credentials from the Vault server") + Main.Flags().StringVar(&vaultTokenFile, "mysql_auth_vault_tokenfile", "", "Path to file containing Vault auth token; token can also be passed using VAULT_TOKEN environment variable") + Main.Flags().StringVar(&vaultRoleID, "mysql_auth_vault_roleid", "", "Vault AppRole id; can also be passed using VAULT_ROLEID environment variable") + Main.Flags().StringVar(&vaultRoleSecretIDFile, "mysql_auth_vault_role_secretidfile", "", "Path to file containing Vault AppRole secret_id; can also be passed using VAULT_SECRETID environment variable") + Main.Flags().StringVar(&vaultRoleMountPoint, "mysql_auth_vault_role_mountpoint", "approle", "Vault AppRole mountpoint; can also be passed using VAULT_MOUNTPOINT environment variable") + + vtgate.RegisterPluginInitializer(func() { + vault.InitAuthServerVault(vaultAddr, vaultTimeout, vaultCACert, vaultPath, vaultCacheTTL, vaultTokenFile, vaultRoleID, vaultRoleSecretIDFile, vaultRoleMountPoint) + }) } diff --git a/go/cmd/vtgate/cli/plugin_consultopo.go b/go/cmd/vtgate/cli/plugin_consultopo.go index a128f294a42..56d178e2975 100644 --- a/go/cmd/vtgate/cli/plugin_consultopo.go +++ b/go/cmd/vtgate/cli/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtgate/cli/plugin_zk2topo.go b/go/cmd/vtgate/cli/plugin_zk2topo.go index 1870a3b2bb3..66d14988c75 100644 --- a/go/cmd/vtgate/cli/plugin_zk2topo.go +++ b/go/cmd/vtgate/cli/plugin_zk2topo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtgate/cli/status.go b/go/cmd/vtgate/cli/status.go index 2fdab073d5a..d38cac2d50e 100644 --- a/go/cmd/vtgate/cli/status.go +++ b/go/cmd/vtgate/cli/status.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -37,7 +37,10 @@ func addStatusParts(vtg *vtgate.VTGate) { servenv.AddStatusPart("Gateway Status", vtgate.StatusTemplate, func() any { return vtg.GetGatewayCacheStatus() }) - servenv.AddStatusPart("Health Check Cache", discovery.HealthCheckTemplate, func() any { + servenv.AddStatusPart("Health Check - Cache", discovery.HealthCheckCacheTemplate, func() any { return vtg.Gateway().TabletsCacheStatus() }) + servenv.AddStatusPart("Health Check - Healthy Tablets", discovery.HealthCheckHealthyTemplate, func() any { + return vtg.Gateway().TabletsHealthyStatus() + }) } diff --git a/go/cmd/vtorc/cli/plugin_consultopo.go b/go/cmd/vtorc/cli/plugin_consultopo.go index a128f294a42..56d178e2975 100644 --- a/go/cmd/vtorc/cli/plugin_consultopo.go +++ b/go/cmd/vtorc/cli/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vtorc/cli/plugin_zk2topo.go b/go/cmd/vtorc/cli/plugin_zk2topo.go index d71a7e2e196..0b2884cc258 100644 --- a/go/cmd/vtorc/cli/plugin_zk2topo.go +++ b/go/cmd/vtorc/cli/plugin_zk2topo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/cli.go b/go/cmd/vttablet/cli/cli.go index 1efa35613d7..872d7a5ef69 100644 --- a/go/cmd/vttablet/cli/cli.go +++ b/go/cmd/vttablet/cli/cli.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/binlog" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" @@ -35,6 +36,7 @@ import ( "vitess.io/vitess/go/vt/tableacl/simpleacl" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/onlineddl" "vitess.io/vitess/go/vt/vttablet/tabletmanager" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vdiff" @@ -110,14 +112,24 @@ func run(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to parse --tablet-path: %w", err) } + mysqlVersion := servenv.MySQLServerVersion() + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: mysqlVersion, + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + return fmt.Errorf("cannot initialize vtenv: %w", err) + } + // config and mycnf initializations are intertwined. - config, mycnf, err := initConfig(tabletAlias) + config, mycnf, err := initConfig(tabletAlias, env.CollationEnv()) if err != nil { return err } ts := topo.Open() - qsc, err := createTabletServer(context.Background(), config, ts, tabletAlias) + qsc, err := createTabletServer(context.Background(), env, config, ts, tabletAlias) if err != nil { ts.Close() return err @@ -130,28 +142,28 @@ func run(cmd *cobra.Command, args []string) error { ts.Close() return fmt.Errorf("failed to extract online DDL binaries: %w", err) } - // Initialize and start tm. gRPCPort := int32(0) if servenv.GRPCPort() != 0 { gRPCPort = int32(servenv.GRPCPort()) } - tablet, err := tabletmanager.BuildTabletFromInput(tabletAlias, int32(servenv.Port()), gRPCPort, config.DB) + tablet, err := tabletmanager.BuildTabletFromInput(tabletAlias, int32(servenv.Port()), gRPCPort, config.DB, env.CollationEnv()) if err != nil { return fmt.Errorf("failed to parse --tablet-path: %w", err) } tm = &tabletmanager.TabletManager{ BatchCtx: context.Background(), + Env: env, TopoServer: ts, Cnf: mycnf, MysqlDaemon: mysqld, DBConfigs: config.DB.Clone(), QueryServiceControl: qsc, - UpdateStream: binlog.NewUpdateStream(ts, tablet.Keyspace, tabletAlias.Cell, qsc.SchemaEngine()), - VREngine: vreplication.NewEngine(config, ts, tabletAlias.Cell, mysqld, qsc.LagThrottler()), - VDiffEngine: vdiff.NewEngine(config, ts, tablet), + UpdateStream: binlog.NewUpdateStream(ts, tablet.Keyspace, tabletAlias.Cell, qsc.SchemaEngine(), env.Parser()), + VREngine: vreplication.NewEngine(env, config, ts, tabletAlias.Cell, mysqld, qsc.LagThrottler()), + VDiffEngine: vdiff.NewEngine(ts, tablet, env.CollationEnv(), env.Parser()), } - if err := tm.Start(tablet, config.Healthcheck.IntervalSeconds.Get()); err != nil { + if err := tm.Start(tablet, config); err != nil { ts.Close() return fmt.Errorf("failed to parse --tablet-path or initialize DB credentials: %w", err) } @@ -169,7 +181,7 @@ func run(cmd *cobra.Command, args []string) error { return nil } -func initConfig(tabletAlias *topodatapb.TabletAlias) (*tabletenv.TabletConfig, *mysqlctl.Mycnf, error) { +func initConfig(tabletAlias *topodatapb.TabletAlias, collationEnv *collations.Environment) (*tabletenv.TabletConfig, *mysqlctl.Mycnf, error) { tabletenv.Init() // Load current config after tabletenv.Init, because it changes it. config := tabletenv.NewCurrentConfig() @@ -211,9 +223,9 @@ func initConfig(tabletAlias *topodatapb.TabletAlias) (*tabletenv.TabletConfig, * // If connection parameters were specified, socketFile will be empty. // Otherwise, the socketFile (read from mycnf) will be used to initialize // dbconfigs. - config.DB.InitWithSocket(socketFile) + config.DB.InitWithSocket(socketFile, collationEnv) for _, cfg := range config.ExternalConnections { - cfg.InitWithSocket("") + cfg.InitWithSocket("", collationEnv) } return config, mycnf, nil } @@ -237,15 +249,16 @@ func extractOnlineDDL() error { return nil } -func createTabletServer(ctx context.Context, config *tabletenv.TabletConfig, ts *topo.Server, tabletAlias *topodatapb.TabletAlias) (*tabletserver.TabletServer, error) { +func createTabletServer(ctx context.Context, env *vtenv.Environment, config *tabletenv.TabletConfig, ts *topo.Server, tabletAlias *topodatapb.TabletAlias) (*tabletserver.TabletServer, error) { if tableACLConfig != "" { // To override default simpleacl, other ACL plugins must set themselves to be default ACL factory tableacl.Register("simpleacl", &simpleacl.Factory{}) } else if enforceTableACLConfig { return nil, fmt.Errorf("table acl config has to be specified with table-acl-config flag because enforce-tableacl-config is set.") } + // creates and registers the query service - qsc := tabletserver.NewTabletServer(ctx, "", config, ts, tabletAlias) + qsc := tabletserver.NewTabletServer(ctx, env, "", config, ts, tabletAlias) servenv.OnRun(func() { qsc.Register() addStatusParts(qsc) diff --git a/go/cmd/vttablet/cli/plugin_cephbackupstorage.go b/go/cmd/vttablet/cli/plugin_cephbackupstorage.go index 171198f5e29..7755e1cae2d 100644 --- a/go/cmd/vttablet/cli/plugin_cephbackupstorage.go +++ b/go/cmd/vttablet/cli/plugin_cephbackupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/plugin_consultopo.go b/go/cmd/vttablet/cli/plugin_consultopo.go index a128f294a42..56d178e2975 100644 --- a/go/cmd/vttablet/cli/plugin_consultopo.go +++ b/go/cmd/vttablet/cli/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/plugin_s3backupstorage.go b/go/cmd/vttablet/cli/plugin_s3backupstorage.go index 4b3ecb33edb..e09f6060809 100644 --- a/go/cmd/vttablet/cli/plugin_s3backupstorage.go +++ b/go/cmd/vttablet/cli/plugin_s3backupstorage.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/plugin_sysloglogger.go b/go/cmd/vttablet/cli/plugin_sysloglogger.go index a7260d6f8cc..90860abe826 100644 --- a/go/cmd/vttablet/cli/plugin_sysloglogger.go +++ b/go/cmd/vttablet/cli/plugin_sysloglogger.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. @@ -7,7 +9,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/plugin_zk2topo.go b/go/cmd/vttablet/cli/plugin_zk2topo.go index d71a7e2e196..0b2884cc258 100644 --- a/go/cmd/vttablet/cli/plugin_zk2topo.go +++ b/go/cmd/vttablet/cli/plugin_zk2topo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttablet/cli/status.go b/go/cmd/vttablet/cli/status.go index 762a9fa646e..de3bfcbce74 100644 --- a/go/cmd/vttablet/cli/status.go +++ b/go/cmd/vttablet/cli/status.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttestserver/cli/main.go b/go/cmd/vttestserver/cli/main.go index ea92ae7dda0..35362aa3263 100644 --- a/go/cmd/vttestserver/cli/main.go +++ b/go/cmd/vttestserver/cli/main.go @@ -104,6 +104,7 @@ func New() (cmd *cobra.Command) { Short: "vttestserver allows users to spawn a self-contained Vitess server for local testing/CI.", Args: cobra.NoArgs, PreRunE: servenv.CobraPreRunE, + Version: servenv.AppVersion.String(), RunE: run, } @@ -177,6 +178,9 @@ func New() (cmd *cobra.Command) { cmd.Flags().StringVar(&config.MySQLBindHost, "mysql_bind_host", "localhost", "which host to bind vtgate mysql listener to") + cmd.Flags().StringVar(&config.VtComboBindAddress, "vtcombo-bind-host", "localhost", + "which host to bind vtcombo servenv listener to") + cmd.Flags().StringVar(&mycnf, "extra_my_cnf", "", "extra files to add to the config, separated by ':'") diff --git a/go/cmd/vttestserver/cli/main_test.go b/go/cmd/vttestserver/cli/main_test.go index 39dc8e4ea78..dbaf256c806 100644 --- a/go/cmd/vttestserver/cli/main_test.go +++ b/go/cmd/vttestserver/cli/main_test.go @@ -34,6 +34,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/tlstest" @@ -189,11 +190,17 @@ func TestCanGetKeyspaces(t *testing.T) { conf := config defer resetConfig(conf) - cluster, err := startCluster() + clusterInstance, err := startCluster() assert.NoError(t, err) - defer cluster.TearDown() + defer clusterInstance.TearDown() - assertGetKeyspaces(t, cluster) + defer func() { + if t.Failed() { + cluster.PrintFiles(t, clusterInstance.Env.Directory(), "vtcombo.INFO", "error.log") + } + }() + + assertGetKeyspaces(t, clusterInstance) } func TestExternalTopoServerConsul(t *testing.T) { diff --git a/go/cmd/vttlstest/cli/vttlstest.go b/go/cmd/vttlstest/cli/vttlstest.go index 4e0f9c2b95e..9791645cdc2 100644 --- a/go/cmd/vttlstest/cli/vttlstest.go +++ b/go/cmd/vttlstest/cli/vttlstest.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/vttlstest/vttlstest.go b/go/cmd/vttlstest/vttlstest.go index 08e994c096d..8b98687c7a8 100644 --- a/go/cmd/vttlstest/vttlstest.go +++ b/go/cmd/vttlstest/vttlstest.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/add_auth.go b/go/cmd/zk/command/add_auth.go index 566c463f4a8..117ddf1cd8a 100644 --- a/go/cmd/zk/command/add_auth.go +++ b/go/cmd/zk/command/add_auth.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/cat.go b/go/cmd/zk/command/cat.go index 1d5460f7006..6dae3c903a9 100644 --- a/go/cmd/zk/command/cat.go +++ b/go/cmd/zk/command/cat.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/chmod.go b/go/cmd/zk/command/chmod.go index 39125d618c4..63bd103fdb2 100644 --- a/go/cmd/zk/command/chmod.go +++ b/go/cmd/zk/command/chmod.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/cp.go b/go/cmd/zk/command/cp.go index e89486413ea..b45baab1b6e 100644 --- a/go/cmd/zk/command/cp.go +++ b/go/cmd/zk/command/cp.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/edit.go b/go/cmd/zk/command/edit.go index ec4b74c4b62..90348161502 100644 --- a/go/cmd/zk/command/edit.go +++ b/go/cmd/zk/command/edit.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/ls.go b/go/cmd/zk/command/ls.go index 83c1d31363b..5d28f20ae60 100644 --- a/go/cmd/zk/command/ls.go +++ b/go/cmd/zk/command/ls.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/rm.go b/go/cmd/zk/command/rm.go index 5e5b5f4c494..8b710b2fb74 100644 --- a/go/cmd/zk/command/rm.go +++ b/go/cmd/zk/command/rm.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/root.go b/go/cmd/zk/command/root.go index f3f02e7d4f2..2aabcd50e4f 100644 --- a/go/cmd/zk/command/root.go +++ b/go/cmd/zk/command/root.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/stat.go b/go/cmd/zk/command/stat.go index 713a68a3d4e..28aa6bb3465 100644 --- a/go/cmd/zk/command/stat.go +++ b/go/cmd/zk/command/stat.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/touch.go b/go/cmd/zk/command/touch.go index 76c390cf169..53e80a05214 100644 --- a/go/cmd/zk/command/touch.go +++ b/go/cmd/zk/command/touch.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/unzip.go b/go/cmd/zk/command/unzip.go index f4c800e0533..81e04bf4564 100644 --- a/go/cmd/zk/command/unzip.go +++ b/go/cmd/zk/command/unzip.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/wait.go b/go/cmd/zk/command/wait.go index 864f6e83626..8aa844d8bc6 100644 --- a/go/cmd/zk/command/wait.go +++ b/go/cmd/zk/command/wait.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/watch.go b/go/cmd/zk/command/watch.go index eb28cc29ca2..7d6de784718 100644 --- a/go/cmd/zk/command/watch.go +++ b/go/cmd/zk/command/watch.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/command/zip.go b/go/cmd/zk/command/zip.go index b765f5bb00e..5f06b97c508 100644 --- a/go/cmd/zk/command/zip.go +++ b/go/cmd/zk/command/zip.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zk/internal/zkfilepath/zfilepath_test.go b/go/cmd/zk/internal/zkfilepath/zfilepath_test.go new file mode 100644 index 00000000000..2ab649fb3a4 --- /dev/null +++ b/go/cmd/zk/internal/zkfilepath/zfilepath_test.go @@ -0,0 +1,97 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package zkfilepath + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/z-division/go-zookeeper/zk" +) + +func TestClean(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"/path/to/some/dir/", "/path/to/some/dir"}, + {"/", "/"}, + {"", "."}, + {"/root", "/root"}, + {"no/slash/at/the/end", "no/slash/at/the/end"}, + } + + for _, test := range tests { + result := Clean(test.input) + assert.Equal(t, test.expected, result) + } +} + +func TestFormat(t *testing.T) { + testTime := time.Now() + stat := &zk.Stat{ + DataLength: 100, + NumChildren: 1, + Mtime: testTime.UnixMilli(), + EphemeralOwner: 1, + } + + tests := []struct { + stat *zk.Stat + zkPath string + showFullPath bool + longListing bool + expected string + }{ + // Checking the effect of showFullPath without longListing + {stat, "/path/to/node", true, false, "/path/to/node\n"}, + {stat, "/path/to/node", false, false, "node\n"}, + + // Checking the effect of showFullPath with longListing + {stat, "/path/to/node", true, true, "drwxrwxrwx zk zk 100 " + testTime.Format(TimeFmt) + " /path/to/node\n"}, + {stat, "/path/to/node", false, true, "drwxrwxrwx zk zk 100 " + testTime.Format(TimeFmt) + " node\n"}, + } + + for _, test := range tests { + result := Format(test.stat, test.zkPath, test.showFullPath, test.longListing) + assert.Equal(t, test.expected, result) + } +} + +func TestGetPermissions(t *testing.T) { + tests := []struct { + numChildren int32 + dataLength int32 + ephemeralOwner int64 + expected string + }{ + // Children, Data, Ephemeral, Expected + {0, 0, 0, "-rw-rw-rw-"}, + {1, 1, 0, "drwxrwxrwx"}, + {1, 0, 123, "drwxrwxrwx"}, + {0, 1, 1, "erw-rw-rw-"}, + {1, 1, 0, "drwxrwxrwx"}, + {0, 0, 1, "erw-rw-rw-"}, + {0, 0, 0, "-rw-rw-rw-"}, + } + + for _, test := range tests { + result := getPermissions(test.numChildren, test.dataLength, test.ephemeralOwner) + assert.Equal(t, test.expected, result) + } +} diff --git a/go/cmd/zk/internal/zkfilepath/zkfilepath.go b/go/cmd/zk/internal/zkfilepath/zkfilepath.go index 7febc7a9677..2e9edb1fdf8 100644 --- a/go/cmd/zk/internal/zkfilepath/zkfilepath.go +++ b/go/cmd/zk/internal/zkfilepath/zkfilepath.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -44,7 +44,7 @@ func Clean(zkPath string) string { // Format returns a path formatted to a canonical string. func Format(stat *zk.Stat, zkPath string, showFullPath bool, longListing bool) string { - var name, perms string + var name string if !showFullPath { name = path.Base(zkPath) @@ -53,19 +53,9 @@ func Format(stat *zk.Stat, zkPath string, showFullPath bool, longListing bool) s } if longListing { - if stat.NumChildren > 0 { - // FIXME(msolomon) do permissions check? - perms = "drwxrwxrwx" - if stat.DataLength > 0 { - // give a visual indication that this node has data as well as children - perms = "nrw-rw-rw-" - } - } else if stat.EphemeralOwner != 0 { - perms = "erw-rw-rw-" - } else { - perms = "-rw-rw-rw-" - } - // always print the Local version of the time. zookeeper's + perms := getPermissions(stat.NumChildren, stat.DataLength, stat.EphemeralOwner) + + // Always print the Local version of the time. zookeeper's // go / C library would return a local time anyway, but // might as well be sure. return fmt.Sprintf("%v %v %v % 8v % 20v %v\n", perms, "zk", "zk", stat.DataLength, zk2topo.Time(stat.Mtime).Local().Format(TimeFmt), name) @@ -73,3 +63,19 @@ func Format(stat *zk.Stat, zkPath string, showFullPath bool, longListing bool) s return fmt.Sprintf("%v\n", name) } } + +// Utility function to return the permissions for a node +func getPermissions(numChildren int32, dataLength int32, ephemeralOwner int64) string { + if numChildren > 0 { + // FIXME(msolomon) do permissions check? + if dataLength > 0 { + // give a visual indication that this node has data as well as children + return "drwxrwxrwx" + } + return "drwxrwxrwx" + } else if ephemeralOwner != 0 { + return "erw-rw-rw-" + } else { + return "-rw-rw-rw-" + } +} diff --git a/go/cmd/zk/internal/zkfs/zkfs.go b/go/cmd/zk/internal/zkfs/zkfs.go index 9bab19ec1e4..9f2fe732ec8 100644 --- a/go/cmd/zk/internal/zkfs/zkfs.go +++ b/go/cmd/zk/internal/zkfs/zkfs.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -158,6 +158,10 @@ func IsFile(path string) bool { // ParsePermMode parses the mode string as a perm mask. func ParsePermMode(mode string) (mask int32) { + if len(mode) < 2 { + panic("invalid mode") + } + for _, c := range mode[2:] { mask |= charPermMap[string(c)] } diff --git a/go/cmd/zk/internal/zkfs/zkfs_test.go b/go/cmd/zk/internal/zkfs/zkfs_test.go new file mode 100644 index 00000000000..2aed00af4c1 --- /dev/null +++ b/go/cmd/zk/internal/zkfs/zkfs_test.go @@ -0,0 +1,83 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package zkfs + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/z-division/go-zookeeper/zk" +) + +func TestIsFile(t *testing.T) { + assert.True(t, IsFile("/zk/somepath")) + assert.False(t, IsFile("/nonzk/somepath")) + assert.False(t, IsFile("nonzkpath")) +} + +func TestParsePermMode(t *testing.T) { + assert.Equal(t, int32(0), ParsePermMode("zk")) + assert.Equal(t, int32(zk.PermRead|zk.PermWrite), ParsePermMode("zkrw")) + assert.Equal(t, int32(zk.PermRead|zk.PermWrite|zk.PermAdmin), ParsePermMode("zkrwa")) + assert.PanicsWithValue(t, "invalid mode", func() { + ParsePermMode("") + }) + assert.PanicsWithValue(t, "invalid mode", func() { + ParsePermMode("z") + }) +} + +func TestFormatACL(t *testing.T) { + testCases := []struct { + name string + acl zk.ACL + expected string + }{ + { + name: "Full Permissions", + acl: zk.ACL{Perms: zk.PermAll}, + expected: "rwdca", + }, + { + name: "Read and Write Permissions", + acl: zk.ACL{Perms: zk.PermRead | zk.PermWrite}, + expected: "rw---", + }, + { + name: "No Permissions", + acl: zk.ACL{Perms: 0}, + expected: "-----", + }, + { + name: "Create and Admin Permissions", + acl: zk.ACL{Perms: zk.PermAdmin | zk.PermCreate}, + expected: "---ca", + }, + { + name: "Mixed Permissions", + acl: zk.ACL{Perms: zk.PermRead | zk.PermDelete | zk.PermAdmin}, + expected: "r-d-a", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, FormatACL(tc.acl)) + }) + } +} diff --git a/go/cmd/zk/zkcmd.go b/go/cmd/zk/zkcmd.go index f03ac41c6ef..f0c39d6b0f8 100644 --- a/go/cmd/zk/zkcmd.go +++ b/go/cmd/zk/zkcmd.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/cmd/zkctld/cli/zkctld.go b/go/cmd/zkctld/cli/zkctld.go index 101f1013722..5ac3520868e 100644 --- a/go/cmd/zkctld/cli/zkctld.go +++ b/go/cmd/zkctld/cli/zkctld.go @@ -41,6 +41,7 @@ var ( Use: "zkctld", Short: "zkctld is a daemon that starts or initializes ZooKeeper with Vitess-specific configuration. It will stay running as long as the underlying ZooKeeper server, and will pass along SIGTERM.", Args: cobra.NoArgs, + Version: servenv.AppVersion.String(), PersistentPreRunE: servenv.CobraPreRunE, PostRun: func(cmd *cobra.Command, args []string) { logutil.Flush() diff --git a/go/errors/errors.go b/go/errors/errors.go index d3349d320ed..22a3ba937e9 100644 --- a/go/errors/errors.go +++ b/go/errors/errors.go @@ -32,7 +32,7 @@ func Unwrap(err error) []error { return nil } -// Unwrap unwraps an error created by errors.Join() in Go 1.20, into its components, recursively +// UnwrapAll unwraps an error created by errors.Join() in Go 1.20, into its components, recursively func UnwrapAll(err error) (errs []error) { if err == nil { return nil @@ -46,7 +46,7 @@ func UnwrapAll(err error) (errs []error) { return []error{err} } -// Unwrap unwraps an error created by errors.Join() in Go 1.20, into its components, recursively, +// UnwrapFirst unwraps an error created by errors.Join() in Go 1.20, into its components, recursively, // and returns one (the first) unwrapped error func UnwrapFirst(err error) error { if err == nil { diff --git a/go/event/event_test.go b/go/event/event_test.go index 36d4d56bf5d..f7356a98f3e 100644 --- a/go/event/event_test.go +++ b/go/event/event_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/event/hooks_test.go b/go/event/hooks_test.go index 197dd59a062..3d8e3361e94 100644 --- a/go/event/hooks_test.go +++ b/go/event/hooks_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/event/syslogger/fake_logger.go b/go/event/syslogger/fake_logger.go index 63c0942c069..852ca2a72a6 100644 --- a/go/event/syslogger/fake_logger.go +++ b/go/event/syslogger/fake_logger.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/event/syslogger/fake_logger_test.go b/go/event/syslogger/fake_logger_test.go new file mode 100644 index 00000000000..df4a8f8294e --- /dev/null +++ b/go/event/syslogger/fake_logger_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package syslogger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetLogsForNoLogs(t *testing.T) { + tl := NewTestLogger() + errLoggerMsg := tl.getLog() + + want := loggerMsg{ + msg: "no logs!", + level: "ERROR", + } + + assert.Equal(t, errLoggerMsg, want) +} + +func TestGetAllLogs(t *testing.T) { + tl := NewTestLogger() + tl.recordInfof("Test info log") + tl.recordErrorf("Test error log") + tl.recordWarningf("Test warning log") + + want := []string{"INFO:Test info log", "ERROR:Test error log", "WARNING:Test warning log"} + loggerMsgs := tl.GetAllLogs() + + assert.Equal(t, loggerMsgs, want) +} diff --git a/go/event/syslogger/syslogger.go b/go/event/syslogger/syslogger.go index 1c8ff22136b..234f2b5a712 100644 --- a/go/event/syslogger/syslogger.go +++ b/go/event/syslogger/syslogger.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. @@ -52,9 +54,11 @@ import ( "fmt" "log/syslog" "os" + "testing" "vitess.io/vitess/go/event" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/servenv" ) // Syslogger is the interface that events should implement if they want to be @@ -143,10 +147,28 @@ func listener(ev Syslogger) { } func init() { + // We only want to init syslog when the app is being initialized + // Some binaries import the syslog package indirectly leading to + // the syslog.New function being called and this might fail if + // running inside Docker without the syslog daemon enabled, leading + // logging the error which will make glog think there are not --log_dir + // flag set as we have not parsed the flags yet. + // https://github.com/vitessio/vitess/issues/15120 + servenv.OnInit(func() { + initSyslog() + }) + + // We still do the init of syslog if we are testing this package. + if testing.Testing() { + initSyslog() + } +} + +func initSyslog() { var err error writer, err = syslog.New(syslog.LOG_INFO|syslog.LOG_USER, os.Args[0]) if err != nil { - log.Errorf("can't connect to syslog") + log.Errorf("can't connect to syslog: %v", err.Error()) writer = nil } diff --git a/go/event/syslogger/syslogger_test.go b/go/event/syslogger/syslogger_test.go index 4847fecac2a..28925d2ac52 100644 --- a/go/event/syslogger/syslogger_test.go +++ b/go/event/syslogger/syslogger_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. @@ -7,7 +9,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -101,6 +103,11 @@ func TestBadWriter(t *testing.T) { if !strings.Contains(tl.getLog().level, wantLevel) { t.Errorf("error log level [%s], want level [%s]", tl.getLog().level, wantLevel) } + ev = &TestEvent{priority: syslog.LOG_EMERG, message: wantMsg} + event.Dispatch(ev) + if !strings.Contains(tl.getLog().level, wantLevel) { + t.Errorf("error log level [%s], want level [%s]", tl.getLog().level, wantLevel) + } wantLevel = "WARNING" ev = &TestEvent{priority: syslog.LOG_WARNING, message: wantMsg} diff --git a/go/exit/exit_test.go b/go/exit/exit_test.go index e51a0938534..7f08f4ea1e2 100644 --- a/go/exit/exit_test.go +++ b/go/exit/exit_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/fileutil/wildcards_test.go b/go/fileutil/wildcards_test.go index 9d332a507db..e4494dd7b2c 100644 --- a/go/fileutil/wildcards_test.go +++ b/go/fileutil/wildcards_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/flags/endtoend/mysqlctl.txt b/go/flags/endtoend/mysqlctl.txt index a8f832d3345..044d12981d5 100644 --- a/go/flags/endtoend/mysqlctl.txt +++ b/go/flags/endtoend/mysqlctl.txt @@ -64,7 +64,7 @@ Flags: --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -81,17 +81,18 @@ Flags: --pid_file string If set, the process will write its pid to the named file, and delete it on graceful shutdown. --pool_hostname_resolve_interval duration if set force an update to all hostnames and reconnect if changed, defaults to 0 (disabled) --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --replication_connect_retry duration how long to wait in between replica reconnect attempts. Only precise to the second. (default 10s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice --socket_file string Local unix socket file to listen on - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. --tablet_uid uint32 Tablet UID. (default 41983) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging Use "mysqlctl [command] --help" for more information about a command. diff --git a/go/flags/endtoend/mysqlctld.txt b/go/flags/endtoend/mysqlctld.txt index 06b48347bf6..0dde59e0d7d 100644 --- a/go/flags/endtoend/mysqlctld.txt +++ b/go/flags/endtoend/mysqlctld.txt @@ -84,12 +84,14 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) -h, --help help for mysqlctld --init_db_sql_file string Path to .sql file to run after mysqld initialization --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -106,16 +108,18 @@ Flags: --pool_hostname_resolve_interval duration if set force an update to all hostnames and reconnect if changed, defaults to 0 (disabled) --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --replication_connect_retry duration how long to wait in between replica reconnect attempts. Only precise to the second. (default 10s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice + --shutdown-wait-time duration How long to wait for mysqld shutdown (default 5m0s) --socket_file string Local unix socket file to listen on - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. --tablet_uid uint32 Tablet UID (default 41983) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging - --wait_time duration How long to wait for mysqld startup or shutdown (default 5m0s) + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging + --wait_time duration How long to wait for mysqld startup (default 5m0s) diff --git a/go/flags/endtoend/topo2topo.txt b/go/flags/endtoend/topo2topo.txt index 4391a32a1a8..c003c3584f3 100644 --- a/go/flags/endtoend/topo2topo.txt +++ b/go/flags/endtoend/topo2topo.txt @@ -27,18 +27,19 @@ Flags: -h, --help help for topo2topo --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) --logtostderr log to standard error instead of files --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --to_implementation string topology implementation to copy data to --to_root string topology server root to copy data to --to_server string topology server address to copy data to --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging diff --git a/go/flags/endtoend/vtaclcheck.txt b/go/flags/endtoend/vtaclcheck.txt index 34bef9a05f9..8917df63c66 100644 --- a/go/flags/endtoend/vtaclcheck.txt +++ b/go/flags/endtoend/vtaclcheck.txt @@ -15,16 +15,17 @@ Flags: -h, --help help for vtaclcheck --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) --logtostderr log to standard error instead of files --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --static-auth-file string The path of the auth_server_static JSON file to check - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging diff --git a/go/flags/endtoend/vtbackup.txt b/go/flags/endtoend/vtbackup.txt index 2dd6bf3ef28..d0c5a328052 100644 --- a/go/flags/endtoend/vtbackup.txt +++ b/go/flags/endtoend/vtbackup.txt @@ -137,7 +137,7 @@ Flags: --grpc_max_message_size int Maximum allowed RPC message size. Larger messages will be rejected by gRPC with the error 'exceeding the max size'. (default 16777216) --grpc_prometheus Enable gRPC monitoring with Prometheus. -h, --help help for vtbackup - --incremental_from_pos string Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position + --incremental_from_pos string Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position. --init_db_name_override string (init parameter) override the name of the db used by vttablet --init_db_sql_file string path to .sql file to run after mysql_install_db --init_keyspace string (init parameter) keyspace to use for this tablet @@ -147,7 +147,7 @@ Flags: --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -174,6 +174,7 @@ Flags: --mycnf_slow_log_path string mysql slow query log path --mycnf_socket_file string mysql socket file --mycnf_tmp_dir string mysql tmp directory + --mysql-shutdown-timeout duration how long to wait for mysqld shutdown (default 5m0s) --mysql_port int mysql port (default 3306) --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --mysql_socket string path to the mysql socket @@ -181,6 +182,7 @@ Flags: --opentsdb_uri string URI of opentsdb /api/put method --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --remote_operation_timeout duration time to wait for a remote operation (default 15s) --restart_before_backup Perform a mysqld clean/full restart after applying binlogs, but before taking the backup. Only makes sense to work around xtrabackup bugs. @@ -201,10 +203,10 @@ Flags: --stats_common_tags strings Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2 --stats_drop_variables string Variables to be dropped from the list of exported variables. --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect @@ -230,7 +232,7 @@ Flags: --upgrade-safe Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades. --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --xbstream_restore_flags string Flags to pass to xbstream command during restore. These should be space separated and will be added to the end of the command. These need to match the ones used for backup e.g. --compress / --decompress, --encrypt / --decrypt --xtrabackup_backup_flags string Flags to pass to backup command. These should be space separated and will be added to the end of the command --xtrabackup_prepare_flags string Flags to pass to prepare command. These should be space separated and will be added to the end of the command diff --git a/go/flags/endtoend/vtbench.txt b/go/flags/endtoend/vtbench.txt index d74dc13ebc8..260451f6b03 100644 --- a/go/flags/endtoend/vtbench.txt +++ b/go/flags/endtoend/vtbench.txt @@ -64,7 +64,7 @@ Flags: --host string VTGate host(s) in the form 'host1,host2,...' --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -72,13 +72,14 @@ Flags: --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --port int VTGate port --pprof strings enable profiling + --pprof-http enable pprof http endpoints --protocol string Client protocol, either mysql (default), grpc-vtgate, or grpc-vttablet (default "mysql") --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --sql string SQL statement to execute --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --tablet_grpc_ca string the server ca to use to validate servers when connecting --tablet_grpc_cert string the cert to use to connect --tablet_grpc_crl string the server crl to use to validate server certificates when connecting @@ -89,7 +90,7 @@ Flags: --user string Username to connect using mysql (password comes from the db-credentials-file) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vtgate_grpc_ca string the server ca to use to validate servers when connecting --vtgate_grpc_cert string the cert to use to connect --vtgate_grpc_crl string the server crl to use to validate server certificates when connecting diff --git a/go/flags/endtoend/vtclient.txt b/go/flags/endtoend/vtclient.txt index 3d17734168c..57ddf892ac8 100644 --- a/go/flags/endtoend/vtclient.txt +++ b/go/flags/endtoend/vtclient.txt @@ -28,7 +28,7 @@ Flags: --json Output JSON instead of human-readable table --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -38,15 +38,16 @@ Flags: --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --parallel int DMLs only: Number of threads executing the same query in parallel. Useful for simple load testing. (default 1) --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --qps int queries per second to throttle each thread at. --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --server string vtgate server to connect to - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --streaming use a streaming query --target string keyspace:shard@tablet_type --timeout duration timeout for queries (default 30s) --use_random_sequence use random sequence for generating [min_sequence_id, max_sequence_id) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging diff --git a/go/flags/endtoend/vtcombo.txt b/go/flags/endtoend/vtcombo.txt index 71c11c54088..59dd8969880 100644 --- a/go/flags/endtoend/vtcombo.txt +++ b/go/flags/endtoend/vtcombo.txt @@ -163,6 +163,8 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) --grpc_use_effective_callerid If set, and SSL is not used, will set the immediate caller id from the effective caller id's principal. --health_check_interval duration Interval between health checks (default 20s) --healthcheck_retry_delay duration health check retry delay (default 2ms) @@ -189,7 +191,7 @@ Flags: --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) --lock_heartbeat_time duration If there is lock function used. This will keep the lock connection active by using this heartbeat (default 5s) --lock_tables_timeout duration How long to keep the table locked before timing out (default 1m0s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_queries_to_file string Enable query logging to the specified file @@ -222,11 +224,13 @@ Flags: --mycnf_tmp_dir string mysql tmp directory --mysql-server-keepalive-period duration TCP period between keep-alives --mysql-server-pool-conn-read-buffers If set, the server will pool incoming connection read buffers + --mysql-shutdown-timeout duration timeout to use when MySQL is being shut down. (default 5m0s) --mysql_allow_clear_text_without_tls If set, the server will allow the use of a clear text password over non-SSL connections. --mysql_auth_server_impl string Which auth server implementation to use. Options: none, ldap, clientcert, static, vault. (default "static") --mysql_default_workload string Default session workload (OLTP, OLAP, DBA) (default "OLTP") --mysql_port int mysql port (default 3306) --mysql_server_bind_address string Binds on this address when listening to MySQL binary protocol. Useful to restrict listening to 'localhost' only for instance. + --mysql_server_flush_delay duration Delay after which buffered response will be flushed to the client. (default 100ms) --mysql_server_port int If set, also listen for MySQL binary protocol connections on this port. (default -1) --mysql_server_query_timeout duration mysql query timeout --mysql_server_read_timeout duration connection read timeout @@ -254,6 +258,7 @@ Flags: --pool_hostname_resolve_interval duration if set force an update to all hostnames and reconnect if changed, defaults to 0 (disabled) --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --proto_topo vttest.TopoData vttest proto definition of the topology, encoded in compact text format. See vttest.proto for more information. --proxy_protocol Enable HAProxy PROXY protocol on MySQL listener socket --proxy_tablets Setting this true will make vtctld proxy the tablet status instead of redirecting to them @@ -269,27 +274,27 @@ Flags: --queryserver-config-acl-exempt-acl string an acl that exempt from table acl checking (this acl is free to access any vitess tables). --queryserver-config-annotate-queries prefix queries to MySQL backend with comment indicating vtgate principal (user) and target tablet type --queryserver-config-enable-table-acl-dry-run If this flag is enabled, tabletserver will emit monitoring metrics and let the request pass regardless of table acl check results - --queryserver-config-idle-timeout duration query server idle timeout (in seconds), vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance. (default 30m0s) + --queryserver-config-idle-timeout duration query server idle timeout, vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance. (default 30m0s) --queryserver-config-max-result-size int query server max result size, maximum number of rows allowed to return from vttablet for non-streaming queries. (default 10000) --queryserver-config-message-postpone-cap int query server message postpone cap is the maximum number of messages that can be postponed at any given time. Set this number to substantially lower than transaction cap, so that the transaction pool isn't exhausted by the message subsystem. (default 4) --queryserver-config-olap-transaction-timeout duration query server transaction timeout (in seconds), after which a transaction in an OLAP session will be killed (default 30s) --queryserver-config-passthrough-dmls query server pass through all dml statements without rewriting - --queryserver-config-pool-conn-max-lifetime duration query server connection max lifetime (in seconds), vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool. (default 0s) + --queryserver-config-pool-conn-max-lifetime duration query server connection max lifetime, vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool. --queryserver-config-pool-size int query server read pool size, connection pool is used by regular queries (non streaming, not in a transaction) (default 16) --queryserver-config-query-cache-memory int query server query cache size in bytes, maximum amount of memory to be used for caching. vttablet analyzes every incoming query and generate a query plan, these plans are being cached in a lru cache. This config controls the capacity of the lru cache. (default 33554432) - --queryserver-config-query-pool-timeout duration query server query pool timeout (in seconds), it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead. (default 0s) + --queryserver-config-query-pool-timeout duration query server query pool timeout, it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead. --queryserver-config-query-pool-waiter-cap int query server query pool waiter limit, this is the maximum number of queries that can be queued waiting to get a connection (default 5000) - --queryserver-config-query-timeout duration query server query timeout (in seconds), this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed. (default 30s) + --queryserver-config-query-timeout duration query server query timeout, this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed. (default 30s) --queryserver-config-schema-change-signal query server schema signal, will signal connected vtgates that schema has changed whenever this is detected. VTGates will need to have -schema_change_signal enabled for this to work (default true) - --queryserver-config-schema-reload-time duration query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance in seconds. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time. (default 30m0s) + --queryserver-config-schema-reload-time duration query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time. (default 30m0s) --queryserver-config-stream-buffer-size int query server stream buffer size, the maximum number of bytes sent from vttablet for each stream call. It's recommended to keep this value in sync with vtgate's stream_buffer_size. (default 32768) --queryserver-config-stream-pool-size int query server stream connection pool size, stream pool is used by stream queries: queries that return results to client in a streaming fashion (default 200) - --queryserver-config-stream-pool-timeout duration query server stream pool timeout (in seconds), it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout. (default 0s) + --queryserver-config-stream-pool-timeout duration query server stream pool timeout, it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout. --queryserver-config-stream-pool-waiter-cap int query server stream pool waiter limit, this is the maximum number of streaming queries that can be queued waiting to get a connection --queryserver-config-strict-table-acl only allow queries that pass table acl checks --queryserver-config-terse-errors prevent bind vars from escaping in client error messages --queryserver-config-transaction-cap int query server transaction cap is the maximum number of transactions allowed to happen at any given point of a time for a single vttablet. E.g. by setting transaction cap to 100, there are at most 100 transactions will be processed by a vttablet and the 101th transaction will be blocked (and fail if it cannot get connection within specified timeout) (default 20) - --queryserver-config-transaction-timeout duration query server transaction timeout (in seconds), a transaction will be killed if it takes longer than this value (default 30s) + --queryserver-config-transaction-timeout duration query server transaction timeout, a transaction will be killed if it takes longer than this value (default 30s) --queryserver-config-truncate-error-len int truncate errors sent to client if they are longer than this value (0 means do not truncate) --queryserver-config-txpool-timeout duration query server transaction pool timeout, it is how long vttablet waits if tx pool is full (default 1s) --queryserver-config-txpool-waiter-cap int query server transaction pool waiter limit, this is the maximum number of transactions that can be queued waiting to get a connection (default 5000) @@ -317,7 +322,7 @@ Flags: --service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice --serving_state_grace_period duration how long to pause after broadcasting health to vtgate, before enforcing a new serving state --shard_sync_retry_delay duration delay between retries of updates to keep the tablet and its shard record in sync (default 30s) - --shutdown_grace_period duration how long to wait (in seconds) for queries and transactions to complete during graceful shutdown. (default 0s) + --shutdown_grace_period duration how long to wait for queries and transactions to complete during graceful shutdown. --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512) --srv_topo_cache_refresh duration how frequently to refresh the topology for cached entries (default 1s) @@ -329,18 +334,18 @@ Flags: --stats_common_tags strings Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2 --stats_drop_variables string Variables to be dropped from the list of exported variables. --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --stream_buffer_size int the number of bytes sent from vtgate for each stream call. It's recommended to keep this value in sync with vttablet's query-server-config-stream-buffer-size. (default 32768) --stream_health_buffer_size uint max streaming health entries to buffer per streaming health client (default 20) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class - --table_gc_lifecycle string States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implcitly always included) (default "hold,purge,evac,drop") + --table_gc_lifecycle string States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implicitly always included) (default "hold,purge,evac,drop") --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. --tablet_filters strings Specifies a comma-separated list of 'keyspace|shard_name or keyrange' values to filter the tablets to watch. --tablet_health_keep_alive duration close streaming tablet health connection if there are no requests for this long (default 5m0s) --tablet_hostname string if not empty, this hostname will be assumed instead of trying to resolve it --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect @@ -348,8 +353,9 @@ Flags: --tablet_manager_protocol string Protocol to use to make tabletmanager RPCs to vttablets. (default "grpc") --tablet_refresh_interval duration Tablet refresh interval. (default 1m0s) --tablet_refresh_known_tablets Whether to reload the tablet's address/port map from topo in case they change. (default true) + --tablet_types_to_wait strings Wait till connected for specified tablet types during Gateway initialization. Should be provided as a comma-separated set of tablet types. --tablet_url_template string Format string describing debug tablet url formatting. See getTabletDebugURL() for how to customize this. (default "http://{{ "{{.GetTabletHostPort}}" }}") - --throttle_tablet_types string Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' aways implicitly included (default "replica") + --throttle_tablet_types string Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' always implicitly included (default "replica") --topo_consul_lock_delay duration LockDelay for consul session. (default 15s) --topo_consul_lock_session_checks string List of checks for consul session. (default "serfHealth") --topo_consul_lock_session_ttl string TTL for consul session. @@ -395,15 +401,12 @@ Flags: --unhealthy_threshold duration replication lag after which a replica is considered unhealthy (default 2h0m0s) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vreplication-parallel-insert-workers int Number of parallel insertion workers to use during copy phase. Set <= 1 to disable parallelism, or > 1 to enable concurrent insertion during copy phase. (default 1) --vreplication_copy_phase_duration duration Duration for each copy phase loop (before running the next catchup: default 1h) (default 1h0m0s) --vreplication_copy_phase_max_innodb_history_list_length int The maximum InnoDB transaction history that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 1000000) --vreplication_copy_phase_max_mysql_replication_lag int The maximum MySQL replication lag (in seconds) that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 43200) --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 3) - --vreplication_healthcheck_retry_delay duration healthcheck retry delay (default 5s) - --vreplication_healthcheck_timeout duration healthcheck retry delay (default 1m0s) - --vreplication_healthcheck_topology_refresh duration refresh interval for re-reading the topology (default 30s) --vreplication_heartbeat_update_interval int Frequency (in seconds, default 1, max 60) at which the time_updated column of a vreplication stream when idling (default 1) --vreplication_max_time_to_retry_on_error duration stop automatically retrying when we've had consecutive failures with the same error for this long after the first occurrence --vreplication_net_read_timeout int Session value of net_read_timeout for vreplication, in seconds (default 300) @@ -411,7 +414,6 @@ Flags: --vreplication_replica_lag_tolerance duration Replica lag threshold duration: once lag is below this we switch from copy phase to the replication (streaming) phase (default 1m0s) --vreplication_retry_delay duration delay before retrying a failed workflow event in the replication phase (default 5s) --vreplication_store_compressed_gtid Store compressed gtids in the pos column of the sidecar database's vreplication table - --vreplication_tablet_type string comma separated list of tablet types used as a source (default "in_order:REPLICA,PRIMARY") --vschema-persistence-dir string If set, per-keyspace vschema will be persisted in this directory and reloaded into the in-memory topology server across restarts. Bookkeeping is performed using a simple watcher goroutine. This is useful when running vtcombo as an application development container (e.g. vttestserver) where you want to keep the same vschema even if developer's machine reboots. This works in tandem with vttestserver's --persistent_mode flag. Needless to say, this is neither a perfect nor a production solution for vschema persistence. Consider using the --external_topo_server flag if you require a more complete solution. This flag is ignored if --external_topo_server is set. --vschema_ddl_authorized_users string List of users authorized to execute vschema ddl operations, or '%' to allow all users. --vstream-binlog-rotation-threshold int Byte size at which a VStreamer will attempt to rotate the source's open binary log before starting a GTID snapshot based stream (e.g. a ResultStreamer or RowStreamer) (default 67108864) diff --git a/go/flags/endtoend/vtctlclient.txt b/go/flags/endtoend/vtctlclient.txt index 7fa186acbd0..3c9c0a3cbb0 100644 --- a/go/flags/endtoend/vtctlclient.txt +++ b/go/flags/endtoend/vtctlclient.txt @@ -22,23 +22,26 @@ Usage of vtctlclient: --jaeger-agent-host string host and port to send spans to. if empty, no tracing will be done --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors + --log_link string If non-empty, add symbolic links in this directory to the log files --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) + --logbuflevel int Buffer log messages logged at this level or lower (-1 means don't buffer; 0 means buffer INFO only; ...). Has limited applicability on non-prod platforms. --logtostderr log to standard error instead of files --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --server string server to use for connection - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --tracer string tracing service to use (default "noop") --tracing-enable-logging whether to enable logging in the tracing service --tracing-sampling-rate float sampling rate for the probabilistic jaeger sampler (default 0.1) --tracing-sampling-type string sampling strategy to use for jaeger. possible values are 'const', 'probabilistic', 'rateLimiting', or 'remote' (default "const") --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vtctl_client_protocol string Protocol to use to talk to the vtctl server. (default "grpc") --vtctld_grpc_ca string the server ca to use to validate servers when connecting --vtctld_grpc_cert string the cert to use to connect diff --git a/go/flags/endtoend/vtctld.txt b/go/flags/endtoend/vtctld.txt index a9a5cebb0f3..62d819fb759 100644 --- a/go/flags/endtoend/vtctld.txt +++ b/go/flags/endtoend/vtctld.txt @@ -82,24 +82,28 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) -h, --help help for vtctld --jaeger-agent-host string host and port to send spans to. if empty, no tracing will be done --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) --logtostderr log to standard error instead of files --max-stack-size int configure the maximum stack size in bytes (default 67108864) + --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --onclose_timeout duration wait no more than this for OnClose handlers before stopping (default 10s) --onterm_timeout duration wait no more than this for OnTermSync handlers before stopping (default 10s) --opentsdb_uri string URI of opentsdb /api/put method --pid_file string If set, the process will write its pid to the named file, and delete it on graceful shutdown. --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --proxy_tablets Setting this true will make vtctld proxy the tablet status instead of redirecting to them --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --remote_operation_timeout duration time to wait for a remote operation (default 15s) @@ -126,7 +130,7 @@ Flags: --stats_common_tags strings Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2 --stats_drop_variables string Variables to be dropped from the list of exported variables. --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. --tablet_grpc_ca string the server ca to use to validate servers when connecting @@ -137,7 +141,7 @@ Flags: --tablet_health_keep_alive duration close streaming tablet health connection if there are no requests for this long (default 5m0s) --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect @@ -171,5 +175,5 @@ Flags: --tracing-sampling-type string sampling strategy to use for jaeger. possible values are 'const', 'probabilistic', 'rateLimiting', or 'remote' (default "const") --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vtctld_sanitize_log_messages When true, vtctld sanitizes logging. diff --git a/go/flags/endtoend/vtctldclient.txt b/go/flags/endtoend/vtctldclient.txt index 7fddb7eebfe..86c1bb3bfa2 100644 --- a/go/flags/endtoend/vtctldclient.txt +++ b/go/flags/endtoend/vtctldclient.txt @@ -1,4 +1,8 @@ Executes a cluster management command on the remote vtctld server. +If there are no running vtctld servers -- for example when bootstrapping +a new Vitess cluster -- you can specify a --server value of 'internal'. +When doing so, you would use the --topo* flags so that the client can +connect directly to the topo server(s). Usage: vtctldclient [flags] @@ -115,18 +119,23 @@ Flags: -h, --help help for vtctldclient --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory + --log_link string If non-empty, add symbolic links in this directory to the log files --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) + --logbuflevel int Buffer log messages logged at this level or lower (-1 means don't buffer; 0 means buffer INFO only; ...). Has limited applicability on non-prod platforms. --logtostderr log to standard error instead of files --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --server string server to use for the connection (required) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) + --topo-global-root string the path of the global topology data in the global topology server (default "/vitess/global") + --topo-global-server-address strings the address of the global topology server(s) (default [localhost:2379]) + --topo-implementation string the topology implementation to use (default "etcd2") -v, --v Level log level for V logs --version version for vtctldclient - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vtctl_client_protocol string Protocol to use to talk to the vtctl server. (default "grpc") --vtctld_grpc_ca string the server ca to use to validate servers when connecting --vtctld_grpc_cert string the cert to use to connect diff --git a/go/flags/endtoend/vtexplain.txt b/go/flags/endtoend/vtexplain.txt index f75559474c0..fdd289e63c7 100644 --- a/go/flags/endtoend/vtexplain.txt +++ b/go/flags/endtoend/vtexplain.txt @@ -54,7 +54,7 @@ Flags: --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --ks-shard-map string JSON map of keyspace name -> shard name -> ShardReference object. The inner map is the same as the output of FindAllShardsInKeyspace --ks-shard-map-file string File containing json blob of keyspace name -> shard name -> ShardReference object - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -64,6 +64,7 @@ Flags: --output-mode string Output in human-friendly text or json (default "text") --planner-version string Sets the default planner to use. Valid values are: Gen4, Gen4Greedy, Gen4Left2Right --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --replication-mode string The replication mode to simulate -- must be set to either ROW or STATEMENT (default "ROW") --schema string The SQL table schema @@ -74,9 +75,9 @@ Flags: --sql-file string Identifies the file that contains the SQL commands to analyze --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vschema string Identifies the VTGate routing schema --vschema-file string Identifies the VTGate routing schema file diff --git a/go/flags/endtoend/vtgate.txt b/go/flags/endtoend/vtgate.txt index 6bad7c768aa..bd9849cfd71 100644 --- a/go/flags/endtoend/vtgate.txt +++ b/go/flags/endtoend/vtgate.txt @@ -93,6 +93,8 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) --grpc_use_effective_callerid If set, and SSL is not used, will set the immediate caller id from the effective caller id's principal. --healthcheck_retry_delay duration health check retry delay (default 2ms) --healthcheck_timeout duration the health check timeout period (default 1m0s) @@ -105,7 +107,7 @@ Flags: --legacy_replication_lag_algorithm Use the legacy algorithm when selecting vttablets for serving. (default true) --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) --lock_heartbeat_time duration If there is lock function used. This will keep the lock connection active by using this heartbeat (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_queries_to_file string Enable query logging to the specified file @@ -163,6 +165,7 @@ Flags: --planner-version string Sets the default planner to use when the session has not changed it. Valid values are: Gen4, Gen4Greedy, Gen4Left2Right --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --proxy_protocol Enable HAProxy PROXY protocol on MySQL listener socket --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --query-timeout int Sets the default query timeout (in ms). Can be overridden by session variable (query_timeout) or comment directive (QUERY_TIMEOUT_MS) @@ -188,7 +191,7 @@ Flags: --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) --statsd_address string Address for statsd client --statsd_sample_rate float Sample rate for statsd metrics (default 1) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --stream_buffer_size int the number of bytes sent from vtgate for each stream call. It's recommended to keep this value in sync with vttablet's query-server-config-stream-buffer-size. (default 32768) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_filters strings Specifies a comma-separated list of 'keyspace|shard_name or keyrange' values to filter the tablets to watch. @@ -228,7 +231,7 @@ Flags: --truncate-error-len int truncate errors sent to client if they are longer than this value (0 means do not truncate) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vschema_ddl_authorized_users string List of users authorized to execute vschema ddl operations, or '%' to allow all users. --vtgate-config-terse-errors prevent bind vars from escaping in returned errors --warming-reads-concurrency int Number of concurrent warming reads allowed (default 500) diff --git a/go/flags/endtoend/vtgateclienttest.txt b/go/flags/endtoend/vtgateclienttest.txt index 4580d4d6ce7..e7d8fc5e177 100644 --- a/go/flags/endtoend/vtgateclienttest.txt +++ b/go/flags/endtoend/vtgateclienttest.txt @@ -40,11 +40,13 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) -h, --help help for vtgateclienttest --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -56,12 +58,13 @@ Flags: --pid_file string If set, the process will write its pid to the named file, and delete it on graceful shutdown. --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only) --service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vschema_ddl_authorized_users string List of users authorized to execute vschema ddl operations, or '%' to allow all users. diff --git a/go/flags/endtoend/vtorc.txt b/go/flags/endtoend/vtorc.txt index b13756e793c..8073323ec1c 100644 --- a/go/flags/endtoend/vtorc.txt +++ b/go/flags/endtoend/vtorc.txt @@ -50,7 +50,7 @@ Flags: --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -61,6 +61,7 @@ Flags: --pid_file string If set, the process will write its pid to the named file, and delete it on graceful shutdown. --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --prevent-cross-cell-failover Prevent VTOrc from promoting a primary in a different cell than the current primary in case of a failover --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --reasonable-replication-lag duration Maximum replication lag on replicas which is deemed to be acceptable (default 10s) @@ -76,16 +77,17 @@ Flags: --stats_common_tags strings Comma-separated list of common tags for the stats backend. It provides both label and values. Example: label1:value1,label2:value2 --stats_drop_variables string Variables to be dropped from the list of exported variables. --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect --tablet_manager_grpc_server_name string the server name to use to validate server certificate --tablet_manager_protocol string Protocol to use to make tabletmanager RPCs to vttablets. (default "grpc") + --tolerable-replication-lag duration Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS --topo-information-refresh-duration duration Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topology server (default 15s) --topo_consul_lock_delay duration LockDelay for consul session. (default 15s) --topo_consul_lock_session_checks string List of checks for consul session. (default "serfHealth") @@ -106,5 +108,5 @@ Flags: --topo_zk_tls_key string the key to use to connect to the zk topo server, enables TLS --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --wait-replicas-timeout duration Duration for which to wait for replica's to respond when issuing RPCs (default 30s) diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index 30fe5e41172..ac25179db9b 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -193,6 +193,8 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) --health_check_interval duration Interval between health checks (default 20s) --heartbeat_enable If true, vttablet records (if master) or checks (if replica) the current time of a replication heartbeat in the sidecar database's heartbeat table. The result is used to inform the serving state of the vttablet via healthchecks. --heartbeat_interval duration How frequently to read and write replication heartbeat. (default 1s) @@ -213,7 +215,7 @@ Flags: --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) --lock-timeout duration Maximum time for which a shard/keyspace lock can be acquired for (default 45s) --lock_tables_timeout duration How long to keep the table locked before timing out (default 1m0s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_queries Enable query logging to syslog. @@ -242,6 +244,7 @@ Flags: --mycnf_slow_log_path string mysql slow query log path --mycnf_socket_file string mysql socket file --mycnf_tmp_dir string mysql tmp directory + --mysql-shutdown-timeout duration timeout to use when MySQL is being shut down. (default 5m0s) --mysql_server_version string MySQL server version to advertise. (default "8.0.30-Vitess") --mysqlctl_mycnf_template string template file to use for generating the my.cnf file during server init --mysqlctl_socket string socket file to use for remote mysqlctl actions (empty for local actions) @@ -253,6 +256,7 @@ Flags: --pool_hostname_resolve_interval duration if set force an update to all hostnames and reconnect if changed, defaults to 0 (disabled) --port int port for the server --pprof strings enable profiling + --pprof-http enable pprof http endpoints --pt-osc-path string override default pt-online-schema-change binary full path --publish_retry_interval duration how long vttablet waits to retry publishing the tablet record (default 30s) --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) @@ -263,27 +267,27 @@ Flags: --queryserver-config-acl-exempt-acl string an acl that exempt from table acl checking (this acl is free to access any vitess tables). --queryserver-config-annotate-queries prefix queries to MySQL backend with comment indicating vtgate principal (user) and target tablet type --queryserver-config-enable-table-acl-dry-run If this flag is enabled, tabletserver will emit monitoring metrics and let the request pass regardless of table acl check results - --queryserver-config-idle-timeout duration query server idle timeout (in seconds), vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance. (default 30m0s) + --queryserver-config-idle-timeout duration query server idle timeout, vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance. (default 30m0s) --queryserver-config-max-result-size int query server max result size, maximum number of rows allowed to return from vttablet for non-streaming queries. (default 10000) --queryserver-config-message-postpone-cap int query server message postpone cap is the maximum number of messages that can be postponed at any given time. Set this number to substantially lower than transaction cap, so that the transaction pool isn't exhausted by the message subsystem. (default 4) --queryserver-config-olap-transaction-timeout duration query server transaction timeout (in seconds), after which a transaction in an OLAP session will be killed (default 30s) --queryserver-config-passthrough-dmls query server pass through all dml statements without rewriting - --queryserver-config-pool-conn-max-lifetime duration query server connection max lifetime (in seconds), vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool. (default 0s) + --queryserver-config-pool-conn-max-lifetime duration query server connection max lifetime, vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool. --queryserver-config-pool-size int query server read pool size, connection pool is used by regular queries (non streaming, not in a transaction) (default 16) --queryserver-config-query-cache-memory int query server query cache size in bytes, maximum amount of memory to be used for caching. vttablet analyzes every incoming query and generate a query plan, these plans are being cached in a lru cache. This config controls the capacity of the lru cache. (default 33554432) - --queryserver-config-query-pool-timeout duration query server query pool timeout (in seconds), it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead. (default 0s) + --queryserver-config-query-pool-timeout duration query server query pool timeout, it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead. --queryserver-config-query-pool-waiter-cap int query server query pool waiter limit, this is the maximum number of queries that can be queued waiting to get a connection (default 5000) - --queryserver-config-query-timeout duration query server query timeout (in seconds), this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed. (default 30s) + --queryserver-config-query-timeout duration query server query timeout, this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed. (default 30s) --queryserver-config-schema-change-signal query server schema signal, will signal connected vtgates that schema has changed whenever this is detected. VTGates will need to have -schema_change_signal enabled for this to work (default true) - --queryserver-config-schema-reload-time duration query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance in seconds. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time. (default 30m0s) + --queryserver-config-schema-reload-time duration query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time. (default 30m0s) --queryserver-config-stream-buffer-size int query server stream buffer size, the maximum number of bytes sent from vttablet for each stream call. It's recommended to keep this value in sync with vtgate's stream_buffer_size. (default 32768) --queryserver-config-stream-pool-size int query server stream connection pool size, stream pool is used by stream queries: queries that return results to client in a streaming fashion (default 200) - --queryserver-config-stream-pool-timeout duration query server stream pool timeout (in seconds), it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout. (default 0s) + --queryserver-config-stream-pool-timeout duration query server stream pool timeout, it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout. --queryserver-config-stream-pool-waiter-cap int query server stream pool waiter limit, this is the maximum number of streaming queries that can be queued waiting to get a connection --queryserver-config-strict-table-acl only allow queries that pass table acl checks --queryserver-config-terse-errors prevent bind vars from escaping in client error messages --queryserver-config-transaction-cap int query server transaction cap is the maximum number of transactions allowed to happen at any given point of a time for a single vttablet. E.g. by setting transaction cap to 100, there are at most 100 transactions will be processed by a vttablet and the 101th transaction will be blocked (and fail if it cannot get connection within specified timeout) (default 20) - --queryserver-config-transaction-timeout duration query server transaction timeout (in seconds), a transaction will be killed if it takes longer than this value (default 30s) + --queryserver-config-transaction-timeout duration query server transaction timeout, a transaction will be killed if it takes longer than this value (default 30s) --queryserver-config-truncate-error-len int truncate errors sent to client if they are longer than this value (0 means do not truncate) --queryserver-config-txpool-timeout duration query server transaction pool timeout, it is how long vttablet waits if tx pool is full (default 1s) --queryserver-config-txpool-waiter-cap int query server transaction pool waiter limit, this is the maximum number of transactions that can be queued waiting to get a connection (default 5000) @@ -318,7 +322,7 @@ Flags: --service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice --serving_state_grace_period duration how long to pause after broadcasting health to vtgate, before enforcing a new serving state --shard_sync_retry_delay duration delay between retries of updates to keep the tablet and its shard record in sync (default 30s) - --shutdown_grace_period duration how long to wait (in seconds) for queries and transactions to complete during graceful shutdown. (default 0s) + --shutdown_grace_period duration how long to wait for queries and transactions to complete during graceful shutdown. --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512) --srv_topo_cache_refresh duration how frequently to refresh the topology for cached entries (default 1s) @@ -331,12 +335,12 @@ Flags: --stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s) --statsd_address string Address for statsd client --statsd_sample_rate float Sample rate for statsd metrics (default 1) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --stream_health_buffer_size uint max streaming health entries to buffer per streaming health client (default 20) --table-acl-config string path to table access checker config file; send SIGHUP to reload this file --table-acl-config-reload-interval duration Ticker to reload ACLs. Duration flag, format e.g.: 30s. Default: do not reload --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class - --table_gc_lifecycle string States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implcitly always included) (default "hold,purge,evac,drop") + --table_gc_lifecycle string States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implicitly always included) (default "hold,purge,evac,drop") --tablet-path string tablet alias --tablet_config string YAML file config for tablet --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. @@ -348,14 +352,14 @@ Flags: --tablet_hostname string if not empty, this hostname will be assumed instead of trying to resolve it --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect --tablet_manager_grpc_server_name string the server name to use to validate server certificate --tablet_manager_protocol string Protocol to use to make tabletmanager RPCs to vttablets. (default "grpc") --tablet_protocol string Protocol to use to make queryservice RPCs to vttablets. (default "grpc") - --throttle_tablet_types string Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' aways implicitly included (default "replica") + --throttle_tablet_types string Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' always implicitly included (default "replica") --topo_consul_lock_delay duration LockDelay for consul session. (default 15s) --topo_consul_lock_session_checks string List of checks for consul session. (default "serfHealth") --topo_consul_lock_session_ttl string TTL for consul session. @@ -400,15 +404,12 @@ Flags: --unhealthy_threshold duration replication lag after which a replica is considered unhealthy (default 2h0m0s) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vreplication-parallel-insert-workers int Number of parallel insertion workers to use during copy phase. Set <= 1 to disable parallelism, or > 1 to enable concurrent insertion during copy phase. (default 1) --vreplication_copy_phase_duration duration Duration for each copy phase loop (before running the next catchup: default 1h) (default 1h0m0s) --vreplication_copy_phase_max_innodb_history_list_length int The maximum InnoDB transaction history that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 1000000) --vreplication_copy_phase_max_mysql_replication_lag int The maximum MySQL replication lag (in seconds) that can exist on a vstreamer (source) before starting another round of copying rows. This helps to limit the impact on the source tablet. (default 43200) --vreplication_experimental_flags int (Bitmask) of experimental features in vreplication to enable (default 3) - --vreplication_healthcheck_retry_delay duration healthcheck retry delay (default 5s) - --vreplication_healthcheck_timeout duration healthcheck retry delay (default 1m0s) - --vreplication_healthcheck_topology_refresh duration refresh interval for re-reading the topology (default 30s) --vreplication_heartbeat_update_interval int Frequency (in seconds, default 1, max 60) at which the time_updated column of a vreplication stream when idling (default 1) --vreplication_max_time_to_retry_on_error duration stop automatically retrying when we've had consecutive failures with the same error for this long after the first occurrence --vreplication_net_read_timeout int Session value of net_read_timeout for vreplication, in seconds (default 300) @@ -416,7 +417,6 @@ Flags: --vreplication_replica_lag_tolerance duration Replica lag threshold duration: once lag is below this we switch from copy phase to the replication (streaming) phase (default 1m0s) --vreplication_retry_delay duration delay before retrying a failed workflow event in the replication phase (default 5s) --vreplication_store_compressed_gtid Store compressed gtids in the pos column of the sidecar database's vreplication table - --vreplication_tablet_type string comma separated list of tablet types used as a source (default "in_order:REPLICA,PRIMARY") --vstream-binlog-rotation-threshold int Byte size at which a VStreamer will attempt to rotate the source's open binary log before starting a GTID snapshot based stream (e.g. a ResultStreamer or RowStreamer) (default 67108864) --vstream_dynamic_packet_size Enable dynamic packet sizing for VReplication. This will adjust the packet size during replication to improve performance. (default true) --vstream_packet_size int Suggested packet size for VReplication streamer. This is used only as a recommendation. The actual packet size may be more or less than this amount. (default 250000) diff --git a/go/flags/endtoend/vttestserver.txt b/go/flags/endtoend/vttestserver.txt index fb9c42d932a..d3af635e353 100644 --- a/go/flags/endtoend/vttestserver.txt +++ b/go/flags/endtoend/vttestserver.txt @@ -69,13 +69,15 @@ Flags: --grpc_server_initial_window_size int gRPC server initial window size --grpc_server_keepalive_enforcement_policy_min_time duration gRPC server minimum keepalive time (default 10s) --grpc_server_keepalive_enforcement_policy_permit_without_stream gRPC server permit client keepalive pings even when there are no active streams (RPCs) + --grpc_server_keepalive_time duration After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive. (default 10s) + --grpc_server_keepalive_timeout duration After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed. (default 10s) -h, --help help for vttestserver --initialize_with_random_data If this flag is each table-shard will be initialized with random data. See also the 'rng_seed' and 'min_shard_size' and 'max_shard_size' flags. --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) --keyspaces strings Comma separated list of keyspaces (default [test_keyspace]) --lameduck-period duration keep running at least this long after SIGTERM before stopping (default 50ms) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) @@ -99,6 +101,7 @@ Flags: --pool_hostname_resolve_interval duration if set force an update to all hostnames and reconnect if changed, defaults to 0 (disabled) --port int Port to use for vtcombo. If this is 0, a random port will be chosen. --pprof strings enable profiling + --pprof-http enable pprof http endpoints --proto_topo string Define the fake cluster topology as a compact text format encoded vttest proto. See vttest.proto for more information. --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) --queryserver-config-transaction-timeout float query server transaction timeout (in seconds), a transaction will be killed if it takes longer than this value @@ -112,13 +115,13 @@ Flags: --snapshot_file string A MySQL DB snapshot file --sql-max-length-errors int truncate queries in error logs to the given length (default unlimited) --sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class --tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid. --tablet_hostname string The hostname to use for the tablet otherwise it will be derived from OS' hostname (default "localhost") --tablet_manager_grpc_ca string the server ca to use to validate servers when connecting --tablet_manager_grpc_cert string the cert to use to connect - --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App}) (default 8) + --tablet_manager_grpc_concurrency int concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler) (default 8) --tablet_manager_grpc_connpool_size int number of tablets to keep tmclient connections open to (default 100) --tablet_manager_grpc_crl string the server crl to use to validate server certificates when connecting --tablet_manager_grpc_key string the key to use to connect @@ -138,8 +141,9 @@ Flags: --transaction_mode string Transaction mode MULTI (default), SINGLE or TWOPC (default "MULTI") --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --vschema_ddl_authorized_users string Comma separated list of users authorized to execute vschema ddl operations via vtgate + --vtcombo-bind-host string which host to bind vtcombo servenv listener to (default "localhost") --vtctl_client_protocol string Protocol to use to talk to the vtctl server. (default "grpc") --vtctld_grpc_ca string the server ca to use to validate servers when connecting --vtctld_grpc_cert string the cert to use to connect diff --git a/go/flags/endtoend/zkctl.txt b/go/flags/endtoend/zkctl.txt index d1aea061ea5..b89528766d8 100644 --- a/go/flags/endtoend/zkctl.txt +++ b/go/flags/endtoend/zkctl.txt @@ -22,17 +22,18 @@ Flags: -h, --help help for zkctl --keep_logs duration keep logs for this long (using ctime) (zero to keep forever) --keep_logs_by_mtime duration keep logs for this long (using mtime) (zero to keep forever) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_backtrace_at traceLocations when logging hits line file:N, emit a stack trace --log_dir string If non-empty, write log files in this directory --log_err_stacks log stack traces for errors --log_rotate_max_size uint size in bytes at which logs are rotated (glog.MaxSize) (default 1887436800) --logtostderr log to standard error instead of files --pprof strings enable profiling + --pprof-http enable pprof http endpoints --purge_logs_interval duration how often try to remove old logs (default 1h0m0s) - --stderrthreshold severity logs at or above this threshold go to stderr (default 1) + --stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1) --v Level log level for V logs -v, --version print binary version - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging + --vmodule vModuleFlag comma-separated list of pattern=N settings for file-filtered logging --zk.cfg string zkid@server1:leaderPort1:electionPort1:clientPort1,...) (default "6@:3801:3802:3803") --zk.extra stringArray extra config line(s) to append verbatim to config (flag can be specified more than once) --zk.myid uint which server do you want to be? only needed when running multiple instance on one box, otherwise myid is implied by hostname diff --git a/go/flags/endtoend/zkctld.txt b/go/flags/endtoend/zkctld.txt index d808bd7ce67..20371e9e2d7 100644 --- a/go/flags/endtoend/zkctld.txt +++ b/go/flags/endtoend/zkctld.txt @@ -4,4 +4,5 @@ Usage: zkctld [flags] Flags: - -h, --help help for zkctld + -h, --help help for zkctld + -v, --version version for zkctld diff --git a/go/flagutil/deprecated_float64_seconds.go b/go/flagutil/deprecated_float64_seconds.go deleted file mode 100644 index d9afb11aaa2..00000000000 --- a/go/flagutil/deprecated_float64_seconds.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2023 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flagutil - -import ( - "strconv" - "time" - - "vitess.io/vitess/go/vt/log" -) - -type DeprecatedFloat64Seconds struct { - name string - val time.Duration -} - -var _ Value[time.Duration] = (*DeprecatedFloat64Seconds)(nil) - -func NewDeprecatedFloat64Seconds(name string, defVal time.Duration) DeprecatedFloat64Seconds { - return DeprecatedFloat64Seconds{ - name: name, - val: defVal, - } -} - -func (f *DeprecatedFloat64Seconds) String() string { return f.val.String() } -func (f *DeprecatedFloat64Seconds) Type() string { return "duration" } - -func (f *DeprecatedFloat64Seconds) Set(arg string) error { - v, err := time.ParseDuration(arg) - if err != nil { - log.Warningf("failed to parse %s as duration (err: %v); falling back to parsing to %s as seconds. this is deprecated and will be removed in a future release", f.name, err, f.val) - - n, err := strconv.ParseFloat(arg, 64) - if err != nil { - return err - } - - v = time.Duration(n * float64(time.Second)) - } - - f.val = v - return nil -} - -func (f DeprecatedFloat64Seconds) Clone() DeprecatedFloat64Seconds { - return DeprecatedFloat64Seconds{ - name: f.name, - val: f.val, - } -} - -func (f DeprecatedFloat64Seconds) Name() string { return f.name } -func (f DeprecatedFloat64Seconds) Get() time.Duration { return f.val } - -func (f *DeprecatedFloat64Seconds) UnmarshalJSON(data []byte) error { - return f.Set(string(data)) -} diff --git a/go/flagutil/enum.go b/go/flagutil/enum.go index 5bc279ee493..1a571aa63a1 100644 --- a/go/flagutil/enum.go +++ b/go/flagutil/enum.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -86,11 +86,12 @@ func newStringEnum(name string, initialValue string, choices []string, caseInsen } return &StringEnum{ - name: name, - val: initialValue, - choices: choiceMap, - choiceNames: choiceNames, - choiceMapper: choiceMapper, + name: name, + val: initialValue, + choices: choiceMap, + choiceNames: choiceNames, + choiceMapper: choiceMapper, + caseInsensitive: caseInsensitive, } } diff --git a/go/flagutil/enum_test.go b/go/flagutil/enum_test.go new file mode 100644 index 00000000000..337710678db --- /dev/null +++ b/go/flagutil/enum_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flagutil + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStringEnum(t *testing.T) { + tests := []struct { + name string + initialValue string + choices []string + caseInsensitive bool + secondValue string + expectedErr string + }{ + { + name: "valid set call", + initialValue: "mango", + choices: []string{"apple", "mango", "kiwi"}, + caseInsensitive: true, + secondValue: "kiwi", + }, + { + name: "invalid set call", + initialValue: "apple", + choices: []string{"apple", "mango", "kiwi"}, + caseInsensitive: false, + secondValue: "banana", + expectedErr: "invalid choice for enum (valid choices: [apple kiwi mango])", + }, + { + name: "invalid set call case insensitive", + initialValue: "apple", + choices: []string{"apple", "kiwi"}, + caseInsensitive: true, + secondValue: "banana", + expectedErr: "invalid choice for enum (valid choices: [apple kiwi] [case insensitive])", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var enum *StringEnum + if tt.caseInsensitive { + enum = NewCaseInsensitiveStringEnum(tt.name, tt.initialValue, tt.choices) + } else { + enum = NewStringEnum(tt.name, tt.initialValue, tt.choices) + } + + require.Equal(t, "string", enum.Type()) + err := enum.Set(tt.secondValue) + if tt.expectedErr == "" { + require.NoError(t, err) + require.Equal(t, tt.secondValue, enum.String()) + } else { + require.ErrorContains(t, err, tt.expectedErr) + require.Equal(t, tt.initialValue, enum.String()) + } + }) + } +} diff --git a/go/flagutil/flagutil.go b/go/flagutil/flagutil.go index ebf4ccef485..28d0b54e4ec 100644 --- a/go/flagutil/flagutil.go +++ b/go/flagutil/flagutil.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/flagutil/flagutil_test.go b/go/flagutil/flagutil_test.go index f95c46a53f7..dda17bb13d7 100644 --- a/go/flagutil/flagutil_test.go +++ b/go/flagutil/flagutil_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -21,6 +21,7 @@ import ( "testing" "github.com/spf13/pflag" + "github.com/stretchr/testify/require" ) func TestStringList(t *testing.T) { @@ -106,3 +107,17 @@ func TestStringMap(t *testing.T) { } } } + +func TestStringListValue(t *testing.T) { + strListVal := StringListValue{"temp", "val"} + require.Equal(t, []string([]string{"temp", "val"}), strListVal.Get()) + require.Equal(t, "strings", strListVal.Type()) +} + +func TestStringMapValue(t *testing.T) { + strMapVal := StringMapValue{ + "key": "val", + } + require.Equal(t, "StringMap", strMapVal.Type()) + require.Equal(t, map[string]string(map[string]string{"key": "val"}), strMapVal.Get()) +} diff --git a/go/flagutil/optional_test.go b/go/flagutil/optional_test.go new file mode 100644 index 00000000000..b7a35a8786e --- /dev/null +++ b/go/flagutil/optional_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flagutil + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewOptionalFloat64(t *testing.T) { + fl := NewOptionalFloat64(4.187) + require.NotEmpty(t, fl) + require.Equal(t, false, fl.IsSet()) + + require.Equal(t, "4.187", fl.String()) + require.Equal(t, "float64", fl.Type()) + + err := fl.Set("invalid value") + require.ErrorContains(t, err, "parse error") + + err = fl.Set("7.77") + require.NoError(t, err) + require.Equal(t, 7.77, fl.Get()) + require.Equal(t, true, fl.IsSet()) + + err = fl.Set("1e1000") + require.ErrorContains(t, err, "value out of range") +} + +func TestNewOptionalString(t *testing.T) { + optStr := NewOptionalString("4.187") + require.NotEmpty(t, optStr) + require.Equal(t, false, optStr.IsSet()) + + require.Equal(t, "4.187", optStr.String()) + require.Equal(t, "string", optStr.Type()) + + err := optStr.Set("value") + require.NoError(t, err) + + require.Equal(t, "value", optStr.Get()) + require.Equal(t, true, optStr.IsSet()) +} diff --git a/go/flagutil/sets_test.go b/go/flagutil/sets_test.go new file mode 100644 index 00000000000..0c07f5b63b7 --- /dev/null +++ b/go/flagutil/sets_test.go @@ -0,0 +1,54 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flagutil + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStringSetFlag(t *testing.T) { + strSetFlag := StringSetFlag{} + set := strSetFlag.ToSet() + require.Empty(t, set) + + set = set.Insert("mango", "apple", "mango") + strSetFlag.set = set + + require.Equal(t, "StringSetFlag", strSetFlag.Type()) + require.Equal(t, "apple, mango", strSetFlag.String()) + + err := strSetFlag.Set("guvava") + require.NoError(t, err) + require.Equal(t, "apple, guvava, mango", strSetFlag.String()) + + require.NotEmpty(t, strSetFlag.ToSet()) +} + +func TestStringSetFlagWithEmptySet(t *testing.T) { + strSetFlag := StringSetFlag{} + require.Equal(t, "", strSetFlag.String()) + + err := strSetFlag.Set("tmp") + require.NoError(t, err) + require.Empty(t, strSetFlag.ToSet()) + + err = strSetFlag.Set("guvava") + require.NoError(t, err) + require.Equal(t, "guvava", strSetFlag.String()) +} diff --git a/go/hack/runtime.go b/go/hack/runtime.go index 5f6b946e33d..83428504818 100644 --- a/go/hack/runtime.go +++ b/go/hack/runtime.go @@ -22,21 +22,10 @@ import ( "unsafe" ) -//go:noescape -//go:linkname memhash runtime.memhash -func memhash(p unsafe.Pointer, h, s uintptr) uintptr - //go:noescape //go:linkname strhash runtime.strhash func strhash(p unsafe.Pointer, h uintptr) uintptr -// RuntimeMemhash provides access to the Go runtime's default hash function for arbitrary bytes. -// This is an optimal hash function which takes an input seed and is potentially implemented in hardware -// for most architectures. This is the same hash function that the language's `map` uses. -func RuntimeMemhash(b []byte, seed uint64) uint64 { - return uint64(memhash(unsafe.Pointer(unsafe.SliceData(b)), uintptr(seed), uintptr(len(b)))) -} - // RuntimeStrhash provides access to the Go runtime's default hash function for strings. // This is an optimal hash function which takes an input seed and is potentially implemented in hardware // for most architectures. This is the same hash function that the language's `map` uses. diff --git a/go/history/history.go b/go/history/history.go index 94af347ec52..f38be9239e0 100644 --- a/go/history/history.go +++ b/go/history/history.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/history/history_test.go b/go/history/history_test.go index 8b0559bd47e..3e817b34bb2 100644 --- a/go/history/history_test.go +++ b/go/history/history_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/json2/marshal_test.go b/go/json2/marshal_test.go index 96b7f508d73..b155126fb17 100644 --- a/go/json2/marshal_test.go +++ b/go/json2/marshal_test.go @@ -19,6 +19,9 @@ package json2 import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + querypb "vitess.io/vitess/go/vt/proto/query" vschemapb "vitess.io/vitess/go/vt/proto/vschema" ) @@ -29,11 +32,21 @@ func TestMarshalPB(t *testing.T) { Type: querypb.Type_VARCHAR, } b, err := MarshalPB(col) - if err != nil { - t.Fatal(err) - } + + require.NoErrorf(t, err, "MarshalPB(%+v) error", col) want := "{\"name\":\"c1\",\"type\":\"VARCHAR\"}" - if string(b) != want { - t.Errorf("MarshalPB(col): %q, want %q", b, want) + assert.Equalf(t, want, string(b), "MarshalPB(%+v)", col) +} + +func TestMarshalIndentPB(t *testing.T) { + col := &vschemapb.Column{ + Name: "c1", + Type: querypb.Type_VARCHAR, } + indent := " " + b, err := MarshalIndentPB(col, indent) + + require.NoErrorf(t, err, "MarshalIndentPB(%+v, %q) error", col, indent) + want := "{\n \"name\": \"c1\",\n \"type\": \"VARCHAR\"\n}" + assert.Equal(t, want, string(b), "MarshalIndentPB(%+v, %q)", col, indent) } diff --git a/go/json2/unmarshal_test.go b/go/json2/unmarshal_test.go index 9b6a6af1ca2..ff18a29def8 100644 --- a/go/json2/unmarshal_test.go +++ b/go/json2/unmarshal_test.go @@ -17,7 +17,14 @@ limitations under the License. package json2 import ( + "fmt" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/emptypb" ) func TestUnmarshal(t *testing.T) { @@ -37,14 +44,50 @@ func TestUnmarshal(t *testing.T) { err: "", }} for _, tcase := range tcases { - out := make(map[string]any) + out := make(map[string]interface{}) err := Unmarshal([]byte(tcase.in), &out) + got := "" if err != nil { got = err.Error() } - if got != tcase.err { - t.Errorf("Unmarshal(%v) err: %v, want %v", tcase.in, got, tcase.err) - } + assert.Equal(t, tcase.err, got, "Unmarshal(%v) err", tcase.in) + } +} + +func TestUnmarshalProto(t *testing.T) { + protoData := &emptypb.Empty{} + protoJSONData, err := protojson.Marshal(protoData) + assert.Nil(t, err, "protojson.Marshal error") + + tcase := struct { + in string + out *emptypb.Empty + }{ + in: string(protoJSONData), + out: &emptypb.Empty{}, + } + + err = Unmarshal([]byte(tcase.in), tcase.out) + + assert.Nil(t, err, "Unmarshal(%v) protobuf message", tcase.in) + assert.Equal(t, protoData, tcase.out, "Unmarshal(%v) protobuf message result", tcase.in) +} + +func TestAnnotate(t *testing.T) { + tcases := []struct { + data []byte + err error + }{ + { + data: []byte("invalid JSON"), + err: fmt.Errorf("line: 1, position 1: invalid character 'i' looking for beginning of value"), + }, + } + + for _, tcase := range tcases { + err := annotate(tcase.data, tcase.err) + + require.Equal(t, tcase.err, err, "annotate(%s, %v) error", string(tcase.data), tcase.err) } } diff --git a/go/list/list_test.go b/go/list/list_test.go new file mode 100644 index 00000000000..16d23d6cc8e --- /dev/null +++ b/go/list/list_test.go @@ -0,0 +1,135 @@ +/* +Copyright 2024 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package list + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInitEmptyList(t *testing.T) { + l := New[int]() + assert.Equal(t, 0, l.Len()) + assert.Nil(t, l.Front()) + assert.Nil(t, l.Back()) +} + +func TestInsertFront(t *testing.T) { + l := New[int]() + e := l.PushFront(1) + assert.Equal(t, 1, l.Len()) + assert.Equal(t, e, l.Front()) + assert.Equal(t, e, l.Back()) +} + +func TestInsertBack(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + assert.Equal(t, 1, l.Len()) + assert.Equal(t, e, l.Front()) + assert.Equal(t, e, l.Back()) +} + +func TestInsertFrontEmptyList(t *testing.T) { + l := New[int]() + e := l.PushFront(1) + assert.Equal(t, 1, l.Len()) + assert.Equal(t, e, l.Front()) + assert.Equal(t, e, l.Back()) +} + +func TestInsertBackEmptyList(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + assert.Equal(t, 1, l.Len()) + assert.Equal(t, e, l.Front()) + assert.Equal(t, e, l.Back()) +} + +func TestRemoveOnlyElement(t *testing.T) { + l := New[int]() + e := l.PushFront(1) + l.Remove(e) + assert.Equal(t, 0, l.Len()) + assert.Nil(t, l.Front()) + assert.Nil(t, l.Back()) +} + +func TestRemoveFromWrongList(t *testing.T) { + l1 := New[int]() + l2 := New[int]() + e := l1.PushFront(1) + assert.Panics(t, func() { l2.Remove(e) }) +} + +func TestGetFirstElement(t *testing.T) { + l := New[int]() + e := l.PushFront(1) + assert.Equal(t, e, l.Front()) +} + +func TestGetLastElement(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + assert.Equal(t, e, l.Back()) +} + +func TestGetNextElement(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + assert.Nil(t, e.Next()) + f := l.PushBack(2) + assert.Equal(t, f, e.Next()) +} + +func TestGetPrevElement(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + assert.Nil(t, e.Prev()) + f := l.PushBack(2) + assert.Equal(t, e, f.Prev()) +} + +func TestMoveElement(t *testing.T) { + l := New[int]() + e := l.PushBack(1) + l.move(e, e) + assert.Equal(t, e, l.Front()) + f := l.PushBack(2) + l.move(e, f) + assert.Equal(t, f, l.Front()) + assert.Equal(t, e, l.Back()) + assert.Equal(t, e, f.next) +} + +func TestPushBackValue(t *testing.T) { + l := New[int]() + m := New[int]() + a := m.PushBack(5) + e := l.PushBack(1) + l.PushBackValue(a) + assert.Equal(t, a, l.Back()) + assert.Equal(t, a, e.next) +} + +func TestPushFrontValue(t *testing.T) { + l := New[int]() + m := New[int]() + a := m.PushBack(5) + e := l.PushBack(1) + l.PushFrontValue(a) + assert.Equal(t, a, l.Front()) + assert.Equal(t, a, e.prev) +} diff --git a/go/mathstats/beta_test.go b/go/mathstats/beta_test.go index 2878493a57d..524beda7fcd 100644 --- a/go/mathstats/beta_test.go +++ b/go/mathstats/beta_test.go @@ -5,7 +5,10 @@ package mathstats import ( + "math" "testing" + + "github.com/stretchr/testify/assert" ) func TestBetaInc(t *testing.T) { @@ -26,3 +29,27 @@ func TestBetaInc(t *testing.T) { 10: 0.01928710937500, }) } + +func TestBetaincPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + assert.Contains(t, r, "betainc: a or b too big; failed to converge") + } else { + t.Error("Expected panic, but no panic occurred") + } + }() + + a := 1e30 + b := 1e30 + x := 0.5 + + _ = mathBetaInc(x, a, b) +} + +func TestMathBetaIncNaN(t *testing.T) { + x := -0.1 + + result := mathBetaInc(x, 2.0, 3.0) + + assert.True(t, math.IsNaN(result), "Expected NaN for x < 0, got %v", result) +} diff --git a/go/mathstats/sample_test.go b/go/mathstats/sample_test.go index fb9d6dbc6ee..c0da3c2b7f4 100644 --- a/go/mathstats/sample_test.go +++ b/go/mathstats/sample_test.go @@ -4,7 +4,12 @@ package mathstats -import "testing" +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) func TestSamplePercentile(t *testing.T) { s := Sample{Xs: []float64{15, 20, 35, 40, 50}} @@ -19,3 +24,206 @@ func TestSamplePercentile(t *testing.T) { 2: 50, }) } + +func TestSamplePercentileEmpty(t *testing.T) { + s := Sample{Xs: []float64{}} + assert.True(t, math.IsNaN(s.Percentile(0.5)), "Percentile should return NaN for empty sample") +} + +func TestSampleStdDev(t *testing.T) { + values := []float64{2, 4, 4, 4, 5, 5, 7, 9} + expected := 2.138089935299395 + + sample := Sample{Xs: values} + result := sample.StdDev() + + assert.Equal(t, expected, result) +} + +func TestBounds(t *testing.T) { + tt := []struct { + xs []float64 + min float64 + max float64 + }{ + {[]float64{15, 20, 35, 40, 50}, 15, 50}, + {[]float64{}, math.NaN(), math.NaN()}, + {[]float64{10, 20, 5, 30, 15}, 5, 30}, + } + + for _, tc := range tt { + min, max := Bounds(tc.xs) + + if len(tc.xs) == 0 { + assert.True(t, math.IsNaN(min), "min value should be NaN") + assert.True(t, math.IsNaN(max), "max value should be NaN") + } else { + assert.Equal(t, tc.min, min, "min value mismatch") + assert.Equal(t, tc.max, max, "max value mismatch") + } + } +} + +func TestSampleBounds(t *testing.T) { + tt := []struct { + sample Sample + min float64 + max float64 + }{ + {Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: false}, 15, 50}, + {Sample{Xs: []float64{}, Sorted: false}, math.NaN(), math.NaN()}, + {Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: true}, 15, 50}, + } + + for _, tc := range tt { + min, max := tc.sample.Bounds() + + if len(tc.sample.Xs) == 0 { + assert.True(t, math.IsNaN(min), "min value should be NaN") + assert.True(t, math.IsNaN(max), "max value should be NaN") + } else { + assert.Equal(t, tc.min, min, "min value mismatch") + assert.Equal(t, tc.max, max, "max value mismatch") + } + } +} + +func TestVecSum(t *testing.T) { + tt := []struct { + xs []float64 + sum float64 + }{ + {[]float64{15, 20, 35, 40, 50}, 160}, + {[]float64{}, 0}, + } + + for _, tc := range tt { + sum := vecSum(tc.xs) + assert.Equal(t, tc.sum, sum, "sum value mismatch") + } +} + +func TestSampleSum(t *testing.T) { + tt := []struct { + sample Sample + sum float64 + }{ + {Sample{Xs: []float64{15, 20, 35, 40, 50}}, 160}, + {Sample{Xs: []float64{}}, 0}, + } + + for _, tc := range tt { + sum := tc.sample.Sum() + assert.Equal(t, tc.sum, sum, "sum value mismatch") + } +} + +func TestMean(t *testing.T) { + tt := []struct { + xs []float64 + expected float64 + }{ + {[]float64{1, 2, 3, 4, 5}, 3}, + {[]float64{-1, 0, 1}, 0}, + {[]float64{}, math.NaN()}, + {[]float64{10}, 10}, + {[]float64{-2, 2, -2, 2}, 0}, + } + + for _, tc := range tt { + mean := Mean(tc.xs) + + if math.IsNaN(tc.expected) { + assert.True(t, math.IsNaN(mean), "Expected NaN") + } else { + assert.Equal(t, tc.expected, mean, "Mean value mismatch") + } + } +} +func TestSampleCopy(t *testing.T) { + s := Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: true} + copySample := s.Copy() + + // Modify the original sample and check if the copy remains unchanged + s.Xs[0] = 100 + + assert.NotEqual(t, s.Xs[0], copySample.Xs[0], "Original and copied samples should not share data") + assert.Equal(t, len(s.Xs), len(copySample.Xs), "Length of original and copied samples should be the same") + assert.Equal(t, s.Sorted, copySample.Sorted, "Sorting status should be the same") +} + +func TestSampleFilterOutliers(t *testing.T) { + s := Sample{Xs: []float64{15, 20, 35, 40, 50, 100, 200}} + s.FilterOutliers() + + expected := []float64{15, 20, 35, 40, 50, 100} + assert.Equal(t, expected, s.Xs, "FilterOutliers should remove outliers") +} + +func TestSampleClear(t *testing.T) { + s := Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: true} + s.Clear() + + assert.Empty(t, s.Xs, "Clear should reset the sample to contain 0 values") + assert.False(t, s.Sorted, "Sorting status should be false after clearing") +} + +func TestSampleSort(t *testing.T) { + tt := []struct { + sample Sample + expected []float64 + }{ + {Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: false}, []float64{15, 20, 35, 40, 50}}, + {Sample{Xs: []float64{}, Sorted: false}, []float64{}}, + {Sample{Xs: []float64{15, 20, 35, 40, 50}, Sorted: true}, []float64{15, 20, 35, 40, 50}}, + {Sample{Xs: []float64{10, 5, 30, 20, 15}, Sorted: false}, []float64{5, 10, 15, 20, 30}}, + } + + for _, tc := range tt { + sortedSample := tc.sample.Sort() + + assert.Equal(t, tc.expected, sortedSample.Xs, "Sorted values mismatch") + } +} + +func TestGeoMean(t *testing.T) { + tt := []struct { + name string + values []float64 + expected float64 + }{ + { + name: "Valid_case", + values: []float64{2, 4, 8, 16}, + expected: 5.65685424949238, + }, + { + name: "Empty_values", + values: []float64{}, + expected: math.NaN(), + }, + { + name: "Zero_value", + values: []float64{1, 0, 3}, + expected: math.NaN(), + }, + { + name: "Negative_value", + values: []float64{2, -4, 8, 16}, + expected: math.NaN(), + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + sample := Sample{Xs: tc.values} + result := sample.GeoMean() + + if math.IsNaN(tc.expected) { + assert.True(t, math.IsNaN(result)) + } else { + assert.Equal(t, tc.expected, result) + } + }) + } +} diff --git a/go/mathstats/tdist_test.go b/go/mathstats/tdist_test.go index b30ba95662b..e243126e47b 100644 --- a/go/mathstats/tdist_test.go +++ b/go/mathstats/tdist_test.go @@ -4,7 +4,12 @@ package mathstats -import "testing" +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) func TestT(t *testing.T) { testFunc(t, "PDF(%v|v=1)", TDist{1}.PDF, map[float64]float64{ @@ -93,3 +98,17 @@ func TestT(t *testing.T) { 8: 0.99975354666971372, 9: 0.9998586600128780}) } +func TestCDFNan(t *testing.T) { + tDist := TDist{V: 1} + + result := tDist.CDF(math.NaN()) + assert.True(t, math.IsNaN(result), "CDF(NaN) = %v, expected NaN", result) +} + +func TestBounds_tdist(t *testing.T) { + tDist := TDist{V: 1} + + lower, upper := tDist.Bounds() + assert.Equal(t, -4.0, lower, "Lower bound should be -4") + assert.Equal(t, 4.0, upper, "Upper bound should be 4") +} diff --git a/go/mathstats/ttest_test.go b/go/mathstats/ttest_test.go index 0c9b78fdb9f..9c23a24ec29 100644 --- a/go/mathstats/ttest_test.go +++ b/go/mathstats/ttest_test.go @@ -4,7 +4,11 @@ package mathstats -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestTTest(t *testing.T) { s1 := Sample{Xs: []float64{2, 1, 3, 4}} @@ -69,3 +73,160 @@ func TestTTest(t *testing.T) { }, 4, 0, 0, 3, 0.5, 1, 0.5) } + +func TestTwoSampleTTestErrors(t *testing.T) { + tt := []struct { + name string + x1 TTestSample + x2 TTestSample + alt LocationHypothesis + err error + }{ + { + name: "One sample size is 0", + x1: &Sample{Xs: []float64{1, 2, 3}}, + x2: &Sample{Xs: []float64{}}, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "Both sample sizes are 0", + x1: &Sample{Xs: []float64{}}, + x2: &Sample{Xs: []float64{}}, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "One sample has zero variance", + x1: &Sample{Xs: []float64{1}}, + x2: &Sample{Xs: []float64{1}}, + alt: LocationDiffers, + err: ErrZeroVariance, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result, err := TwoSampleTTest(tc.x1, tc.x2, tc.alt) + assert.Equal(t, tc.err, err) + assert.Nil(t, result) + }) + } +} + +func TestTwoSampleWelchTTestErrors(t *testing.T) { + tt := []struct { + name string + x1 TTestSample + x2 TTestSample + alt LocationHypothesis + err error + }{ + { + name: "One sample size is 1", + x1: &Sample{Xs: []float64{1}}, + x2: &Sample{Xs: []float64{2, 3, 4}}, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "Both sample sizes are 1", + x1: &Sample{Xs: []float64{1}}, + x2: &Sample{Xs: []float64{2}}, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "One sample has zero variance", + x1: &Sample{Xs: []float64{1, 1, 1}}, + x2: &Sample{Xs: []float64{2, 2, 2}}, + alt: LocationDiffers, + err: ErrZeroVariance, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result, err := TwoSampleWelchTTest(tc.x1, tc.x2, tc.alt) + assert.Equal(t, tc.err, err) + assert.Nil(t, result) + }) + } +} + +func TestPairedTTestErrors(t *testing.T) { + tt := []struct { + name string + x1 []float64 + x2 []float64 + μ0 float64 + alt LocationHypothesis + err error + }{ + { + name: "Samples have different lengths", + x1: []float64{1, 2, 3}, + x2: []float64{4, 5}, + μ0: 0, + alt: LocationDiffers, + err: ErrMismatchedSamples, + }, + { + name: "Samples have length <= 1", + x1: []float64{1}, + x2: []float64{2}, + μ0: 0, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "Samples result in zero standard deviation", + x1: []float64{1, 1, 1}, + x2: []float64{1, 1, 1}, + μ0: 0, + alt: LocationDiffers, + err: ErrZeroVariance, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result, err := PairedTTest(tc.x1, tc.x2, tc.μ0, tc.alt) + assert.Equal(t, tc.err, err) + assert.Nil(t, result) + }) + } +} + +func TestOneSampleTTestErrors(t *testing.T) { + tt := []struct { + name string + x TTestSample + μ0 float64 + alt LocationHypothesis + err error + }{ + { + name: "Sample size is 0", + x: &Sample{Xs: []float64{}}, + μ0: 0, + alt: LocationDiffers, + err: ErrSampleSize, + }, + { + name: "Sample has zero variance", + x: &Sample{Xs: []float64{1, 1, 1}}, + μ0: 0, + alt: LocationDiffers, + err: ErrZeroVariance, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result, err := OneSampleTTest(tc.x, tc.μ0, tc.alt) + assert.Equal(t, tc.err, err) + assert.Nil(t, result) + }) + } +} diff --git a/go/mathutil/equivalence_relation_test.go b/go/mathutil/equivalence_relation_test.go index 3873562a080..754e9f792ab 100644 --- a/go/mathutil/equivalence_relation_test.go +++ b/go/mathutil/equivalence_relation_test.go @@ -174,3 +174,154 @@ func TestEquivalenceRelation(t *testing.T) { }) } } + +func TestEquivalenceRelationError(t *testing.T) { + type ttError struct { + name string + element1 string + element2 string + expectedErr string + } + + testsRelateErrorCases := []ttError{ + { + name: "UnknownElementError", + element1: "x", + element2: "b", + expectedErr: "unknown element x", + }, + { + name: "UnknownClassError", + element1: "a", + element2: "y", + expectedErr: "unknown element y", + }, + } + + for _, tc := range testsRelateErrorCases { + t.Run(tc.name, func(t *testing.T) { + r := NewEquivalenceRelation() + r.AddAll([]string{"a", "b", "c"}) + + _, err := r.Relate(tc.element1, tc.element2) + assert.Error(t, err) + assert.EqualError(t, err, tc.expectedErr) + }) + } +} + +func TestUnknownElementError(t *testing.T) { + err := &UnknownElementError{element: "test_element"} + + assert.EqualError(t, err, "unknown element test_element") +} + +func TestUnknownClassError(t *testing.T) { + err := &UnknownClassError{class: 42} + + assert.EqualError(t, err, "unknown class 42") +} + +func TestAdd(t *testing.T) { + r := NewEquivalenceRelation() + initialElements := []string{"a", "b", "c"} + + for _, element := range initialElements { + r.Add(element) + } + + for _, element := range initialElements { + class, err := r.ElementClass(element) + require.NoError(t, err) + assert.Contains(t, r.classElementsMap[class], element) + } + + classCounter := r.classCounter + r.Add("a") + assert.Equal(t, classCounter, r.classCounter) +} + +func TestElementClass(t *testing.T) { + r := NewEquivalenceRelation() + element := "test_element" + + _, err := r.ElementClass(element) + assert.Error(t, err) + + r.Add(element) + class, err := r.ElementClass(element) + require.NoError(t, err) + assert.Greater(t, class, -1) +} + +func TestRelated(t *testing.T) { + type tt struct { + name string + relations []string + element1 string + element2 string + expect bool + err error + } + + tests := []tt{ + { + name: "related, same class", + relations: []string{"ab"}, + element1: "a", + element2: "b", + expect: true, + err: nil, + }, + { + name: "related, different classes", + relations: []string{"ab, cd"}, + element1: "a", + element2: "c", + expect: false, + err: nil, + }, + { + name: "related, unknown element", + relations: []string{"ab"}, + element1: "x", + element2: "b", + expect: false, + err: &UnknownElementError{element: "x"}, + }, + { + name: "related, unknown element 2", + relations: []string{"ab"}, + element1: "a", + element2: "y", + expect: false, + err: &UnknownElementError{element: "y"}, + }, + { + name: "related, both elements unknown", + relations: []string{"ab"}, + element1: "x", + element2: "y", + expect: false, + err: &UnknownElementError{element: "x"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + r := NewEquivalenceRelation() + r.AddAll([]string{"a", "b", "c", "d"}) + for _, relation := range tc.relations { + _, err := r.Relate(relation[0:1], relation[1:2]) + require.NoError(t, err) + } + + result, err := r.Related(tc.element1, tc.element2) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + } else { + assert.Equal(t, tc.expect, result) + } + }) + } +} diff --git a/go/mysql/auth_server_clientcert.go b/go/mysql/auth_server_clientcert.go index 10a01487208..bb0a4028683 100644 --- a/go/mysql/auth_server_clientcert.go +++ b/go/mysql/auth_server_clientcert.go @@ -23,17 +23,8 @@ import ( "github.com/spf13/pflag" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" ) -var clientcertAuthMethod string - -func init() { - servenv.OnParseFor("vtgate", func(fs *pflag.FlagSet) { - fs.StringVar(&clientcertAuthMethod, "mysql_clientcert_auth_method", string(MysqlClearPassword), "client-side authentication method to use. Supported values: mysql_clear_password, dialog.") - }) -} - // AuthServerClientCert implements AuthServer which enforces client side certificates type AuthServerClientCert struct { methods []AuthMethod @@ -41,7 +32,7 @@ type AuthServerClientCert struct { } // InitAuthServerClientCert is public so it can be called from plugin_auth_clientcert.go (go/cmd/vtgate) -func InitAuthServerClientCert() { +func InitAuthServerClientCert(clientcertAuthMethod string) { if pflag.CommandLine.Lookup("mysql_server_ssl_ca").Value.String() == "" { log.Info("Not configuring AuthServerClientCert because mysql_server_ssl_ca is empty") return @@ -50,11 +41,11 @@ func InitAuthServerClientCert() { log.Exitf("Invalid mysql_clientcert_auth_method value: only support mysql_clear_password or dialog") } - ascc := newAuthServerClientCert() + ascc := newAuthServerClientCert(clientcertAuthMethod) RegisterAuthServer("clientcert", ascc) } -func newAuthServerClientCert() *AuthServerClientCert { +func newAuthServerClientCert(clientcertAuthMethod string) *AuthServerClientCert { ascc := &AuthServerClientCert{ Method: AuthMethodDescription(clientcertAuthMethod), } diff --git a/go/mysql/auth_server_clientcert_test.go b/go/mysql/auth_server_clientcert_test.go index 28ed19fd9c5..eff92053d94 100644 --- a/go/mysql/auth_server_clientcert_test.go +++ b/go/mysql/auth_server_clientcert_test.go @@ -33,19 +33,13 @@ import ( const clientCertUsername = "Client Cert" -func init() { - // These tests do not invoke the servenv.Parse codepaths, so this default - // does not get set by the OnParseFor hook. - clientcertAuthMethod = string(MysqlClearPassword) -} - func TestValidCert(t *testing.T) { th := &testHandler{} - authServer := newAuthServerClientCert() + authServer := newAuthServerClientCert(string(MysqlClearPassword)) // Create the listener, so we can get its host. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -111,10 +105,10 @@ func TestValidCert(t *testing.T) { func TestNoCert(t *testing.T) { th := &testHandler{} - authServer := newAuthServerClientCert() + authServer := newAuthServerClientCert(string(MysqlClearPassword)) // Create the listener, so we can get its host. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() diff --git a/go/mysql/auth_server_static.go b/go/mysql/auth_server_static.go index fae886039f0..6e3a9693c69 100644 --- a/go/mysql/auth_server_static.go +++ b/go/mysql/auth_server_static.go @@ -27,34 +27,15 @@ import ( "syscall" "time" - "github.com/spf13/pflag" - "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/vtrpc" ) -var ( - mysqlAuthServerStaticFile string - mysqlAuthServerStaticString string - mysqlAuthServerStaticReloadInterval time.Duration - mysqlServerFlushDelay = 100 * time.Millisecond -) - -func init() { - servenv.OnParseFor("vtgate", func(fs *pflag.FlagSet) { - fs.StringVar(&mysqlAuthServerStaticFile, "mysql_auth_server_static_file", "", "JSON File to read the users/passwords from.") - fs.StringVar(&mysqlAuthServerStaticString, "mysql_auth_server_static_string", "", "JSON representation of the users/passwords config.") - fs.DurationVar(&mysqlAuthServerStaticReloadInterval, "mysql_auth_static_reload_interval", 0, "Ticker to reload credentials") - fs.DurationVar(&mysqlServerFlushDelay, "mysql_server_flush_delay", mysqlServerFlushDelay, "Delay after which buffered response will be flushed to the client.") - }) -} - const ( localhostName = "localhost" ) @@ -94,7 +75,7 @@ type AuthServerStaticEntry struct { } // InitAuthServerStatic Handles initializing the AuthServerStatic if necessary. -func InitAuthServerStatic() { +func InitAuthServerStatic(mysqlAuthServerStaticFile, mysqlAuthServerStaticString string, mysqlAuthServerStaticReloadInterval time.Duration) { // Check parameters. if mysqlAuthServerStaticFile == "" && mysqlAuthServerStaticString == "" { // Not configured, nothing to do. diff --git a/go/mysql/auth_server_static_flaky_test.go b/go/mysql/auth_server_static_test.go similarity index 100% rename from go/mysql/auth_server_static_flaky_test.go rename to go/mysql/auth_server_static_test.go diff --git a/go/mysql/binlog/rbr_test.go b/go/mysql/binlog/rbr_test.go index 260af2f3821..1dfaf90a33e 100644 --- a/go/mysql/binlog/rbr_test.go +++ b/go/mysql/binlog/rbr_test.go @@ -78,7 +78,7 @@ func TestCellLengthAndData(t *testing.T) { styp: querypb.Type_UINT32, data: []byte{0x84, 0x83, 0x82, 0x81}, out: sqltypes.MakeTrusted(querypb.Type_UINT32, - []byte(fmt.Sprintf("%v", 0x81828384))), + []byte(fmt.Sprintf("%v", uint32(0x81828384)))), }, { typ: TypeLong, styp: querypb.Type_INT32, diff --git a/go/mysql/capabilities/capability.go b/go/mysql/capabilities/capability.go new file mode 100644 index 00000000000..d4b66821096 --- /dev/null +++ b/go/mysql/capabilities/capability.go @@ -0,0 +1,125 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package capabilities + +import ( + "strconv" + "strings" + + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" +) + +var ( + ErrUnspecifiedServerVersion = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "server version unspecified") +) + +type FlavorCapability int + +const ( + NoneFlavorCapability FlavorCapability = iota // default placeholder + FastDropTableFlavorCapability // supported in MySQL 8.0.23 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html + TransactionalGtidExecutedFlavorCapability // + InstantDDLFlavorCapability // ALGORITHM=INSTANT general support + InstantAddLastColumnFlavorCapability // + InstantAddDropVirtualColumnFlavorCapability // + InstantAddDropColumnFlavorCapability // Adding/dropping column in any position/ordinal. + InstantChangeColumnDefaultFlavorCapability // + InstantExpandEnumCapability // + MySQLJSONFlavorCapability // JSON type supported + MySQLUpgradeInServerFlavorCapability // + DynamicRedoLogCapacityFlavorCapability // supported in MySQL 8.0.30 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-30.html + DisableRedoLogFlavorCapability // supported in MySQL 8.0.21 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-21.html + CheckConstraintsCapability // supported in MySQL 8.0.16 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html + PerformanceSchemaDataLocksTableCapability +) + +type CapableOf func(capability FlavorCapability) (bool, error) + +// ServerVersionAtLeast returns true if current server is at least given value. +// Example: if input is []int{8, 0, 23}... the function returns 'true' if we're +// on MySQL 8.0.23, 8.0.24, ... +func ServerVersionAtLeast(serverVersion string, parts ...int) (bool, error) { + if serverVersion == "" { + return false, ErrUnspecifiedServerVersion + } + versionPrefix := strings.Split(serverVersion, "-")[0] + versionTokens := strings.Split(versionPrefix, ".") + for i, part := range parts { + if len(versionTokens) <= i { + return false, nil + } + tokenValue, err := strconv.Atoi(versionTokens[i]) + if err != nil { + return false, err + } + if tokenValue > part { + return true, nil + } + if tokenValue < part { + return false, nil + } + } + return true, nil +} + +// MySQLVersionHasCapability is specific to MySQL flavors (of all versions) and answers whether +// the given server version has the requested capability. +func MySQLVersionHasCapability(serverVersion string, capability FlavorCapability) (bool, error) { + atLeast := func(parts ...int) (bool, error) { + return ServerVersionAtLeast(serverVersion, parts...) + } + // Capabilities sorted by version. + switch capability { + case MySQLJSONFlavorCapability: + return atLeast(5, 7, 0) + case InstantDDLFlavorCapability, + InstantExpandEnumCapability, + InstantAddLastColumnFlavorCapability, + InstantAddDropVirtualColumnFlavorCapability, + InstantChangeColumnDefaultFlavorCapability: + return atLeast(8, 0, 0) + case PerformanceSchemaDataLocksTableCapability: + return atLeast(8, 0, 1) + case MySQLUpgradeInServerFlavorCapability: + return atLeast(8, 0, 16) + case CheckConstraintsCapability: + return atLeast(8, 0, 16) + case TransactionalGtidExecutedFlavorCapability: + return atLeast(8, 0, 17) + case DisableRedoLogFlavorCapability: + return atLeast(8, 0, 21) + case FastDropTableFlavorCapability: + return atLeast(8, 0, 23) + case InstantAddDropColumnFlavorCapability: + return atLeast(8, 0, 29) + case DynamicRedoLogCapacityFlavorCapability: + return atLeast(8, 0, 30) + default: + return false, nil + } +} + +// MySQLVersionCapableOf returns a CapableOf function specific to MySQL flavors +func MySQLVersionCapableOf(serverVersion string) CapableOf { + if serverVersion == "" { + return nil + } + return func(capability FlavorCapability) (bool, error) { + return MySQLVersionHasCapability(serverVersion, capability) + } +} diff --git a/go/mysql/capabilities/capability_test.go b/go/mysql/capabilities/capability_test.go new file mode 100644 index 00000000000..339c05f1017 --- /dev/null +++ b/go/mysql/capabilities/capability_test.go @@ -0,0 +1,260 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package capabilities + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestServerVersionAtLeast(t *testing.T) { + testcases := []struct { + version string + parts []int + expect bool + expectError bool + }{ + { + version: "8.0.14", + parts: []int{8, 0, 14}, + expect: true, + }, + { + version: "8.0.14-log", + parts: []int{8, 0, 14}, + expect: true, + }, + { + version: "8.0.14", + parts: []int{8, 0, 13}, + expect: true, + }, + { + version: "8.0.14-log", + parts: []int{8, 0, 13}, + expect: true, + }, + { + version: "8.0.14", + parts: []int{8, 0, 15}, + expect: false, + }, + { + version: "8.0.14-log", + parts: []int{8, 0, 15}, + expect: false, + }, + { + version: "8.0.14", + parts: []int{7, 5, 20}, + expect: true, + }, + { + version: "8.0.14", + parts: []int{7, 5}, + expect: true, + }, + { + version: "8.0.14", + parts: []int{5, 7}, + expect: true, + }, + { + version: "8.0.14-log", + parts: []int{7, 5, 20}, + expect: true, + }, + { + version: "8.0.14", + parts: []int{8, 1, 2}, + expect: false, + }, + { + version: "8.0.14", + parts: []int{10, 1, 2}, + expect: false, + }, + { + version: "8.0", + parts: []int{8, 0, 14}, + expect: false, + }, + { + version: "8.0.x", + parts: []int{8, 0, 14}, + expectError: true, + }, + { + version: "", + parts: []int{8, 0, 14}, + expectError: true, + }, + } + for _, tc := range testcases { + result, err := ServerVersionAtLeast(tc.version, tc.parts...) + if tc.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expect, result) + } + } +} + +func TestMySQLVersionCapableOf(t *testing.T) { + testcases := []struct { + version string + capability FlavorCapability + isCapable bool + expectNil bool + }{ + { + version: "8.0.14", + capability: InstantDDLFlavorCapability, + isCapable: true, + }, + { + version: "8.0.20", + capability: TransactionalGtidExecutedFlavorCapability, + isCapable: true, + }, + { + version: "8.0.0", + capability: InstantAddLastColumnFlavorCapability, + isCapable: true, + }, + { + version: "8.0.0", + capability: InstantAddDropColumnFlavorCapability, + isCapable: false, + }, + { + version: "5.6.7", + capability: InstantDDLFlavorCapability, + isCapable: false, + }, + { + version: "5.7.29", + capability: TransactionalGtidExecutedFlavorCapability, + isCapable: false, + }, + { + version: "5.6.7", + capability: MySQLJSONFlavorCapability, + isCapable: false, + }, + { + version: "5.7.29", + capability: MySQLJSONFlavorCapability, + isCapable: true, + }, + { + version: "8.0.30", + capability: DynamicRedoLogCapacityFlavorCapability, + isCapable: true, + }, + { + version: "8.0.29", + capability: DynamicRedoLogCapacityFlavorCapability, + isCapable: false, + }, + { + version: "5.7.38", + capability: DynamicRedoLogCapacityFlavorCapability, + isCapable: false, + }, + { + version: "8.0.21", + capability: DisableRedoLogFlavorCapability, + isCapable: true, + }, + { + version: "8.0.20", + capability: DisableRedoLogFlavorCapability, + isCapable: false, + }, + { + version: "8.0.15", + capability: CheckConstraintsCapability, + isCapable: false, + }, + { + version: "8.0.15-log", + capability: CheckConstraintsCapability, + isCapable: false, + }, + { + version: "8.0.20", + capability: CheckConstraintsCapability, + isCapable: true, + }, + { + version: "8.0.20-log", + capability: CheckConstraintsCapability, + isCapable: true, + }, + { + version: "5.7.38", + capability: PerformanceSchemaDataLocksTableCapability, + isCapable: false, + }, + { + version: "8.0", + capability: PerformanceSchemaDataLocksTableCapability, + isCapable: false, + }, + { + version: "8.0.0", + capability: PerformanceSchemaDataLocksTableCapability, + isCapable: false, + }, + { + version: "8.0.20", + capability: PerformanceSchemaDataLocksTableCapability, + isCapable: true, + }, + { + // What happens if server version is unspecified + version: "", + capability: CheckConstraintsCapability, + isCapable: false, + expectNil: true, + }, + { + // Some ridiculous version. But seeing that we force the question on a MySQLVersionCapableOf + // then this far futuristic version should actually work. + version: "5914.234.17", + capability: CheckConstraintsCapability, + isCapable: true, + }, + } + for _, tc := range testcases { + name := fmt.Sprintf("%s %v", tc.version, tc.capability) + t.Run(name, func(t *testing.T) { + capableOf := MySQLVersionCapableOf(tc.version) + if tc.expectNil { + assert.Nil(t, capableOf) + return + } + isCapable, err := capableOf(tc.capability) + assert.NoError(t, err) + assert.Equal(t, tc.isCapable, isCapable) + }) + } +} diff --git a/go/mysql/client.go b/go/mysql/client.go index c4dd87d95cc..16740bf38db 100644 --- a/go/mysql/client.go +++ b/go/mysql/client.go @@ -106,7 +106,7 @@ func Connect(ctx context.Context, params *ConnParams) (*Conn, error) { } // Send the connection back, so the other side can close it. - c := newConn(conn) + c := newConn(conn, params.FlushDelay, params.TruncateErrLen) status <- connectResult{ c: c, } @@ -229,11 +229,6 @@ func (c *Conn) clientHandshake(params *ConnParams) error { c.Capabilities = capabilities & (CapabilityClientDeprecateEOF) } - charset, err := collations.Local().ParseConnectionCharset(params.Charset) - if err != nil { - return err - } - // Handle switch to SSL if necessary. if params.SslEnabled() { // If client asked for SSL, but server doesn't support it, @@ -270,7 +265,7 @@ func (c *Conn) clientHandshake(params *ConnParams) error { } // Send the SSLRequest packet. - if err := c.writeSSLRequest(capabilities, charset, params); err != nil { + if err := c.writeSSLRequest(capabilities, uint8(params.Charset), params); err != nil { return err } @@ -302,7 +297,7 @@ func (c *Conn) clientHandshake(params *ConnParams) error { // Build and send our handshake response 41. // Note this one will never have SSL flag on. - if err := c.writeHandshakeResponse41(capabilities, scrambledPassword, charset, params); err != nil { + if err := c.writeHandshakeResponse41(capabilities, scrambledPassword, uint8(params.Charset), params); err != nil { return err } diff --git a/go/mysql/client_test.go b/go/mysql/client_test.go index c349cdcd531..da577e338b2 100644 --- a/go/mysql/client_test.go +++ b/go/mysql/client_test.go @@ -151,7 +151,7 @@ func TestTLSClientDisabled(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -223,7 +223,7 @@ func TestTLSClientPreferredDefault(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -296,7 +296,7 @@ func TestTLSClientRequired(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -343,7 +343,7 @@ func TestTLSClientVerifyCA(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -426,7 +426,7 @@ func TestTLSClientVerifyIdentity(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() diff --git a/go/mysql/collations/cached_size.go b/go/mysql/collations/cached_size.go new file mode 100644 index 00000000000..630bf41230a --- /dev/null +++ b/go/mysql/collations/cached_size.go @@ -0,0 +1,111 @@ +/* +Copyright 2021 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by Sizegen. DO NOT EDIT. + +package collations + +import ( + "math" + "reflect" + "unsafe" + + hack "vitess.io/vitess/go/hack" +) + +//go:nocheckptr +func (cached *Environment) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field byName map[string]vitess.io/vitess/go/mysql/collations.ID + if cached.byName != nil { + size += int64(48) + hmap := reflect.ValueOf(cached.byName) + numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) + numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) + size += hack.RuntimeAllocSize(int64(numOldBuckets * 160)) + if len(cached.byName) > 0 || numBuckets > 1 { + size += hack.RuntimeAllocSize(int64(numBuckets * 160)) + } + for k := range cached.byName { + size += hack.RuntimeAllocSize(int64(len(k))) + } + } + // field byCharset map[string]*vitess.io/vitess/go/mysql/collations.colldefaults + if cached.byCharset != nil { + size += int64(48) + hmap := reflect.ValueOf(cached.byCharset) + numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) + numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) + size += hack.RuntimeAllocSize(int64(numOldBuckets * 208)) + if len(cached.byCharset) > 0 || numBuckets > 1 { + size += hack.RuntimeAllocSize(int64(numBuckets * 208)) + } + for k, v := range cached.byCharset { + size += hack.RuntimeAllocSize(int64(len(k))) + if v != nil { + size += hack.RuntimeAllocSize(int64(4)) + } + } + } + // field byCharsetName map[vitess.io/vitess/go/mysql/collations.ID]string + if cached.byCharsetName != nil { + size += int64(48) + hmap := reflect.ValueOf(cached.byCharsetName) + numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) + numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) + size += hack.RuntimeAllocSize(int64(numOldBuckets * 160)) + if len(cached.byCharsetName) > 0 || numBuckets > 1 { + size += hack.RuntimeAllocSize(int64(numBuckets * 160)) + } + for _, v := range cached.byCharsetName { + size += hack.RuntimeAllocSize(int64(len(v))) + } + } + // field unsupported map[string]vitess.io/vitess/go/mysql/collations.ID + if cached.unsupported != nil { + size += int64(48) + hmap := reflect.ValueOf(cached.unsupported) + numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) + numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) + size += hack.RuntimeAllocSize(int64(numOldBuckets * 160)) + if len(cached.unsupported) > 0 || numBuckets > 1 { + size += hack.RuntimeAllocSize(int64(numBuckets * 160)) + } + for k := range cached.unsupported { + size += hack.RuntimeAllocSize(int64(len(k))) + } + } + // field byID map[vitess.io/vitess/go/mysql/collations.ID]string + if cached.byID != nil { + size += int64(48) + hmap := reflect.ValueOf(cached.byID) + numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) + numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) + size += hack.RuntimeAllocSize(int64(numOldBuckets * 160)) + if len(cached.byID) > 0 || numBuckets > 1 { + size += hack.RuntimeAllocSize(int64(numBuckets * 160)) + } + for _, v := range cached.byID { + size += hack.RuntimeAllocSize(int64(len(v))) + } + } + return size +} diff --git a/go/mysql/collations/charset/eightbit/8bit.go b/go/mysql/collations/charset/eightbit/8bit.go index 5bd930c61cb..12630749d5d 100644 --- a/go/mysql/collations/charset/eightbit/8bit.go +++ b/go/mysql/collations/charset/eightbit/8bit.go @@ -81,3 +81,17 @@ func (Charset_8bit) Length(src []byte) int { func (Charset_8bit) MaxWidth() int { return 1 } + +func (Charset_8bit) Slice(src []byte, from, to int) []byte { + if from >= len(src) { + return nil + } + if to > len(src) { + to = len(src) + } + return src[from:to] +} + +func (Charset_8bit) Validate(src []byte) bool { + return true +} diff --git a/go/mysql/collations/charset/eightbit/binary.go b/go/mysql/collations/charset/eightbit/binary.go index 44824bbc342..fa36fcf66a5 100644 --- a/go/mysql/collations/charset/eightbit/binary.go +++ b/go/mysql/collations/charset/eightbit/binary.go @@ -62,3 +62,17 @@ func (Charset_binary) Length(src []byte) int { func (Charset_binary) MaxWidth() int { return 1 } + +func (Charset_binary) Slice(src []byte, from, to int) []byte { + if from >= len(src) { + return nil + } + if to > len(src) { + to = len(src) + } + return src[from:to] +} + +func (Charset_binary) Validate(src []byte) bool { + return true +} diff --git a/go/mysql/collations/charset/eightbit/latin1.go b/go/mysql/collations/charset/eightbit/latin1.go index 67fa07c62c2..f32b4523a18 100644 --- a/go/mysql/collations/charset/eightbit/latin1.go +++ b/go/mysql/collations/charset/eightbit/latin1.go @@ -230,3 +230,17 @@ func (Charset_latin1) Length(src []byte) int { func (Charset_latin1) MaxWidth() int { return 1 } + +func (Charset_latin1) Slice(src []byte, from, to int) []byte { + if from >= len(src) { + return nil + } + if to > len(src) { + to = len(src) + } + return src[from:to] +} + +func (Charset_latin1) Validate(src []byte) bool { + return true +} diff --git a/go/mysql/collations/colldata/collation.go b/go/mysql/collations/colldata/collation.go index 7697c08cbed..a041006ddc7 100644 --- a/go/mysql/collations/colldata/collation.go +++ b/go/mysql/collations/colldata/collation.go @@ -17,6 +17,7 @@ limitations under the License. package colldata import ( + "bytes" "fmt" "math" @@ -380,3 +381,46 @@ coerceToRight: return charset.Convert(dst, rightCS, in, leftCS) }, nil, nil } + +func Index(col Collation, str, sub []byte, offset int) int { + cs := col.Charset() + if offset > 0 { + l := charset.Length(cs, str) + if offset > l { + return -1 + } + str = charset.Slice(cs, str, offset, len(str)) + } + + pos := instr(col, str, sub) + if pos < 0 { + return -1 + } + return offset + pos +} + +func instr(col Collation, str, sub []byte) int { + if len(sub) == 0 { + return 0 + } + + if len(str) == 0 { + return -1 + } + + if col.IsBinary() && col.Charset().MaxWidth() == 1 { + return bytes.Index(str, sub) + } + + var pos int + cs := col.Charset() + for len(str) > 0 { + if col.Collate(str, sub, true) == 0 { + return pos + } + _, size := cs.DecodeRune(str) + str = str[size:] + pos++ + } + return -1 +} diff --git a/go/mysql/collations/colldata/mysqlucadata.go b/go/mysql/collations/colldata/mysqlucadata.go index 0affc45d11f..9f9e2b7e238 100644 --- a/go/mysql/collations/colldata/mysqlucadata.go +++ b/go/mysql/collations/colldata/mysqlucadata.go @@ -20,7 +20,7 @@ package colldata import ( _ "embed" - unsafe "unsafe" + "unsafe" ) var weightTable_uca900_page000 = weightsUCA_embed(0, 2560) @@ -1417,5 +1417,5 @@ var weightTable_uca520 = []*[]uint16{ var weightsUCA_embed_data string func weightsUCA_embed(pos, length int) []uint16 { - return (*[0x7fff0000]uint16)(unsafe.Pointer(unsafe.StringData(weightsUCA_embed_data)))[pos : pos+length] + return (*[0x3fffffff]uint16)(unsafe.Pointer(unsafe.StringData(weightsUCA_embed_data)))[pos : pos+length] } diff --git a/go/mysql/collations/colldata/uca_contraction_test.go b/go/mysql/collations/colldata/uca_contraction_test.go index d17ff21e255..d09b2a6d982 100644 --- a/go/mysql/collations/colldata/uca_contraction_test.go +++ b/go/mysql/collations/colldata/uca_contraction_test.go @@ -21,7 +21,6 @@ import ( "fmt" "math/rand" "os" - "reflect" "sort" "testing" "unicode/utf8" @@ -36,7 +35,6 @@ type CollationWithContractions struct { Collation Collation Contractions []uca.Contraction ContractFast uca.Contractor - ContractTrie uca.Contractor } func findContractedCollations(t testing.TB, unique bool) (result []CollationWithContractions) { @@ -58,7 +56,7 @@ func findContractedCollations(t testing.TB, unique bool) (result []CollationWith continue } - rf, err := os.Open(fmt.Sprintf("testdata/mysqldata/%s.json", collation.Name())) + rf, err := os.Open(fmt.Sprintf("../testdata/mysqldata/%s.json", collation.Name())) if err != nil { t.Skipf("failed to open JSON metadata (%v). did you run colldump?", err) } @@ -91,14 +89,13 @@ func findContractedCollations(t testing.TB, unique bool) (result []CollationWith Collation: collation, Contractions: meta.Contractions, ContractFast: contract, - ContractTrie: uca.NewTrieContractor(meta.Contractions), }) } return } func testMatch(t *testing.T, name string, cnt uca.Contraction, result []uint16, remainder []byte, skip int) { - assert.True(t, reflect.DeepEqual(cnt.Weights, result), "%s didn't match: expected %#v, got %#v", name, cnt.Weights, result) + assert.Equal(t, result, cnt.Weights, "%s didn't match: expected %#v, got %#v", name, cnt.Weights, result) assert.Equal(t, 0, len(remainder), "%s bad remainder: %#v", name, remainder) assert.Equal(t, len(cnt.Path), skip, "%s bad skipped length %d for %#v", name, skip, cnt.Path) @@ -112,10 +109,7 @@ func TestUCAContractions(t *testing.T) { head := cnt.Path[0] tail := cnt.Path[1] - result := cwc.ContractTrie.FindContextual(head, tail) - testMatch(t, "ContractTrie", cnt, result, nil, 2) - - result = cwc.ContractFast.FindContextual(head, tail) + result := cwc.ContractFast.FindContextual(head, tail) testMatch(t, "ContractFast", cnt, result, nil, 2) continue } @@ -123,10 +117,7 @@ func TestUCAContractions(t *testing.T) { head := cnt.Path[0] tail := string(cnt.Path[1:]) - result, remainder, skip := cwc.ContractTrie.Find(charset.Charset_utf8mb4{}, head, []byte(tail)) - testMatch(t, "ContractTrie", cnt, result, remainder, skip) - - result, remainder, skip = cwc.ContractFast.Find(charset.Charset_utf8mb4{}, head, []byte(tail)) + result, remainder, skip := cwc.ContractFast.Find(charset.Charset_utf8mb4{}, head, []byte(tail)) testMatch(t, "ContractFast", cnt, result, remainder, skip) } }) @@ -239,10 +230,6 @@ func BenchmarkUCAContractions(b *testing.B) { b.Run(fmt.Sprintf("%s-%.02f-fast", cwc.Collation.Name(), frequency), func(b *testing.B) { benchmarkFind(b, input, cwc.ContractFast) }) - - b.Run(fmt.Sprintf("%s-%.02f-trie", cwc.Collation.Name(), frequency), func(b *testing.B) { - benchmarkFind(b, input, cwc.ContractTrie) - }) } } @@ -259,9 +246,5 @@ func BenchmarkUCAContractionsJA(b *testing.B) { b.Run(fmt.Sprintf("%s-%.02f-fast", cwc.Collation.Name(), frequency), func(b *testing.B) { benchmarkFindJA(b, input, cwc.ContractFast) }) - - b.Run(fmt.Sprintf("%s-%.02f-trie", cwc.Collation.Name(), frequency), func(b *testing.B) { - benchmarkFindJA(b, input, cwc.ContractTrie) - }) } } diff --git a/go/mysql/collations/colldata/uca_tables_test.go b/go/mysql/collations/colldata/uca_tables_test.go index 40c2f3bbed3..ee982b1f25c 100644 --- a/go/mysql/collations/colldata/uca_tables_test.go +++ b/go/mysql/collations/colldata/uca_tables_test.go @@ -24,8 +24,8 @@ import ( "testing" "unsafe" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gotest.tools/assert" "vitess.io/vitess/go/mysql/collations/charset" "vitess.io/vitess/go/mysql/collations/internal/uca" @@ -58,14 +58,13 @@ func verifyAllCodepoints(t *testing.T, expected map[rune][]uint16, weights uca.W for i := range vitessWeights { a, b := mysqlWeights[i], vitessWeights[i] assert.Equal(t, b, a, "weight mismatch for U+%04X (collation entity %d): mysql=%v vitess=%v", cp, i+1, a, b) - } } } } func loadExpectedWeights(t *testing.T, weights string) map[rune][]uint16 { - fullpath := fmt.Sprintf("testdata/mysqldata/%s.json", weights) + fullpath := fmt.Sprintf("../testdata/mysqldata/%s.json", weights) weightsMysqlFile, err := os.Open(fullpath) if err != nil { t.Skipf("failed to load %q (did you run 'colldump' locally?)", fullpath) diff --git a/go/mysql/collations/env.go b/go/mysql/collations/env.go index 91fc2a8bd8c..ae5419a5797 100644 --- a/go/mysql/collations/env.go +++ b/go/mysql/collations/env.go @@ -248,10 +248,10 @@ func (env *Environment) CollationAlias(collation string) (string, bool) { // to a Collation ID, with the exception that it can only fit in 1 byte. // For MySQL 8.0+ environments, the default charset is `utf8mb4_0900_ai_ci`. // For older MySQL environments, the default charset is `utf8mb4_general_ci`. -func (env *Environment) DefaultConnectionCharset() uint8 { +func (env *Environment) DefaultConnectionCharset() ID { switch env.version { case collverMySQL8: - return uint8(CollationUtf8mb4ID) + return CollationUtf8mb4ID default: return 45 } @@ -267,7 +267,7 @@ func (env *Environment) DefaultConnectionCharset() uint8 { // handshake. // - empty, in which case the default connection charset for this MySQL version // is returned. -func (env *Environment) ParseConnectionCharset(csname string) (uint8, error) { +func (env *Environment) ParseConnectionCharset(csname string) (ID, error) { if csname == "" { return env.DefaultConnectionCharset(), nil } @@ -282,7 +282,7 @@ func (env *Environment) ParseConnectionCharset(csname string) (uint8, error) { if collid == 0 || collid > 255 { return 0, fmt.Errorf("unsupported connection charset: %q", csname) } - return uint8(collid), nil + return collid, nil } func (env *Environment) AllCollationIDs() []ID { @@ -301,3 +301,8 @@ func (env *Environment) LookupByCharset(name string) *colldefaults { func (env *Environment) LookupCharsetName(coll ID) string { return env.byCharsetName[coll] } + +func (env *Environment) IsSupported(coll ID) bool { + _, supported := env.byID[coll] + return supported +} diff --git a/go/mysql/collations/integration/charset_test.go b/go/mysql/collations/integration/charset_test.go index 8a4d12a0e4d..b1b747e768b 100644 --- a/go/mysql/collations/integration/charset_test.go +++ b/go/mysql/collations/integration/charset_test.go @@ -45,7 +45,7 @@ func TestLocalEncodings(t *testing.T) { defer conn.Close() for _, tc := range cases { - local := collations.Local().LookupByName(tc.collation) + local := collations.MySQL8().LookupByName(tc.collation) remote := remote.NewCollation(conn, tc.collation) verifyTranscoding(t, colldata.Lookup(local), remote, tc.input) } diff --git a/go/mysql/collations/integration/coercion_test.go b/go/mysql/collations/integration/coercion_test.go index dad55bcafad..c194b48c071 100644 --- a/go/mysql/collations/integration/coercion_test.go +++ b/go/mysql/collations/integration/coercion_test.go @@ -54,7 +54,7 @@ type testConcat struct { } func (tc *testConcat) Expression() string { - env := collations.Local() + env := collations.MySQL8() return fmt.Sprintf("CONCAT((_%s X'%x' COLLATE %q), (_%s X'%x' COLLATE %q))", colldata.Lookup(tc.left.Collation).Charset().Name(), tc.left.Text, env.LookupName(tc.left.Collation), colldata.Lookup(tc.right.Collation).Charset().Name(), tc.right.Text, env.LookupName(tc.right.Collation), @@ -63,7 +63,7 @@ func (tc *testConcat) Expression() string { func (tc *testConcat) Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coercion1, coercion2 colldata.Coercion) { localCollation := colldata.Lookup(local.Collation) - remoteName := collations.Local().LookupName(remote.Collation) + remoteName := collations.MySQL8().LookupName(remote.Collation) assert.Equal(t, remoteName, localCollation.Name(), "bad collation resolved: local is %s, remote is %s", localCollation.Name(), remoteName) assert.Equal(t, remote.Coercibility, local.Coercibility, "bad coercibility resolved: local is %d, remote is %d", local.Coercibility, remote.Coercibility) @@ -85,8 +85,8 @@ func (tc *testConcat) Test(t *testing.T, remote *RemoteCoercionResult, local col rEBytes, err := remote.Expr.ToBytes() require.NoError(t, err) - assert.True(t, bytes.Equal(concat.Bytes(), rEBytes), "failed to concatenate text;\n\tCONCAT(%v COLLATE %s, %v COLLATE %s) = \n\tCONCAT(%v, %v) COLLATE %s = \n\t\t%v\n\n\texpected: %v", tc.left.Text, collations.Local().LookupName(tc.left.Collation), - tc.right.Text, collations.Local().LookupName(tc.right.Collation), leftText, rightText, localCollation.Name(), + assert.True(t, bytes.Equal(concat.Bytes(), rEBytes), "failed to concatenate text;\n\tCONCAT(%v COLLATE %s, %v COLLATE %s) = \n\tCONCAT(%v, %v) COLLATE %s = \n\t\t%v\n\n\texpected: %v", tc.left.Text, collations.MySQL8().LookupName(tc.left.Collation), + tc.right.Text, collations.MySQL8().LookupName(tc.right.Collation), leftText, rightText, localCollation.Name(), concat.Bytes(), rEBytes) } @@ -96,7 +96,7 @@ type testComparison struct { } func (tc *testComparison) Expression() string { - env := collations.Local() + env := collations.MySQL8() return fmt.Sprintf("(_%s X'%x' COLLATE %q) = (_%s X'%x' COLLATE %q)", env.LookupCharsetName(tc.left.Collation), tc.left.Text, env.LookupName(tc.left.Collation), env.LookupCharsetName(tc.right.Collation), tc.right.Text, env.LookupName(tc.right.Collation), @@ -135,7 +135,7 @@ func TestComparisonSemantics(t *testing.T) { t.Skipf("The behavior of Coercion Semantics is not correct before 8.0.31") } - for _, coll := range colldata.All(collations.Local()) { + for _, coll := range colldata.All(collations.MySQL8()) { text := verifyTranscoding(t, coll, remote.NewCollation(conn, coll.Name()), []byte(BaseString)) testInputs = append(testInputs, &TextWithCollation{Text: text, Collation: coll.ID()}) } @@ -175,7 +175,7 @@ func TestComparisonSemantics(t *testing.T) { Coercibility: 0, Repertoire: collations.RepertoireASCII, } - resultLocal, coercionLocal1, coercionLocal2, errLocal := colldata.Merge(collations.Local(), left, right, + resultLocal, coercionLocal1, coercionLocal2, errLocal := colldata.Merge(collations.MySQL8(), left, right, colldata.CoercionOptions{ ConvertToSuperset: true, ConvertWithCoercion: true, @@ -194,7 +194,7 @@ func TestComparisonSemantics(t *testing.T) { query := fmt.Sprintf("SELECT CAST((%s) AS BINARY), COLLATION(%s), COERCIBILITY(%s)", expr, expr, expr) resultRemote, errRemote := conn.ExecuteFetch(query, 1, false) - env := collations.Local() + env := collations.MySQL8() if errRemote != nil { require.True(t, strings.Contains(errRemote.Error(), "Illegal mix of collations"), "query %s failed: %v", query, errRemote) @@ -212,7 +212,7 @@ func TestComparisonSemantics(t *testing.T) { continue } - remoteCollation := collations.Local().LookupByName(resultRemote.Rows[0][1].ToString()) + remoteCollation := collations.MySQL8().LookupByName(resultRemote.Rows[0][1].ToString()) remoteCI, _ := resultRemote.Rows[0][2].ToInt64() remoteTest.Test(t, &RemoteCoercionResult{ Expr: resultRemote.Rows[0][0], diff --git a/go/mysql/collations/integration/collations_test.go b/go/mysql/collations/integration/collations_test.go index c8f53eeb242..519f4560faf 100644 --- a/go/mysql/collations/integration/collations_test.go +++ b/go/mysql/collations/integration/collations_test.go @@ -38,7 +38,6 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/remote" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" ) @@ -46,9 +45,7 @@ var collationEnv *collations.Environment func init() { // We require MySQL 8.0 collations for the comparisons in the tests - mySQLVersion := "8.0.0" - servenv.SetMySQLServerVersionForTest(mySQLVersion) - collationEnv = collations.NewEnvironment(mySQLVersion) + collationEnv = collations.NewEnvironment("8.0.30") } func getSQLQueries(t *testing.T, testfile string) []string { @@ -59,11 +56,11 @@ func getSQLQueries(t *testing.T, testfile string) []string { defer tf.Close() var chunks []string - var curchunk bytes.Buffer + var curchunk strings.Builder addchunk := func() { if curchunk.Len() > 0 { - stmts, err := sqlparser.SplitStatementToPieces(curchunk.String()) + stmts, err := sqlparser.NewTestParser().SplitStatementToPieces(curchunk.String()) if err != nil { t.Fatal(err) } @@ -219,8 +216,8 @@ func TestCollationsOnMysqld(t *testing.T) { } func TestRemoteKanaSensitivity(t *testing.T) { - var Kana1 = []byte("の東京ノ") - var Kana2 = []byte("ノ東京の") + Kana1 := []byte("の東京ノ") + Kana2 := []byte("ノ東京の") testRemoteComparison(t, nil, []testcmp{ {"utf8mb4_0900_as_cs", Kana1, Kana2}, diff --git a/go/mysql/collations/integration/helpers_test.go b/go/mysql/collations/integration/helpers_test.go index d436280f04b..a5d2bb0cc36 100644 --- a/go/mysql/collations/integration/helpers_test.go +++ b/go/mysql/collations/integration/helpers_test.go @@ -52,7 +52,7 @@ func testRemoteWeights(t *testing.T, golden io.Writer, cases []testweight) { for _, tc := range cases { t.Run(tc.collation, func(t *testing.T) { - local := collations.Local().LookupByName(tc.collation) + local := collations.MySQL8().LookupByName(tc.collation) remote := remote.NewCollation(conn, tc.collation) localResult := colldata.Lookup(local).WeightString(nil, tc.input, 0) remoteResult := remote.WeightString(nil, tc.input, 0) @@ -85,7 +85,7 @@ func testRemoteComparison(t *testing.T, golden io.Writer, cases []testcmp) { for _, tc := range cases { t.Run(tc.collation, func(t *testing.T) { - local := collations.Local().LookupByName(tc.collation) + local := collations.MySQL8().LookupByName(tc.collation) remote := remote.NewCollation(conn, tc.collation) localResult := normalizecmp(colldata.Lookup(local).Collate(tc.left, tc.right, false)) remoteResult := remote.Collate(tc.left, tc.right, false) diff --git a/go/mysql/collations/integration/main_test.go b/go/mysql/collations/integration/main_test.go index f0d8d4dfdfa..23c6f8d2716 100644 --- a/go/mysql/collations/integration/main_test.go +++ b/go/mysql/collations/integration/main_test.go @@ -47,7 +47,7 @@ func mysqlconn(t *testing.T) *mysql.Conn { if err != nil { t.Fatal(err) } - if !strings.HasPrefix(conn.ServerVersion, "8.0.") { + if !strings.HasPrefix(conn.ServerVersion, "8.") { conn.Close() t.Skipf("collation integration tests are only supported in MySQL 8.0+") } diff --git a/go/mysql/collations/integration/weight_string_test.go b/go/mysql/collations/integration/weight_string_test.go index 666856ca38b..ad4ad4270fc 100644 --- a/go/mysql/collations/integration/weight_string_test.go +++ b/go/mysql/collations/integration/weight_string_test.go @@ -60,7 +60,7 @@ func TestWeightStringsComprehensive(t *testing.T) { conn := mysqlconn(t) defer conn.Close() - allCollations := colldata.All(collations.Local()) + allCollations := colldata.All(collations.MySQL8()) sort.Slice(allCollations, func(i, j int) bool { return allCollations[i].ID() < allCollations[j].ID() }) @@ -104,7 +104,7 @@ func TestCJKWeightStrings(t *testing.T) { conn := mysqlconn(t) defer conn.Close() - allCollations := colldata.All(collations.Local()) + allCollations := colldata.All(collations.MySQL8()) testdata, _ := filepath.Glob("../internal/charset/testdata/*.txt") for _, testfile := range testdata { cs := filepath.Base(testfile) diff --git a/go/mysql/collations/integration/wildcard_test.go b/go/mysql/collations/integration/wildcard_test.go index 6475a35dd21..6a0271218dc 100644 --- a/go/mysql/collations/integration/wildcard_test.go +++ b/go/mysql/collations/integration/wildcard_test.go @@ -79,7 +79,7 @@ func TestRemoteWildcardMatches(t *testing.T) { {"Ǎḅeçd", "a%bd"}, } - for _, local := range colldata.All(collations.Local()) { + for _, local := range colldata.All(collations.MySQL8()) { t.Run(local.Name(), func(t *testing.T) { var remote = remote.NewCollation(conn, local.Name()) var err error diff --git a/go/mysql/collations/internal/uca/contractions.go b/go/mysql/collations/internal/uca/contractions.go index d894b0e206e..5866cf5bf53 100644 --- a/go/mysql/collations/internal/uca/contractions.go +++ b/go/mysql/collations/internal/uca/contractions.go @@ -17,93 +17,9 @@ limitations under the License. package uca import ( - "fmt" - "vitess.io/vitess/go/mysql/collations/charset" ) -type trie struct { - children map[rune]*trie - weights []uint16 -} - -func (t *trie) walkCharset(cs charset.Charset, remainder []byte, depth int) ([]uint16, []byte, int) { - if len(remainder) > 0 { - cp, width := cs.DecodeRune(remainder) - if cp == charset.RuneError && width < 3 { - return nil, nil, 0 - } - if ch := t.children[cp]; ch != nil { - return ch.walkCharset(cs, remainder[width:], depth+1) - } - } - return t.weights, remainder, depth + 1 -} - -func (t *trie) insert(path []rune, weights []uint16) { - if len(path) == 0 { - if t.weights != nil { - panic("duplicate contraction") - } - t.weights = weights - return - } - - if t.children == nil { - t.children = make(map[rune]*trie) - } - ch := t.children[path[0]] - if ch == nil { - ch = &trie{} - t.children[path[0]] = ch - } - ch.insert(path[1:], weights) -} - -type trieContractor struct { - tr trie -} - -func (ctr *trieContractor) insert(c *Contraction) { - if len(c.Path) < 2 { - panic("contraction is too short") - } - if len(c.Weights)%3 != 0 { - panic(fmt.Sprintf("weights are not well-formed: %#v has len=%d", c.Weights, len(c.Weights))) - } - if c.Contextual && len(c.Path) != 2 { - panic("contextual contractions can only span 2 codepoints") - } - ctr.tr.insert(c.Path, c.Weights) -} - -func (ctr *trieContractor) Find(cs charset.Charset, cp rune, remainder []byte) ([]uint16, []byte, int) { - if tr := ctr.tr.children[cp]; tr != nil { - return tr.walkCharset(cs, remainder, 0) - } - return nil, nil, 0 -} - -func (ctr *trieContractor) FindContextual(cp, prev rune) []uint16 { - if tr := ctr.tr.children[cp]; tr != nil { - if trc := tr.children[prev]; trc != nil { - return trc.weights - } - } - return nil -} - -func NewTrieContractor(all []Contraction) Contractor { - if len(all) == 0 { - return nil - } - ctr := &trieContractor{} - for _, c := range all { - ctr.insert(&c) - } - return ctr -} - type Contraction struct { Path []rune Weights []uint16 diff --git a/go/mysql/collations/local.go b/go/mysql/collations/local.go index 3cf81b270c7..090420e07a7 100644 --- a/go/mysql/collations/local.go +++ b/go/mysql/collations/local.go @@ -19,37 +19,14 @@ limitations under the License. package collations import ( - "sync" - - "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/servenv" ) -var defaultEnv *Environment -var defaultEnvInit sync.Once - -// Local is the default collation Environment for Vitess. This depends -// on the value of the `mysql_server_version` flag passed to this Vitess process. -func Local() *Environment { - defaultEnvInit.Do(func() { - if !flag.Parsed() { - panic("collations.Local() called too early") - } - defaultEnv = NewEnvironment(servenv.MySQLServerVersion()) - }) - return defaultEnv -} - -// Default returns the default collation for this Vitess process. -// This is based on the local collation environment, which is based on the user's configured -// MySQL version for this Vitess deployment. -func Default() ID { - return ID(Local().DefaultConnectionCharset()) -} - -func DefaultCollationForType(t sqltypes.Type) ID { - return CollationForType(t, Default()) +// MySQL8 is the collation Environment for MySQL 8. This should +// only be used for testing where we know it's safe to use this +// version, and we don't need a specific other version. +func MySQL8() *Environment { + return fetchCacheEnvironment(collverMySQL8) } func CollationForType(t sqltypes.Type, fallback ID) ID { diff --git a/go/mysql/collations/tools/colldump/Dockerfile b/go/mysql/collations/tools/colldump/Dockerfile index 3e5acf4d9a6..f6834b438bc 100644 --- a/go/mysql/collations/tools/colldump/Dockerfile +++ b/go/mysql/collations/tools/colldump/Dockerfile @@ -8,7 +8,7 @@ RUN cd /tmp && \ curl -OL https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-${MYSQL_VERSION}.tar.gz && \ tar zxvf mysql-${MYSQL_VERSION}.tar.gz -ADD colldump.cc /tmp/mysql-${MYSQL_VERSION}/strings/colldump.cc +ADD https://gist.githubusercontent.com/vmg/11625faa79574a4d389fb3c04bdd0582/raw/b46389f1d431392cc64d920d4a30306970cff21f/colldump.cc /tmp/mysql-${MYSQL_VERSION}/strings/colldump.cc RUN echo "MYSQL_ADD_EXECUTABLE(colldump colldump.cc SKIP_INSTALL)\nTARGET_LINK_LIBRARIES(colldump strings)\n" >> /tmp/mysql-${MYSQL_VERSION}/strings/CMakeLists.txt RUN cd /tmp/mysql-${MYSQL_VERSION} && \ diff --git a/go/mysql/collations/tools/colldump/colldump.cc b/go/mysql/collations/tools/colldump/colldump.cc deleted file mode 100644 index 7668ae1dc70..00000000000 --- a/go/mysql/collations/tools/colldump/colldump.cc +++ /dev/null @@ -1,418 +0,0 @@ -/* Copyright (c) 2023, The Vitess Authors - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2.0, - as published by the Free Software Foundation. - - This program is also distributed with certain software (including - but not limited to OpenSSL) that is licensed under separate terms, - as designated in a particular file or component or in included license - documentation. The authors of MySQL hereby grant you an additional - permission to link the program and your derivative works with the - separately licensed software that they have included with MySQL. - - Without limiting anything contained in the foregoing, this file, - which is part of C Driver for MySQL (Connector/C), is also subject to the - Universal FOSS Exception, version 1.0, a copy of which can be found at - http://oss.oracle.com/licenses/universal-foss-exception. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License, version 2.0, for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include -#include -#include -#include -#include -#include - -#include "m_ctype.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "my_sys.h" -#include "my_config.h" -#include "my_compiler.h" -#include "my_inttypes.h" -#include "my_io.h" -#include "my_loglevel.h" -#include "my_macros.h" -#include "str_uca_type.h" - -#include "rapidjson/rapidjson.h" -#include "rapidjson/filewritestream.h" -#include "rapidjson/writer.h" - -template -static void print_contractions_1(J &json, my_wc_t *path, size_t depth, bool contextual, const MY_CONTRACTION &contraction) -{ - path[depth] = contraction.ch; - - if (contraction.is_contraction_tail) - { - json.StartObject(); - - json.Key("Path"); - json.StartArray(); - for (size_t i = 0; i <= depth; i++) - { - json.Uint((unsigned int)path[i]); - } - json.EndArray(); - - json.Key("Weights"); - json.StartArray(); - for (size_t i = 0; i < MY_UCA_MAX_WEIGHT_SIZE; i++) - { - json.Uint(contraction.weight[i]); - } - json.EndArray(); - - if (contextual) - { - json.Key("Contextual"); - json.Bool(true); - } - - json.EndObject(); - } - - for (const MY_CONTRACTION &ctr : contraction.child_nodes) - { - print_contractions_1(json, path, depth + 1, false, ctr); - } - for (const MY_CONTRACTION &ctr : contraction.child_nodes_context) - { - print_contractions_1(json, path, depth + 1, true, ctr); - } -} - -template -static void print_contractions(J &json, std::vector *contractions) -{ - my_wc_t path[256]; - json.StartArray(); - for (const MY_CONTRACTION &ctr : *contractions) - { - print_contractions_1(json, path, 0, false, ctr); - } - json.EndArray(); -} - -template -static void print_reorder_params(J &json, struct Reorder_param *reorder) -{ - json.StartArray(); - for (int i = 0; i < reorder->wt_rec_num; i++) - { - struct Reorder_wt_rec &r = reorder->wt_rec[i]; - json.StartArray(); - json.Uint(r.old_wt_bdy.begin); - json.Uint(r.old_wt_bdy.end); - json.Uint(r.new_wt_bdy.begin); - json.Uint(r.new_wt_bdy.end); - json.EndArray(); - } - json.EndArray(); -} - -template -static void print_unipages(J &json, const MY_UNI_IDX *unicodeidx) -{ - json.StartArray(); - for (const MY_UNI_IDX *idx = unicodeidx; idx->tab != NULL; idx++) - { - json.StartObject(); - json.Key("From"); - json.Uint(idx->from); - json.Key("To"); - json.Uint(idx->to); - json.Key("Tab"); - json.StartArray(); - const size_t entries = idx->to - idx->from; - for (size_t i = 0; i <= entries; i++) - { - json.Uint(idx->tab[i]); - } - json.EndArray(); - json.EndObject(); - } - json.EndArray(); -} - -template -static void print_uca_weights_900(J &json, int codepoint, uint16 **weights) -{ - uint16 *page = weights[codepoint >> 8]; - if (page == NULL) - return; - - int offset = codepoint & 0xFF; - int cecount = page[offset]; - char key[32]; - snprintf(key, sizeof(key), "U+%04X", codepoint); - - json.Key(key); - json.StartArray(); - for (int ce = 0; ce < cecount; ce++) - { - json.Uint(page[256 + (ce * 3 + 0) * 256 + offset]); - json.Uint(page[256 + (ce * 3 + 1) * 256 + offset]); - json.Uint(page[256 + (ce * 3 + 2) * 256 + offset]); - } - json.EndArray(); -} - -template -static void print_uca_weights_legacy(J &json, int codepoint, uint16 **weights, uchar *lengths) -{ - uint16 *page = weights[codepoint >> 8]; - if (page == NULL) - return; - - int offset = codepoint & 0xFF; - uint16 *w = page + offset * lengths[codepoint >> 8]; - if (!w[0]) - return; - - char key[32]; - snprintf(key, sizeof(key), "U+%04X", codepoint); - - json.Key(key); - json.StartArray(); - for (; w[0]; w++) - { - json.Uint(w[0]); - } - json.EndArray(); -} - -template -static void print_array_uchar(J &json, const uchar *arr, size_t len) -{ - json.StartArray(); - for (size_t i = 0; i < len; ++i) - { - json.Uint(arr[i]); - } - json.EndArray(); -} - -template -static void print_array_uint16(J &json, const uint16 *arr, size_t len) -{ - json.StartArray(); - for (size_t i = 0; i < len; ++i) - { - json.Uint(arr[i]); - } - json.EndArray(); -} - -static CHARSET_INFO *init_collation(const char *name) -{ - MY_CHARSET_LOADER loader; - return my_collation_get_by_name(&loader, name, MYF(0)); -} - -#define MY_UCA_MAXCHAR (0x10FFFF + 1) -#define MY_UCA_CHARS_PER_PAGE 256 - -extern MY_COLLATION_HANDLER my_collation_uca_900_handler; -extern MY_COLLATION_HANDLER my_collation_any_uca_handler; -extern MY_COLLATION_HANDLER my_collation_utf16_uca_handler; -extern MY_COLLATION_HANDLER my_collation_utf32_uca_handler; -extern MY_COLLATION_HANDLER my_collation_ucs2_uca_handler; - -struct KNOWN_HANDLER -{ - const char *name; - const MY_COLLATION_HANDLER *h; -}; - -static KNOWN_HANDLER known_handlers[] = { - {"8bit_bin", &my_collation_8bit_bin_handler}, - {"8bit_simple_ci", &my_collation_8bit_simple_ci_handler}, - {"any_uca", &my_collation_any_uca_handler}, - {"uca_900", &my_collation_uca_900_handler}, - {"utf16_uca", &my_collation_utf16_uca_handler}, - {"utf32_uca", &my_collation_utf32_uca_handler}, - {"ucs2_uca", &my_collation_ucs2_uca_handler}, -}; - -static int dumpall(const char *dumppath) -{ - char pathbuf[4096]; - char jsonbuf[4096 * 4]; - - // bootstrap the `all_charsets` collation array - init_collation("utf8mb4_0900_ai_ci"); - - for (const CHARSET_INFO *charset : all_charsets) - { - if (!charset || (charset->state & MY_CS_AVAILABLE) == 0) - continue; - - charset = init_collation(charset->m_coll_name); - snprintf(pathbuf, sizeof(pathbuf), "%s/%s.json", dumppath, charset->m_coll_name); - - FILE *jsonfile = fopen(pathbuf, "w"); - if (jsonfile == NULL) - { - fprintf(stderr, "failed to create '%s'\n", pathbuf); - return 1; - } - - rapidjson::FileWriteStream os(jsonfile, jsonbuf, sizeof(jsonbuf)); - rapidjson::Writer, rapidjson::ASCII<>> json(os); - - json.StartObject(); - json.Key("Name"); - json.String(charset->m_coll_name); - json.Key("Charset"); - json.String(charset->csname); - json.Key("Number"); - json.Uint(charset->number); - - json.Key("Flags"); - json.StartObject(); - - json.Key("Binary"); - json.Bool((charset->state & MY_CS_BINSORT) != 0); - json.Key("ASCII"); - json.Bool((charset->state & MY_CS_PUREASCII) != 0); - json.Key("Default"); - json.Bool((charset->state & MY_CS_PRIMARY) != 0); - - json.EndObject(); - - for (const KNOWN_HANDLER &handler : known_handlers) - { - if (charset->coll == handler.h) - { - json.Key("CollationImpl"); - json.String(handler.name); - break; - } - } - - if (charset->ctype != NULL) - { - json.Key("CType"); - print_array_uchar(json, charset->ctype, 256); - } - - if (charset->to_lower != NULL) - { - json.Key("ToLower"); - print_array_uchar(json, charset->to_lower, 256); - } - - if (charset->to_upper != NULL) - { - json.Key("ToUpper"); - print_array_uchar(json, charset->to_upper, 256); - } - - if (charset->tab_to_uni != NULL) - { - json.Key("TabToUni"); - print_array_uint16(json, charset->tab_to_uni, 256); - } - - if (charset->tab_from_uni != NULL) - { - json.Key("TabFromUni"); - print_unipages(json, charset->tab_from_uni); - } - - if (charset->sort_order != NULL) - { - json.Key("SortOrder"); - print_array_uchar(json, charset->sort_order, 256); - } - - if (charset->uca != NULL) - { - MY_UCA_INFO *uca = charset->uca; - - json.Key("UCAVersion"); - - switch (uca->version) - { - case UCA_V400: - json.Uint(400); - break; - case UCA_V520: - json.Uint(520); - break; - case UCA_V900: - json.Uint(900); - break; - default: - json.Uint(0); - break; - } - - json.Key("Weights"); - json.StartObject(); - if (uca->version == UCA_V900) - { - for (my_wc_t cp = 0; cp < MY_UCA_MAXCHAR; cp++) - { - print_uca_weights_900(json, cp, uca->weights); - } - } - else - { - for (my_wc_t cp = 0; cp < uca->maxchar; cp++) - { - print_uca_weights_legacy(json, cp, uca->weights, uca->lengths); - } - } - json.EndObject(); - - if (uca->have_contractions) - { - json.Key("Contractions"); - print_contractions(json, uca->contraction_nodes); - } - } - - if (charset->coll_param != NULL) - { - json.Key("UppercaseFirst"); - json.Bool(charset->coll_param->case_first == CASE_FIRST_UPPER); - - if (charset->coll_param->reorder_param != NULL) - { - json.Key("Reorder"); - print_reorder_params(json, charset->coll_param->reorder_param); - } - } - - json.EndObject(); - os.Flush(); - fclose(jsonfile); - } - return 0; -} - -int main(int argc, char **argv) -{ - if (argc < 2) - { - fprintf(stderr, "usage: %s \n", argv[0]); - return 1; - } - - return dumpall(argv[1]); -} \ No newline at end of file diff --git a/go/mysql/collations/tools/makecolldata/codegen/tablegen.go b/go/mysql/collations/tools/makecolldata/codegen/tablegen.go index b12d32f59d7..e1549c23bff 100644 --- a/go/mysql/collations/tools/makecolldata/codegen/tablegen.go +++ b/go/mysql/collations/tools/makecolldata/codegen/tablegen.go @@ -224,20 +224,6 @@ func (tg *TableGenerator) entryForCodepoint(codepoint rune) (*page, *entry) { return page, entry } -func (tg *TableGenerator) Add900(codepoint rune, rhs [][3]uint16) { - page, entry := tg.entryForCodepoint(codepoint) - page.entryCount++ - - for i, weights := range rhs { - if i >= uca.MaxCollationElementsPerCodepoint { - break - } - for _, we := range weights { - entry.weights = append(entry.weights, we) - } - } -} - func (tg *TableGenerator) Add(codepoint rune, weights []uint16) { page, entry := tg.entryForCodepoint(codepoint) page.entryCount++ @@ -248,22 +234,6 @@ func (tg *TableGenerator) Add(codepoint rune, weights []uint16) { entry.weights = append(entry.weights, weights...) } -func (tg *TableGenerator) AddFromAllkeys(lhs []rune, rhs [][]int, vars []int) { - if len(lhs) > 1 || lhs[0] > tg.maxChar { - // TODO: support contractions - return - } - - var weights [][3]uint16 - for _, we := range rhs { - if len(we) != 3 { - panic("non-triplet weight in allkeys.txt") - } - weights = append(weights, [3]uint16{uint16(we[0]), uint16(we[1]), uint16(we[2])}) - } - tg.Add900(lhs[0], weights) -} - func (tg *TableGenerator) writePage(g *Generator, p *page, layout uca.Layout) string { var weights []uint16 diff --git a/go/mysql/collations/tools/maketestdata/maketestdata.go b/go/mysql/collations/tools/maketestdata/maketestdata.go index edad1c840a3..7adee5d5dfd 100644 --- a/go/mysql/collations/tools/maketestdata/maketestdata.go +++ b/go/mysql/collations/tools/maketestdata/maketestdata.go @@ -167,7 +167,7 @@ func main() { fs := pflag.NewFlagSet("maketestdata", pflag.ExitOnError) flag.Parse(fs) - var defaults = collations.Local() + var defaults = collations.MySQL8() var collationsForLanguage = make(map[testutil.Lang][]collations.ID) var allcollations = colldata.All(defaults) for lang := range testutil.KnownLanguages { diff --git a/go/mysql/config/config.go b/go/mysql/config/config.go new file mode 100644 index 00000000000..cc08107f0a3 --- /dev/null +++ b/go/mysql/config/config.go @@ -0,0 +1,4 @@ +package config + +const DefaultSQLMode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" +const DefaultMySQLVersion = "8.0.30" diff --git a/go/mysql/conn.go b/go/mysql/conn.go index 9cac35ea01f..925dfbfa8e4 100644 --- a/go/mysql/conn.go +++ b/go/mysql/conn.go @@ -28,7 +28,6 @@ import ( "strings" "sync" "sync/atomic" - "syscall" "time" "vitess.io/vitess/go/bucketpool" @@ -44,6 +43,8 @@ import ( ) const ( + DefaultFlushDelay = 100 * time.Millisecond + // connBufferSize is how much we buffer for reading and // writing. It is also how much we allocate for ephemeral buffers. connBufferSize = 16 * 1024 @@ -129,6 +130,7 @@ type Conn struct { bufferedReader *bufio.Reader flushTimer *time.Timer + flushDelay time.Duration header [packetHeaderSize]byte // Keep track of how and of the buffer we allocated for an @@ -213,10 +215,9 @@ type Conn struct { // this is used to mark the connection to be closed so that the command phase for the connection can be stopped and // the connection gets closed. closing bool -} -// splitStatementFunciton is the function that is used to split the statement in case of a multi-statement query. -var splitStatementFunction = sqlparser.SplitStatementToPieces + truncateErrLen int +} // PrepareData is a buffer used for store prepare statement meta data type PrepareData struct { @@ -247,10 +248,15 @@ var readersPool = sync.Pool{New: func() any { return bufio.NewReaderSize(nil, co // newConn is an internal method to create a Conn. Used by client and server // side for common creation code. -func newConn(conn net.Conn) *Conn { +func newConn(conn net.Conn, flushDelay time.Duration, truncateErrLen int) *Conn { + if flushDelay == 0 { + flushDelay = DefaultFlushDelay + } return &Conn{ conn: conn, bufferedReader: bufio.NewReaderSize(conn, connBufferSize), + flushDelay: flushDelay, + truncateErrLen: truncateErrLen, } } @@ -271,10 +277,12 @@ func newServerConn(conn net.Conn, listener *Listener) *Conn { } c := &Conn{ - conn: conn, - listener: listener, - PrepareData: make(map[uint32]*PrepareData), - keepAliveOn: enabledKeepAlive, + conn: conn, + listener: listener, + PrepareData: make(map[uint32]*PrepareData), + keepAliveOn: enabledKeepAlive, + flushDelay: listener.flushDelay, + truncateErrLen: listener.truncateErrLen, } if listener.connReadBufferSize > 0 { @@ -348,7 +356,7 @@ func (c *Conn) returnReader() { // startFlushTimer must be called while holding lock on bufMu. func (c *Conn) startFlushTimer() { if c.flushTimer == nil { - c.flushTimer = time.AfterFunc(mysqlServerFlushDelay, func() { + c.flushTimer = time.AfterFunc(c.flushDelay, func() { c.bufMu.Lock() defer c.bufMu.Unlock() @@ -358,7 +366,7 @@ func (c *Conn) startFlushTimer() { c.bufferedWriter.Flush() }) } else { - c.flushTimer.Reset(mysqlServerFlushDelay) + c.flushTimer.Reset(c.flushDelay) } } @@ -1228,7 +1236,7 @@ func (c *Conn) handleComPrepare(handler Handler, data []byte) (kontinue bool) { var queries []string if c.Capabilities&CapabilityClientMultiStatements != 0 { var err error - queries, err = splitStatementFunction(query) + queries, err = handler.Env().Parser().SplitStatementToPieces(query) if err != nil { log.Errorf("Conn %v: Error splitting query: %v", c, err) return c.writeErrorPacketFromErrorAndLog(err) @@ -1241,14 +1249,14 @@ func (c *Conn) handleComPrepare(handler Handler, data []byte) (kontinue bool) { queries = []string{query} } - // Popoulate PrepareData + // Populate PrepareData c.StatementID++ prepare := &PrepareData{ StatementID: c.StatementID, PrepareStmt: queries[0], } - statement, err := sqlparser.ParseStrictDDL(query) + statement, err := handler.Env().Parser().ParseStrictDDL(query) if err != nil { log.Errorf("Conn %v: Error parsing prepared statement: %v", c, err) if !c.writeErrorPacketFromErrorAndLog(err) { @@ -1356,7 +1364,7 @@ func (c *Conn) handleComQuery(handler Handler, data []byte) (kontinue bool) { var queries []string var err error if c.Capabilities&CapabilityClientMultiStatements != 0 { - queries, err = splitStatementFunction(query) + queries, err = handler.Env().Parser().SplitStatementToPieces(query) if err != nil { log.Errorf("Conn %v: Error splitting query: %v", c, err) return c.writeErrorPacketFromErrorAndLog(err) @@ -1517,35 +1525,30 @@ type PacketOK struct { sessionStateData string } -func (c *Conn) parseOKPacket(in []byte) (*PacketOK, error) { +func (c *Conn) parseOKPacket(packetOK *PacketOK, in []byte) error { data := &coder{ data: in, pos: 1, // We already read the type. } - packetOK := &PacketOK{} - - fail := func(format string, args ...any) (*PacketOK, error) { - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, format, args...) - } // Affected rows. affectedRows, ok := data.readLenEncInt() if !ok { - return fail("invalid OK packet affectedRows: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet affectedRows: %v", data.data) } packetOK.affectedRows = affectedRows // Last Insert ID. lastInsertID, ok := data.readLenEncInt() if !ok { - return fail("invalid OK packet lastInsertID: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet lastInsertID: %v", data.data) } packetOK.lastInsertID = lastInsertID // Status flags. statusFlags, ok := data.readUint16() if !ok { - return fail("invalid OK packet statusFlags: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet statusFlags: %v", data.data) } packetOK.statusFlags = statusFlags @@ -1553,7 +1556,7 @@ func (c *Conn) parseOKPacket(in []byte) (*PacketOK, error) { // Warnings. warnings, ok := data.readUint16() if !ok { - return fail("invalid OK packet warnings: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet warnings: %v", data.data) } packetOK.warnings = warnings @@ -1570,7 +1573,7 @@ func (c *Conn) parseOKPacket(in []byte) (*PacketOK, error) { if !ok || length == 0 { // In case we have no more data or a zero length string, there's no additional information so // we can return the packet. - return packetOK, nil + return nil } // Alright, now we need to read each sub packet from the session state change. @@ -1582,7 +1585,7 @@ func (c *Conn) parseOKPacket(in []byte) (*PacketOK, error) { } sessionLen, ok := data.readLenEncInt() if !ok { - return fail("invalid OK packet session state change length for type %v", sscType) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet session state change length for type %v", sscType) } if sscType != SessionTrackGtids { @@ -1595,19 +1598,19 @@ func (c *Conn) parseOKPacket(in []byte) (*PacketOK, error) { // read (and ignore for now) the GTIDS encoding specification code: 1 byte _, ok = data.readByte() if !ok { - return fail("invalid OK packet gtids type: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet gtids type: %v", data.data) } gtids, ok := data.readLenEncString() if !ok { - return fail("invalid OK packet gtids: %v", data) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid OK packet gtids: %v", data.data) } packetOK.sessionStateData = gtids } } } - return packetOK, nil + return nil } // isErrorPacket determines whether or not the packet is an error packet. Mostly here for @@ -1699,41 +1702,3 @@ func (c *Conn) IsMarkedForClose() bool { func (c *Conn) IsShuttingDown() bool { return c.listener.shutdown.Load() } - -// ConnCheck ensures that this connection to the MySQL server hasn't been broken. -// This is a fast, non-blocking check. For details on its implementation, please read -// "Three Bugs in the Go MySQL Driver" (Vicent Marti, GitHub, 2020) -// https://github.blog/2020-05-20-three-bugs-in-the-go-mysql-driver/ -func (c *Conn) ConnCheck() error { - conn := c.conn - if tlsconn, ok := conn.(*tls.Conn); ok { - conn = tlsconn.NetConn() - } - if conn, ok := conn.(syscall.Conn); ok { - rc, err := conn.SyscallConn() - if err != nil { - return err - } - - var n int - var buff [1]byte - rerr := rc.Read(func(fd uintptr) bool { - n, err = syscall.Read(int(fd), buff[:]) - return true - }) - - switch { - case rerr != nil: - return rerr - case n == 0 && err == nil: - return io.EOF - case n > 0: - return sqlerror.NewSQLError(sqlerror.CRUnknownError, sqlerror.SSUnknownSQLState, "unexpected read from conn") - case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK: - return nil - default: - return err - } - } - return nil -} diff --git a/go/mysql/conn_fake.go b/go/mysql/conn_fake.go index e61f90d33f1..7bc4fd5ff61 100644 --- a/go/mysql/conn_fake.go +++ b/go/mysql/conn_fake.go @@ -84,7 +84,7 @@ var _ net.Addr = (*mockAddress)(nil) // GetTestConn returns a conn for testing purpose only. func GetTestConn() *Conn { - return newConn(testConn{}) + return newConn(testConn{}, DefaultFlushDelay, 0) } // GetTestServerConn is only meant to be used for testing. diff --git a/go/mysql/conn_params.go b/go/mysql/conn_params.go index 061aa23f220..46e733f6021 100644 --- a/go/mysql/conn_params.go +++ b/go/mysql/conn_params.go @@ -17,35 +17,38 @@ limitations under the License. package mysql import ( + "time" + + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/vttls" ) // ConnParams contains all the parameters to use to connect to mysql. type ConnParams struct { - Host string `json:"host"` - Port int `json:"port"` - Uname string `json:"uname"` - Pass string `json:"pass"` - DbName string `json:"dbname"` - UnixSocket string `json:"unix_socket"` - Charset string `json:"charset"` - Flags uint64 `json:"flags"` - Flavor string `json:"flavor,omitempty"` + Host string + Port int + Uname string + Pass string + DbName string + UnixSocket string + Charset collations.ID + Flags uint64 + Flavor string // The following SSL flags control the SSL behavior. // // Not setting this value implies preferred mode unless // the CapabilityClientSSL bit is set in db_flags. In the // flag is set, it ends up equivalent to verify_identity mode. - SslMode vttls.SslMode `json:"ssl_mode"` - SslCa string `json:"ssl_ca"` - SslCaPath string `json:"ssl_ca_path"` - SslCert string `json:"ssl_cert"` - SslCrl string `json:"ssl_crl"` - SslKey string `json:"ssl_key"` - TLSMinVersion string `json:"tls_min_version"` - ServerName string `json:"server_name"` - ConnectTimeoutMs uint64 `json:"connect_timeout_ms"` + SslMode vttls.SslMode + SslCa string + SslCaPath string + SslCert string + SslCrl string + SslKey string + TLSMinVersion string + ServerName string + ConnectTimeoutMs uint64 // The following is only set to force the client to connect without // using CapabilityClientDeprecateEOF @@ -57,6 +60,11 @@ type ConnParams struct { // for informative purposes. It has no programmatic value. Returning this field is // disabled by default. EnableQueryInfo bool + + // FlushDelay is the delay after which buffered response will be flushed to the client. + FlushDelay time.Duration + + TruncateErrLen int } // EnableSSL will set the right flag on the parameters. diff --git a/go/mysql/conn_flaky_test.go b/go/mysql/conn_test.go similarity index 98% rename from go/mysql/conn_flaky_test.go rename to go/mysql/conn_test.go index 9df52a47589..69ba1fa5e0c 100644 --- a/go/mysql/conn_flaky_test.go +++ b/go/mysql/conn_test.go @@ -31,18 +31,15 @@ import ( "testing" "time" - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/sqlparser" - "github.com/stretchr/testify/assert" - - "vitess.io/vitess/go/test/utils" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vtenv" ) func createSocketPair(t *testing.T) (net.Listener, *Conn, *Conn) { @@ -77,8 +74,8 @@ func createSocketPair(t *testing.T) (net.Listener, *Conn, *Conn) { require.Nil(t, serverErr, "Accept failed: %v", serverErr) // Create a Conn on both sides. - cConn := newConn(clientConn) - sConn := newConn(serverConn) + cConn := newConn(clientConn, DefaultFlushDelay, 0) + sConn := newConn(serverConn, DefaultFlushDelay, 0) sConn.PrepareData = map[uint32]*PrepareData{} return listener, sConn, cConn @@ -249,7 +246,8 @@ func TestBasicPackets(t *testing.T) { require.NotEmpty(data) assert.EqualValues(data[0], OKPacket, "OKPacket") - packetOk, err := cConn.parseOKPacket(data) + var packetOk PacketOK + err = cConn.parseOKPacket(&packetOk, data) require.NoError(err) assert.EqualValues(12, packetOk.affectedRows) assert.EqualValues(34, packetOk.lastInsertID) @@ -275,7 +273,7 @@ func TestBasicPackets(t *testing.T) { require.NotEmpty(data) assert.EqualValues(data[0], OKPacket, "OKPacket") - packetOk, err = cConn.parseOKPacket(data) + err = cConn.parseOKPacket(&packetOk, data) require.NoError(err) assert.EqualValues(23, packetOk.affectedRows) assert.EqualValues(45, packetOk.lastInsertID) @@ -298,7 +296,7 @@ func TestBasicPackets(t *testing.T) { require.NotEmpty(data) assert.True(cConn.isEOFPacket(data), "expected EOF") - packetOk, err = cConn.parseOKPacket(data) + err = cConn.parseOKPacket(&packetOk, data) require.NoError(err) assert.EqualValues(12, packetOk.affectedRows) assert.EqualValues(34, packetOk.lastInsertID) @@ -359,7 +357,7 @@ func TestOkPackets(t *testing.T) { dataIn: ` 00000000 00 00 00 02 00 |.....|`, cc: CapabilityClientTransactions, - expectedErr: "invalid OK packet warnings: &{[0 0 0 2 0] 0}", + expectedErr: "invalid OK packet warnings: [0 0 0 2 0]", }, { dataIn: ` 00000000 FE 00 00 22 40 00 00 |.....|`, @@ -693,7 +691,8 @@ func TestOkPackets(t *testing.T) { cConn.Capabilities = testCase.cc sConn.Capabilities = testCase.cc // parse the packet - packetOk, err := cConn.parseOKPacket(data) + var packetOk PacketOK + err := cConn.parseOKPacket(&packetOk, data) if testCase.expectedErr != "" { require.Error(t, err) require.Equal(t, testCase.expectedErr, err.Error()) @@ -702,7 +701,7 @@ func TestOkPackets(t *testing.T) { require.NoError(t, err, "failed to parse OK packet") // write the ok packet from server - err = sConn.writeOKPacket(packetOk) + err = sConn.writeOKPacket(&packetOk) require.NoError(t, err, "failed to write OK packet") // receive the ok packet on client @@ -878,14 +877,6 @@ func TestMultiStatement(t *testing.T) { func TestMultiStatementOnSplitError(t *testing.T) { listener, sConn, cConn := createSocketPair(t) - // Set the splitStatementFunction to return an error. - splitStatementFunction = func(blob string) (pieces []string, err error) { - return nil, fmt.Errorf("Error in split statements") - } - defer func() { - // Set the splitStatementFunction to the correct function back - splitStatementFunction = sqlparser.SplitStatementToPieces - }() sConn.Capabilities |= CapabilityClientMultiStatements defer func() { listener.Close() @@ -893,7 +884,7 @@ func TestMultiStatementOnSplitError(t *testing.T) { cConn.Close() }() - err := cConn.WriteComQuery("select 1;select 2") + err := cConn.WriteComQuery("broken>'query 1;parse 0: + return sqlerror.NewSQLError(sqlerror.CRUnknownError, sqlerror.SSUnknownSQLState, "unexpected read from conn") + case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK: + return nil + default: + return err + } + } + return nil +} diff --git a/go/mysql/conn_windows.go b/go/mysql/conn_windows.go new file mode 100644 index 00000000000..695c5703cdb --- /dev/null +++ b/go/mysql/conn_windows.go @@ -0,0 +1,24 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysql + +// ConnCheck is not implemented for Windows. +func (c *Conn) ConnCheck() error { + return nil +} diff --git a/go/mysql/constants.go b/go/mysql/constants.go index 194ed568b39..1d848c7b8ac 100644 --- a/go/mysql/constants.go +++ b/go/mysql/constants.go @@ -17,7 +17,7 @@ limitations under the License. package mysql import ( - "vitess.io/vitess/go/mysql/binlog" + "vitess.io/vitess/go/sqltypes" ) const ( @@ -274,10 +274,15 @@ const ( AuthSwitchRequestPacket = 0xfe ) +var typeInt24, _ = sqltypes.TypeToMySQL(sqltypes.Int24) +var typeTimestamp, _ = sqltypes.TypeToMySQL(sqltypes.Timestamp) +var typeYear, _ = sqltypes.TypeToMySQL(sqltypes.Year) +var typeNewDecimal, _ = sqltypes.TypeToMySQL(sqltypes.Decimal) + // IsNum returns true if a MySQL type is a numeric value. // It is the same as IS_NUM defined in mysql.h. func IsNum(typ uint8) bool { - return (typ <= binlog.TypeInt24 && typ != binlog.TypeTimestamp) || - typ == binlog.TypeYear || - typ == binlog.TypeNewDecimal + return (typ <= typeInt24 && typ != typeTimestamp) || + typ == typeYear || + typ == typeNewDecimal } diff --git a/go/mysql/datetime/datetime.go b/go/mysql/datetime/datetime.go index bf73ac85c27..1673a71d662 100644 --- a/go/mysql/datetime/datetime.go +++ b/go/mysql/datetime/datetime.go @@ -245,7 +245,14 @@ func (d Date) Hash(h *vthash.Hasher) { } func (d Date) Weekday() time.Weekday { - return d.ToStdTime(time.Local).Weekday() + // Go considers 0000-01-01 day as Saturday, while + // MySQL considers it to be Sunday, now 0000-02-29 exists in + // Go but not in MySQL so it balances out after that + wd := d.ToStdTime(time.Local).Weekday() + if d.Year() == 0 && d.Month() <= 2 { + wd = (wd + 1) % 7 + } + return wd } func (d Date) Yearday() int { @@ -315,12 +322,16 @@ func (d Date) Week(mode int) int { year, week := d.SundayWeek() if year < d.Year() { return 0 + } else if year > d.Year() { + return 53 } return week case 1: year, week := d.ISOWeek() if year < d.Year() { return 0 + } else if year > d.Year() { + return 53 } return week case 2: @@ -333,12 +344,16 @@ func (d Date) Week(mode int) int { year, week := d.Sunday4DayWeek() if year < d.Year() { return 0 + } else if year > d.Year() { + return 53 } return week case 5: year, week := d.MondayWeek() if year < d.Year() { return 0 + } else if year > d.Year() { + return 53 } return week case 6: @@ -360,9 +375,12 @@ func (d Date) YearWeek(mode int) int { case 1, 3: year, week := d.ISOWeek() return year*100 + week - case 4, 5, 6, 7: - // TODO - return 0 + case 4, 6: + year, week := d.Sunday4DayWeek() + return year*100 + week + case 5, 7: + year, week := d.MondayWeek() + return year*100 + week default: return d.YearWeek(DefaultWeekMode) } @@ -432,12 +450,16 @@ func (t Time) AddInterval(itv *Interval, stradd bool) (Time, uint8, bool) { return dt.Time, itv.precision(stradd), ok } -func (t Time) toSeconds() int { - tsecs := t.Hour()*secondsPerHour + t.Minute()*secondsPerMinute + t.Second() +func (t Time) toDuration() time.Duration { + dur := time.Duration(t.hour)*time.Hour + time.Duration(t.minute)*time.Minute + time.Duration(t.second)*time.Second + time.Duration(t.nanosecond)*time.Nanosecond if t.Neg() { - return -tsecs + return -dur } - return tsecs + return dur +} + +func (t Time) ToSeconds() int64 { + return int64(t.ToDuration().Seconds()) } func (d Date) ToStdTime(loc *time.Location) (out time.Time) { @@ -538,9 +560,9 @@ func (dt DateTime) Compare(dt2 DateTime) int { return dt.Time.Compare(dt2.Time) } -func (dt DateTime) AddInterval(itv *Interval, stradd bool) (DateTime, uint8, bool) { +func (dt DateTime) AddInterval(itv *Interval, prec uint8, stradd bool) (DateTime, uint8, bool) { ok := dt.addInterval(itv) - return dt, itv.precision(stradd), ok + return dt, max(prec, itv.precision(stradd)), ok } func (dt DateTime) Round(p int) (r DateTime) { @@ -569,8 +591,12 @@ func (dt DateTime) Round(p int) (r DateTime) { return r } -func (dt DateTime) toSeconds() int { - return (dt.Date.Day()-1)*secondsPerDay + dt.Time.toSeconds() +func (dt DateTime) toDuration() time.Duration { + dur := dt.Time.toDuration() + if !dt.Date.IsZero() { + dur += time.Duration(dt.Date.Day()-1) * durationPerDay + } + return dur } func (dt *DateTime) addInterval(itv *Interval) bool { @@ -580,29 +606,25 @@ func (dt *DateTime) addInterval(itv *Interval) bool { return false } - nsec := dt.Time.Nanosecond() + itv.nsec - sec := dt.toSeconds() + itv.toSeconds() + (nsec / int(time.Second)) - nsec = nsec % int(time.Second) - - if nsec < 0 { - nsec += int(time.Second) - sec-- - } - - days := sec / secondsPerDay - sec -= days * secondsPerDay + dur := dt.toDuration() + dur += itv.toDuration() + days := time.Duration(0) + if !dt.Date.IsZero() { + days = dur / durationPerDay + dur -= days * durationPerDay - if sec < 0 { - sec += secondsPerDay - days-- + if dur < 0 { + dur += durationPerDay + days-- + } } - dt.Time.nanosecond = uint32(nsec) - dt.Time.second = uint8(sec % secondsPerMinute) - dt.Time.minute = uint8((sec / secondsPerMinute) % secondsPerMinute) - dt.Time.hour = uint16(sec / secondsPerHour) + dt.Time.nanosecond = uint32((dur % time.Second) / time.Nanosecond) + dt.Time.second = uint8((dur % time.Minute) / time.Second) + dt.Time.minute = uint8((dur % time.Hour) / time.Minute) + dt.Time.hour = uint16(dur / time.Hour) - daynum := mysqlDayNumber(dt.Date.Year(), dt.Date.Month(), 1) + days + daynum := MysqlDayNumber(dt.Date.Year(), dt.Date.Month(), 1) + int(days) if daynum < 0 || daynum > maxDay { return false } @@ -611,7 +633,7 @@ func (dt *DateTime) addInterval(itv *Interval) bool { return true case itv.unit.HasDayParts(): - daynum := mysqlDayNumber(dt.Date.Year(), dt.Date.Month(), dt.Date.Day()) + daynum := MysqlDayNumber(dt.Date.Year(), dt.Date.Month(), dt.Date.Day()) daynum += itv.day dt.Date.year, dt.Date.month, dt.Date.day = mysqlDateFromDayNumber(daynum) return true diff --git a/go/mysql/datetime/helpers.go b/go/mysql/datetime/helpers.go index 33d673782fc..b91114fd791 100644 --- a/go/mysql/datetime/helpers.go +++ b/go/mysql/datetime/helpers.go @@ -245,7 +245,7 @@ func daysIn(m time.Month, year int) int { } func isLeap(year int) bool { - return year%4 == 0 && (year%100 != 0 || year%400 == 0) + return year%4 == 0 && (year%100 != 0 || year%400 == 0) && (year != 0) } func daysInYear(year int) int { @@ -285,7 +285,5 @@ func parseNanoseconds[bytes []byte | string](value bytes, nbytes int) (ns int, l } const ( - secondsPerMinute = 60 - secondsPerHour = 60 * secondsPerMinute - secondsPerDay = 24 * secondsPerHour + durationPerDay = 24 * time.Hour ) diff --git a/go/mysql/datetime/interval.go b/go/mysql/datetime/interval.go index 21395f2174d..75e1ce7bb45 100644 --- a/go/mysql/datetime/interval.go +++ b/go/mysql/datetime/interval.go @@ -258,13 +258,13 @@ func (itv *Interval) inRange() bool { if itv.day > maxDay { return false } - if itv.hour > maxDay*24 { + if itv.hour/24 > maxDay { return false } - if itv.min > maxDay*24*60 { + if itv.min/24/60 > maxDay { return false } - if itv.sec > maxDay*24*60*60 { + if itv.sec/24/60/60 > maxDay { return false } return true diff --git a/go/mysql/datetime/mydate.go b/go/mysql/datetime/mydate.go index 1d4a2eaf958..62cbb3f2524 100644 --- a/go/mysql/datetime/mydate.go +++ b/go/mysql/datetime/mydate.go @@ -16,7 +16,7 @@ limitations under the License. package datetime -// mysqlDayNumber converts a date into an absolute day number. +// MysqlDayNumber converts a date into an absolute day number. // This is an algorithm that has been reverse engineered from MySQL; // the tables used as a reference can be found in `testdata/year_to_daynr.json`. // It is worth noting that this absolute day number does not match the @@ -29,7 +29,7 @@ package datetime // This API should only be used when performing datetime calculations (addition // and subtraction), so that the results match MySQL's. All other date handling // operations must use our helpers based on Go's standard library. -func mysqlDayNumber(year, month, day int) int { +func MysqlDayNumber(year, month, day int) int { if year == 0 && month == 0 { return 0 } @@ -49,8 +49,8 @@ func mysqlDayNumber(year, month, day int) int { // mysqlDateFromDayNumber converts an absolute day number into a date (a year, month, day triplet). // This is an algorithm that has been reverse engineered from MySQL; // the tables used as a reference can be found in `testdata/daynr_to_date.json`. -// See the warning from mysqlDayNumber: the day number used as an argument to -// this function must come from mysqlDayNumber or the results won't be correct. +// See the warning from MysqlDayNumber: the day number used as an argument to +// this function must come from MysqlDayNumber or the results won't be correct. // This API should only be used when performing datetime calculations (addition // and subtraction), so that the results match MySQL's. All other date handling // operations must use our helpers based on Go's standard library. @@ -81,3 +81,11 @@ func mysqlDateFromDayNumber(daynr int) (uint16, uint8, uint8) { panic("unreachable: yday is too large?") } + +// DateFromDayNumber converts an absolute day number into a Date. +// Returns zero date if day number exceeds 3652499 or is less than 366. +func DateFromDayNumber(daynr int) Date { + var d Date + d.year, d.month, d.day = mysqlDateFromDayNumber(daynr) + return d +} diff --git a/go/mysql/datetime/mydate_test.go b/go/mysql/datetime/mydate_test.go index 29ecd2df9d2..ba575ed4e05 100644 --- a/go/mysql/datetime/mydate_test.go +++ b/go/mysql/datetime/mydate_test.go @@ -35,7 +35,7 @@ func TestDayNumber(t *testing.T) { require.NoError(t, err) for year, daynr := range expected { - assert.Equal(t, daynr, mysqlDayNumber(year, 1, 1)) + assert.Equal(t, daynr, MysqlDayNumber(year, 1, 1)) } } @@ -54,6 +54,6 @@ func TestDayNumberFields(t *testing.T) { assert.Equal(t, tc[2], int(m)) assert.Equal(t, tc[3], int(d)) - assert.Equalf(t, tc[0], mysqlDayNumber(tc[1], tc[2], tc[3]), "date %d-%d-%d", tc[1], tc[2], tc[3]) + assert.Equalf(t, tc[0], MysqlDayNumber(tc[1], tc[2], tc[3]), "date %d-%d-%d", tc[1], tc[2], tc[3]) } } diff --git a/go/mysql/datetime/parse.go b/go/mysql/datetime/parse.go index e8f17191f4c..0d9e6bb2326 100644 --- a/go/mysql/datetime/parse.go +++ b/go/mysql/datetime/parse.go @@ -24,42 +24,45 @@ import ( "vitess.io/vitess/go/mysql/fastparse" ) -func parsetimeHours(tp *timeparts, in string) (out string, ok bool) { +func parsetimeHours(tp *timeparts, in string) (string, TimeState) { + var ok bool if tp.hour, in, ok = getnumn(in); ok { tp.day = tp.day + tp.hour/24 tp.hour = tp.hour % 24 switch { case len(in) == 0: - return "", true + return "", TimeOK case in[0] == ':': return parsetimeMinutes(tp, in[1:]) } } - return "", false + return "", TimePartial } -func parsetimeMinutes(tp *timeparts, in string) (out string, ok bool) { +func parsetimeMinutes(tp *timeparts, in string) (string, TimeState) { + var ok bool if tp.min, in, ok = getnum(in, false); ok { switch { case tp.min > 59: - return "", false + return "", TimeInvalid case len(in) == 0: - return "", true + return "", TimeOK case in[0] == ':': return parsetimeSeconds(tp, in[1:]) } } - return "", false + return "", TimePartial } -func parsetimeSeconds(tp *timeparts, in string) (out string, ok bool) { +func parsetimeSeconds(tp *timeparts, in string) (string, TimeState) { + var ok bool if tp.sec, in, ok = getnum(in, false); ok { switch { case tp.sec > 59: - return "", false + return "", TimeInvalid case len(in) == 0: - return "", true + return "", TimeOK case len(in) > 1 && in[0] == '.': n := 1 for ; n < len(in) && isDigit(in, n); n++ { @@ -67,14 +70,18 @@ func parsetimeSeconds(tp *timeparts, in string) (out string, ok bool) { var l int tp.nsec, l, ok = parseNanoseconds(in, n) tp.prec = uint8(l) - return "", ok && len(in) == n + if ok && len(in) == n { + return "", TimeOK + } + return "", TimePartial } } - return "", false + return "", TimePartial } -func parsetimeAny(tp *timeparts, in string) (out string, ok bool) { +func parsetimeAny(tp *timeparts, in string) (out string, state TimeState) { orig := in + var ok bool for i := 0; i < len(in); i++ { switch r := in[i]; { case isSpace(r): @@ -91,7 +98,7 @@ func parsetimeAny(tp *timeparts, in string) (out string, ok bool) { return parsetimeNoDelimiters(tp, orig) } if tp.day > 34 { - return "", clampTimeparts(tp) + return "", clampTimeparts(tp, state) } return parsetimeHours(tp, in) case r == ':': @@ -101,8 +108,9 @@ func parsetimeAny(tp *timeparts, in string) (out string, ok bool) { return parsetimeNoDelimiters(tp, in) } -func parsetimeNoDelimiters(tp *timeparts, in string) (out string, ok bool) { +func parsetimeNoDelimiters(tp *timeparts, in string) (out string, state TimeState) { var integral int + var ok bool for ; integral < len(in); integral++ { if in[integral] == '.' || !isDigit(in, integral) { break @@ -112,12 +120,9 @@ func parsetimeNoDelimiters(tp *timeparts, in string) (out string, ok bool) { switch integral { default: // MySQL limits this to a numeric value that fits in a 32-bit unsigned integer. - i, _ := fastparse.ParseInt64(in[:integral], 10) + i, _ := fastparse.ParseUint64(in[:integral], 10) if i > math.MaxUint32 { - return "", false - } - if i < -math.MaxUint32 { - return "", false + return "", TimeInvalid } tp.hour, in, ok = getnuml(in, integral-4) @@ -132,7 +137,7 @@ func parsetimeNoDelimiters(tp *timeparts, in string) (out string, ok bool) { case 3, 4: tp.min, in, ok = getnuml(in, integral-2) if !ok || tp.min > 59 { - return "", false + return "", TimeInvalid } integral = 2 fallthrough @@ -140,10 +145,10 @@ func parsetimeNoDelimiters(tp *timeparts, in string) (out string, ok bool) { case 1, 2: tp.sec, in, ok = getnuml(in, integral) if !ok || tp.sec > 59 { - return "", false + return "", TimeInvalid } case 0: - return "", false + return "", TimeInvalid } if len(in) > 1 && in[0] == '.' && isDigit(in, 1) { @@ -152,14 +157,18 @@ func parsetimeNoDelimiters(tp *timeparts, in string) (out string, ok bool) { } var l int tp.nsec, l, ok = parseNanoseconds(in, n) + if !ok { + state = TimeInvalid + } tp.prec = uint8(l) in = in[n:] } - return in, clampTimeparts(tp) && ok + state = clampTimeparts(tp, state) + return in, state } -func clampTimeparts(tp *timeparts) bool { +func clampTimeparts(tp *timeparts, state TimeState) TimeState { // Maximum time is 838:59:59, so we have to clamp // it to that value here if we otherwise successfully // parser the time. @@ -168,15 +177,31 @@ func clampTimeparts(tp *timeparts) bool { tp.hour = 22 tp.min = 59 tp.sec = 59 - return false + if state == TimeOK { + return TimePartial + } } - return true + return state } -func ParseTime(in string, prec int) (t Time, l int, ok bool) { +type TimeState uint8 + +const ( + // TimeOK indicates that the parsed value is valid and complete. + TimeOK TimeState = iota + // TimePartial indicates that the parsed value has a partially parsed value + // but it is not fully complete and valid. There could be additional stray + // data in the input, or it has an overflow. + TimePartial + // TimeInvalid indicates that the parsed value is invalid and no partial + // TIME value could be extracted from the input. + TimeInvalid +) + +func ParseTime(in string, prec int) (t Time, l int, state TimeState) { in = strings.Trim(in, " \t\r\n") if len(in) == 0 { - return Time{}, 0, false + return Time{}, 0, TimeInvalid } var neg bool if in[0] == '-' { @@ -185,11 +210,15 @@ func ParseTime(in string, prec int) (t Time, l int, ok bool) { } var tp timeparts - in, ok = parsetimeAny(&tp, in) - ok = clampTimeparts(&tp) && ok + in, state = parsetimeAny(&tp, in) + if state == TimeInvalid { + return Time{}, 0, state + } + + state = clampTimeparts(&tp, state) hours := uint16(24*tp.day + tp.hour) - if !tp.isZero() && neg { + if neg { hours |= negMask } @@ -206,7 +235,13 @@ func ParseTime(in string, prec int) (t Time, l int, ok bool) { t = t.Round(prec) } - return t, prec, ok && len(in) == 0 + switch { + case state == TimeOK && len(in) == 0: + state = TimeOK + case state == TimeOK && len(in) > 0: + state = TimePartial + } + return t, prec, state } func ParseDate(s string) (Date, bool) { diff --git a/go/mysql/datetime/parse_test.go b/go/mysql/datetime/parse_test.go index 6ed342edfb3..66fb8a73b2f 100644 --- a/go/mysql/datetime/parse_test.go +++ b/go/mysql/datetime/parse_test.go @@ -96,32 +96,33 @@ func TestParseTime(t *testing.T) { output testTime norm string l int - err bool + state TimeState }{ {input: "00:00:00", norm: "00:00:00.000000", output: testTime{}}, - {input: "00:00:00foo", norm: "00:00:00.000000", output: testTime{}, err: true}, + {input: "-00:00:00", norm: "-00:00:00.000000", output: testTime{negative: true}}, + {input: "00:00:00foo", norm: "00:00:00.000000", output: testTime{}, state: TimePartial}, {input: "11:12:13", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}}, - {input: "11:12:13foo", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}, err: true}, + {input: "11:12:13foo", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}, state: TimePartial}, {input: "11:12:13.1", norm: "11:12:13.100000", output: testTime{11, 12, 13, 100000000, false}, l: 1}, - {input: "11:12:13.foo", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}, err: true}, - {input: "11:12:13.1foo", norm: "11:12:13.100000", output: testTime{11, 12, 13, 100000000, false}, l: 1, err: true}, + {input: "11:12:13.foo", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}, state: TimePartial}, + {input: "11:12:13.1foo", norm: "11:12:13.100000", output: testTime{11, 12, 13, 100000000, false}, l: 1, state: TimePartial}, {input: "11:12:13.123456", norm: "11:12:13.123456", output: testTime{11, 12, 13, 123456000, false}, l: 6}, {input: "11:12:13.000001", norm: "11:12:13.000001", output: testTime{11, 12, 13, 1000, false}, l: 6}, {input: "11:12:13.000000", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}, l: 6}, - {input: "11:12:13.123456foo", norm: "11:12:13.123456", output: testTime{11, 12, 13, 123456000, false}, l: 6, err: true}, + {input: "11:12:13.123456foo", norm: "11:12:13.123456", output: testTime{11, 12, 13, 123456000, false}, l: 6, state: TimePartial}, {input: "3 11:12:13", norm: "83:12:13.000000", output: testTime{3*24 + 11, 12, 13, 0, false}}, - {input: "3 11:12:13foo", norm: "83:12:13.000000", output: testTime{3*24 + 11, 12, 13, 0, false}, err: true}, + {input: "3 11:12:13foo", norm: "83:12:13.000000", output: testTime{3*24 + 11, 12, 13, 0, false}, state: TimePartial}, {input: "3 41:12:13", norm: "113:12:13.000000", output: testTime{3*24 + 41, 12, 13, 0, false}}, - {input: "3 41:12:13foo", norm: "113:12:13.000000", output: testTime{3*24 + 41, 12, 13, 0, false}, err: true}, - {input: "34 23:12:13", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: "35 11:12:13", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, + {input: "3 41:12:13foo", norm: "113:12:13.000000", output: testTime{3*24 + 41, 12, 13, 0, false}, state: TimePartial}, + {input: "34 23:12:13", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, + {input: "35 11:12:13", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, {input: "11:12", norm: "11:12:00.000000", output: testTime{11, 12, 0, 0, false}}, {input: "5 11:12", norm: "131:12:00.000000", output: testTime{5*24 + 11, 12, 0, 0, false}}, {input: "-2 11:12", norm: "-59:12:00.000000", output: testTime{2*24 + 11, 12, 0, 0, true}}, - {input: "--2 11:12", norm: "00:00:00.000000", err: true}, - {input: "nonsense", norm: "00:00:00.000000", err: true}, + {input: "--2 11:12", norm: "00:00:00.000000", state: TimeInvalid}, + {input: "nonsense", norm: "00:00:00.000000", state: TimeInvalid}, {input: "2 11", norm: "59:00:00.000000", output: testTime{2*24 + 11, 0, 0, 0, false}}, - {input: "2 -11", norm: "00:00:02.000000", output: testTime{0, 0, 2, 0, false}, err: true}, + {input: "2 -11", norm: "00:00:02.000000", output: testTime{0, 0, 2, 0, false}, state: TimePartial}, {input: "13", norm: "00:00:13.000000", output: testTime{0, 0, 13, 0, false}}, {input: "111213", norm: "11:12:13.000000", output: testTime{11, 12, 13, 0, false}}, {input: "111213.123456", norm: "11:12:13.123456", output: testTime{11, 12, 13, 123456000, false}, l: 6}, @@ -130,19 +131,21 @@ func TestParseTime(t *testing.T) { {input: "25:12:13", norm: "25:12:13.000000", output: testTime{25, 12, 13, 0, false}}, {input: "32:35", norm: "32:35:00.000000", output: testTime{32, 35, 0, 0, false}}, {input: "101:34:58", norm: "101:34:58.000000", output: testTime{101, 34, 58, 0, false}}, + {input: "101:64:58", norm: "00:00:00.000000", state: TimeInvalid}, + {input: "101:34:68", norm: "00:00:00.000000", state: TimeInvalid}, {input: "1", norm: "00:00:01.000000", output: testTime{0, 0, 1, 0, false}}, {input: "11", norm: "00:00:11.000000", output: testTime{0, 0, 11, 0, false}}, {input: "111", norm: "00:01:11.000000", output: testTime{0, 1, 11, 0, false}}, {input: "1111", norm: "00:11:11.000000", output: testTime{0, 11, 11, 0, false}}, {input: "11111", norm: "01:11:11.000000", output: testTime{1, 11, 11, 0, false}}, {input: "111111", norm: "11:11:11.000000", output: testTime{11, 11, 11, 0, false}}, - {input: "1foo", norm: "00:00:01.000000", output: testTime{0, 0, 1, 0, false}, err: true}, - {input: "11foo", norm: "00:00:11.000000", output: testTime{0, 0, 11, 0, false}, err: true}, - {input: "111foo", norm: "00:01:11.000000", output: testTime{0, 1, 11, 0, false}, err: true}, - {input: "1111foo", norm: "00:11:11.000000", output: testTime{0, 11, 11, 0, false}, err: true}, - {input: "11111foo", norm: "01:11:11.000000", output: testTime{1, 11, 11, 0, false}, err: true}, - {input: "111111foo", norm: "11:11:11.000000", output: testTime{11, 11, 11, 0, false}, err: true}, - {input: "1111111foo", norm: "111:11:11.000000", output: testTime{111, 11, 11, 0, false}, err: true}, + {input: "1foo", norm: "00:00:01.000000", output: testTime{0, 0, 1, 0, false}, state: TimePartial}, + {input: "11foo", norm: "00:00:11.000000", output: testTime{0, 0, 11, 0, false}, state: TimePartial}, + {input: "111foo", norm: "00:01:11.000000", output: testTime{0, 1, 11, 0, false}, state: TimePartial}, + {input: "1111foo", norm: "00:11:11.000000", output: testTime{0, 11, 11, 0, false}, state: TimePartial}, + {input: "11111foo", norm: "01:11:11.000000", output: testTime{1, 11, 11, 0, false}, state: TimePartial}, + {input: "111111foo", norm: "11:11:11.000000", output: testTime{11, 11, 11, 0, false}, state: TimePartial}, + {input: "1111111foo", norm: "111:11:11.000000", output: testTime{111, 11, 11, 0, false}, state: TimePartial}, {input: "-1", norm: "-00:00:01.000000", output: testTime{0, 0, 1, 0, true}}, {input: "-11", norm: "-00:00:11.000000", output: testTime{0, 0, 11, 0, true}}, {input: "-111", norm: "-00:01:11.000000", output: testTime{0, 1, 11, 0, true}}, @@ -172,44 +175,31 @@ func TestParseTime(t *testing.T) { {input: "11111.1", norm: "01:11:11.100000", output: testTime{1, 11, 11, 100000000, false}, l: 1}, {input: "111111.1", norm: "11:11:11.100000", output: testTime{11, 11, 11, 100000000, false}, l: 1}, {input: "1111111.1", norm: "111:11:11.100000", output: testTime{111, 11, 11, 100000000, false}, l: 1}, - {input: "20000101", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: "-20000101", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, err: true}, - {input: "999995959", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: "-999995959", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, err: true}, - {input: "4294965959", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: "-4294965959", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, err: true}, - {input: "4294975959", norm: "00:00:00.000000", err: true}, - {input: "-4294975959", norm: "00:00:00.000000", err: true}, - {input: "\t34 foo\t", norm: "00:00:34.000000", output: testTime{0, 0, 34, 0, false}, err: true}, - {input: "\t34 1foo\t", norm: "817:00:00.000000", output: testTime{817, 0, 0, 0, false}, err: true}, - {input: "\t34 23foo\t", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: "\t35 foo\t", norm: "00:00:35.000000", output: testTime{0, 0, 35, 0, false}, err: true}, - {input: "\t35 1foo\t", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, err: true}, - {input: " 255 foo", norm: "00:02:55.000000", output: testTime{0, 2, 55, 0, false}, err: true}, - {input: "255", norm: "00:02:55.000000", output: testTime{0, 2, 55, 0, false}}, + {input: "20000101", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, + {input: "-20000101", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, state: TimePartial}, + {input: "999995959", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, + {input: "-999995959", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, state: TimePartial}, + {input: "4294965959", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, + {input: "-4294965959", norm: "-838:59:59.000000", output: testTime{838, 59, 59, 0, true}, state: TimePartial}, + {input: "4294975959", norm: "00:00:00.000000", state: TimeInvalid}, + {input: "-4294975959", norm: "00:00:00.000000", state: TimeInvalid}, + {input: "\t34 foo\t", norm: "00:00:34.000000", output: testTime{0, 0, 34, 0, false}, state: TimePartial}, + {input: "\t34 1foo\t", norm: "817:00:00.000000", output: testTime{817, 0, 0, 0, false}, state: TimePartial}, + {input: "\t34 23foo\t", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, + {input: "\t35 foo\t", norm: "00:00:35.000000", output: testTime{0, 0, 35, 0, false}, state: TimePartial}, + {input: "\t35 1foo\t", norm: "838:59:59.000000", output: testTime{838, 59, 59, 0, false}, state: TimePartial}, } for _, test := range tests { t.Run(test.input, func(t *testing.T) { - got, l, ok := ParseTime(test.input, -1) - if test.err { - assert.Equal(t, test.output.hour, got.Hour()) - assert.Equal(t, test.output.minute, got.Minute()) - assert.Equal(t, test.output.second, got.Second()) - assert.Equal(t, test.output.nanosecond, got.Nanosecond()) - assert.Equal(t, test.norm, string(got.AppendFormat(nil, 6))) - assert.Equal(t, test.l, l) - assert.Falsef(t, ok, "did not fail to parse %s", test.input) - return - } - - require.True(t, ok) + got, l, state := ParseTime(test.input, -1) + assert.Equal(t, test.state, state) assert.Equal(t, test.output.hour, got.Hour()) assert.Equal(t, test.output.minute, got.Minute()) assert.Equal(t, test.output.second, got.Second()) assert.Equal(t, test.output.nanosecond, got.Nanosecond()) - assert.Equal(t, test.l, l) assert.Equal(t, test.norm, string(got.AppendFormat(nil, 6))) + assert.Equal(t, test.l, l) }) } } diff --git a/go/mysql/datetime/timeparts.go b/go/mysql/datetime/timeparts.go index a774099a93a..ccc0d0a3640 100644 --- a/go/mysql/datetime/timeparts.go +++ b/go/mysql/datetime/timeparts.go @@ -87,6 +87,6 @@ func (tp *timeparts) isZero() bool { return tp.year == 0 && tp.month == 0 && tp.day == 0 && tp.hour == 0 && tp.min == 0 && tp.sec == 0 && tp.nsec == 0 } -func (tp *timeparts) toSeconds() int { - return tp.day*secondsPerDay + tp.hour*3600 + tp.min*60 + tp.sec +func (tp *timeparts) toDuration() time.Duration { + return time.Duration(tp.day)*durationPerDay + time.Duration(tp.hour)*time.Hour + time.Duration(tp.min)*time.Minute + time.Duration(tp.sec)*time.Second + time.Duration(tp.nsec)*time.Nanosecond } diff --git a/go/mysql/decimal/scan.go b/go/mysql/decimal/scan.go index 761eea5cdcf..12fc73af4e2 100644 --- a/go/mysql/decimal/scan.go +++ b/go/mysql/decimal/scan.go @@ -311,17 +311,12 @@ func pow(x big.Word, n int) (p big.Word) { } func parseLargeDecimal(integral, fractional []byte) (*big.Int, error) { - const ( - b1 = big.Word(10) - bn = big.Word(1e19) - n = 19 - ) var ( di = big.Word(0) // 0 <= di < b1**i < bn i = 0 // 0 <= i < n - // 5 is the largest possible size for a MySQL decimal; anything - // that doesn't fit in 5 words won't make it to this func - z = make([]big.Word, 0, 5) + // s is the largest possible size for a MySQL decimal; anything + // that doesn't fit in s words won't make it to this func + z = make([]big.Word, 0, s) ) parseChunk := func(partial []byte) error { diff --git a/go/mysql/decimal/scan_32.go b/go/mysql/decimal/scan_32.go new file mode 100644 index 00000000000..c0417a1ffce --- /dev/null +++ b/go/mysql/decimal/scan_32.go @@ -0,0 +1,28 @@ +//go:build 386 || arm || mips || mipsle + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package decimal + +import "math/big" + +const ( + b1 = big.Word(10) + bn = big.Word(1e9) + n = 9 + s = 10 +) diff --git a/go/mysql/decimal/scan_64.go b/go/mysql/decimal/scan_64.go new file mode 100644 index 00000000000..55b3f77c2bd --- /dev/null +++ b/go/mysql/decimal/scan_64.go @@ -0,0 +1,28 @@ +//go:build !386 && !arm && !mips && !mipsle + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package decimal + +import "math/big" + +const ( + b1 = big.Word(10) + bn = big.Word(1e19) + n = 19 + s = 5 +) diff --git a/go/mysql/endtoend/client_test.go b/go/mysql/endtoend/client_test.go index 6591c454e8a..ce01c57369d 100644 --- a/go/mysql/endtoend/client_test.go +++ b/go/mysql/endtoend/client_test.go @@ -210,7 +210,11 @@ func doTestMultiResult(t *testing.T, disableClientDeprecateEOF bool) { assert.EqualValues(t, 1, result.RowsAffected, "insert into returned RowsAffected") } - qr, more, err = conn.ExecuteFetchMulti("update a set name = concat(name, ' updated'); select * from a; select count(*) from a", 300, true) + // Verify that a ExecuteFetchMultiDrain leaves the connection/packet in valid state. + err = conn.ExecuteFetchMultiDrain("update a set name = concat(name, ', multi drain 1'); select * from a; select count(*) from a") + expectNoError(t, err) + // If the previous command leaves packet in invalid state, this will fail. + qr, more, err = conn.ExecuteFetchMulti("update a set name = concat(name, ', fetch multi'); select * from a; select count(*) from a", 300, true) expectNoError(t, err) expectFlag(t, "ExecuteMultiFetch(multi result)", more, true) assert.EqualValues(t, 255, qr.RowsAffected) @@ -225,6 +229,13 @@ func doTestMultiResult(t *testing.T, disableClientDeprecateEOF bool) { expectFlag(t, "ReadQueryResult(2)", more, false) assert.EqualValues(t, 1, len(qr.Rows), "ReadQueryResult(1)") + // Verify that a ExecuteFetchMultiDrain is happy to operate again after all the above. + err = conn.ExecuteFetchMultiDrain("update a set name = concat(name, ', multi drain 2'); select * from a; select count(*) from a") + expectNoError(t, err) + + err = conn.ExecuteFetchMultiDrain("update b set name = concat(name, ' nonexistent table'); select * from a; select count(*) from a") + require.Error(t, err) + _, err = conn.ExecuteFetch("drop table a", 10, true) require.NoError(t, err) } diff --git a/go/mysql/endtoend/query_test.go b/go/mysql/endtoend/query_test.go index 576960f2acb..847d1274854 100644 --- a/go/mysql/endtoend/query_test.go +++ b/go/mysql/endtoend/query_test.go @@ -322,6 +322,6 @@ func TestSysInfo(t *testing.T) { } func getDefaultCollationID() collations.ID { - collationHandler := collations.Local() + collationHandler := collations.MySQL8() return collationHandler.DefaultCollationForCharset(charsetName) } diff --git a/go/mysql/endtoend/replication_test.go b/go/mysql/endtoend/replication_test.go index 0c1fa006347..441209b35f7 100644 --- a/go/mysql/endtoend/replication_test.go +++ b/go/mysql/endtoend/replication_test.go @@ -908,6 +908,10 @@ func TestRowReplicationTypes(t *testing.T) { t.Fatal(err) } defer dConn.Close() + // We have tests for zero dates, so we need to allow that for this session. + if _, err := dConn.ExecuteFetch("SET @@session.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')", 0, false); err != nil { + t.Fatal(err) + } // Set the connection time zone for execution of the // statements to PST. That way we're sure to test the diff --git a/go/mysql/endtoend/schema_change_test.go b/go/mysql/endtoend/schema_change_test.go deleted file mode 100644 index a9e72aaef5b..00000000000 --- a/go/mysql/endtoend/schema_change_test.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright 2021 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package endtoend - -import ( - "context" - "fmt" - "strings" - "testing" - - "vitess.io/vitess/go/constants/sidecar" - "vitess.io/vitess/go/vt/sqlparser" - - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" -) - -var ctx = context.Background() - -const ( - createUserTable = `create table vttest.product (id bigint(20) primary key, name char(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci, created bigint(20))` - dropTestTable = `drop table if exists product` -) - -func TestChangeSchemaIsNoticed(t *testing.T) { - conn, err := mysql.Connect(ctx, &connParams) - require.NoError(t, err) - defer conn.Close() - - clearQuery := sqlparser.BuildParsedQuery(mysql.ClearSchemaCopy, sidecar.GetIdentifier()).Query - insertQuery := sqlparser.BuildParsedQuery(mysql.InsertIntoSchemaCopy, sidecar.GetIdentifier()).Query - detectQuery := sqlparser.BuildParsedQuery(mysql.DetectSchemaChange, sidecar.GetIdentifier()).Query - - tests := []struct { - name string - changeQ string - }{{ - name: "add column", - changeQ: "alter table vttest.product add column phone VARCHAR(15)", - }, { - name: "rename column", - changeQ: "alter table vttest.product change name firstname char(10)", - }, { - name: "change column type", - changeQ: "alter table vttest.product change name name char(100)", - }, { - name: "remove column", - changeQ: "alter table vttest.product drop column name", - }, { - name: "remove last column", - changeQ: "alter table vttest.product drop column created", - }, { - name: "remove table", - changeQ: "drop table product", - }, { - name: "create table", - changeQ: `create table vttest.new_table (id bigint(20) primary key)`, - }, { - name: "change character set", - changeQ: "alter table vttest.product change name name char(10) CHARACTER SET utf8mb4", - }, { - name: "change collation", - changeQ: "alter table vttest.product change name name char(10) COLLATE utf8_unicode_520_ci", - }, { - name: "drop PK", - changeQ: "alter table vttest.product drop primary key", - }, { - name: "change PK", - changeQ: "alter table vttest.product drop primary key, add primary key (name)", - }, { - name: "two tables changes", - changeQ: "create table vttest.new_table2 (id bigint(20) primary key);alter table vttest.product drop column name", - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // reset schemacopy - _, err := conn.ExecuteFetch(clearQuery, 1000, true) - require.NoError(t, err) - _, err = conn.ExecuteFetch(dropTestTable, 1000, true) - require.NoError(t, err) - _, err = conn.ExecuteFetch(createUserTable, 1000, true) - require.NoError(t, err) - rs, err := conn.ExecuteFetch(insertQuery, 1000, true) - require.NoError(t, err) - require.NotZero(t, rs.RowsAffected) - - // make sure no changes are detected - rs, err = conn.ExecuteFetch(detectQuery, 1000, true) - require.NoError(t, err) - require.Empty(t, rs.Rows) - - for _, q := range strings.Split(test.changeQ, ";") { - // make the schema change - _, err = conn.ExecuteFetch(q, 1000, true) - require.NoError(t, err) - } - - // make sure the change is detected - rs, err = conn.ExecuteFetch(detectQuery, 1000, true) - require.NoError(t, err) - require.NotEmpty(t, rs.Rows) - - var tables []string - for _, row := range rs.Rows { - apa := sqlparser.NewStrLiteral(row[0].ToString()) - tables = append(tables, "table_name = "+sqlparser.String(apa)) - } - tableNamePredicates := strings.Join(tables, " OR ") - del := fmt.Sprintf("%s AND %s", clearQuery, tableNamePredicates) - upd := fmt.Sprintf("%s AND %s", insertQuery, tableNamePredicates) - - _, err = conn.ExecuteFetch(del, 1000, true) - require.NoError(t, err) - _, err = conn.ExecuteFetch(upd, 1000, true) - require.NoError(t, err) - - // make sure the change is detected - rs, err = conn.ExecuteFetch(detectQuery, 1000, true) - require.NoError(t, err) - require.Empty(t, rs.Rows) - }) - } -} diff --git a/go/mysql/fakesqldb/server.go b/go/mysql/fakesqldb/server.go index 4dc71dd3662..d42f21b7007 100644 --- a/go/mysql/fakesqldb/server.go +++ b/go/mysql/fakesqldb/server.go @@ -29,16 +29,13 @@ import ( "testing" "time" - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/vt/sqlparser" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" - - "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" ) const appendEntry = -1 @@ -129,6 +126,8 @@ type DB struct { // lastError stores the last error in returning a query result. lastErrorMu sync.Mutex lastError error + + env *vtenv.Environment } // QueryHandler is the interface used by the DB to simulate executed queries @@ -182,6 +181,7 @@ func New(t testing.TB) *DB { queryPatternUserCallback: make(map[*regexp.Regexp]func(string)), patternData: make(map[string]exprResult), lastErrorMu: sync.Mutex{}, + env: vtenv.NewTestEnv(), } db.Handler = db @@ -189,7 +189,7 @@ func New(t testing.TB) *DB { authServer := mysql.NewAuthServerNone() // Start listening. - db.listener, err = mysql.NewListener("unix", socketFile, authServer, db, 0, 0, false, false, 0) + db.listener, err = mysql.NewListener("unix", socketFile, authServer, db, 0, 0, false, false, 0, 0) if err != nil { t.Fatalf("NewListener failed: %v", err) } @@ -291,23 +291,23 @@ func (db *DB) WaitForClose(timeout time.Duration) error { } // ConnParams returns the ConnParams to connect to the DB. -func (db *DB) ConnParams() dbconfigs.Connector { - return dbconfigs.New(&mysql.ConnParams{ +func (db *DB) ConnParams() *mysql.ConnParams { + return &mysql.ConnParams{ UnixSocket: db.socketFile, Uname: "user1", Pass: "password1", DbName: "fakesqldb", - }) + } } // ConnParamsWithUname returns ConnParams to connect to the DB with the Uname set to the provided value. -func (db *DB) ConnParamsWithUname(uname string) dbconfigs.Connector { - return dbconfigs.New(&mysql.ConnParams{ +func (db *DB) ConnParamsWithUname(uname string) *mysql.ConnParams { + return &mysql.ConnParams{ UnixSocket: db.socketFile, Uname: uname, Pass: "password1", DbName: "fakesqldb", - }) + } } // @@ -425,7 +425,11 @@ func (db *DB) HandleQuery(c *mysql.Conn, query string, callback func(*sqltypes.R userCallback, ok := db.queryPatternUserCallback[pat.expr] db.mu.Unlock() if ok { + // Since the user call back can be indefinitely stuck, we shouldn't hold the lock indefinitely. + // This is only test code, so no actual cause for concern. + db.mu.Unlock() userCallback(query) + db.mu.Lock() } if pat.err != "" { return fmt.Errorf(pat.err) @@ -440,9 +444,10 @@ func (db *DB) HandleQuery(c *mysql.Conn, query string, callback func(*sqltypes.R return callback(&sqltypes.Result{}) } // Nothing matched. + parser := sqlparser.NewTestParser() err = fmt.Errorf("fakesqldb:: query: '%s' is not supported on %v", - sqlparser.TruncateForUI(query), db.name) - log.Errorf("Query not found: %s", sqlparser.TruncateForUI(query)) + parser.TruncateForUI(query), db.name) + log.Errorf("Query not found: %s", parser.TruncateForUI(query)) return err } @@ -846,3 +851,7 @@ func (db *DB) GetQueryPatternResult(key string) (func(string), ExpectedResult, b return nil, ExpectedResult{nil, nil}, false, nil } + +func (db *DB) Env() *vtenv.Environment { + return db.env +} diff --git a/go/mysql/fastparse/fastparse.go b/go/mysql/fastparse/fastparse.go index f9aca692abd..a669a584d72 100644 --- a/go/mysql/fastparse/fastparse.go +++ b/go/mysql/fastparse/fastparse.go @@ -26,11 +26,19 @@ import ( "vitess.io/vitess/go/hack" ) +func ParseUint64(s string, base int) (uint64, error) { + return parseUint64(s, base, false) +} + +func ParseUint64WithNeg(s string, base int) (uint64, error) { + return parseUint64(s, base, true) +} + // ParseUint64 parses uint64 from s. // // It is equivalent to strconv.ParseUint(s, base, 64) in case it succeeds, // but on error it will return the best effort value of what it has parsed so far. -func ParseUint64(s string, base int) (uint64, error) { +func parseUint64(s string, base int, allowNeg bool) (uint64, error) { if len(s) == 0 { return 0, fmt.Errorf("cannot parse uint64 from empty string") } @@ -45,6 +53,22 @@ func ParseUint64(s string, base int) (uint64, error) { i++ } + if i >= uint(len(s)) { + return 0, fmt.Errorf("cannot parse uint64 from %q", s) + } + // For some reason, MySQL parses things as uint64 even with + // a negative sign and then turns it into the 2s complement value. + minus := s[i] == '-' + if minus { + if !allowNeg { + return 0, fmt.Errorf("cannot parse uint64 from %q", s) + } + i++ + if i >= uint(len(s)) { + return 0, fmt.Errorf("cannot parse uint64 from %q", s) + } + } + d := uint64(0) j := i next: @@ -75,17 +99,23 @@ next: cutoff = math.MaxUint64/uint64(base) + 1 } if d >= cutoff { + if minus { + return 0, fmt.Errorf("cannot parse uint64 from %q: %w", s, ErrOverflow) + } return math.MaxUint64, fmt.Errorf("cannot parse uint64 from %q: %w", s, ErrOverflow) } v := d*uint64(base) + uint64(b) if v < d { + if minus { + return 0, fmt.Errorf("cannot parse uint64 from %q: %w", s, ErrOverflow) + } return math.MaxUint64, fmt.Errorf("cannot parse uint64 from %q: %w", s, ErrOverflow) } d = v i++ } if i <= j { - return d, fmt.Errorf("cannot parse uint64 from %q", s) + return uValue(d, minus), fmt.Errorf("cannot parse uint64 from %q", s) } for i < uint(len(s)) { @@ -97,9 +127,9 @@ next: if i < uint(len(s)) { // Unparsed tail left. - return d, fmt.Errorf("unparsed tail left after parsing uint64 from %q: %q", s, s[i:]) + return uValue(d, minus), fmt.Errorf("unparsed tail left after parsing uint64 from %q: %q", s, s[i:]) } - return d, nil + return uValue(d, minus), nil } var ErrOverflow = errors.New("overflow") @@ -123,6 +153,9 @@ func ParseInt64(s string, base int) (int64, error) { i++ } + if i >= uint(len(s)) { + return 0, fmt.Errorf("cannot parse int64 from %q", s) + } minus := s[i] == '-' if minus { i++ @@ -160,21 +193,15 @@ next: default: cutoff = math.MaxInt64/uint64(base) + 1 } - if d >= cutoff { - if minus { - return math.MinInt64, fmt.Errorf("cannot parse int64 from %q: %w", s, ErrOverflow) - } + if !minus && d >= cutoff { return math.MaxInt64, fmt.Errorf("cannot parse int64 from %q: %w", s, ErrOverflow) } - v := d*uint64(base) + uint64(b) - if v < d { - if minus { - return math.MinInt64, fmt.Errorf("cannot parse int64 from %q: %w", s, ErrOverflow) - } - return math.MaxInt64, fmt.Errorf("cannot parse int64 from %q: %w", s, ErrOverflow) + if minus && d > cutoff { + return math.MinInt64, fmt.Errorf("cannot parse int64 from %q: %w", s, ErrOverflow) } - d = v + + d = d*uint64(base) + uint64(b) i++ } @@ -264,3 +291,10 @@ func isSpace(c byte) bool { return false } } + +func uValue(v uint64, neg bool) uint64 { + if neg { + return -v + } + return v +} diff --git a/go/mysql/fastparse/fastparse_test.go b/go/mysql/fastparse/fastparse_test.go index bec312b0bb5..5ee87a617d1 100644 --- a/go/mysql/fastparse/fastparse_test.go +++ b/go/mysql/fastparse/fastparse_test.go @@ -17,6 +17,8 @@ package fastparse import ( "math" + "math/big" + "strconv" "testing" "github.com/stretchr/testify/require" @@ -190,6 +192,48 @@ func TestParseInt64(t *testing.T) { expected: 42, err: `unparsed tail left after parsing int64 from "\t 42 \n": "\n"`, }, + { + input: "", + base: 10, + expected: 0, + err: `cannot parse int64 from empty string`, + }, + { + input: "256", + base: 1, + expected: 0, + err: `invalid base 1; must be in [2, 36]`, + }, + { + input: "256", + base: 37, + expected: 0, + err: `invalid base 37; must be in [2, 36]`, + }, + { + input: " -", + base: 10, + expected: 0, + err: `cannot parse int64 from " -"`, + }, + { + input: "-18446744073709551615", + base: 10, + expected: -9223372036854775808, + err: `cannot parse int64 from "-18446744073709551615": overflow`, + }, + { + input: " ", + base: 10, + expected: 0, + err: `cannot parse int64 from " "`, + }, + { + input: " :", + base: 10, + expected: 0, + err: `cannot parse int64 from " :"`, + }, } for _, tc := range testcases { t.Run(tc.input, func(t *testing.T) { @@ -205,6 +249,69 @@ func TestParseInt64(t *testing.T) { } } +func TestParseEdgeInt64(t *testing.T) { + for i := int64(math.MinInt64); i < math.MinInt64+1000; i++ { + for base := 2; base <= 36; base++ { + val, err := ParseInt64(strconv.FormatInt(i, base), base) + require.NoError(t, err, "base %d", base) + require.Equal(t, int64(i), val) + } + } + for i := int64(math.MaxInt64 - 1000); i < math.MaxInt64; i++ { + for base := 2; base <= 36; base++ { + val, err := ParseInt64(strconv.FormatInt(i, base), base) + require.NoError(t, err) + require.NoError(t, err, "base %d", base) + require.Equal(t, int64(i), val) + } + } +} + +func TestParseOverflowInt64(t *testing.T) { + for i := int64(1); i <= 1000; i++ { + b := big.NewInt(math.MinInt64) + b.Sub(b, big.NewInt(i)) + for base := 2; base <= 36; base++ { + val, err := ParseInt64(b.Text(base), base) + require.Error(t, err) + require.Equal(t, int64(math.MinInt64), val) + } + } + + for i := int64(1); i <= 1000; i++ { + b := big.NewInt(math.MaxInt64) + b.Add(b, big.NewInt(i)) + for base := 2; base <= 36; base++ { + val, err := ParseInt64(b.Text(base), base) + require.Error(t, err) + require.Equal(t, int64(math.MaxInt64), val) + } + } +} + +func TestParseEdgeUint64(t *testing.T) { + for i := uint64(math.MaxUint64 - 1000); i < math.MaxUint64; i++ { + for base := 2; base <= 36; base++ { + val, err := ParseUint64(strconv.FormatUint(i, base), base) + require.NoError(t, err, "base %d", base) + require.Equal(t, uint64(i), val) + } + } +} + +func TestParseOverflowUint64(t *testing.T) { + var b big.Int + for i := int64(1); i <= 1000; i++ { + b.SetUint64(math.MaxUint64) + b.Add(&b, big.NewInt(i)) + for base := 2; base <= 36; base++ { + val, err := ParseUint64(b.Text(base), base) + require.Error(t, err) + require.Equal(t, uint64(math.MaxUint64), val) + } + } +} + func TestParseUint64(t *testing.T) { testcases := []struct { input string @@ -227,6 +334,17 @@ func TestParseUint64(t *testing.T) { base: 2, expected: 1, }, + { + input: "-", + base: 10, + expected: 0, + err: `cannot parse uint64 from "-"`, + }, + { + input: "-1", + base: 10, + err: `cannot parse uint64 from "-1"`, + }, { input: "10", base: 2, @@ -326,6 +444,36 @@ func TestParseUint64(t *testing.T) { expected: 42, err: `unparsed tail left after parsing uint64 from "\t 42 \n": "\n"`, }, + { + input: "", + base: 10, + expected: 0, + err: `cannot parse uint64 from empty string`, + }, + { + input: "256", + base: 1, + expected: 0, + err: `invalid base 1; must be in [2, 36]`, + }, + { + input: "256", + base: 37, + expected: 0, + err: `invalid base 37; must be in [2, 36]`, + }, + { + input: " ", + base: 10, + expected: 0, + err: `cannot parse uint64 from " "`, + }, + { + input: " :", + base: 10, + expected: 0, + err: `cannot parse uint64 from " :"`, + }, } for _, tc := range testcases { t.Run(tc.input, func(t *testing.T) { @@ -341,6 +489,61 @@ func TestParseUint64(t *testing.T) { } } +func TestParseUint64WithNeg(t *testing.T) { + testcases := []struct { + input string + base int + expected uint64 + err string + }{ + { + input: "-", + base: 10, + expected: 0, + err: `cannot parse uint64 from "-"`, + }, + { + input: "-1", + base: 10, + expected: 18446744073709551615, + }, + { + input: "-9223372036854775808", + base: 10, + expected: 9223372036854775808, + }, + { + input: "-9223372036854775809", + base: 10, + expected: 9223372036854775807, + }, + { + input: "-18446744073709551616", + base: 10, + expected: 0, + err: `cannot parse uint64 from "-18446744073709551616": overflow`, + }, + { + input: "-31415926535897932384", + base: 10, + expected: 0, + err: `cannot parse uint64 from "-31415926535897932384": overflow`, + }, + } + for _, tc := range testcases { + t.Run(tc.input, func(t *testing.T) { + val, err := ParseUint64WithNeg(tc.input, tc.base) + if tc.err == "" { + require.NoError(t, err) + require.Equal(t, tc.expected, val) + } else { + require.Equal(t, tc.expected, val) + require.EqualError(t, err, tc.err) + } + }) + } +} + func TestParseFloat64(t *testing.T) { testcases := []struct { input string diff --git a/go/mysql/flavor.go b/go/mysql/flavor.go index edb64913c31..619496a9246 100644 --- a/go/mysql/flavor.go +++ b/go/mysql/flavor.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -39,24 +40,6 @@ var ( ErrNoPrimaryStatus = errors.New("no master status") ) -type FlavorCapability int - -const ( - NoneFlavorCapability FlavorCapability = iota // default placeholder - FastDropTableFlavorCapability // supported in MySQL 8.0.23 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html - TransactionalGtidExecutedFlavorCapability - InstantDDLFlavorCapability - InstantAddLastColumnFlavorCapability - InstantAddDropVirtualColumnFlavorCapability - InstantAddDropColumnFlavorCapability - InstantChangeColumnDefaultFlavorCapability - InstantExpandEnumCapability - MySQLJSONFlavorCapability - MySQLUpgradeInServerFlavorCapability - DynamicRedoLogCapacityFlavorCapability // supported in MySQL 8.0.30 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-30.html - DisableRedoLogFlavorCapability // supported in MySQL 8.0.21 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-21.html -) - const ( // mariaDBReplicationHackPrefix is the prefix of a version for MariaDB 10.0 // versions, to work around replication bugs. @@ -145,47 +128,21 @@ type flavor interface { // with parsed executed position. primaryStatus(c *Conn) (replication.PrimaryStatus, error) - // waitUntilPositionCommand returns the SQL command to issue - // to wait until the given position, until the context - // expires. The command returns -1 if it times out. It - // returns NULL if GTIDs are not enabled. - waitUntilPositionCommand(ctx context.Context, pos replication.Position) (string, error) + // waitUntilPosition waits until the given position is reached or + // until the context expires. It returns an error if we did not + // succeed. + waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error baseShowTables() string baseShowTablesWithSizes() string - supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) + supportsCapability(capability capabilities.FlavorCapability) (bool, error) } -type CapableOf func(capability FlavorCapability) (bool, error) - -// flavors maps flavor names to their implementation. +// flavorFuncs maps flavor names to their implementation. // Flavors need to register only if they support being specified in the // connection parameters. -var flavors = make(map[string]func() flavor) - -// ServerVersionAtLeast returns true if current server is at least given value. -// Example: if input is []int{8, 0, 23}... the function returns 'true' if we're on MySQL 8.0.23, 8.0.24, ... -func ServerVersionAtLeast(serverVersion string, parts ...int) (bool, error) { - versionPrefix := strings.Split(serverVersion, "-")[0] - versionTokens := strings.Split(versionPrefix, ".") - for i, part := range parts { - if len(versionTokens) <= i { - return false, nil - } - tokenValue, err := strconv.Atoi(versionTokens[i]) - if err != nil { - return false, err - } - if tokenValue > part { - return true, nil - } - if tokenValue < part { - return false, nil - } - } - return true, nil -} +var flavorFuncs = make(map[string]func() flavor) // GetFlavor fills in c.Flavor. If the params specify the flavor, // that is used. Otherwise, we auto-detect. @@ -199,32 +156,37 @@ func ServerVersionAtLeast(serverVersion string, parts ...int) (bool, error) { // Note on such servers, 'select version()' would return 10.0.21-MariaDB-... // as well (not matching what c.ServerVersion is, but matching after we remove // the prefix). -func GetFlavor(serverVersion string, flavorFunc func() flavor) (f flavor, capableOf CapableOf, canonicalVersion string) { +func GetFlavor(serverVersion string, flavorFunc func() flavor) (f flavor, capableOf capabilities.CapableOf, canonicalVersion string) { canonicalVersion = serverVersion switch { case flavorFunc != nil: f = flavorFunc() case strings.HasPrefix(serverVersion, mariaDBReplicationHackPrefix): canonicalVersion = serverVersion[len(mariaDBReplicationHackPrefix):] - f = mariadbFlavor101{} + f = mariadbFlavor101{mariadbFlavor{serverVersion: canonicalVersion}} case strings.Contains(serverVersion, mariaDBVersionString): mariadbVersion, err := strconv.ParseFloat(serverVersion[:4], 64) if err != nil || mariadbVersion < 10.2 { - f = mariadbFlavor101{} + f = mariadbFlavor101{mariadbFlavor{serverVersion: fmt.Sprintf("%f", mariadbVersion)}} } else { - f = mariadbFlavor102{} + f = mariadbFlavor102{mariadbFlavor{serverVersion: fmt.Sprintf("%f", mariadbVersion)}} } case strings.HasPrefix(serverVersion, mysql57VersionPrefix): - f = mysqlFlavor57{} + f = mysqlFlavor57{mysqlFlavor{serverVersion: serverVersion}} case strings.HasPrefix(serverVersion, mysql80VersionPrefix): - f = mysqlFlavor80{} + f = mysqlFlavor80{mysqlFlavor{serverVersion: serverVersion}} default: - f = mysqlFlavor56{} + // If unknown, return the most basic flavor: MySQL 56. + f = mysqlFlavor56{mysqlFlavor{serverVersion: serverVersion}} } - return f, - func(capability FlavorCapability) (bool, error) { - return f.supportsCapability(serverVersion, capability) - }, canonicalVersion + return f, f.supportsCapability, canonicalVersion +} + +// ServerVersionCapableOf is a convenience function that returns a CapableOf function given a server version. +// It is a shortcut for GetFlavor(serverVersion, nil). +func ServerVersionCapableOf(serverVersion string) (capableOf capabilities.CapableOf) { + _, capableOf, _ = GetFlavor(serverVersion, nil) + return capableOf } // fillFlavor fills in c.Flavor. If the params specify the flavor, @@ -240,14 +202,14 @@ func GetFlavor(serverVersion string, flavorFunc func() flavor) (f flavor, capabl // as well (not matching what c.ServerVersion is, but matching after we remove // the prefix). func (c *Conn) fillFlavor(params *ConnParams) { - flavorFunc := flavors[params.Flavor] + flavorFunc := flavorFuncs[params.Flavor] c.flavor, _, c.ServerVersion = GetFlavor(c.ServerVersion, flavorFunc) } // ServerVersionAtLeast returns 'true' if server version is equal or greater than given parts. e.g. // "8.0.14-log" is at least [8, 0, 13] and [8, 0, 14], but not [8, 0, 15] func (c *Conn) ServerVersionAtLeast(parts ...int) (bool, error) { - return ServerVersionAtLeast(c.ServerVersion, parts...) + return capabilities.ServerVersionAtLeast(c.ServerVersion, parts...) } // @@ -447,21 +409,18 @@ func (c *Conn) ShowPrimaryStatus() (replication.PrimaryStatus, error) { return c.flavor.primaryStatus(c) } -// WaitUntilPositionCommand returns the SQL command to issue -// to wait until the given position, until the context -// expires. The command returns -1 if it times out. It -// returns NULL if GTIDs are not enabled. -func (c *Conn) WaitUntilPositionCommand(ctx context.Context, pos replication.Position) (string, error) { - return c.flavor.waitUntilPositionCommand(ctx, pos) +// WaitUntilPosition waits until the given position is reached or until the +// context expires. It returns an error if we did not succeed. +func (c *Conn) WaitUntilPosition(ctx context.Context, pos replication.Position) error { + return c.flavor.waitUntilPosition(ctx, c, pos) } -// WaitUntilFilePositionCommand returns the SQL command to issue -// to wait until the given position, until the context -// expires for the file position flavor. The command returns -1 if it times out. It -// returns NULL if GTIDs are not enabled. -func (c *Conn) WaitUntilFilePositionCommand(ctx context.Context, pos replication.Position) (string, error) { +// WaitUntilFilePosition waits until the given position is reached or until +// the context expires for the file position flavor. It returns an error if +// we did not succeed. +func (c *Conn) WaitUntilFilePosition(ctx context.Context, pos replication.Position) error { filePosFlavor := filePosFlavor{} - return filePosFlavor.waitUntilPositionCommand(ctx, pos) + return filePosFlavor.waitUntilPosition(ctx, c, pos) } // BaseShowTables returns a query that shows tables @@ -475,10 +434,10 @@ func (c *Conn) BaseShowTablesWithSizes() string { } // SupportsCapability checks if the database server supports the given capability -func (c *Conn) SupportsCapability(capability FlavorCapability) (bool, error) { - return c.flavor.supportsCapability(c.ServerVersion, capability) +func (c *Conn) SupportsCapability(capability capabilities.FlavorCapability) (bool, error) { + return c.flavor.supportsCapability(capability) } func init() { - flavors[replication.FilePosFlavorID] = newFilePosFlavor + flavorFuncs[replication.FilePosFlavorID] = newFilePosFlavor } diff --git a/go/mysql/flavor_filepos.go b/go/mysql/flavor_filepos.go index bf4076b85b1..afdc8282e19 100644 --- a/go/mysql/flavor_filepos.go +++ b/go/mysql/flavor_filepos.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -266,22 +267,54 @@ func (flv *filePosFlavor) primaryStatus(c *Conn) (replication.PrimaryStatus, err return replication.ParseFilePosPrimaryStatus(resultMap) } -// waitUntilPositionCommand is part of the Flavor interface. -func (flv *filePosFlavor) waitUntilPositionCommand(ctx context.Context, pos replication.Position) (string, error) { +// waitUntilPosition is part of the Flavor interface. +func (flv *filePosFlavor) waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error { filePosPos, ok := pos.GTIDSet.(replication.FilePosGTID) if !ok { - return "", fmt.Errorf("Position is not filePos compatible: %#v", pos.GTIDSet) + return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "position is not filePos compatible: %#v", pos.GTIDSet) } + query := fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d)", filePosPos.File, filePosPos.Pos) if deadline, ok := ctx.Deadline(); ok { timeout := time.Until(deadline) if timeout <= 0 { - return "", fmt.Errorf("timed out waiting for position %v", pos) + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) } - return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d, %.6f)", filePosPos.File, filePosPos.Pos, timeout.Seconds()), nil + query = fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d, %.6f)", filePosPos.File, filePosPos.Pos, timeout.Seconds()) } - return fmt.Sprintf("SELECT MASTER_POS_WAIT('%s', %d)", filePosPos.File, filePosPos.Pos), nil + result, err := c.ExecuteFetch(query, 1, false) + if err != nil { + return err + } + + // For MASTER_POS_WAIT(), the return value is the number of log events + // the replica had to wait for to advance to the specified position. + // The function returns NULL if the replica SQL thread is not started, + // the replica's source information is not initialized, the arguments + // are incorrect, or an error occurs. It returns -1 if the timeout has + // been exceeded. If the replica SQL thread stops while MASTER_POS_WAIT() + // is waiting, the function returns NULL. If the replica is past the + // specified position, the function returns immediately. + if len(result.Rows) != 1 || len(result.Rows[0]) != 1 { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid results: %#v", result) + } + val := result.Rows[0][0] + if val.IsNull() { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "replication is not running") + } + state, err := val.ToInt64() + if err != nil { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %#v", val) + } + switch { + case state == -1: + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) + case state >= 0: + return nil + default: + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %d", state) + } } func (*filePosFlavor) startReplicationUntilAfter(pos replication.Position) string { @@ -303,7 +336,7 @@ func (*filePosFlavor) baseShowTablesWithSizes() string { } // supportsCapability is part of the Flavor interface. -func (*filePosFlavor) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { +func (*filePosFlavor) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { switch capability { default: return false, nil diff --git a/go/mysql/flavor_mariadb.go b/go/mysql/flavor_mariadb.go index 15718542b45..2f77a71ea00 100644 --- a/go/mysql/flavor_mariadb.go +++ b/go/mysql/flavor_mariadb.go @@ -23,14 +23,18 @@ import ( "io" "time" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" + + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // mariadbFlavor implements the Flavor interface for MariaDB. -type mariadbFlavor struct{} +type mariadbFlavor struct { + serverVersion string +} type mariadbFlavor101 struct { mariadbFlavor } @@ -48,7 +52,7 @@ func (mariadbFlavor) primaryGTIDSet(c *Conn) (replication.GTIDSet, error) { return nil, err } if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_binlog_pos: %#v", qr) + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected result format for gtid_binlog_pos: %#v", qr) } return replication.ParseMariadbGTIDSet(qr.Rows[0][0].ToString()) @@ -223,22 +227,45 @@ func (m mariadbFlavor) primaryStatus(c *Conn) (replication.PrimaryStatus, error) return status, err } -// waitUntilPositionCommand is part of the Flavor interface. +// waitUntilPosition is part of the Flavor interface. // // Note: Unlike MASTER_POS_WAIT(), MASTER_GTID_WAIT() will continue waiting even // if the sql thread stops. If that is a problem, we'll have to change this. -func (mariadbFlavor) waitUntilPositionCommand(ctx context.Context, pos replication.Position) (string, error) { +func (mariadbFlavor) waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error { + // Omit the timeout to wait indefinitely. In MariaDB, a timeout of 0 means + // return immediately. + query := fmt.Sprintf("SELECT MASTER_GTID_WAIT('%s')", pos) if deadline, ok := ctx.Deadline(); ok { timeout := time.Until(deadline) if timeout <= 0 { - return "", vterrors.Errorf(vtrpc.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) } - return fmt.Sprintf("SELECT MASTER_GTID_WAIT('%s', %.6f)", pos, timeout.Seconds()), nil + query = fmt.Sprintf("SELECT MASTER_GTID_WAIT('%s', %.6f)", pos, timeout.Seconds()) } - // Omit the timeout to wait indefinitely. In MariaDB, a timeout of 0 means - // return immediately. - return fmt.Sprintf("SELECT MASTER_GTID_WAIT('%s')", pos), nil + result, err := c.ExecuteFetch(query, 1, false) + if err != nil { + return err + } + + // For MASTER_GTID_WAIT(), if the wait completes without a timeout 0 is + // returned and -1 if there was a timeout. + if len(result.Rows) != 1 || len(result.Rows[0]) != 1 { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid results: %#v", result) + } + val := result.Rows[0][0] + state, err := val.ToInt64() + if err != nil { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %#v", val) + } + switch state { + case 0: + return nil + case -1: + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) + default: + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %d", state) + } } // readBinlogEvent is part of the Flavor interface. @@ -262,7 +289,7 @@ func (mariadbFlavor) readBinlogEvent(c *Conn) (BinlogEvent, error) { } // supportsCapability is part of the Flavor interface. -func (mariadbFlavor) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { +func (mariadbFlavor) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { switch capability { default: return false, nil diff --git a/go/mysql/flavor_mysql.go b/go/mysql/flavor_mysql.go index bc5f31006e5..c0a06540368 100644 --- a/go/mysql/flavor_mysql.go +++ b/go/mysql/flavor_mysql.go @@ -22,14 +22,18 @@ import ( "io" "time" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" + + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // mysqlFlavor implements the Flavor interface for Mysql. -type mysqlFlavor struct{} +type mysqlFlavor struct { + serverVersion string +} type mysqlFlavor56 struct { mysqlFlavor } @@ -52,7 +56,7 @@ func (mysqlFlavor) primaryGTIDSet(c *Conn) (replication.GTIDSet, error) { return nil, err } if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_executed: %#v", qr) + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected result format for gtid_executed: %#v", qr) } return replication.ParseMysql56GTIDSet(qr.Rows[0][0].ToString()) } @@ -65,7 +69,7 @@ func (mysqlFlavor) purgedGTIDSet(c *Conn) (replication.GTIDSet, error) { return nil, err } if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_purged: %#v", qr) + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected result format for gtid_purged: %#v", qr) } return replication.ParseMysql56GTIDSet(qr.Rows[0][0].ToString()) } @@ -78,7 +82,7 @@ func (mysqlFlavor) serverUUID(c *Conn) (string, error) { return "", err } if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return "", vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for server_uuid: %#v", qr) + return "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected result format for server_uuid: %#v", qr) } return qr.Rows[0][0].ToString(), nil } @@ -90,7 +94,7 @@ func (mysqlFlavor) gtidMode(c *Conn) (string, error) { return "", err } if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return "", vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_mode: %#v", qr) + return "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected result format for gtid_mode: %#v", qr) } return qr.Rows[0][0].ToString(), nil } @@ -135,7 +139,7 @@ func (mysqlFlavor) startSQLThreadCommand() string { func (mysqlFlavor) sendBinlogDumpCommand(c *Conn, serverID uint32, binlogFilename string, startPos replication.Position) error { gtidSet, ok := startPos.GTIDSet.(replication.Mysql56GTIDSet) if !ok { - return vterrors.Errorf(vtrpc.Code_INTERNAL, "startPos.GTIDSet is wrong type - expected Mysql56GTIDSet, got: %#v", startPos.GTIDSet) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "startPos.GTIDSet is wrong type - expected Mysql56GTIDSet, got: %#v", startPos.GTIDSet) } // Build the command. @@ -216,14 +220,14 @@ func (mysqlFlavor) primaryStatus(c *Conn) (replication.PrimaryStatus, error) { return replication.ParseMysqlPrimaryStatus(resultMap) } -// waitUntilPositionCommand is part of the Flavor interface. -func (mysqlFlavor) waitUntilPositionCommand(ctx context.Context, pos replication.Position) (string, error) { +// waitUntilPosition is part of the Flavor interface. +func (mysqlFlavor) waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error { // A timeout of 0 means wait indefinitely. timeoutSeconds := 0 if deadline, ok := ctx.Deadline(); ok { timeout := time.Until(deadline) if timeout <= 0 { - return "", vterrors.Errorf(vtrpc.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) } // Only whole numbers of seconds are supported. @@ -234,7 +238,30 @@ func (mysqlFlavor) waitUntilPositionCommand(ctx context.Context, pos replication } } - return fmt.Sprintf("SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('%s', %v)", pos, timeoutSeconds), nil + query := fmt.Sprintf("SELECT WAIT_FOR_EXECUTED_GTID_SET('%s', %v)", pos, timeoutSeconds) + result, err := c.ExecuteFetch(query, 1, false) + if err != nil { + return err + } + + // For WAIT_FOR_EXECUTED_GTID_SET(), the return value is the state of the query, where + // 0 represents success, and 1 represents timeout. Any other failures generate an error. + if len(result.Rows) != 1 || len(result.Rows[0]) != 1 { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid results: %#v", result) + } + val := result.Rows[0][0] + state, err := val.ToInt64() + if err != nil { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %#v", val) + } + switch state { + case 0: + return nil + case 1: + return vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos) + default: + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid result of %d", state) + } } // readBinlogEvent is part of the Flavor interface. @@ -347,11 +374,10 @@ func (mysqlFlavor56) baseShowTablesWithSizes() string { } // supportsCapability is part of the Flavor interface. -func (mysqlFlavor56) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { - switch capability { - default: - return false, nil - } +func (f mysqlFlavor56) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { + // We do not support MySQL 5.6. Also, for consistency, and seeing that MySQL56 is the default + // flavor if no version specified, we return false for all capabilities. + return false, nil } // baseShowTablesWithSizes is part of the Flavor interface. @@ -360,13 +386,8 @@ func (mysqlFlavor57) baseShowTablesWithSizes() string { } // supportsCapability is part of the Flavor interface. -func (mysqlFlavor57) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { - switch capability { - case MySQLJSONFlavorCapability: - return true, nil - default: - return false, nil - } +func (f mysqlFlavor57) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { + return capabilities.MySQLVersionHasCapability(f.serverVersion, capability) } // baseShowTablesWithSizes is part of the Flavor interface. @@ -375,29 +396,6 @@ func (mysqlFlavor80) baseShowTablesWithSizes() string { } // supportsCapability is part of the Flavor interface. -func (mysqlFlavor80) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { - switch capability { - case InstantDDLFlavorCapability, - InstantExpandEnumCapability, - InstantAddLastColumnFlavorCapability, - InstantAddDropVirtualColumnFlavorCapability, - InstantChangeColumnDefaultFlavorCapability: - return true, nil - case InstantAddDropColumnFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 29) - case TransactionalGtidExecutedFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 17) - case FastDropTableFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 23) - case MySQLJSONFlavorCapability: - return true, nil - case MySQLUpgradeInServerFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 16) - case DynamicRedoLogCapacityFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 30) - case DisableRedoLogFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 21) - default: - return false, nil - } +func (f mysqlFlavor80) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { + return capabilities.MySQLVersionHasCapability(f.serverVersion, capability) } diff --git a/go/mysql/flavor_mysql_test.go b/go/mysql/flavor_mysql_test.go index 0e1b749633a..7a6ddd47005 100644 --- a/go/mysql/flavor_mysql_test.go +++ b/go/mysql/flavor_mysql_test.go @@ -73,5 +73,4 @@ func TestMysql56SetReplicationSourceCommandSSL(t *testing.T) { conn := &Conn{flavor: mysqlFlavor57{}} got := conn.SetReplicationSourceCommand(params, host, port, connectRetry) assert.Equal(t, want, got, "mysqlFlavor.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want) - } diff --git a/go/mysql/flavor_mysqlgr.go b/go/mysql/flavor_mysqlgr.go index e96a6433f73..c572a554ca2 100644 --- a/go/mysql/flavor_mysqlgr.go +++ b/go/mysql/flavor_mysqlgr.go @@ -21,6 +21,7 @@ import ( "fmt" "math" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/vt/proto/vtrpc" @@ -248,31 +249,10 @@ func (mysqlGRFlavor) baseShowTablesWithSizes() string { } // supportsCapability is part of the Flavor interface. -func (mysqlGRFlavor) supportsCapability(serverVersion string, capability FlavorCapability) (bool, error) { - switch capability { - case InstantDDLFlavorCapability, - InstantExpandEnumCapability, - InstantAddLastColumnFlavorCapability, - InstantAddDropVirtualColumnFlavorCapability, - InstantChangeColumnDefaultFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 0) - case InstantAddDropColumnFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 29) - case TransactionalGtidExecutedFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 17) - case FastDropTableFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 23) - case MySQLJSONFlavorCapability: - return ServerVersionAtLeast(serverVersion, 5, 7, 0) - case MySQLUpgradeInServerFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 16) - case DynamicRedoLogCapacityFlavorCapability: - return ServerVersionAtLeast(serverVersion, 8, 0, 30) - default: - return false, nil - } +func (f mysqlGRFlavor) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { + return capabilities.MySQLVersionHasCapability(f.serverVersion, capability) } func init() { - flavors[GRFlavorID] = newMysqlGRFlavor + flavorFuncs[GRFlavorID] = newMysqlGRFlavor } diff --git a/go/mysql/flavor_mysqlgr_test.go b/go/mysql/flavor_mysqlgr_test.go index df7876eca1c..348aefca934 100644 --- a/go/mysql/flavor_mysqlgr_test.go +++ b/go/mysql/flavor_mysqlgr_test.go @@ -16,13 +16,15 @@ limitations under the License. package mysql import ( + "fmt" "testing" - "gotest.tools/assert" + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -53,3 +55,130 @@ func TestMysqlGRReplicationApplierLagParse(t *testing.T) { parseReplicationApplierLag(&res, row) assert.Equal(t, uint32(100), res.ReplicationLagSeconds) } + +func TestMysqlGRSupportCapability(t *testing.T) { + testcases := []struct { + version string + capability capabilities.FlavorCapability + isCapable bool + expectError error + }{ + { + version: "8.0.14", + capability: capabilities.InstantDDLFlavorCapability, + isCapable: true, + }, + { + version: "8.0.20", + capability: capabilities.TransactionalGtidExecutedFlavorCapability, + isCapable: true, + }, + { + version: "8.0.0", + capability: capabilities.InstantAddLastColumnFlavorCapability, + isCapable: true, + }, + { + version: "8.0.0", + capability: capabilities.InstantAddDropColumnFlavorCapability, + isCapable: false, + }, + { + version: "5.6.7", + capability: capabilities.InstantDDLFlavorCapability, + isCapable: false, + }, + { + version: "5.7.29", + capability: capabilities.TransactionalGtidExecutedFlavorCapability, + isCapable: false, + }, + { + version: "5.6.7", + capability: capabilities.MySQLJSONFlavorCapability, + isCapable: false, + }, + { + version: "5.7.29", + capability: capabilities.MySQLJSONFlavorCapability, + isCapable: true, + }, + { + version: "8.0.30", + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, + isCapable: true, + }, + { + version: "8.0.29", + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, + isCapable: false, + }, + { + version: "5.7.38", + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, + isCapable: false, + }, + { + version: "8.0.21", + capability: capabilities.DisableRedoLogFlavorCapability, + isCapable: true, + }, + { + version: "8.0.20", + capability: capabilities.DisableRedoLogFlavorCapability, + isCapable: false, + }, + { + version: "8.0.15", + capability: capabilities.CheckConstraintsCapability, + isCapable: false, + }, + { + version: "8.0.20", + capability: capabilities.CheckConstraintsCapability, + isCapable: true, + }, + { + version: "8.0.20-log", + capability: capabilities.CheckConstraintsCapability, + isCapable: true, + }, + { + version: "5.7.38", + capability: capabilities.PerformanceSchemaDataLocksTableCapability, + isCapable: false, + }, + { + version: "8.0.20", + capability: capabilities.PerformanceSchemaDataLocksTableCapability, + isCapable: true, + }, + { + // What happens if server version is unspecified + version: "", + capability: capabilities.CheckConstraintsCapability, + isCapable: false, + expectError: capabilities.ErrUnspecifiedServerVersion, + }, + { + // Some ridiculous version. But seeing that we force the flavor to be mysqlGR, + // then this far futuristic version should actually work. + version: "5914.234.17", + capability: capabilities.CheckConstraintsCapability, + isCapable: true, + }, + } + for _, tc := range testcases { + name := fmt.Sprintf("%s %v", tc.version, tc.capability) + t.Run(name, func(t *testing.T) { + flavor := &mysqlGRFlavor{mysqlFlavor{serverVersion: tc.version}} + isCapable, err := flavor.supportsCapability(tc.capability) + if tc.expectError != nil { + assert.ErrorContains(t, err, tc.expectError.Error()) + return + } + assert.NoError(t, err) + assert.Equal(t, tc.isCapable, isCapable) + }) + } +} diff --git a/go/mysql/flavor_test.go b/go/mysql/flavor_test.go index 891725b5afc..b0ef7a1aafc 100644 --- a/go/mysql/flavor_test.go +++ b/go/mysql/flavor_test.go @@ -18,153 +18,123 @@ import ( "testing" "github.com/stretchr/testify/assert" -) -func TestServerVersionAtLeast(t *testing.T) { - testcases := []struct { - version string - parts []int - expect bool - expectError bool - }{ - { - version: "8.0.14", - parts: []int{8, 0, 14}, - expect: true, - }, - { - version: "8.0.14-log", - parts: []int{8, 0, 14}, - expect: true, - }, - { - version: "8.0.14", - parts: []int{8, 0, 13}, - expect: true, - }, - { - version: "8.0.14", - parts: []int{7, 5, 20}, - expect: true, - }, - { - version: "8.0.14", - parts: []int{7, 5}, - expect: true, - }, - { - version: "8.0.14-log", - parts: []int{7, 5, 20}, - expect: true, - }, - { - version: "8.0.14", - parts: []int{8, 1, 2}, - expect: false, - }, - { - version: "8.0.14", - parts: []int{10, 1, 2}, - expect: false, - }, - { - version: "8.0", - parts: []int{8, 0, 14}, - expect: false, - }, - { - version: "8.0.x", - parts: []int{8, 0, 14}, - expectError: true, - }, - } - for _, tc := range testcases { - result, err := ServerVersionAtLeast(tc.version, tc.parts...) - if tc.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tc.expect, result) - } - } -} + "vitess.io/vitess/go/mysql/capabilities" +) -func TestGetFlavor(t *testing.T) { +func TestServerVersionCapableOf(t *testing.T) { testcases := []struct { version string - capability FlavorCapability + capability capabilities.FlavorCapability isCapable bool }{ { version: "8.0.14", - capability: InstantDDLFlavorCapability, + capability: capabilities.InstantDDLFlavorCapability, isCapable: true, }, { version: "8.0.20", - capability: TransactionalGtidExecutedFlavorCapability, + capability: capabilities.TransactionalGtidExecutedFlavorCapability, isCapable: true, }, { version: "8.0.0", - capability: InstantAddLastColumnFlavorCapability, + capability: capabilities.InstantAddLastColumnFlavorCapability, isCapable: true, }, { version: "8.0.0", - capability: InstantAddDropColumnFlavorCapability, + capability: capabilities.InstantAddDropColumnFlavorCapability, isCapable: false, }, { version: "5.6.7", - capability: InstantDDLFlavorCapability, + capability: capabilities.InstantDDLFlavorCapability, isCapable: false, }, { version: "5.7.29", - capability: TransactionalGtidExecutedFlavorCapability, + capability: capabilities.TransactionalGtidExecutedFlavorCapability, isCapable: false, }, { version: "5.6.7", - capability: MySQLJSONFlavorCapability, + capability: capabilities.MySQLJSONFlavorCapability, isCapable: false, }, { version: "5.7.29", - capability: MySQLJSONFlavorCapability, + capability: capabilities.MySQLJSONFlavorCapability, isCapable: true, }, { version: "8.0.30", - capability: DynamicRedoLogCapacityFlavorCapability, + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, isCapable: true, }, { version: "8.0.29", - capability: DynamicRedoLogCapacityFlavorCapability, + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, isCapable: false, }, { version: "5.7.38", - capability: DynamicRedoLogCapacityFlavorCapability, + capability: capabilities.DynamicRedoLogCapacityFlavorCapability, isCapable: false, }, { version: "8.0.21", - capability: DisableRedoLogFlavorCapability, + capability: capabilities.DisableRedoLogFlavorCapability, + isCapable: true, + }, + { + version: "8.0.20", + capability: capabilities.DisableRedoLogFlavorCapability, + isCapable: false, + }, + { + version: "8.0.15", + capability: capabilities.CheckConstraintsCapability, + isCapable: false, + }, + { + version: "8.0.20", + capability: capabilities.CheckConstraintsCapability, isCapable: true, }, + { + version: "8.0.20-log", + capability: capabilities.CheckConstraintsCapability, + isCapable: true, + }, + { + version: "5.7.38", + capability: capabilities.PerformanceSchemaDataLocksTableCapability, + isCapable: false, + }, { version: "8.0.20", - capability: DisableRedoLogFlavorCapability, + capability: capabilities.PerformanceSchemaDataLocksTableCapability, + isCapable: true, + }, + { + // What happens if server version is unspecified + version: "", + capability: capabilities.CheckConstraintsCapability, + isCapable: false, + }, + { + // Some ridiculous version + version: "5914.234.17", + capability: capabilities.CheckConstraintsCapability, isCapable: false, }, } for _, tc := range testcases { name := fmt.Sprintf("%s %v", tc.version, tc.capability) t.Run(name, func(t *testing.T) { - _, capableOf, _ := GetFlavor(tc.version, nil) + capableOf := ServerVersionCapableOf(tc.version) isCapable, err := capableOf(tc.capability) assert.NoError(t, err) assert.Equal(t, tc.isCapable, isCapable) diff --git a/go/mysql/format/float.go b/go/mysql/format/float.go index d9655281e1c..b9a147e5c14 100644 --- a/go/mysql/format/float.go +++ b/go/mysql/format/float.go @@ -25,11 +25,7 @@ const expUpperThreshold = 1000000000000000.0 const expLowerThreshold = 0.000000000000001 // FormatFloat formats a float64 as a byte string in a similar way to what MySQL does -func FormatFloat(v float64) []byte { - return AppendFloat(nil, v) -} - -func AppendFloat(buf []byte, f float64) []byte { +func FormatFloat(f float64) []byte { format := byte('f') if f >= expUpperThreshold || f <= -expUpperThreshold || (f < expLowerThreshold && f > -expLowerThreshold) { format = 'g' @@ -39,7 +35,7 @@ func AppendFloat(buf []byte, f float64) []byte { // do that, and there's no way to customize it, so we must strip the // redundant positive sign manually // e.g. 1.234E+56789 -> 1.234E56789 - fstr := strconv.AppendFloat(buf, f, format, -1, 64) + fstr := strconv.AppendFloat(nil, f, format, -1, 64) if idx := bytes.IndexByte(fstr, 'e'); idx >= 0 { if fstr[idx+1] == '+' { fstr = append(fstr[:idx+1], fstr[idx+2:]...) diff --git a/go/mysql/format/float_test.go b/go/mysql/format/float_test.go new file mode 100644 index 00000000000..218e139840b --- /dev/null +++ b/go/mysql/format/float_test.go @@ -0,0 +1,42 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package format + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFormatFloat(t *testing.T) { + testCases := []struct { + input float64 + want []byte + }{ + {123.456, []byte("123.456")}, + {-1.13456e15, []byte("-1.13456e15")}, + {2e15, []byte("2e15")}, + {2e-15, []byte("0.000000000000002")}, + {-1e-16, []byte("-1e-16")}, + {0.0, []byte("0")}, + } + + for _, tCase := range testCases { + got := FormatFloat(tCase.input) + assert.Equal(t, tCase.want, got) + } +} diff --git a/go/mysql/handshake_test.go b/go/mysql/handshake_test.go index c2b27d6f6d4..284189c30e8 100644 --- a/go/mysql/handshake_test.go +++ b/go/mysql/handshake_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/tlstest" @@ -45,7 +46,7 @@ func TestClearTextClientAuth(t *testing.T) { defer authServer.close() // Create the listener. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -77,6 +78,7 @@ func TestClearTextClientAuth(t *testing.T) { defer conn.Close() + assert.Equal(t, collations.ID(collations.CollationUtf8mb4ID), conn.CharacterSet) // Run a 'select rows' command with results. result, err := conn.ExecuteFetch("select rows", 10000, true) require.NoError(t, err, "ExecuteFetch failed: %v", err) @@ -99,7 +101,7 @@ func TestSSLConnection(t *testing.T) { defer authServer.close() // Create the listener, so we can get its host. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -176,6 +178,7 @@ func testSSLConnectionBasics(t *testing.T, params *ConnParams) { defer conn.Close() assert.Equal(t, "user1", conn.User, "Invalid conn.User, got %v was expecting user1", conn.User) + assert.Equal(t, collations.ID(collations.CollationUtf8mb4ID), conn.CharacterSet) // Run a 'select rows' command with results. result, err := conn.ExecuteFetch("select rows", 10000, true) diff --git a/go/mysql/hex/hex_test.go b/go/mysql/hex/hex_test.go new file mode 100644 index 00000000000..afd94e306e3 --- /dev/null +++ b/go/mysql/hex/hex_test.go @@ -0,0 +1,102 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package hex + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEncodeBytes(t *testing.T) { + testCases := []struct { + input []byte + want []byte + }{ + {[]byte{0xAB, 0xCD, 0xEF}, []byte("ABCDEF")}, + {[]byte{0x01, 0x23, 0x45}, []byte("012345")}, + } + + for _, tCase := range testCases { + got := EncodeBytes(tCase.input) + assert.Equal(t, tCase.want, got) + } +} + +func TestEncodeUint(t *testing.T) { + testCases := []struct { + input uint64 + want []byte + }{ + {0, []byte("0")}, + {123, []byte("7B")}, + {255, []byte("FF")}, + {4096, []byte("1000")}, + } + + for _, tCase := range testCases { + got := EncodeUint(tCase.input) + assert.Equal(t, tCase.want, got) + } +} + +func TestDecodeUint(t *testing.T) { + testCases := []struct { + input uint64 + want []byte + }{ + {0, []byte{0}}, + {123, []byte{0x01, 0x23}}, + {255, []byte{0x02, 0x55}}, + {4096, []byte{0x40, 0x96}}, + } + + for _, tCase := range testCases { + got := DecodeUint(tCase.input) + assert.Equal(t, tCase.want, got) + } +} + +func TestDecodedLen(t *testing.T) { + testCases := []struct { + input []byte + want int + }{ + {[]byte{0}, 1}, + {[]byte{0x01, 0x23}, 1}, + {[]byte("ABCDE"), 3}, + {[]byte("0123456789ABCDEF"), 8}, + } + + for _, tCase := range testCases { + got := DecodedLen(tCase.input) + assert.Equal(t, tCase.want, got) + } +} + +func TestDecodeBytes(t *testing.T) { + err := DecodeBytes([]byte("testDst"), []byte("1")) + assert.NoError(t, err) + + err = DecodeBytes([]byte("testDst"), []byte("12")) + assert.NoError(t, err) + + // DecodeBytes should return an error for "é" as + // hex.decode returns an error for non-ASCII characters + err = DecodeBytes([]byte("testDst"), []byte("é")) + assert.Error(t, err) +} diff --git a/go/mysql/icuregex/compiler.go b/go/mysql/icuregex/compiler.go index 971cd439fb3..6029bed7101 100644 --- a/go/mysql/icuregex/compiler.go +++ b/go/mysql/icuregex/compiler.go @@ -2698,7 +2698,7 @@ func (c *compiler) compileInterval(init opcode, loop opcode) { // Goes at end of the block being looped over, so just append to the code so far. c.appendOp(loop, topOfBlock) - if (c.intervalLow&0xff000000) != 0 || (c.intervalUpper > 0 && (c.intervalUpper&0xff000000) != 0) { + if c.intervalLow > 0x00ffffff || (c.intervalUpper > 0 && c.intervalUpper > 0x00ffffff) { c.error(NumberTooBig) } @@ -3195,7 +3195,7 @@ func (c *compiler) maxMatchLength(start, end int) int32 { } blockLen := c.maxMatchLength(loc+4, loopEndLoc-1) // Recursive call. - updatedLen := int(currentLen) + int(blockLen)*maxLoopCount + updatedLen := int64(currentLen) + int64(blockLen)*int64(maxLoopCount) if updatedLen >= math.MaxInt32 { currentLen = math.MaxInt32 break diff --git a/go/mysql/icuregex/internal/uset/unicode_set.go b/go/mysql/icuregex/internal/uset/unicode_set.go index e2f7bd8cbca..d85bab47532 100644 --- a/go/mysql/icuregex/internal/uset/unicode_set.go +++ b/go/mysql/icuregex/internal/uset/unicode_set.go @@ -58,10 +58,6 @@ func New() *UnicodeSet { return &UnicodeSet{list: buf} } -func FromRunes(list []rune) *UnicodeSet { - return &UnicodeSet{list: list} -} - func (u *UnicodeSet) ensureBufferCapacity(c int) { if cap(u.buffer) < c { u.buffer = make([]rune, c) diff --git a/go/mysql/json/marshal.go b/go/mysql/json/marshal.go index 8e63cddb171..d1a0072ccbb 100644 --- a/go/mysql/json/marshal.go +++ b/go/mysql/json/marshal.go @@ -169,13 +169,12 @@ func MarshalSQLValue(buf []byte) (*sqltypes.Value, error) { if len(buf) == 0 { buf = sqltypes.NullBytes } + jsonVal, err := parser.ParseBytes(buf) if err != nil { return nil, err } + newVal := sqltypes.MakeTrusted(querypb.Type_JSON, jsonVal.MarshalSQLTo(nil)) - if err != nil { - return nil, err - } return &newVal, nil } diff --git a/go/mysql/json/parser.go b/go/mysql/json/parser.go index 35278263877..707d890df93 100644 --- a/go/mysql/json/parser.go +++ b/go/mysql/json/parser.go @@ -941,8 +941,8 @@ func (v *Value) Time() (datetime.Time, bool) { if v.t != TypeTime { return datetime.Time{}, false } - t, _, ok := datetime.ParseTime(v.s, datetime.DefaultPrecision) - return t, ok + t, _, state := datetime.ParseTime(v.s, datetime.DefaultPrecision) + return t, state == datetime.TimeOK } // Object returns the underlying JSON object for the v. diff --git a/go/mysql/ldapauthserver/auth_server_ldap.go b/go/mysql/ldapauthserver/auth_server_ldap.go index d5fcea027ac..5e6010fac0e 100644 --- a/go/mysql/ldapauthserver/auth_server_ldap.go +++ b/go/mysql/ldapauthserver/auth_server_ldap.go @@ -24,32 +24,16 @@ import ( "sync" "time" - "github.com/spf13/pflag" - ldap "gopkg.in/ldap.v2" + "gopkg.in/ldap.v2" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vttls" querypb "vitess.io/vitess/go/vt/proto/query" ) -var ( - ldapAuthConfigFile string - ldapAuthConfigString string - ldapAuthMethod string -) - -func init() { - servenv.OnParseFor("vtgate", func(fs *pflag.FlagSet) { - fs.StringVar(&ldapAuthConfigFile, "mysql_ldap_auth_config_file", "", "JSON File from which to read LDAP server config.") - fs.StringVar(&ldapAuthConfigString, "mysql_ldap_auth_config_string", "", "JSON representation of LDAP server config.") - fs.StringVar(&ldapAuthMethod, "mysql_ldap_auth_method", string(mysql.MysqlClearPassword), "client-side authentication method to use. Supported values: mysql_clear_password, dialog.") - }) -} - // AuthServerLdap implements AuthServer with an LDAP backend type AuthServerLdap struct { Client @@ -63,7 +47,7 @@ type AuthServerLdap struct { } // Init is public so it can be called from plugin_auth_ldap.go (go/cmd/vtgate) -func Init() { +func Init(ldapAuthConfigFile, ldapAuthConfigString, ldapAuthMethod string) { if ldapAuthConfigFile == "" && ldapAuthConfigString == "" { log.Infof("Not configuring AuthServerLdap because mysql_ldap_auth_config_file and mysql_ldap_auth_config_string are empty") return diff --git a/go/mysql/mysql_fuzzer.go b/go/mysql/mysql_fuzzer.go index 2a3e797a797..7370ad8a479 100644 --- a/go/mysql/mysql_fuzzer.go +++ b/go/mysql/mysql_fuzzer.go @@ -31,6 +31,7 @@ import ( gofuzzheaders "github.com/AdaLogics/go-fuzz-headers" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/tlstest" @@ -76,8 +77,8 @@ func createFuzzingSocketPair() (net.Listener, *Conn, *Conn) { } // Create a Conn on both sides. - cConn := newConn(clientConn) - sConn := newConn(serverConn) + cConn := newConn(clientConn, DefaultFlushDelay) + sConn := newConn(serverConn, DefaultFlushDelay) return listener, sConn, cConn } @@ -196,7 +197,7 @@ func FuzzHandleNextCommand(data []byte) int { writeToPass: []bool{false}, pos: -1, queryPacket: data, - }) + }, DefaultFlushDelay) sConn.PrepareData = map[uint32]*PrepareData{} handler := &fuzztestRun{} @@ -327,7 +328,7 @@ func FuzzTLSServer(data []byte) int { Password: "password1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, 0, DefaultFlushDelay, fmt.Sprintf("%s-Vitess", config.DefaultMySQLVersion), 512, 0) if err != nil { return -1 } diff --git a/go/mysql/query.go b/go/mysql/query.go index 7cfeafd258f..22299e5cc80 100644 --- a/go/mysql/query.go +++ b/go/mysql/query.go @@ -17,6 +17,7 @@ limitations under the License. package mysql import ( + "errors" "fmt" "math" "strconv" @@ -26,6 +27,7 @@ import ( "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" @@ -33,6 +35,17 @@ import ( // This file contains the methods related to queries. +var ( + ErrExecuteFetchMultipleResults = vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected multiple results. Use ExecuteFetchMulti instead.") +) + +const ( + // Use as `maxrows` in `ExecuteFetch` and related functions, to indicate no rows should be fetched. + // This is different than specifying `0`, because `0` means "expect zero results", while this means + // "do not attempt to read any results into memory". + FETCH_NO_ROWS = math.MinInt +) + // // Client side methods. // @@ -147,7 +160,7 @@ func (c *Conn) readColumnDefinition(field *querypb.Field, index int) error { } // Convert MySQL type to Vitess type. - field.Type, err = sqltypes.MySQLToType(int64(t), int64(flags)) + field.Type, err = sqltypes.MySQLToType(t, int64(flags)) if err != nil { return sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "MySQLToType(%v,%v) failed for column %v: %v", t, flags, index, err) } @@ -243,7 +256,7 @@ func (c *Conn) readColumnDefinitionType(field *querypb.Field, index int) error { } // Convert MySQL type to Vitess type. - field.Type, err = sqltypes.MySQLToType(int64(t), int64(flags)) + field.Type, err = sqltypes.MySQLToType(t, int64(flags)) if err != nil { return sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "MySQLToType(%v,%v) failed for column %v: %v", t, flags, index, err) } @@ -302,10 +315,35 @@ func (c *Conn) parseRow(data []byte, fields []*querypb.Field, reader func([]byte // 2. if the server closes the connection when a command is in flight, // readComQueryResponse will fail, and we'll return CRServerLost(2013). func (c *Conn) ExecuteFetch(query string, maxrows int, wantfields bool) (result *sqltypes.Result, err error) { - result, _, err = c.ExecuteFetchMulti(query, maxrows, wantfields) + result, more, err := c.ExecuteFetchMulti(query, maxrows, wantfields) + if more { + // Multiple results are unexpected. Prioritize this "unexpected" error over whatever error we got from the first result. + err = errors.Join(ErrExecuteFetchMultipleResults, err) + } + // draining to make the connection clean. + err = c.drainMoreResults(more, err) return result, err } +// ExecuteFetchMultiDrain is for executing multiple statements in one call, but without +// caring for any results. The function returns an error if any of the statements fail. +// The function drains the query results of all statements, even if there's an error. +func (c *Conn) ExecuteFetchMultiDrain(query string) (err error) { + _, more, err := c.ExecuteFetchMulti(query, FETCH_NO_ROWS, false) + return c.drainMoreResults(more, err) +} + +// drainMoreResults ensures to drain all query results, even if there's an error. +// We collect all errors until we consume all results. +func (c *Conn) drainMoreResults(more bool, err error) error { + for more { + var moreErr error + _, more, _, moreErr = c.ReadQueryResult(FETCH_NO_ROWS, false) + err = errors.Join(err, moreErr) + } + return err +} + // ExecuteFetchMulti is for fetching multiple results from a multi-statement result. // It returns an additional 'more' flag. If it is set, you must fetch the additional // results using ReadQueryResult. @@ -313,7 +351,7 @@ func (c *Conn) ExecuteFetchMulti(query string, maxrows int, wantfields bool) (re defer func() { if err != nil { if sqlerr, ok := err.(*sqlerror.SQLError); ok { - sqlerr.Query = query + sqlerr.Query = sqlparser.TruncateQuery(query, c.truncateErrLen) } } }() @@ -337,7 +375,7 @@ func (c *Conn) ExecuteFetchWithWarningCount(query string, maxrows int, wantfield defer func() { if err != nil { if sqlerr, ok := err.(*sqlerror.SQLError); ok { - sqlerr.Query = query + sqlerr.Query = sqlparser.TruncateQuery(query, c.truncateErrLen) } } }() @@ -353,8 +391,9 @@ func (c *Conn) ExecuteFetchWithWarningCount(query string, maxrows int, wantfield // ReadQueryResult gets the result from the last written query. func (c *Conn) ReadQueryResult(maxrows int, wantfields bool) (*sqltypes.Result, bool, uint16, error) { + var packetOk PacketOK // Get the result. - colNumber, packetOk, err := c.readComQueryResponse() + colNumber, err := c.readComQueryResponse(&packetOk) if err != nil { return nil, false, 0, err } @@ -440,15 +479,15 @@ func (c *Conn) ReadQueryResult(maxrows int, wantfields bool) (*sqltypes.Result, more = (statusFlags & ServerMoreResultsExists) != 0 result.StatusFlags = statusFlags } else { - packetOk, err := c.parseOKPacket(data) - if err != nil { + var packetEof PacketOK + if err := c.parseOKPacket(&packetEof, data); err != nil { return nil, false, 0, err } - warnings = packetOk.warnings - more = (packetOk.statusFlags & ServerMoreResultsExists) != 0 - result.SessionStateChanges = packetOk.sessionStateData - result.StatusFlags = packetOk.statusFlags - result.Info = packetOk.info + warnings = packetEof.warnings + more = (packetEof.statusFlags & ServerMoreResultsExists) != 0 + result.SessionStateChanges = packetEof.sessionStateData + result.StatusFlags = packetEof.statusFlags + result.Info = packetEof.info } return result, more, warnings, nil @@ -458,6 +497,11 @@ func (c *Conn) ReadQueryResult(maxrows int, wantfields bool) (*sqltypes.Result, return nil, false, 0, ParseErrorPacket(data) } + if maxrows == FETCH_NO_ROWS { + c.recycleReadPacket() + continue + } + // Check we're not over the limit before we add more. if len(result.Rows) == maxrows { c.recycleReadPacket() @@ -496,35 +540,34 @@ func (c *Conn) drainResults() error { } } -func (c *Conn) readComQueryResponse() (int, *PacketOK, error) { +func (c *Conn) readComQueryResponse(packetOk *PacketOK) (int, error) { data, err := c.readEphemeralPacket() if err != nil { - return 0, nil, sqlerror.NewSQLError(sqlerror.CRServerLost, sqlerror.SSUnknownSQLState, "%v", err) + return 0, sqlerror.NewSQLError(sqlerror.CRServerLost, sqlerror.SSUnknownSQLState, "%v", err) } defer c.recycleReadPacket() if len(data) == 0 { - return 0, nil, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "invalid empty COM_QUERY response packet") + return 0, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "invalid empty COM_QUERY response packet") } switch data[0] { case OKPacket: - packetOk, err := c.parseOKPacket(data) - return 0, packetOk, err + return 0, c.parseOKPacket(packetOk, data) case ErrPacket: // Error - return 0, nil, ParseErrorPacket(data) + return 0, ParseErrorPacket(data) case 0xfb: // Local infile - return 0, nil, vterrors.Errorf(vtrpc.Code_UNIMPLEMENTED, "not implemented") + return 0, vterrors.Errorf(vtrpc.Code_UNIMPLEMENTED, "not implemented") } n, pos, ok := readLenEncInt(data, 0) if !ok { - return 0, nil, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "cannot get column number") + return 0, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "cannot get column number") } if pos != len(data) { - return 0, nil, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "extra data in COM_QUERY response") + return 0, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "extra data in COM_QUERY response") } - return int(n), &PacketOK{}, nil + return int(n), nil } // @@ -596,7 +639,7 @@ func (c *Conn) parseComStmtExecute(prepareData map[uint32]*PrepareData, data []b } // convert MySQL type to internal type. - valType, err := sqltypes.MySQLToType(int64(mysqlType), int64(flags)) + valType, err := sqltypes.MySQLToType(mysqlType, int64(flags)) if err != nil { return stmtID, 0, sqlerror.NewSQLError(sqlerror.CRMalformedPacket, sqlerror.SSUnknownSQLState, "MySQLToType(%v,%v) failed: %v", mysqlType, flags, err) } @@ -930,7 +973,7 @@ func (c *Conn) writeColumnDefinition(field *querypb.Field) error { pos = writeByte(data, pos, 0x0c) pos = writeUint16(data, pos, uint16(field.Charset)) pos = writeUint32(data, pos, field.ColumnLength) - pos = writeByte(data, pos, byte(typ)) + pos = writeByte(data, pos, typ) pos = writeUint16(data, pos, uint16(flags)) pos = writeByte(data, pos, byte(field.Decimals)) pos = writeUint16(data, pos, uint16(0x0000)) diff --git a/go/mysql/query_test.go b/go/mysql/query_test.go index 07012f83b9f..0e1f48c1804 100644 --- a/go/mysql/query_test.go +++ b/go/mysql/query_test.go @@ -413,7 +413,7 @@ func TestQueries(t *testing.T) { { Name: "name", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ @@ -451,15 +451,15 @@ func TestQueries(t *testing.T) { {Name: "Type_DATETIME ", Type: querypb.Type_DATETIME, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, {Name: "Type_YEAR ", Type: querypb.Type_YEAR, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_UNSIGNED_FLAG | querypb.MySqlFlag_NUM_FLAG)}, {Name: "Type_DECIMAL ", Type: querypb.Type_DECIMAL, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "Type_TEXT ", Type: querypb.Type_TEXT, Charset: uint32(collations.Default())}, + {Name: "Type_TEXT ", Type: querypb.Type_TEXT, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, {Name: "Type_BLOB ", Type: querypb.Type_BLOB, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, - {Name: "Type_VARCHAR ", Type: querypb.Type_VARCHAR, Charset: uint32(collations.Default())}, + {Name: "Type_VARCHAR ", Type: querypb.Type_VARCHAR, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, {Name: "Type_VARBINARY", Type: querypb.Type_VARBINARY, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, - {Name: "Type_CHAR ", Type: querypb.Type_CHAR, Charset: uint32(collations.Default())}, + {Name: "Type_CHAR ", Type: querypb.Type_CHAR, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, {Name: "Type_BINARY ", Type: querypb.Type_BINARY, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, {Name: "Type_BIT ", Type: querypb.Type_BIT, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, - {Name: "Type_ENUM ", Type: querypb.Type_ENUM, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_ENUM_FLAG)}, - {Name: "Type_SET ", Type: querypb.Type_SET, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_SET_FLAG)}, + {Name: "Type_ENUM ", Type: querypb.Type_ENUM, Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), Flags: uint32(querypb.MySqlFlag_ENUM_FLAG)}, + {Name: "Type_SET ", Type: querypb.Type_SET, Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), Flags: uint32(querypb.MySqlFlag_SET_FLAG)}, // Skip TUPLE, not possible in Result. {Name: "Type_GEOMETRY ", Type: querypb.Type_GEOMETRY, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG | querypb.MySqlFlag_BLOB_FLAG)}, {Name: "Type_JSON ", Type: querypb.Type_JSON, Charset: collations.CollationUtf8mb4ID}, @@ -537,7 +537,7 @@ func TestQueries(t *testing.T) { { Name: "name", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ diff --git a/go/mysql/replication/replication_position.go b/go/mysql/replication/replication_position.go index 240321f2c6f..a1a9fc2c9c1 100644 --- a/go/mysql/replication/replication_position.go +++ b/go/mysql/replication/replication_position.go @@ -214,23 +214,3 @@ func (rp *Position) MatchesFlavor(flavor string) bool { } return false } - -// Comparable returns whether the receiver is comparable to the supplied position, based on whether one -// of the two positions contains the other. -func (rp *Position) Comparable(other Position) bool { - return rp.GTIDSet.Contains(other.GTIDSet) || other.GTIDSet.Contains(rp.GTIDSet) -} - -// AllPositionsComparable returns true if all positions in the supplied list are comparable with one another, and false -// if any are non-comparable. -func AllPositionsComparable(positions []Position) bool { - for i := 0; i < len(positions); i++ { - for j := i + 1; j < len(positions); j++ { - if !positions[i].Comparable(positions[j]) { - return false - } - } - } - - return true -} diff --git a/go/mysql/schema.go b/go/mysql/schema.go index 933ce657c3a..d0b9bfe2e79 100644 --- a/go/mysql/schema.go +++ b/go/mysql/schema.go @@ -35,33 +35,6 @@ const ( // ShowRowsRead is the query used to find the number of rows read. ShowRowsRead = "show status like 'Innodb_rows_read'" - // DetectSchemaChange query detects if there is any schema change from previous copy. - DetectSchemaChange = ` -SELECT DISTINCT table_name -FROM ( - SELECT table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key - FROM information_schema.columns - WHERE table_schema = database() - - UNION ALL - - SELECT table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key - FROM %s.schemacopy - WHERE table_schema = database() -) _inner -GROUP BY table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key -HAVING COUNT(*) = 1 -` - - // ClearSchemaCopy query clears the schemacopy table. - ClearSchemaCopy = `delete from %s.schemacopy where table_schema = database()` - - // InsertIntoSchemaCopy query copies over the schema information from information_schema.columns table. - InsertIntoSchemaCopy = `insert %s.schemacopy -select table_schema, table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key -from information_schema.columns -where table_schema = database()` - // GetColumnNamesQueryPatternForTable is used for mocking queries in unit tests GetColumnNamesQueryPatternForTable = `SELECT COLUMN_NAME.*TABLE_NAME.*%s.*` ) diff --git a/go/mysql/server.go b/go/mysql/server.go index ec2d7538daa..e21281710b7 100644 --- a/go/mysql/server.go +++ b/go/mysql/server.go @@ -27,10 +27,9 @@ import ( "github.com/pires/go-proxyproto" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" - - "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" @@ -39,7 +38,7 @@ import ( "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" ) @@ -133,6 +132,8 @@ type Handler interface { WarningCount(c *Conn) uint16 ComResetConnection(c *Conn) + + Env() *vtenv.Environment } // UnimplementedHandler implemnts all of the optional callbacks so as to satisy @@ -212,6 +213,14 @@ type Listener struct { // handled further by the MySQL handler. An non-nil error will stop // processing the connection by the MySQL handler. PreHandleFunc func(context.Context, net.Conn, uint32) (net.Conn, error) + + // flushDelay is the delay after which buffered response will be flushed to the client. + flushDelay time.Duration + + // charset is the default server side character set to use for the connection + charset collations.ID + // parser to use for this listener, configured with the correct version. + truncateErrLen int } // NewFromListener creates a new mysql listener from an existing net.Listener @@ -223,6 +232,7 @@ func NewFromListener( connWriteTimeout time.Duration, connBufferPooling bool, keepAlivePeriod time.Duration, + flushDelay time.Duration, ) (*Listener, error) { cfg := ListenerConfig{ Listener: l, @@ -233,6 +243,7 @@ func NewFromListener( ConnReadBufferSize: connBufferSize, ConnBufferPooling: connBufferPooling, ConnKeepAlivePeriod: keepAlivePeriod, + FlushDelay: flushDelay, } return NewListenerWithConfig(cfg) } @@ -247,6 +258,7 @@ func NewListener( proxyProtocol bool, connBufferPooling bool, keepAlivePeriod time.Duration, + flushDelay time.Duration, ) (*Listener, error) { listener, err := net.Listen(protocol, address) if err != nil { @@ -254,10 +266,10 @@ func NewListener( } if proxyProtocol { proxyListener := &proxyproto.Listener{Listener: listener} - return NewFromListener(proxyListener, authServer, handler, connReadTimeout, connWriteTimeout, connBufferPooling, keepAlivePeriod) + return NewFromListener(proxyListener, authServer, handler, connReadTimeout, connWriteTimeout, connBufferPooling, keepAlivePeriod, flushDelay) } - return NewFromListener(listener, authServer, handler, connReadTimeout, connWriteTimeout, connBufferPooling, keepAlivePeriod) + return NewFromListener(listener, authServer, handler, connReadTimeout, connWriteTimeout, connBufferPooling, keepAlivePeriod, flushDelay) } // ListenerConfig should be used with NewListenerWithConfig to specify listener parameters. @@ -273,6 +285,7 @@ type ListenerConfig struct { ConnReadBufferSize int ConnBufferPooling bool ConnKeepAlivePeriod time.Duration + FlushDelay time.Duration } // NewListenerWithConfig creates new listener using provided config. There are @@ -293,13 +306,16 @@ func NewListenerWithConfig(cfg ListenerConfig) (*Listener, error) { authServer: cfg.AuthServer, handler: cfg.Handler, listener: l, - ServerVersion: servenv.AppVersion.MySQLVersion(), + ServerVersion: cfg.Handler.Env().MySQLVersion(), connectionID: 1, connReadTimeout: cfg.ConnReadTimeout, connWriteTimeout: cfg.ConnWriteTimeout, connReadBufferSize: cfg.ConnReadBufferSize, connBufferPooling: cfg.ConnBufferPooling, connKeepAlivePeriod: cfg.ConnKeepAlivePeriod, + flushDelay: cfg.FlushDelay, + truncateErrLen: cfg.Handler.Env().TruncateErrLen(), + charset: cfg.Handler.Env().CollationEnv().DefaultConnectionCharset(), }, nil } @@ -375,7 +391,7 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32, acceptTime time.Ti defer connCount.Add(-1) // First build and send the server handshake packet. - serverAuthPluginData, err := c.writeHandshakeV10(l.ServerVersion, l.authServer, l.TLSConfig.Load() != nil) + serverAuthPluginData, err := c.writeHandshakeV10(l.ServerVersion, l.authServer, uint8(l.charset), l.TLSConfig.Load() != nil) if err != nil { if err != io.EOF { log.Errorf("Cannot send HandshakeV10 packet to %s: %v", c, err) @@ -556,7 +572,7 @@ func (l *Listener) Shutdown() { // writeHandshakeV10 writes the Initial Handshake Packet, server side. // It returns the salt data. -func (c *Conn) writeHandshakeV10(serverVersion string, authServer AuthServer, enableTLS bool) ([]byte, error) { +func (c *Conn) writeHandshakeV10(serverVersion string, authServer AuthServer, charset uint8, enableTLS bool) ([]byte, error) { capabilities := CapabilityClientLongPassword | CapabilityClientFoundRows | CapabilityClientLongFlag | @@ -631,7 +647,7 @@ func (c *Conn) writeHandshakeV10(serverVersion string, authServer AuthServer, en pos = writeUint16(data, pos, uint16(capabilities)) // Character set. - pos = writeByte(data, pos, collations.Local().DefaultConnectionCharset()) + pos = writeByte(data, pos, charset) // Status flag. pos = writeUint16(data, pos, c.StatusFlags) diff --git a/go/mysql/server_flaky_test.go b/go/mysql/server_test.go similarity index 96% rename from go/mysql/server_flaky_test.go rename to go/mysql/server_test.go index 509fccaa47a..082a176e3af 100644 --- a/go/mysql/server_flaky_test.go +++ b/go/mysql/server_test.go @@ -32,15 +32,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" - - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" - vtenv "vitess.io/vitess/go/vt/env" + venv "vitess.io/vitess/go/vt/env" "vitess.io/vitess/go/vt/tlstest" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttls" @@ -146,7 +145,7 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R { Name: "schema_name", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ @@ -165,7 +164,7 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R { Name: "ssl_flag", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ @@ -180,12 +179,12 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R { Name: "user", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, { Name: "user_data", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ @@ -200,7 +199,7 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R Fields: []*querypb.Field{{ Name: "result", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, }) time.Sleep(50 * time.Millisecond) @@ -216,7 +215,7 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R { Name: "result", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }, }, Rows: [][]sqltypes.Value{ @@ -256,6 +255,10 @@ func (th *testHandler) WarningCount(c *Conn) uint16 { return th.warnings } +func (th *testHandler) Env() *vtenv.Environment { + return vtenv.NewTestEnv() +} + func getHostPort(t *testing.T, a net.Addr) (string, int) { host := a.(*net.TCPAddr).IP.String() port := a.(*net.TCPAddr).Port @@ -277,7 +280,7 @@ func TestConnectionFromListener(t *testing.T) { listener, err := net.Listen("tcp", "127.0.0.1:") require.NoError(t, err, "net.Listener failed") - l, err := NewFromListener(listener, authServer, th, 0, 0, false, 0) + l, err := NewFromListener(listener, authServer, th, 0, 0, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -306,7 +309,7 @@ func TestConnectionWithoutSourceHost(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -339,7 +342,7 @@ func TestConnectionWithSourceHost(t *testing.T) { } defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -372,7 +375,7 @@ func TestConnectionUseMysqlNativePasswordWithSourceHost(t *testing.T) { } defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -410,7 +413,7 @@ func TestConnectionUnixSocket(t *testing.T) { os.Remove(unixSocket.Name()) - l, err := NewListener("unix", unixSocket.Name(), authServer, th, 0, 0, false, false, 0) + l, err := NewListener("unix", unixSocket.Name(), authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -436,7 +439,7 @@ func TestClientFoundRows(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -471,11 +474,6 @@ func TestClientFoundRows(t *testing.T) { func TestConnCounts(t *testing.T) { th := &testHandler{} - initialNumUsers := len(connCountPerUser.Counts()) - - // FIXME: we should be able to ResetAll counters instead of computing a delta, but it doesn't work for some reason - // connCountPerUser.ResetAll() - user := "anotherNotYetConnectedUser1" passwd := "password1" @@ -485,7 +483,7 @@ func TestConnCounts(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed") defer l.Close() go l.Accept() @@ -503,29 +501,26 @@ func TestConnCounts(t *testing.T) { c, err := Connect(context.Background(), params) require.NoError(t, err, "Connect failed") - connCounts := connCountPerUser.Counts() - assert.Equal(t, 1, len(connCounts)-initialNumUsers) checkCountsForUser(t, user, 1) // Test with a second new connection. c2, err := Connect(context.Background(), params) require.NoError(t, err) - connCounts = connCountPerUser.Counts() - // There is still only one new user. - assert.Equal(t, 1, len(connCounts)-initialNumUsers) checkCountsForUser(t, user, 2) - // Test after closing connections. time.Sleep lets it work, but seems flakey. + // Test after closing connections. c.Close() - // time.Sleep(10 * time.Millisecond) - // checkCountsForUser(t, user, 1) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + checkCountsForUser(t, user, 1) + }, 1*time.Second, 10*time.Millisecond) c2.Close() - // time.Sleep(10 * time.Millisecond) - // checkCountsForUser(t, user, 0) + assert.EventuallyWithT(t, func(t *assert.CollectT) { + checkCountsForUser(t, user, 0) + }, 1*time.Second, 10*time.Millisecond) } -func checkCountsForUser(t *testing.T, user string, expected int64) { +func checkCountsForUser(t assert.TestingT, user string, expected int64) { connCounts := connCountPerUser.Counts() userCount, ok := connCounts[user] @@ -542,7 +537,7 @@ func TestServer(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) l.SlowConnectWarnThreshold.Store(time.Nanosecond.Nanoseconds()) defer l.Close() @@ -642,7 +637,7 @@ func TestServerStats(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) l.SlowConnectWarnThreshold.Store(time.Nanosecond.Nanoseconds()) defer l.Close() @@ -716,7 +711,7 @@ func TestClearTextServer(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() go l.Accept() @@ -789,7 +784,7 @@ func TestDialogServer(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) l.AllowClearTextWithoutTLS.Store(true) defer l.Close() @@ -832,7 +827,7 @@ func TestTLSServer(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -930,7 +925,7 @@ func TestTLSRequired(t *testing.T) { // Below, we are enabling --ssl-verify-server-cert, which adds // a check that the common name of the certificate matches the // server host name we connect to. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() @@ -1019,7 +1014,7 @@ func TestCachingSha2PasswordAuthWithTLS(t *testing.T) { defer authServer.close() // Create the listener, so we can get its host. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -1113,7 +1108,7 @@ func TestCachingSha2PasswordAuthWithMoreData(t *testing.T) { defer authServer.close() // Create the listener, so we can get its host. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -1182,7 +1177,7 @@ func TestCachingSha2PasswordAuthWithoutTLS(t *testing.T) { defer authServer.close() // Create the listener. - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err, "NewListener failed: %v", err) defer l.Close() host := l.Addr().(*net.TCPAddr).IP.String() @@ -1224,7 +1219,7 @@ func TestErrorCodes(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() go l.Accept() @@ -1318,7 +1313,7 @@ func runMysql(t *testing.T, params *ConnParams, command string) (string, bool) { } func runMysqlWithErr(t *testing.T, params *ConnParams, command string) (string, error) { - dir, err := vtenv.VtMysqlRoot() + dir, err := venv.VtMysqlRoot() require.NoError(t, err) name, err := binaryPath(dir, "mysql") require.NoError(t, err) @@ -1402,7 +1397,7 @@ func TestListenerShutdown(t *testing.T) { UserData: "userData1", }} defer authServer.close() - l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", authServer, th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() go l.Accept() @@ -1470,12 +1465,10 @@ func TestParseConnAttrs(t *testing.T) { } func TestServerFlush(t *testing.T) { - defer func(saved time.Duration) { mysqlServerFlushDelay = saved }(mysqlServerFlushDelay) - mysqlServerFlushDelay = 10 * time.Millisecond - + mysqlServerFlushDelay := 10 * time.Millisecond th := &testHandler{} - l, err := NewListener("tcp", "127.0.0.1:", NewAuthServerNone(), th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", NewAuthServerNone(), th, 0, 0, false, false, 0, mysqlServerFlushDelay) require.NoError(t, err) defer l.Close() go l.Accept() @@ -1502,7 +1495,7 @@ func TestServerFlush(t *testing.T) { want1 := []*querypb.Field{{ Name: "result", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }} assert.Equal(t, want1, flds) @@ -1521,7 +1514,7 @@ func TestServerFlush(t *testing.T) { func TestTcpKeepAlive(t *testing.T) { th := &testHandler{} - l, err := NewListener("tcp", "127.0.0.1:", NewAuthServerNone(), th, 0, 0, false, false, 0) + l, err := NewListener("tcp", "127.0.0.1:", NewAuthServerNone(), th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer l.Close() go l.Accept() diff --git a/go/mysql/sqlerror/constants.go b/go/mysql/sqlerror/constants.go index 0074e904e4a..fdec64588c1 100644 --- a/go/mysql/sqlerror/constants.go +++ b/go/mysql/sqlerror/constants.go @@ -34,7 +34,8 @@ func (e ErrorCode) ToString() string { // See above reference for more information on each code. const ( // Vitess specific errors, (100-999) - ERNotReplica = ErrorCode(100) + ERNotReplica = ErrorCode(100) + ERNonAtomicCommit = ErrorCode(301) // unknown ERUnknownError = ErrorCode(1105) @@ -250,6 +251,7 @@ const ( ERJSONValueTooBig = ErrorCode(3150) ERJSONDocumentTooDeep = ErrorCode(3157) + ERLockNowait = ErrorCode(3572) ERRegexpStringNotTerminated = ErrorCode(3684) ERRegexpBufferOverflow = ErrorCode(3684) ERRegexpIllegalArgument = ErrorCode(3685) diff --git a/go/mysql/sqlerror/sql_error.go b/go/mysql/sqlerror/sql_error.go index 9b1f65c82e3..3ffbc7f9c8c 100644 --- a/go/mysql/sqlerror/sql_error.go +++ b/go/mysql/sqlerror/sql_error.go @@ -17,13 +17,11 @@ limitations under the License. package sqlerror import ( - "bytes" "fmt" "regexp" "strconv" "strings" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -53,17 +51,17 @@ func NewSQLError(number ErrorCode, sqlState string, format string, args ...any) // Error implements the error interface func (se *SQLError) Error() string { - buf := &bytes.Buffer{} + var buf strings.Builder buf.WriteString(se.Message) // Add MySQL errno and SQLSTATE in a format that we can later parse. // There's no avoiding string parsing because all errors // are converted to strings anyway at RPC boundaries. // See NewSQLErrorFromError. - fmt.Fprintf(buf, " (errno %v) (sqlstate %v)", se.Num, se.State) + fmt.Fprintf(&buf, " (errno %v) (sqlstate %v)", se.Num, se.State) if se.Query != "" { - fmt.Fprintf(buf, " during query: %s", sqlparser.TruncateForLog(se.Query)) + fmt.Fprintf(&buf, " during query: %s", se.Query) } return buf.String() @@ -79,7 +77,7 @@ func (se *SQLError) SQLState() string { return se.State } -var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\) \(sqlstate ([0-9a-zA-Z]{5})\).*`) +var errExtract = regexp.MustCompile(`\(errno ([0-9]*)\) \(sqlstate ([0-9a-zA-Z]{5})\)`) // NewSQLErrorFromError returns a *SQLError from the provided error. // If it's not the right type, it still tries to get it from a regexp. @@ -219,6 +217,7 @@ var stateToMysqlCode = map[vterrors.State]mysqlCode{ vterrors.WrongValueCountOnRow: {num: ERWrongValueCountOnRow, state: SSWrongValueCountOnRow}, vterrors.WrongArguments: {num: ERWrongArguments, state: SSUnknownSQLState}, vterrors.UnknownStmtHandler: {num: ERUnknownStmtHandler, state: SSUnknownSQLState}, + vterrors.KeyDoesNotExist: {num: ERKeyDoesNotExist, state: SSClientError}, vterrors.UnknownTimeZone: {num: ERUnknownTimeZone, state: SSUnknownSQLState}, vterrors.RegexpStringNotTerminated: {num: ERRegexpStringNotTerminated, state: SSUnknownSQLState}, vterrors.RegexpBufferOverflow: {num: ERRegexpBufferOverflow, state: SSUnknownSQLState}, @@ -243,6 +242,7 @@ var stateToMysqlCode = map[vterrors.State]mysqlCode{ vterrors.CharacterSetMismatch: {num: ERCharacterSetMismatch, state: SSUnknownSQLState}, vterrors.WrongParametersToNativeFct: {num: ERWrongParametersToNativeFct, state: SSUnknownSQLState}, vterrors.KillDeniedError: {num: ERKillDenied, state: SSUnknownSQLState}, + vterrors.BadNullError: {num: ERBadNullError, state: SSConstraintViolation}, } func getStateToMySQLState(state vterrors.State) mysqlCode { diff --git a/go/mysql/sqlerror/sql_error_test.go b/go/mysql/sqlerror/sql_error_test.go index 3c7f3114b68..b38cec26388 100644 --- a/go/mysql/sqlerror/sql_error_test.go +++ b/go/mysql/sqlerror/sql_error_test.go @@ -20,10 +20,11 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" - - "github.com/stretchr/testify/assert" ) func TestDemuxResourceExhaustedErrors(t *testing.T) { @@ -173,11 +174,17 @@ func TestNewSQLErrorFromError(t *testing.T) { num: EROutOfResources, ss: SSUnknownSQLState, }, + { + err: vterrors.Errorf(vtrpc.Code_RESOURCE_EXHAUSTED, "vttablet: rpc error: code = AlreadyExists desc = Duplicate entry '1' for key 'PRIMARY' (errno 1062) (sqlstate 23000) (CallerID: userData1): Sql: \"insert into test(id, `name`) values (:vtg1 /* INT64 */, :vtg2 /* VARCHAR */)\", BindVars: {vtg1: \"type:INT64 value:\\\"1\\\"\"vtg2: \"type:VARCHAR value:\\\"(errno 1366) (sqlstate 10000)\\\"\"}"), + num: ERDupEntry, + ss: SSConstraintViolation, + }, } for _, tc := range tCases { t.Run(tc.err.Error(), func(t *testing.T) { - err := NewSQLErrorFromError(tc.err).(*SQLError) + var err *SQLError + require.ErrorAs(t, NewSQLErrorFromError(tc.err), &err) assert.Equal(t, tc.num, err.Number()) assert.Equal(t, tc.ss, err.SQLState()) }) diff --git a/go/mysql/streaming_query.go b/go/mysql/streaming_query.go index 257c56e076f..3d0d9ef49e8 100644 --- a/go/mysql/streaming_query.go +++ b/go/mysql/streaming_query.go @@ -19,6 +19,7 @@ package mysql import ( "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/sqlparser" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -32,7 +33,7 @@ func (c *Conn) ExecuteStreamFetch(query string) (err error) { defer func() { if err != nil { if sqlerr, ok := err.(*sqlerror.SQLError); ok { - sqlerr.Query = query + sqlerr.Query = sqlparser.TruncateQuery(query, c.truncateErrLen) } } }() @@ -48,7 +49,8 @@ func (c *Conn) ExecuteStreamFetch(query string) (err error) { } // Get the result. - colNumber, _, err := c.readComQueryResponse() + var packetOk PacketOK + colNumber, err := c.readComQueryResponse(&packetOk) if err != nil { return err } diff --git a/go/mysql/vault/auth_server_vault.go b/go/mysql/vault/auth_server_vault.go index ccdef9f1d53..d2bc2548817 100644 --- a/go/mysql/vault/auth_server_vault.go +++ b/go/mysql/vault/auth_server_vault.go @@ -28,41 +28,12 @@ import ( "time" vaultapi "github.com/aquarapid/vaultlib" - "github.com/spf13/pflag" - - "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" ) -var ( - vaultAddr string - vaultTimeout time.Duration - vaultCACert string - vaultPath string - vaultCacheTTL time.Duration - vaultTokenFile string - vaultRoleID string - vaultRoleSecretIDFile string - vaultRoleMountPoint string -) - -func init() { - servenv.OnParseFor("vtgate", func(fs *pflag.FlagSet) { - fs.StringVar(&vaultAddr, "mysql_auth_vault_addr", "", "URL to Vault server") - fs.DurationVar(&vaultTimeout, "mysql_auth_vault_timeout", 10*time.Second, "Timeout for vault API operations") - fs.StringVar(&vaultCACert, "mysql_auth_vault_tls_ca", "", "Path to CA PEM for validating Vault server certificate") - fs.StringVar(&vaultPath, "mysql_auth_vault_path", "", "Vault path to vtgate credentials JSON blob, e.g.: secret/data/prod/vtgatecreds") - fs.DurationVar(&vaultCacheTTL, "mysql_auth_vault_ttl", 30*time.Minute, "How long to cache vtgate credentials from the Vault server") - fs.StringVar(&vaultTokenFile, "mysql_auth_vault_tokenfile", "", "Path to file containing Vault auth token; token can also be passed using VAULT_TOKEN environment variable") - fs.StringVar(&vaultRoleID, "mysql_auth_vault_roleid", "", "Vault AppRole id; can also be passed using VAULT_ROLEID environment variable") - fs.StringVar(&vaultRoleSecretIDFile, "mysql_auth_vault_role_secretidfile", "", "Path to file containing Vault AppRole secret_id; can also be passed using VAULT_SECRETID environment variable") - fs.StringVar(&vaultRoleMountPoint, "mysql_auth_vault_role_mountpoint", "approle", "Vault AppRole mountpoint; can also be passed using VAULT_MOUNTPOINT environment variable") - }) -} - // AuthServerVault implements AuthServer with a config loaded from Vault. type AuthServerVault struct { methods []mysql.AuthMethod @@ -80,7 +51,7 @@ type AuthServerVault struct { } // InitAuthServerVault - entrypoint for initialization of Vault AuthServer implementation -func InitAuthServerVault() { +func InitAuthServerVault(vaultAddr string, vaultTimeout time.Duration, vaultCACert, vaultPath string, vaultCacheTTL time.Duration, vaultTokenFile, vaultRoleID, vaultRoleSecretIDFile, vaultRoleMountPoint string) { // Check critical parameters. if vaultAddr == "" { log.Infof("Not configuring AuthServerVault, as --mysql_auth_vault_addr is empty.") diff --git a/go/netutil/netutil.go b/go/netutil/netutil.go index fbac6e88424..e440c2148fc 100644 --- a/go/netutil/netutil.go +++ b/go/netutil/netutil.go @@ -20,71 +20,13 @@ package netutil import ( "bytes" "fmt" - "math/rand" "net" "os" "sort" "strconv" "strings" - "time" ) -// byPriorityWeight sorts records by ascending priority and weight. -type byPriorityWeight []*net.SRV - -func (addrs byPriorityWeight) Len() int { return len(addrs) } - -func (addrs byPriorityWeight) Swap(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] } - -func (addrs byPriorityWeight) Less(i, j int) bool { - return addrs[i].Priority < addrs[j].Priority || - (addrs[i].Priority == addrs[j].Priority && addrs[i].Weight < addrs[j].Weight) -} - -// shuffleByWeight shuffles SRV records by weight using the algorithm -// described in RFC 2782. -// NOTE(msolo) This is disabled when the weights are zero. -func (addrs byPriorityWeight) shuffleByWeight(rand *rand.Rand) { - sum := 0 - for _, addr := range addrs { - sum += int(addr.Weight) - } - for sum > 0 && len(addrs) > 1 { - s := 0 - n := rand.Intn(sum) - for i := range addrs { - s += int(addrs[i].Weight) - if s > n { - if i > 0 { - t := addrs[i] - copy(addrs[1:i+1], addrs[0:i]) - addrs[0] = t - } - break - } - } - sum -= int(addrs[0].Weight) - addrs = addrs[1:] - } -} - -func (addrs byPriorityWeight) sortRfc2782(rand *rand.Rand) { - sort.Sort(addrs) - i := 0 - for j := 1; j < len(addrs); j++ { - if addrs[i].Priority != addrs[j].Priority { - addrs[i:j].shuffleByWeight(rand) - i = j - } - } - addrs[i:].shuffleByWeight(rand) -} - -// SortRfc2782 reorders SRV records as specified in RFC 2782. -func SortRfc2782(srvs []*net.SRV) { - byPriorityWeight(srvs).sortRfc2782(rand.New(rand.NewSource(time.Now().UTC().UnixNano()))) -} - // SplitHostPort is an alternative to net.SplitHostPort that also parses the // integer port. In addition, it is more tolerant of improperly escaped IPv6 // addresses, such as "::1:456", which should actually be "[::1]:456". @@ -164,29 +106,6 @@ func FullyQualifiedHostnameOrPanic() string { return hostname } -// ResolveIPv4Addrs resolves the address:port part into IP address:port pairs -func ResolveIPv4Addrs(addr string) ([]string, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - ipAddrs, err := net.LookupIP(host) - if err != nil { - return nil, err - } - result := make([]string, 0, len(ipAddrs)) - for _, ipAddr := range ipAddrs { - ipv4 := ipAddr.To4() - if ipv4 != nil { - result = append(result, net.JoinHostPort(ipv4.String(), port)) - } - } - if len(result) == 0 { - return nil, fmt.Errorf("no IPv4addr for name %v", host) - } - return result, nil -} - func dnsLookup(host string) ([]net.IP, error) { addrs, err := net.LookupHost(host) if err != nil { diff --git a/go/netutil/netutil_test.go b/go/netutil/netutil_test.go index b8cfc563acb..c0c0e16cfed 100644 --- a/go/netutil/netutil_test.go +++ b/go/netutil/netutil_test.go @@ -17,70 +17,9 @@ limitations under the License. package netutil import ( - "fmt" - "math/rand" - "net" - "reflect" "testing" ) -func checkDistribution(t *testing.T, rand *rand.Rand, data []*net.SRV, margin float64) { - sum := 0 - for _, srv := range data { - sum += int(srv.Weight) - } - - results := make(map[string]int) - - count := 1000 - for j := 0; j < count; j++ { - d := make([]*net.SRV, len(data)) - copy(d, data) - byPriorityWeight(d).shuffleByWeight(rand) - key := d[0].Target - results[key] = results[key] + 1 - } - - actual := results[data[0].Target] - expected := float64(count) * float64(data[0].Weight) / float64(sum) - diff := float64(actual) - expected - t.Logf("actual: %v diff: %v e: %v m: %v", actual, diff, expected, margin) - if diff < 0 { - diff = -diff - } - if diff > (expected * margin) { - t.Errorf("missed target weight: expected %v, %v", expected, actual) - } -} - -func testUniformity(t *testing.T, size int, margin float64) { - data := make([]*net.SRV, size) - for i := 0; i < size; i++ { - data[i] = &net.SRV{Target: fmt.Sprintf("%c", 'a'+i), Weight: 1} - } - checkDistribution(t, rand.New(rand.NewSource(1)), data, margin) -} - -func TestUniformity(t *testing.T) { - testUniformity(t, 2, 0.05) - testUniformity(t, 3, 0.10) - testUniformity(t, 10, 0.20) - testWeighting(t, 0.05) -} - -func testWeighting(t *testing.T, margin float64) { - data := []*net.SRV{ - {Target: "a", Weight: 60}, - {Target: "b", Weight: 30}, - {Target: "c", Weight: 10}, - } - checkDistribution(t, rand.New(rand.NewSource(1)), data, margin) -} - -func TestWeighting(t *testing.T) { - testWeighting(t, 0.05) -} - func TestSplitHostPort(t *testing.T) { type addr struct { host string @@ -133,43 +72,6 @@ func TestJoinHostPort(t *testing.T) { } } -func TestResolveIPv4Addrs(t *testing.T) { - cases := []struct { - address string - expected []string - expectedError bool - }{ - { - address: "localhost:3306", - expected: []string{"127.0.0.1:3306"}, - }, - { - address: "127.0.0.256:3306", - expectedError: true, - }, - { - address: "localhost", - expectedError: true, - }, - { - address: "InvalidHost:3306", - expectedError: true, - }, - } - - for _, c := range cases { - t.Run(c.address, func(t *testing.T) { - got, err := ResolveIPv4Addrs(c.address) - if (err != nil) != c.expectedError { - t.Errorf("expected error but got: %v", err) - } - if !reflect.DeepEqual(got, c.expected) { - t.Errorf("expected: %v, got: %v", c.expected, got) - } - }) - } -} - func TestNormalizeIP(t *testing.T) { table := map[string]string{ "1.2.3.4": "1.2.3.4", diff --git a/go/pools/numbered.go b/go/pools/numbered.go index 6e1699a5dd8..8a50624f54c 100644 --- a/go/pools/numbered.go +++ b/go/pools/numbered.go @@ -30,7 +30,7 @@ type Numbered struct { mu sync.Mutex empty *sync.Cond // Broadcast when pool becomes empty resources map[int64]*numberedWrapper - recentlyUnregistered *cache.LRUCache + recentlyUnregistered *cache.LRUCache[*unregistered] } type numberedWrapper struct { @@ -47,10 +47,8 @@ type unregistered struct { // NewNumbered creates a new numbered func NewNumbered() *Numbered { n := &Numbered{ - resources: make(map[int64]*numberedWrapper), - recentlyUnregistered: cache.NewLRUCache(1000, func(_ any) int64 { - return 1 - }), + resources: make(map[int64]*numberedWrapper), + recentlyUnregistered: cache.NewLRUCache[*unregistered](1000), } n.empty = sync.NewCond(&n.mu) return n @@ -107,8 +105,7 @@ func (nu *Numbered) Get(id int64, purpose string) (val any, err error) { defer nu.mu.Unlock() nw, ok := nu.resources[id] if !ok { - if val, ok := nu.recentlyUnregistered.Get(fmt.Sprintf("%v", id)); ok { - unreg := val.(*unregistered) + if unreg, ok := nu.recentlyUnregistered.Get(fmt.Sprintf("%v", id)); ok { return nil, fmt.Errorf("ended at %v (%v)", unreg.timeUnregistered.Format("2006-01-02 15:04:05.000 MST"), unreg.reason) } return nil, fmt.Errorf("not found") diff --git a/go/pools/smartconnpool/pool_test.go b/go/pools/smartconnpool/pool_test.go index c9c2235d90f..9a9fb9500b6 100644 --- a/go/pools/smartconnpool/pool_test.go +++ b/go/pools/smartconnpool/pool_test.go @@ -149,7 +149,7 @@ func TestOpen(t *testing.T) { } // Test that Get waits - ch := make(chan bool) + done := make(chan struct{}) go func() { for i := 0; i < 5; i++ { if i%2 == 0 { @@ -163,14 +163,16 @@ func TestOpen(t *testing.T) { for i := 0; i < 5; i++ { p.put(resources[i]) } - ch <- true + close(done) }() for i := 0; i < 5; i++ { - // Sleep to ensure the goroutine waits - time.Sleep(10 * time.Millisecond) + // block until we have a client wait for a connection, then offer it + for p.wait.waiting() == 0 { + time.Sleep(time.Millisecond) + } p.put(resources[i]) } - <-ch + <-done assert.EqualValues(t, 5, p.Metrics.WaitCount()) assert.Equal(t, 5, len(state.waits)) // verify start times are monotonic increasing diff --git a/go/protoutil/binlogsource.go b/go/protoutil/binlogsource.go new file mode 100644 index 00000000000..385f472c202 --- /dev/null +++ b/go/protoutil/binlogsource.go @@ -0,0 +1,60 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package protoutil + +import ( + "slices" + "sort" + "strings" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +// SortBinlogSourceTables sorts the table related contents of the +// BinlogSource struct lexicographically by table name in order to +// produce consistent results. +func SortBinlogSourceTables(bls *binlogdatapb.BinlogSource) { + if bls == nil { + return + } + + // Sort the tables by name to ensure a consistent order. + slices.Sort(bls.Tables) + + if bls.Filter == nil || len(bls.Filter.Rules) == 0 { + return + } + sort.Slice(bls.Filter.Rules, func(i, j int) bool { + // Exclude filters should logically be processed first. + if bls.Filter.Rules[i].Filter == "exclude" && bls.Filter.Rules[j].Filter != "exclude" { + return true + } + if bls.Filter.Rules[j].Filter == "exclude" && bls.Filter.Rules[i].Filter != "exclude" { + return false + } + + // Remove preceding slash from the match string. + // That is used when the filter is a regular expression. + fi, _ := strings.CutPrefix(bls.Filter.Rules[i].Match, "/") + fj, _ := strings.CutPrefix(bls.Filter.Rules[j].Match, "/") + if fi != fj { + return fi < fj + } + + return bls.Filter.Rules[i].Filter < bls.Filter.Rules[j].Filter + }) +} diff --git a/go/protoutil/binlogsource_test.go b/go/protoutil/binlogsource_test.go new file mode 100644 index 00000000000..fe5564535bd --- /dev/null +++ b/go/protoutil/binlogsource_test.go @@ -0,0 +1,209 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package protoutil + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +func TestSortBinlogSourceTables(t *testing.T) { + tests := []struct { + name string + inSource *binlogdatapb.BinlogSource + outSource *binlogdatapb.BinlogSource + }{ + { + name: "Basic", + inSource: &binlogdatapb.BinlogSource{ + Tables: []string{"wuts1", "atable", "1table", "ztable2", "table3"}, + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "ztable2", + }, + { + Match: "table3", + }, + { + Match: "/wuts", + }, + { + Match: "1table", + Filter: "a", + }, + { + Match: "1table", + }, + { + Match: "atable", + }, + }, + }, + }, + outSource: &binlogdatapb.BinlogSource{ + Tables: []string{"1table", "atable", "table3", "wuts1", "ztable2"}, + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "1table", + }, + { + Match: "1table", + Filter: "a", + }, + { + Match: "atable", + }, + { + Match: "table3", + }, + { + Match: "/wuts", + }, + { + Match: "ztable2", + }, + }, + }, + }, + }, + { + name: "With excludes", + inSource: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "./*", + }, + { + Match: "no4", + Filter: "exclude", + }, + { + Match: "no2", + Filter: "exclude", + }, + { + Match: "ztable2", + }, + { + Match: "atable2", + }, + }, + }, + }, + outSource: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "no2", + Filter: "exclude", + }, + { + Match: "no4", + Filter: "exclude", + }, + { + Match: "./*", + }, + { + Match: "atable2", + }, + { + Match: "ztable2", + }, + }, + }, + }, + }, + { + name: "With excludes", + inSource: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "no4", + Filter: "exclude", + }, + { + Match: "no2", + Filter: "exclude", + }, + { + Match: "./*", + }, + }, + }, + }, + outSource: &binlogdatapb.BinlogSource{ + Filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{ + { + Match: "no2", + Filter: "exclude", + }, + { + Match: "no4", + Filter: "exclude", + }, + { + Match: "./*", + }, + }, + }, + }, + }, + { + name: "Nil", + inSource: nil, + outSource: nil, + }, + { + name: "No filter", + inSource: &binlogdatapb.BinlogSource{ + Tables: []string{"wuts1", "atable", "1table", "ztable2", "table3"}, + Filter: nil, + }, + outSource: &binlogdatapb.BinlogSource{ + Tables: []string{"1table", "atable", "table3", "wuts1", "ztable2"}, + Filter: nil, + }, + }, + { + name: "No filter rules", + inSource: &binlogdatapb.BinlogSource{ + Tables: []string{"wuts1", "atable", "1table", "ztable2", "table3"}, + Filter: &binlogdatapb.Filter{}, + }, + outSource: &binlogdatapb.BinlogSource{ + Tables: []string{"1table", "atable", "table3", "wuts1", "ztable2"}, + Filter: &binlogdatapb.Filter{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + SortBinlogSourceTables(tt.inSource) + require.True(t, proto.Equal(tt.inSource, tt.outSource), "got: %s, want: %s", tt.inSource.String(), tt.outSource.String()) + }) + } +} diff --git a/go/protoutil/duration_test.go b/go/protoutil/duration_test.go index 20f01482563..9f72a439910 100644 --- a/go/protoutil/duration_test.go +++ b/go/protoutil/duration_test.go @@ -26,7 +26,6 @@ import ( ) func TestDurationFromProto(t *testing.T) { - t.Parallel() tests := []struct { name string @@ -59,13 +58,30 @@ func TestDurationFromProto(t *testing.T) { isOk: true, shouldErr: true, }, + { + name: "nanoseconds", + in: &vttime.Duration{ + Seconds: 1, + Nanos: 500000000, + }, + expected: time.Second + 500*time.Millisecond, + isOk: true, + shouldErr: false, + }, + { + name: "out of range nanoseconds", + in: &vttime.Duration{ + Seconds: -1, + Nanos: 500000000, + }, + expected: 0, + isOk: true, + shouldErr: true, + }, } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() actual, ok, err := DurationFromProto(tt.in) if tt.shouldErr { @@ -80,3 +96,36 @@ func TestDurationFromProto(t *testing.T) { }) } } + +func TestDurationToProto(t *testing.T) { + + tests := []struct { + name string + in time.Duration + expected *vttime.Duration + }{ + { + name: "success", + in: time.Second * 1000, + expected: &vttime.Duration{Seconds: 1000}, + }, + { + name: "zero duration", + in: 0, + expected: &vttime.Duration{}, + }, + { + name: "nanoseconds", + in: time.Second + 500*time.Millisecond, + expected: &vttime.Duration{Seconds: 1, Nanos: 500000000}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + actual := DurationToProto(tt.in) + assert.Equal(t, tt.expected, actual) + }) + } +} diff --git a/go/ptr/ptr.go b/go/ptr/ptr.go new file mode 100644 index 00000000000..8fd7f6c0bf9 --- /dev/null +++ b/go/ptr/ptr.go @@ -0,0 +1,31 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package ptr + +// Of returns a pointer to the given value +func Of[T any](x T) *T { + return &x +} + +// Unwrap dereferences the given pointer if it's not nil. +// Otherwise, it returns default_ +func Unwrap[T any](x *T, default_ T) T { + if x != nil { + return *x + } + return default_ +} diff --git a/go/ratelimiter/ratelimiter.go b/go/ratelimiter/ratelimiter.go deleted file mode 100644 index ddadb8659da..00000000000 --- a/go/ratelimiter/ratelimiter.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package ratelimiter implements rate limiting functionality. -package ratelimiter - -import ( - "sync" - "time" -) - -// RateLimiter was inspired by https://github.com/golang/go/wiki/RateLimiting. -// However, the go example is not good for setting high qps limits because -// it will cause the ticker to fire too often. Also, the ticker will continue -// to fire when the system is idle. This new Ratelimiter achieves the same thing, -// but by using just counters with no tickers or channels. -type RateLimiter struct { - maxCount int - interval time.Duration - - mu sync.Mutex - curCount int - lastTime time.Time -} - -// NewRateLimiter creates a new RateLimiter. maxCount is the max burst allowed -// while interval specifies the duration for a burst. The effective rate limit is -// equal to maxCount/interval. For example, if you want to a max QPS of 5000, -// and want to limit bursts to no more than 500, you'd specify a maxCount of 500 -// and an interval of 100*time.Millilsecond. -func NewRateLimiter(maxCount int, interval time.Duration) *RateLimiter { - return &RateLimiter{ - maxCount: maxCount, - interval: interval, - } -} - -// Allow returns true if a request is within the rate limit norms. -// Otherwise, it returns false. -func (rl *RateLimiter) Allow() bool { - rl.mu.Lock() - defer rl.mu.Unlock() - if time.Since(rl.lastTime) < rl.interval { - if rl.curCount > 0 { - rl.curCount-- - return true - } - return false - } - rl.curCount = rl.maxCount - 1 - rl.lastTime = time.Now() - return true -} diff --git a/go/ratelimiter/ratelimiter_test.go b/go/ratelimiter/ratelimiter_test.go deleted file mode 100644 index 768584b20f7..00000000000 --- a/go/ratelimiter/ratelimiter_test.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ratelimiter - -import ( - "testing" - "time" -) - -func TestLimiter1(t *testing.T) { - rl := NewRateLimiter(1, 10*time.Millisecond) - result := rl.Allow() - if !result { - t.Error("Allow: false, want true") - } - result = rl.Allow() - if result { - t.Error("Allow: true, want false") - } - - time.Sleep(11 * time.Millisecond) - result = rl.Allow() - if !result { - t.Error("Allow: false, want true") - } - result = rl.Allow() - if result { - t.Error("Allow: true, want false") - } -} - -func TestLimiter2(t *testing.T) { - rl := NewRateLimiter(2, 10*time.Millisecond) - var result bool - for i := 0; i < 2; i++ { - result = rl.Allow() - if !result { - t.Errorf("Allow(%d): false, want true", i) - } - } - result = rl.Allow() - if result { - t.Error("Allow: true, want false") - } - - time.Sleep(11 * time.Millisecond) - for i := 0; i < 2; i++ { - result = rl.Allow() - if !result { - t.Errorf("Allow(%d): false, want true", i) - } - } - result = rl.Allow() - if result { - t.Error("Allow: true, want false") - } -} diff --git a/go/sets/set_test.go b/go/sets/set_test.go new file mode 100644 index 00000000000..db453727d6c --- /dev/null +++ b/go/sets/set_test.go @@ -0,0 +1,85 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sets + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInsert(t *testing.T) { + testSet := New[int](1, 2, 3) + testSet.Insert(4, 5, 6) + compareSet := New[int](1, 2, 3, 4, 5, 6) + assert.Equal(t, testSet, compareSet) +} + +func TestDelete(t *testing.T) { + testSet := New[int](1, 2, 3, 4, 5, 6) + testSet.Delete(1, 5) + compareSet := New[int](2, 3, 4, 6) + assert.Equal(t, testSet, compareSet) + testSet.Delete(2, 3, 4, 6) + assert.Empty(t, testSet) +} + +func TestHas(t *testing.T) { + testSet := New[int](1, 2, 3) + assert.True(t, testSet.Has(3)) + assert.False(t, testSet.Has(-1)) +} + +func TestHasAny(t *testing.T) { + testSet := New[int](1, 2, 3) + assert.True(t, testSet.HasAny(1, 10, 11)) + assert.False(t, testSet.HasAny(-1, 10, 11)) +} + +func TestDifference(t *testing.T) { + testSet := New[int](1, 2, 3) + compareSet := New[int](-1, -2, 1, 2, 3) + diffSet := New[int](-1, -2) + assert.Equal(t, diffSet, compareSet.Difference(testSet)) +} + +func TestIntersection(t *testing.T) { + setA := New[int](1, 2, 3) + setB := New[int](1, 2, 8, 9, 10) + expectedSet := New[int](1, 2) + assert.Equal(t, expectedSet, setA.Intersection(setB)) +} + +func TestEqual(t *testing.T) { + testSet := New[int](1, 2, 3, 4, 5, 6) + compareSet := New[int](1, 2, 3, 4, 5, 6) + assert.True(t, testSet.Equal(compareSet)) + compareSet.Insert(-1, -2) + assert.False(t, testSet.Equal(compareSet)) +} + +func TestLen(t *testing.T) { + testSet := New[int](1, 2, 3) + assert.Equal(t, testSet.Len(), 3) +} + +func TestList(t *testing.T) { + testSet := New[string]("a string", "testing", "Capital", "34") + list := List(testSet) + require.EqualValues(t, []string{"34", "Capital", "a string", "testing"}, list) +} diff --git a/go/slice/slice_test.go b/go/slice/slice_test.go new file mode 100644 index 00000000000..d079ac51b9b --- /dev/null +++ b/go/slice/slice_test.go @@ -0,0 +1,198 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package slice + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAll(t *testing.T) { + testCases := []struct { + name string + input []int + fn func(int) bool + expected bool + }{ + { + name: "EmptySlice", + input: []int{}, + fn: func(i int) bool { return i > 0 }, + expected: true, + }, + { + name: "AllElementsTrue", + input: []int{1, 2, 3}, + fn: func(i int) bool { return i > 0 }, + expected: true, + }, + { + name: "SomeElementsFalse", + input: []int{1, 2, 0}, + fn: func(i int) bool { return i > 0 }, + expected: false, + }, + { + name: "SingleElementFalse", + input: []int{0}, + fn: func(i int) bool { return i > 0 }, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := All(tc.input, tc.fn) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestAny(t *testing.T) { + testCases := []struct { + name string + input []int + fn func(int) bool + expected bool + }{ + { + name: "ReturnsTrue", + input: []int{1, 2, 3, 4, 5}, + fn: func(n int) bool { return n == 3 }, + expected: true, + }, + { + name: "EmptySliceInput", + input: []int{}, + fn: func(n int) bool { return n > 0 }, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := Any(tc.input, tc.fn) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestMap(t *testing.T) { + testCases := []struct { + name string + input []int + fn func(int) int + expected []int + }{ + { + name: "AppliesFunction", + input: []int{1, 2, 3, 4, 5}, + fn: func(n int) int { return n * 2 }, + expected: []int{2, 4, 6, 8, 10}, + }, + { + name: "EmptySliceInput", + input: nil, + fn: func(n int) int { return n }, + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := Map(tc.input, tc.fn) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestFilter(t *testing.T) { + testCases := []struct { + name string + input []int + fn func(int) bool + expected []int + }{ + { + name: "Filtering", + input: []int{1, 2, 3, 4, 5}, + fn: func(i int) bool { return i%2 == 0 }, + expected: []int{2, 4}, + }, + { + name: "AllElementsFilteredOut", + input: []int{1, 3, 5}, + fn: func(i int) bool { return i%2 == 0 }, + expected: []int{}, + }, + { + name: "EmptySliceInput", + input: nil, + fn: func(n int) bool { return n > 0 }, + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := Filter(tc.input, tc.fn) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestMapWithError(t *testing.T) { + testCases := []struct { + name string + input []int + fn func(int) (int, error) + expected []int + expectedErr string + }{ + { + name: "SuccessfulMapping", + input: []int{1, 2, 3}, + fn: func(i int) (int, error) { return i * 2, nil }, + expected: []int{2, 4, 6}, + }, + { + name: "ErrorReturned", + input: []int{1, 2, 3}, + fn: func(i int) (int, error) { return 0, errors.New("error") }, + expectedErr: "error", + }, + { + name: "EmptySliceInput", + input: nil, + fn: func(n int) (int, error) { return n, nil }, + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := MapWithError(tc.input, tc.fn) + if tc.expectedErr != "" { + assert.EqualError(t, err, tc.expectedErr) + } else { + assert.Equal(t, tc.expected, result) + } + }) + } +} diff --git a/go/sqlescape/ids.go b/go/sqlescape/ids.go index 3983db13362..a70d48c1cd2 100644 --- a/go/sqlescape/ids.go +++ b/go/sqlescape/ids.go @@ -14,6 +14,7 @@ limitations under the License. package sqlescape import ( + "fmt" "strings" ) @@ -52,11 +53,65 @@ func EscapeIDs(identifiers []string) []string { return result } -// UnescapeID reverses any backticking in the input string. -func UnescapeID(in string) string { +// UnescapeID reverses any backticking in the input string by EscapeID. +func UnescapeID(in string) (string, error) { l := len(in) - if l >= 2 && in[0] == '`' && in[l-1] == '`' { - return in[1 : l-1] + + if l == 0 || in == "``" { + return "", fmt.Errorf("UnescapeID err: invalid input identifier '%s'", in) + + } + + if l == 1 { + if in[0] == '`' { + return "", fmt.Errorf("UnescapeID err: invalid input identifier '`'") + } + return in, nil + } + + first, last := in[0], in[l-1] + + if first == '`' && last != '`' { + return "", fmt.Errorf("UnescapeID err: unexpected single backtick at position %d in '%s'", 0, in) + } + if first != '`' && last == '`' { + return "", fmt.Errorf("UnescapeID err: unexpected single backtick at position %d in '%s'", l, in) + } + if first != '`' && last != '`' { + if idx := strings.IndexByte(in, '`'); idx != -1 { + return "", fmt.Errorf("UnescapeID err: no outer backticks found in the identifier '%s'", in) + } + return in, nil + } + + in = in[1 : l-1] + + if idx := strings.IndexByte(in, '`'); idx == -1 { + return in, nil + } + + var buf strings.Builder + buf.Grow(len(in)) + + for i := 0; i < len(in); i++ { + buf.WriteByte(in[i]) + + if i < len(in)-1 && in[i] == '`' { + if in[i+1] == '`' { + i++ // halves the number of backticks + } else { + return "", fmt.Errorf("UnescapeID err: unexpected single backtick at position %d in '%s'", i, in) + } + } + } + + return buf.String(), nil +} + +func EnsureEscaped(in string) (string, error) { + out, err := UnescapeID(in) + if err != nil { + return "", err } - return in + return EscapeID(out), nil } diff --git a/go/sqlescape/ids_test.go b/go/sqlescape/ids_test.go index a2d2e69be6f..67ae233d7d6 100644 --- a/go/sqlescape/ids_test.go +++ b/go/sqlescape/ids_test.go @@ -14,7 +14,11 @@ limitations under the License. package sqlescape import ( + "fmt" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestEscapeID(t *testing.T) { @@ -26,12 +30,185 @@ func TestEscapeID(t *testing.T) { }, { in: "a`a", out: "`a``a`", + }, { + in: "a a", + out: "`a a`", + }, { + in: "`fo`o`", + out: "```fo``o```", + }, { + in: "", + out: "``", }} for _, tc := range testcases { - out := EscapeID(tc.in) - if out != tc.out { - t.Errorf("EscapeID(%s): %s, want %s", tc.in, out, tc.out) - } + t.Run(tc.in, func(t *testing.T) { + out := EscapeID(tc.in) + assert.Equal(t, out, tc.out) + }) + } +} + +func TestUnescapeID(t *testing.T) { + testcases := []struct { + in, out string + err bool + }{ + { + in: "``", + out: "", + err: true, + }, + { + in: "a", + out: "a", + err: false, + }, + { + in: "`aa`", + out: "aa", + err: false, + }, + { + in: "`a``a`", + out: "a`a", + err: false, + }, + { + in: "`foo", + out: "", + err: true, + }, + { + in: "foo`", + out: "", + err: true, + }, + { + in: "`fo`o", + out: "", + err: true, + }, + { + in: "`fo`o`", + out: "", + err: true, + }, + { + in: "``fo``o``", + out: "", + err: true, + }, + { + in: "```fo``o```", + out: "`fo`o`", + err: false, + }, + { + in: "```fo`o```", + out: "", + err: true, + }, + { + in: "foo", + out: "foo", + err: false, + }, + { + in: "f`oo", + out: "", + err: true, + }, + { + in: "", + out: "", + err: true, + }, + { + in: "`", + out: "", + err: true, + }, + } + for _, tc := range testcases { + t.Run(tc.in, func(t *testing.T) { + out, err := UnescapeID(tc.in) + if tc.err { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tc.out, out, "output mismatch") + } + }) + } +} + +func TestEnsureEscaped(t *testing.T) { + tt := []struct { + in string + out string + err bool + }{ + { + in: "", + out: "", + err: true, + }, + { + in: "foo", + out: "`foo`", + err: false, + }, + { + in: "`foo`", + out: "`foo`", + err: false, + }, + { + in: "```fo``o```", + out: "```fo``o```", + err: false, + }, + { + in: "`fo``o`", + out: "`fo``o`", + err: false, + }, + { + in: "f`oo", + out: "", + err: true, + }, + { + in: "`fo`o", + out: "", + err: true, + }, + { + in: "`foo", + out: "", + err: true, + }, + { + in: "foo`", + out: "", + err: true, + }, + { + in: "`fo`o`", + out: "", + err: true, + }, + } + for _, tc := range tt { + t.Run(tc.in, func(t *testing.T) { + out, err := EnsureEscaped(tc.in) + if tc.err { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tc.out, out, "output mismatch") + } + }) } } @@ -41,6 +218,7 @@ func BenchmarkEscapeID(b *testing.B) { testcases := []string{ "aa", "a`a", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", } + for _, tc := range testcases { name := tc if len(name) > 10 { @@ -53,3 +231,30 @@ func BenchmarkEscapeID(b *testing.B) { }) } } + +func TestEscapeIDs(t *testing.T) { + testCases := []struct { + input []string + expected []string + }{ + { + input: []string{"abc", "def", "ghi"}, + expected: []string{"`abc`", "`def`", "`ghi`"}, + }, + { + input: []string{"abc", "a`a", "`ghi`"}, + expected: []string{"`abc`", "`a``a`", "```ghi```"}, + }, + { + input: []string{}, + expected: []string{}, + }, + } + + for _, tt := range testCases { + t.Run(fmt.Sprintf("%v", tt.input), func(t *testing.T) { + out := EscapeIDs(tt.input) + assert.Equal(t, tt.expected, out) + }) + } +} diff --git a/go/sqltypes/bind_variables.go b/go/sqltypes/bind_variables.go index 18beda37702..9b8969bc814 100644 --- a/go/sqltypes/bind_variables.go +++ b/go/sqltypes/bind_variables.go @@ -17,10 +17,10 @@ limitations under the License. package sqltypes import ( - "bytes" "errors" "fmt" "strconv" + "strings" "google.golang.org/protobuf/proto" @@ -418,7 +418,7 @@ func FormatBindVariables(bindVariables map[string]*querypb.BindVariable, full, a } if asJSON { - var buf bytes.Buffer + var buf strings.Builder buf.WriteString("{") first := true for k, v := range out { diff --git a/go/sqltypes/named_result_test.go b/go/sqltypes/named_result_test.go index 8c9c32554da..ae42d4257dd 100644 --- a/go/sqltypes/named_result_test.go +++ b/go/sqltypes/named_result_test.go @@ -20,12 +20,15 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" querypb "vitess.io/vitess/go/vt/proto/query" ) func TestToNamedResult(t *testing.T) { + require.Nil(t, ToNamedResult(nil)) + in := &Result{ Fields: []*querypb.Field{{ Name: "id", @@ -57,3 +60,116 @@ func TestToNamedResult(t *testing.T) { require.Equal(t, uint64(i), named.Rows[i].AsUint64("uid", 0)) } } + +func TestToNumericTypes(t *testing.T) { + row := RowNamedValues{ + "test": Value{ + val: []byte("0x1234"), + }, + } + tests := []struct { + name string + fieldName string + expectedErr string + }{ + { + name: "random fieldName", + fieldName: "random", + expectedErr: "No such field in RowNamedValues", + }, + { + name: "right fieldName", + fieldName: "test", + expectedErr: "Cannot convert value to desired type", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := row.ToInt(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + + _, err = row.ToInt32(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + + _, err = row.ToInt64(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + + _, err = row.ToUint64(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + + _, err = row.ToFloat64(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + + _, err = row.ToBool(tt.fieldName) + if tt.expectedErr != "" { + require.ErrorContains(t, err, tt.expectedErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestToBytes(t *testing.T) { + row := RowNamedValues{ + "test": Value{ + val: []byte("0x1234"), + }, + } + + _, err := row.ToBytes("random") + require.ErrorContains(t, err, "No such field in RowNamedValues") + + val, err := row.ToBytes("test") + require.NoError(t, err) + require.Equal(t, []byte{0x30, 0x78, 0x31, 0x32, 0x33, 0x34}, val) +} + +func TestRow(t *testing.T) { + row := RowNamedValues{} + tests := []struct { + name string + res *NamedResult + expectedRow RowNamedValues + }{ + { + name: "empty results", + res: &NamedResult{}, + expectedRow: nil, + }, + { + name: "non-empty results", + res: &NamedResult{ + Rows: []RowNamedValues{row}, + }, + expectedRow: row, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expectedRow, tt.res.Row()) + }) + } +} diff --git a/go/sqltypes/parse_rows_test.go b/go/sqltypes/parse_rows_test.go index a32f2fd35b0..45c55da019b 100644 --- a/go/sqltypes/parse_rows_test.go +++ b/go/sqltypes/parse_rows_test.go @@ -168,20 +168,123 @@ func TestRowParsing(t *testing.T) { } func TestRowsEquals(t *testing.T) { - var cases = []struct { + tests := []struct { + name string left, right string + expectedErr string }{ - {"[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]"}, + { + name: "Both equal", + left: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + right: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + }, + { + name: "length mismatch", + left: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + right: "[[INT64(2)] [INT64(2)] [INT64(1)]]", + expectedErr: "results differ: expected 4 rows in result, got 3\n\twant: [[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]\n\tgot: [[INT64(2)] [INT64(2)] [INT64(1)]]", + }, + { + name: "elements mismatch", + left: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + right: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(4)]]", + expectedErr: "results differ: row [INT64(1)] is missing from result\n\twant: [[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]\n\tgot: [[INT64(1)] [INT64(2)] [INT64(2)] [INT64(4)]]", + }, } - for _, tc := range cases { - left, err := ParseRows(tc.left) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + left, err := ParseRows(tt.left) + require.NoError(t, err) - right, err := ParseRows(tc.right) - require.NoError(t, err) + right, err := ParseRows(tt.right) + require.NoError(t, err) - err = RowsEquals(left, right) - require.NoError(t, err) + err = RowsEquals(left, right) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} + +func TestRowsEqualStr(t *testing.T) { + tests := []struct { + name string + want string + got []Row + expectedErr string + }{ + { + name: "Unknown type", + want: "[[RANDOM(1)]]", + got: []Row{ + { + NewInt64(1), + }, + }, + expectedErr: "malformed row assertion: unknown SQL type \"RANDOM\" at :1:3", + }, + { + name: "Invalid row", + want: "[[INT64(1]]", + got: []Row{ + { + NewInt64(1), + }, + }, + expectedErr: "malformed row assertion: unexpected token ']' at :1:10", + }, + { + name: "Both equal", + want: "[[INT64(1)]]", + got: []Row{ + { + NewInt64(1), + }, + }, + }, + { + name: "length mismatch", + want: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + got: []Row{ + { + NewInt64(1), + }, + }, + expectedErr: "results differ: expected 4 rows in result, got 1\n\twant: [[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]\n\tgot: [[INT64(1)]]", + }, + { + name: "elements mismatch", + want: "[[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]", + got: []Row{ + { + NewInt64(1), + }, + { + NewInt64(1), + }, + { + NewInt64(1), + }, + { + NewInt64(1), + }, + }, + expectedErr: "results differ: row [INT64(2)] is missing from result\n\twant: [[INT64(1)] [INT64(2)] [INT64(2)] [INT64(1)]]\n\tgot: [[INT64(1)] [INT64(1)] [INT64(1)] [INT64(1)]]", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := RowsEqualsStr(tt.want, tt.got) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) } } diff --git a/go/sqltypes/query_response_test.go b/go/sqltypes/query_response_test.go new file mode 100644 index 00000000000..30b6fe62e14 --- /dev/null +++ b/go/sqltypes/query_response_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sqltypes + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestQueryResponsesEqual(t *testing.T) { + tests := []struct { + name string + r1 []QueryResponse + r2 []QueryResponse + isEqual bool + }{ + { + name: "1 response in each", + r1: []QueryResponse{ + { + QueryResult: &Result{}, + QueryError: nil, + }, + }, + r2: []QueryResponse{ + { + QueryResult: &Result{}, + QueryError: nil, + }, + }, + isEqual: true, + }, + { + name: "different lengths", + r1: []QueryResponse{ + { + QueryResult: &Result{}, + QueryError: nil, + }, + }, + r2: []QueryResponse{}, + isEqual: false, + }, + { + name: "different query errors", + r1: []QueryResponse{ + { + QueryResult: &Result{}, + QueryError: fmt.Errorf("some error"), + }, + }, + r2: []QueryResponse{ + { + QueryResult: &Result{ + Info: "Test", + }, + QueryError: nil, + }, + }, + isEqual: false, + }, + { + name: "different query results", + r1: []QueryResponse{ + { + QueryResult: &Result{ + RowsAffected: 7, + }, + QueryError: nil, + }, + }, + r2: []QueryResponse{ + { + QueryResult: &Result{ + RowsAffected: 10, + }, + QueryError: nil, + }, + }, + isEqual: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.isEqual, QueryResponsesEqual(tt.r1, tt.r2)) + }) + } +} diff --git a/go/sqltypes/testing.go b/go/sqltypes/testing.go index 3894635eae0..649462333c7 100644 --- a/go/sqltypes/testing.go +++ b/go/sqltypes/testing.go @@ -17,7 +17,6 @@ limitations under the License. package sqltypes import ( - "bytes" crand "crypto/rand" "encoding/base64" "encoding/hex" @@ -77,7 +76,7 @@ func MakeTestResult(fields []*querypb.Field, rows ...string) *Result { for i, row := range rows { result.Rows[i] = make([]Value, len(fields)) for j, col := range split(row) { - if col == "null" { + if strings.ToLower(col) == "null" { result.Rows[i][j] = NULL continue } @@ -155,13 +154,13 @@ func TestTuple(vals ...Value) Value { // PrintResults prints []*Results into a string. // This function should only be used for testing. func PrintResults(results []*Result) string { - b := new(bytes.Buffer) + var b strings.Builder for i, r := range results { if i == 0 { - fmt.Fprintf(b, "%v", r) + fmt.Fprintf(&b, "%v", r) continue } - fmt.Fprintf(b, ", %v", r) + fmt.Fprintf(&b, ", %v", r) } return b.String() } @@ -230,7 +229,7 @@ var RandomGenerators = map[Type]RandomGenerator{ return NewFloat64(rand.ExpFloat64()) }, Decimal: func() Value { - dec := fmt.Sprintf("%d.%d", rand.Intn(9999999999), rand.Intn(9999999999)) + dec := fmt.Sprintf("%d.%d", rand.Intn(999999999), rand.Intn(999999999)) if rand.Int()&0x1 == 1 { dec = "-" + dec } diff --git a/go/sqltypes/type.go b/go/sqltypes/type.go index 9157db685e9..964dd6b5d83 100644 --- a/go/sqltypes/type.go +++ b/go/sqltypes/type.go @@ -89,6 +89,10 @@ func IsText(t querypb.Type) bool { return int(t)&flagIsText == flagIsText } +func IsTextOrBinary(t querypb.Type) bool { + return int(t)&flagIsText == flagIsText || int(t)&flagIsBinary == flagIsBinary +} + // IsBinary returns true if querypb.Type is a binary. // If you have a Value object, use its member function. func IsBinary(t querypb.Type) bool { @@ -185,7 +189,7 @@ const ( // If you add to this map, make sure you add a test case // in tabletserver/endtoend. -var mysqlToType = map[int64]querypb.Type{ +var mysqlToType = map[byte]querypb.Type{ 0: Decimal, 1: Int8, 2: Int16, @@ -271,7 +275,7 @@ func modifyType(typ querypb.Type, flags int64) querypb.Type { } // MySQLToType computes the vitess type from mysql type and flags. -func MySQLToType(mysqlType, flags int64) (typ querypb.Type, err error) { +func MySQLToType(mysqlType byte, flags int64) (typ querypb.Type, err error) { result, ok := mysqlToType[mysqlType] if !ok { return 0, fmt.Errorf("unsupported type: %d", mysqlType) @@ -299,7 +303,7 @@ func AreTypesEquivalent(mysqlTypeFromBinlog, mysqlTypeFromSchema querypb.Type) b // typeToMySQL is the reverse of mysqlToType. var typeToMySQL = map[querypb.Type]struct { - typ int64 + typ byte flags int64 }{ Int8: {typ: 1}, @@ -338,7 +342,7 @@ var typeToMySQL = map[querypb.Type]struct { } // TypeToMySQL returns the equivalent mysql type and flag for a vitess type. -func TypeToMySQL(typ querypb.Type) (mysqlType, flags int64) { +func TypeToMySQL(typ querypb.Type) (mysqlType byte, flags int64) { val := typeToMySQL[typ] return val.typ, val.flags } diff --git a/go/sqltypes/type_test.go b/go/sqltypes/type_test.go index f223c5811e3..8493dc23e05 100644 --- a/go/sqltypes/type_test.go +++ b/go/sqltypes/type_test.go @@ -20,6 +20,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" + querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -285,7 +287,7 @@ func TestTypeToMySQL(t *testing.T) { func TestMySQLToType(t *testing.T) { testcases := []struct { - intype int64 + intype byte inflags int64 outtype querypb.Type }{{ @@ -512,3 +514,88 @@ func TestPrintTypeChecks(t *testing.T) { t.Logf("%s(): %s", f.name, strings.Join(match, ", ")) } } + +func TestIsTextOrBinary(t *testing.T) { + tests := []struct { + name string + ty querypb.Type + isTextorBinary bool + }{ + { + name: "null type", + ty: querypb.Type_NULL_TYPE, + isTextorBinary: false, + }, + { + name: "blob type", + ty: querypb.Type_BLOB, + isTextorBinary: true, + }, + { + name: "text type", + ty: querypb.Type_TEXT, + isTextorBinary: true, + }, + { + name: "binary type", + ty: querypb.Type_BINARY, + isTextorBinary: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.isTextorBinary, IsTextOrBinary(tt.ty)) + }) + } +} + +func TestIsDateOrTime(t *testing.T) { + tests := []struct { + name string + ty querypb.Type + isDateOrTime bool + }{ + { + name: "null type", + ty: querypb.Type_NULL_TYPE, + isDateOrTime: false, + }, + { + name: "blob type", + ty: querypb.Type_BLOB, + isDateOrTime: false, + }, + { + name: "timestamp type", + ty: querypb.Type_TIMESTAMP, + isDateOrTime: true, + }, + { + name: "date type", + ty: querypb.Type_DATE, + isDateOrTime: true, + }, + { + name: "time type", + ty: querypb.Type_TIME, + isDateOrTime: true, + }, + { + name: "date time type", + ty: querypb.Type_DATETIME, + isDateOrTime: true, + }, + { + name: "year type", + ty: querypb.Type_YEAR, + isDateOrTime: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.isDateOrTime, IsDateOrTime(tt.ty)) + }) + } +} diff --git a/go/sqltypes/value.go b/go/sqltypes/value.go index 45415814700..20a30cbc1c1 100644 --- a/go/sqltypes/value.go +++ b/go/sqltypes/value.go @@ -733,7 +733,13 @@ func (v Value) TinyWeightCmp(other Value) int { if v.flags&other.flags&flagTinyWeight == 0 { return 0 } - return int(int64(v.tinyweight) - int64(other.tinyweight)) + if v.tinyweight == other.tinyweight { + return 0 + } + if v.tinyweight < other.tinyweight { + return -1 + } + return 1 } func (v Value) TinyWeight() uint32 { diff --git a/go/sqltypes/value_test.go b/go/sqltypes/value_test.go index 86c751f3d0d..10a46e09a9e 100644 --- a/go/sqltypes/value_test.go +++ b/go/sqltypes/value_test.go @@ -463,13 +463,13 @@ func TestEncode(t *testing.T) { outASCII: "'YQ=='", }} for _, tcase := range testcases { - buf := &bytes.Buffer{} - tcase.in.EncodeSQL(buf) + var buf strings.Builder + tcase.in.EncodeSQL(&buf) if tcase.outSQL != buf.String() { t.Errorf("%v.EncodeSQL = %q, want %q", tcase.in, buf.String(), tcase.outSQL) } - buf = &bytes.Buffer{} - tcase.in.EncodeASCII(buf) + buf.Reset() + tcase.in.EncodeASCII(&buf) if tcase.outASCII != buf.String() { t.Errorf("%v.EncodeASCII = %q, want %q", tcase.in, buf.String(), tcase.outASCII) } diff --git a/go/stats/counters.go b/go/stats/counters.go index e79da39c48b..bcf7fc3a8b6 100644 --- a/go/stats/counters.go +++ b/go/stats/counters.go @@ -62,7 +62,7 @@ func (c *counters) set(name string, value int64) { func (c *counters) reset() { c.mu.Lock() defer c.mu.Unlock() - c.counts = make(map[string]int64) + clear(c.counts) } // ZeroAll zeroes out all values @@ -70,7 +70,9 @@ func (c *counters) ZeroAll() { c.mu.Lock() defer c.mu.Unlock() - clear(c.counts) + for k := range c.counts { + c.counts[k] = 0 + } } // Counts returns a copy of the Counters' map. diff --git a/go/stats/histogram.go b/go/stats/histogram.go index 833c09b86bb..4a51098d606 100644 --- a/go/stats/histogram.go +++ b/go/stats/histogram.go @@ -74,6 +74,11 @@ func NewGenericHistogram(name, help string, cutoffs []int64, labels []string, co return h } +// Adds a hook that will be called every time a new value is added to the histogram +func (h *Histogram) AddHook(hook func(int64)) { + h.hook = hook +} + // Add adds a new measurement to the Histogram. func (h *Histogram) Add(value int64) { for i := range h.labels { diff --git a/go/stats/histogram_test.go b/go/stats/histogram_test.go index 1c7b05d8e9a..caa2a6ba722 100644 --- a/go/stats/histogram_test.go +++ b/go/stats/histogram_test.go @@ -19,6 +19,8 @@ package stats import ( "expvar" "testing" + + "github.com/stretchr/testify/assert" ) func TestHistogram(t *testing.T) { @@ -27,30 +29,28 @@ func TestHistogram(t *testing.T) { for i := 0; i < 10; i++ { h.Add(int64(i)) } - want := `{"1": 2, "5": 4, "inf": 4, "Count": 10, "Total": 45}` - if h.String() != want { - t.Errorf("got %v, want %v", h.String(), want) - } + + assert.Equal(t, h.String(), `{"1": 2, "5": 4, "inf": 4, "Count": 10, "Total": 45}`) + counts := h.Counts() counts["Count"] = h.Count() counts["Total"] = h.Total() - for k, want := range map[string]int64{ + for key, want := range map[string]int64{ "1": 2, "5": 4, "inf": 4, "Count": 10, "Total": 45, } { - if got := counts[k]; got != want { - t.Errorf("histogram counts [%v]: got %d, want %d", k, got, want) - } - } - if got, want := h.CountLabel(), "Count"; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := h.TotalLabel(), "Total"; got != want { - t.Errorf("got %v, want %v", got, want) + assert.Equal(t, counts[key], want) } + + assert.Equal(t, h.CountLabel(), "Count") + assert.Equal(t, h.TotalLabel(), "Total") + assert.Equal(t, h.Labels(), []string{"1", "5", "inf"}) + assert.Equal(t, h.Cutoffs(), []int64{1, 5}) + assert.Equal(t, h.Buckets(), []int64{2, 4, 4}) + assert.Equal(t, h.Help(), "help") } func TestGenericHistogram(t *testing.T) { @@ -63,27 +63,69 @@ func TestGenericHistogram(t *testing.T) { "count", "total", ) - want := `{"one": 0, "five": 0, "max": 0, "count": 0, "total": 0}` - if got := h.String(); got != want { - t.Errorf("got %v, want %v", got, want) - } + assert.Equal(t, h.String(), `{"one": 0, "five": 0, "max": 0, "count": 0, "total": 0}`) +} + +func TestInvalidGenericHistogram(t *testing.T) { + // Use a deferred function to capture the panic that the code should throw + defer func() { + r := recover() + assert.NotNil(t, r) + assert.Equal(t, r, "mismatched cutoff and label lengths") + }() + + clearStats() + NewGenericHistogram( + "histgen", + "help", + []int64{1, 5}, + []string{"one", "five"}, + "count", + "total", + ) } func TestHistogramHook(t *testing.T) { - var gotname string - var gotv *Histogram + // Check the results of Register hook function + var gotName string + var gotV *Histogram clearStats() Register(func(name string, v expvar.Var) { - gotname = name - gotv = v.(*Histogram) + gotName = name + gotV = v.(*Histogram) }) - name := "hist2" - v := NewHistogram(name, "help", []int64{1}) - if gotname != name { - t.Errorf("got %v; want %v", gotname, name) - } - if gotv != v { - t.Errorf("got %#v, want %#v", gotv, v) - } + v := NewHistogram("hist2", "help", []int64{1}) + + assert.Equal(t, gotName, "hist2") + assert.Equal(t, gotV, v) + + // Check the results of AddHook function + hookCalled := false + var addedValue int64 + + v.AddHook(func(value int64) { + hookCalled = true + addedValue = value + }) + + v.Add(42) + assert.Equal(t, hookCalled, true) + assert.Equal(t, addedValue, int64(42)) + + // Check the results of RegisterHistogramHook function + hookCalled = false + addedValue = 0 + gotName = "" + + RegisterHistogramHook(func(name string, value int64) { + hookCalled = true + gotName = name + addedValue = value + }) + + v.Add(10) + assert.Equal(t, gotName, "hist2") + assert.Equal(t, hookCalled, true) + assert.Equal(t, addedValue, int64(10)) } diff --git a/go/stats/hooks_test.go b/go/stats/hooks_test.go new file mode 100644 index 00000000000..72b6d1071c7 --- /dev/null +++ b/go/stats/hooks_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package stats + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStatsdHook(t *testing.T) { + t.Run("RegisterTimerHook", func(t *testing.T) { + defaultStatsdHook = statsdHook{} + + // Create a dummy timerHook function + dummyTimerHook := func(name, tags string, value int64, timings *Timings) { + assert.Equal(t, "dummyName", name) + assert.Equal(t, "dummyTags", tags) + assert.Equal(t, int64(42), value) + } + + // Register the dummy timerHook and then call the same + RegisterTimerHook(dummyTimerHook) + + assert.NotNil(t, defaultStatsdHook.timerHook) + assert.Nil(t, defaultStatsdHook.histogramHook) + defaultStatsdHook.timerHook("dummyName", "dummyTags", 42, nil) + }) + + t.Run("RegisterHistogramHook", func(t *testing.T) { + defaultStatsdHook = statsdHook{} + + // Create a dummy histogramHook function + dummyHistogramHook := func(name string, value int64) { + assert.Equal(t, "dummyName", name) + assert.Equal(t, int64(42), value) + } + + RegisterHistogramHook(dummyHistogramHook) + + assert.NotNil(t, defaultStatsdHook.histogramHook) + assert.Nil(t, defaultStatsdHook.timerHook) + defaultStatsdHook.histogramHook("dummyName", 42) + }) +} diff --git a/go/stats/opentsdb/collector.go b/go/stats/opentsdb/collector.go index 9b870815067..d2c40432e7f 100644 --- a/go/stats/opentsdb/collector.go +++ b/go/stats/opentsdb/collector.go @@ -17,7 +17,6 @@ limitations under the License. package opentsdb import ( - "bytes" "encoding/json" "expvar" "strings" @@ -65,7 +64,7 @@ func (dc *collector) addFloat(metric string, val float64, tags map[string]string // Also make everything lowercase, since opentsdb is case sensitive and lowercase // simplifies the convention. sanitize := func(text string) string { - var b bytes.Buffer + var b strings.Builder for _, r := range text { if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '-' || r == '_' || r == '/' || r == '.' { b.WriteRune(r) diff --git a/go/stats/ring_test.go b/go/stats/ring_test.go new file mode 100644 index 00000000000..d6f8ddb73af --- /dev/null +++ b/go/stats/ring_test.go @@ -0,0 +1,43 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package stats + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRingInt64(t *testing.T) { + t.Run("Add Values", func(t *testing.T) { + ri := NewRingInt64(3) + ri.Add(1) + ri.Add(2) + ri.Add(3) + + assert.Equal(t, []int64{1, 2, 3}, ri.Values()) + + ri.Add(4) + ri.Add(5) + assert.Equal(t, []int64{3, 4, 5}, ri.Values()) + }) + + t.Run("Empty Ring", func(t *testing.T) { + ri := NewRingInt64(3) + assert.Empty(t, ri.Values()) + }) +} diff --git a/go/stats/snake_case_converter_test.go b/go/stats/snake_case_converter_test.go index 2552ade8df3..c8a3892020e 100644 --- a/go/stats/snake_case_converter_test.go +++ b/go/stats/snake_case_converter_test.go @@ -36,7 +36,7 @@ func TestToSnakeCase(t *testing.T) { } for _, tt := range snakeCaseTest { - if got, want := toSnakeCase(tt.input), tt.output; got != want { + if got, want := GetSnakeName(tt.input), tt.output; got != want { t.Errorf("want '%s', got '%s'", want, got) } } diff --git a/go/stats/statsd/statsd.go b/go/stats/statsd/statsd.go index f791d7b742d..099b8eea0f6 100644 --- a/go/stats/statsd/statsd.go +++ b/go/stats/statsd/statsd.go @@ -8,7 +8,7 @@ import ( "strings" "sync" - "github.com/DataDog/datadog-go/statsd" + "github.com/DataDog/datadog-go/v5/statsd" "github.com/spf13/pflag" "vitess.io/vitess/go/stats" @@ -81,15 +81,18 @@ func InitWithoutServenv(namespace string) { log.Info("statsdAddress is empty") return } - statsdC, err := statsd.NewBuffered(statsdAddress, 100) + opts := []statsd.Option{ + statsd.WithMaxMessagesPerPayload(100), + statsd.WithNamespace(namespace), + } + if tags := stats.ParseCommonTags(stats.CommonTags); len(tags) > 0 { + opts = append(opts, statsd.WithTags(makeCommonTags(tags))) + } + statsdC, err := statsd.New(statsdAddress, opts...) if err != nil { log.Errorf("Failed to create statsd client %v", err) return } - statsdC.Namespace = namespace + "." - if tags := stats.ParseCommonTags(stats.CommonTags); len(tags) > 0 { - statsdC.Tags = makeCommonTags(tags) - } sb.namespace = namespace sb.statsdClient = statsdC sb.sampleRate = statsdSampleRate diff --git a/go/stats/statsd/statsd_test.go b/go/stats/statsd/statsd_test.go index c615da3cdfd..feab8ae6759 100644 --- a/go/stats/statsd/statsd_test.go +++ b/go/stats/statsd/statsd_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/DataDog/datadog-go/statsd" + "github.com/DataDog/datadog-go/v5/statsd" "github.com/stretchr/testify/assert" "vitess.io/vitess/go/stats" @@ -19,8 +19,7 @@ func getBackend(t *testing.T) (StatsBackend, *net.UDPConn) { udpAddr, _ := net.ResolveUDPAddr("udp", addr) server, _ := net.ListenUDP("udp", udpAddr) bufferLength := 9 - client, _ := statsd.NewBuffered(addr, bufferLength) - client.Namespace = "test." + client, _ := statsd.New(addr, statsd.WithMaxMessagesPerPayload(bufferLength), statsd.WithNamespace("test")) var sb StatsBackend sb.namespace = "foo" sb.sampleRate = 1 diff --git a/go/streamlog/streamlog.go b/go/streamlog/streamlog.go index 26248fcd1b1..6d9f81f98d9 100644 --- a/go/streamlog/streamlog.go +++ b/go/streamlog/streamlog.go @@ -23,10 +23,8 @@ import ( "net/http" "net/url" "os" - "os/signal" "strings" "sync" - "syscall" "github.com/spf13/pflag" @@ -63,18 +61,10 @@ func SetRedactDebugUIQueries(newRedactDebugUIQueries bool) { redactDebugUIQueries = newRedactDebugUIQueries } -func GetQueryLogFilterTag() string { - return queryLogFilterTag -} - func SetQueryLogFilterTag(newQueryLogFilterTag string) { queryLogFilterTag = newQueryLogFilterTag } -func GetQueryLogRowThreshold() uint64 { - return queryLogRowThreshold -} - func SetQueryLogRowThreshold(newQueryLogRowThreshold uint64) { queryLogRowThreshold = newQueryLogRowThreshold } @@ -215,7 +205,7 @@ func (logger *StreamLogger[T]) ServeLogs(url string, logf LogFormatter) { // it. func (logger *StreamLogger[T]) LogToFile(path string, logf LogFormatter) (chan T, error) { rotateChan := make(chan os.Signal, 1) - signal.Notify(rotateChan, syscall.SIGUSR2) + setupRotate(rotateChan) logChan := logger.Subscribe("FileLog") formatParams := map[string][]string{"full": {}} diff --git a/go/streamlog/streamlog_flaky_test.go b/go/streamlog/streamlog_test.go similarity index 68% rename from go/streamlog/streamlog_flaky_test.go rename to go/streamlog/streamlog_test.go index 9c0b0366a1d..e653de74ea8 100644 --- a/go/streamlog/streamlog_flaky_test.go +++ b/go/streamlog/streamlog_test.go @@ -18,6 +18,7 @@ package streamlog import ( "bufio" + "bytes" "fmt" "io" "net" @@ -29,6 +30,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/servenv" ) @@ -260,3 +263,124 @@ func TestFile(t *testing.T) { t.Errorf("streamlog file: want %q got %q", want, got) } } + +func TestShouldEmitLog(t *testing.T) { + origQueryLogFilterTag := queryLogFilterTag + origQueryLogRowThreshold := queryLogRowThreshold + defer func() { + SetQueryLogFilterTag(origQueryLogFilterTag) + SetQueryLogRowThreshold(origQueryLogRowThreshold) + }() + + tests := []struct { + sql string + qLogFilterTag string + qLogRowThreshold uint64 + rowsAffected uint64 + rowsReturned uint64 + ok bool + }{ + { + sql: "queryLogThreshold smaller than affected and returned", + qLogFilterTag: "", + qLogRowThreshold: 2, + rowsAffected: 7, + rowsReturned: 7, + ok: true, + }, + { + sql: "queryLogThreshold greater than affected and returned", + qLogFilterTag: "", + qLogRowThreshold: 27, + rowsAffected: 7, + rowsReturned: 17, + ok: false, + }, + { + sql: "this doesn't contains queryFilterTag: TAG", + qLogFilterTag: "special tag", + qLogRowThreshold: 10, + rowsAffected: 7, + rowsReturned: 17, + ok: false, + }, + { + sql: "this contains queryFilterTag: TAG", + qLogFilterTag: "TAG", + qLogRowThreshold: 0, + rowsAffected: 7, + rowsReturned: 17, + ok: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.sql, func(t *testing.T) { + SetQueryLogFilterTag(tt.qLogFilterTag) + SetQueryLogRowThreshold(tt.qLogRowThreshold) + + require.Equal(t, tt.ok, ShouldEmitLog(tt.sql, tt.rowsAffected, tt.rowsReturned)) + }) + } +} + +func TestGetFormatter(t *testing.T) { + tests := []struct { + name string + logger *StreamLogger[string] + params url.Values + val any + expectedErr string + expectedOutput string + }{ + { + name: "unexpected value error", + logger: &StreamLogger[string]{ + name: "test-logger", + }, + params: url.Values{ + "keys": []string{"key1", "key2"}, + }, + val: "temp val", + expectedOutput: "Error: unexpected value of type string in test-logger!", + expectedErr: "", + }, + { + name: "mock formatter", + logger: &StreamLogger[string]{ + name: "test-logger", + }, + params: url.Values{ + "keys": []string{"key1", "key2"}, + }, + val: &mockFormatter{err: fmt.Errorf("formatter error")}, + expectedErr: "formatter error", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + var buffer bytes.Buffer + logFormatterFunc := GetFormatter[string](tt.logger) + err := logFormatterFunc(&buffer, tt.params, tt.val) + if tt.expectedErr == "" { + require.NoError(t, err) + require.Equal(t, tt.expectedOutput, buffer.String()) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} + +type mockFormatter struct { + called bool + err error +} + +func (mf *mockFormatter) Logf(w io.Writer, params url.Values) error { + mf.called = true + return mf.err +} diff --git a/go/streamlog/streamlog_unix.go b/go/streamlog/streamlog_unix.go new file mode 100644 index 00000000000..0cfa4b9e6bc --- /dev/null +++ b/go/streamlog/streamlog_unix.go @@ -0,0 +1,29 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package streamlog + +import ( + "os" + "os/signal" + "syscall" +) + +func setupRotate(ch chan os.Signal) { + signal.Notify(ch, syscall.SIGUSR2) +} diff --git a/go/streamlog/streamlog_windows.go b/go/streamlog/streamlog_windows.go new file mode 100644 index 00000000000..ef69058b97c --- /dev/null +++ b/go/streamlog/streamlog_windows.go @@ -0,0 +1,29 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package streamlog + +import ( + "os" + + "vitess.io/vitess/go/vt/log" +) + +func setupRotate(ch chan os.Signal) { + log.Warningf("signal based log rotation is not supported on Windows") +} diff --git a/go/sync2/consolidator.go b/go/sync2/consolidator.go index 604d7fff35b..401daaef1f1 100644 --- a/go/sync2/consolidator.go +++ b/go/sync2/consolidator.go @@ -127,21 +127,19 @@ func (rs *pendingResult) Wait() { // It is also used by the txserializer package to count how often transactions // have been queued and had to wait because they targeted the same row (range). type ConsolidatorCache struct { - *cache.LRUCache + *cache.LRUCache[*ccount] } // NewConsolidatorCache creates a new cache with the given capacity. func NewConsolidatorCache(capacity int64) *ConsolidatorCache { - return &ConsolidatorCache{cache.NewLRUCache(capacity, func(_ any) int64 { - return 1 - })} + return &ConsolidatorCache{cache.NewLRUCache[*ccount](capacity)} } // Record increments the count for "query" by 1. // If it's not in the cache yet, it will be added. func (cc *ConsolidatorCache) Record(query string) { - if v, ok := cc.Get(query); ok { - v.(*ccount).add(1) + if c, ok := cc.Get(query); ok { + c.add(1) } else { c := ccount(1) cc.Set(query, &c) @@ -159,7 +157,7 @@ func (cc *ConsolidatorCache) Items() []ConsolidatorCacheItem { items := cc.LRUCache.Items() ret := make([]ConsolidatorCacheItem, len(items)) for i, v := range items { - ret[i] = ConsolidatorCacheItem{Query: v.Key, Count: v.Value.(*ccount).get()} + ret[i] = ConsolidatorCacheItem{Query: v.Key, Count: v.Value.get()} } return ret } diff --git a/go/syscallutil/kill_unix.go b/go/syscallutil/kill_unix.go new file mode 100644 index 00000000000..d0b1776ae3c --- /dev/null +++ b/go/syscallutil/kill_unix.go @@ -0,0 +1,27 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package syscallutil + +import ( + "syscall" +) + +func Kill(pid int, signum syscall.Signal) (err error) { + return syscall.Kill(pid, signum) +} diff --git a/go/syscallutil/kill_windows.go b/go/syscallutil/kill_windows.go new file mode 100644 index 00000000000..091fcdf759d --- /dev/null +++ b/go/syscallutil/kill_windows.go @@ -0,0 +1,28 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package syscallutil + +import ( + "errors" + "syscall" +) + +func Kill(pid int, signum syscall.Signal) (err error) { + return errors.New("kill is not supported on windows") +} diff --git a/go/tb/error_test.go b/go/tb/error_test.go new file mode 100644 index 00000000000..db257bc85b6 --- /dev/null +++ b/go/tb/error_test.go @@ -0,0 +1,95 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tb + +import ( + "bytes" + "fmt" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStackTrace(t *testing.T) { + testErr := "test err" + testStackTrace := "test stack trace" + testStackError := stackError{ + err: fmt.Errorf("%s", testErr), + stackTrace: testStackTrace, + } + + expectedErr := fmt.Sprintf("%s\n%s", testErr, testStackTrace) + assert.Equal(t, expectedErr, testStackError.Error()) + assert.Equal(t, testStackTrace, testStackError.StackTrace()) +} + +func TestStack(t *testing.T) { + // skip is set to 2 to check if the 3rd function in + // the go routine stack is called from this file + // 1st func is expected to be stack and 2nd to be Stack + b := Stack(2) + l := bytes.Split(b, []byte(":")) + + _, file, _, _ := runtime.Caller(0) + assert.Equal(t, string(l[0]), file) +} + +func TestFunction(t *testing.T) { + pc, _, _, _ := runtime.Caller(0) + name := function(pc) + + assert.Equal(t, "io/vitess/go/tb.TestFunction", string(name)) + + // invalid program counter + name = function(0) + assert.Equal(t, name, dunno) +} + +func TestErrorf(t *testing.T) { + s1 := stackError{ + err: fmt.Errorf("err1"), + stackTrace: "stackTrace1", + } + s2 := stackError{ + err: fmt.Errorf("err2"), + stackTrace: "stackTrace2", + } + err := Errorf("test msg %v %v", s1, s2) + + expectedMsg := fmt.Sprintf("test msg %v %v", s1, s2) + expectedErr := fmt.Sprintf("%v\n%v", expectedMsg, "stackTrace1") + assert.Equal(t, err.Error(), expectedErr) + + err = Errorf("test msg") + s := string(Stack(4)) + expectedErr = fmt.Sprintf("%v\n%v", "test msg", s) + assert.Equal(t, err.Error(), expectedErr) +} + +func TestSource(t *testing.T) { + lines := [][]byte{ + []byte("\ttest line 1\t"), + []byte("\ttest line 2\t"), + []byte("\ttest line 3\t"), + } + + assert.Equal(t, []byte("test line 1"), source(lines, 0)) + assert.Equal(t, []byte("test line 2"), source(lines, 1)) + assert.Equal(t, dunno, source(lines, -1)) + assert.Equal(t, dunno, source(lines, 3)) +} diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index 5e80d5d3cc3..3f5389d2726 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -83,10 +83,10 @@ func TestTabletInitialBackup(t *testing.T) { restore(t, primary, "replica", "NOT_SERVING") // Vitess expects that the user has set the database into ReadWrite mode before calling // TabletExternallyReparented - err = localCluster.VtctlclientProcess.ExecuteCommand( - "SetReadWrite", primary.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand( + "SetWritable", primary.Alias, "true") require.Nil(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand( + err = localCluster.VtctldClientProcess.ExecuteCommand( "TabletExternallyReparented", primary.Alias) require.Nil(t, err) restore(t, replica1, "replica", "SERVING") @@ -277,7 +277,7 @@ func initTablets(t *testing.T, startTablet bool, initShardPrimary bool) { if initShardPrimary { // choose primary and start replication - err := localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) + err := localCluster.VtctldClientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) require.Nil(t, err) } } @@ -300,11 +300,12 @@ func resetTabletDirectory(t *testing.T, tablet cluster.Vttablet, initMysql bool) extraArgs := []string{"--db-credentials-file", dbCredentialFile} tablet.MysqlctlProcess.ExtraArgs = extraArgs - // Shutdown Mysql - err := tablet.MysqlctlProcess.Stop() - require.Nil(t, err) // Teardown Tablet - err = tablet.VttabletProcess.TearDown() + err := tablet.VttabletProcess.TearDown() + require.Nil(t, err) + + // Shutdown Mysql + err = tablet.MysqlctlProcess.Stop() require.Nil(t, err) // Clear out the previous data @@ -326,25 +327,19 @@ func tearDown(t *testing.T, initMysql bool) { } caughtUp := waitForReplicationToCatchup([]cluster.Vttablet{*replica1, *replica2}) require.True(t, caughtUp, "Timed out waiting for all replicas to catch up") - promoteCommands := "STOP SLAVE; RESET SLAVE ALL; RESET MASTER;" - disableSemiSyncCommands := "SET GLOBAL rpl_semi_sync_master_enabled = false; SET GLOBAL rpl_semi_sync_slave_enabled = false" + promoteCommands := []string{"STOP SLAVE", "RESET SLAVE ALL", "RESET MASTER"} + disableSemiSyncCommands := []string{"SET GLOBAL rpl_semi_sync_master_enabled = false", " SET GLOBAL rpl_semi_sync_slave_enabled = false"} for _, tablet := range []cluster.Vttablet{*primary, *replica1, *replica2} { - _, err := tablet.VttabletProcess.QueryTablet(promoteCommands, keyspaceName, true) + err := tablet.VttabletProcess.QueryTabletMultiple(promoteCommands, keyspaceName, true) require.Nil(t, err) - _, err = tablet.VttabletProcess.QueryTablet(disableSemiSyncCommands, keyspaceName, true) + err = tablet.VttabletProcess.QueryTabletMultiple(disableSemiSyncCommands, keyspaceName, true) require.Nil(t, err) } - // TODO: Ideally we should not be resetting the mysql. - // So in below code we will have to uncomment the commented code and remove resetTabletDirectory for _, tablet := range []cluster.Vttablet{*primary, *replica1, *replica2} { - //Tear down Tablet - //err := tablet.VttabletProcess.TearDown() - //require.Nil(t, err) - resetTabletDirectory(t, tablet, initMysql) // DeleteTablet on a primary will cause tablet to shutdown, so should only call it after tablet is already shut down - err := localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", tablet.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("DeleteTablets", "--allow-primary", tablet.Alias) require.Nil(t, err) } } diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go index 36bfae123d8..367956c9827 100644 --- a/go/test/endtoend/backup/vtbackup/main_test.go +++ b/go/test/endtoend/backup/vtbackup/main_test.go @@ -43,8 +43,6 @@ var ( shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName) dbCredentialFile string commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index 1ca56db68c2..c20ab70e652 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -74,8 +74,6 @@ var ( dbCredentialFile string shardName = "0" commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", @@ -259,7 +257,7 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp return replica3, nil } - if err := localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shard.Name, cell, primary.TabletUID); err != nil { + if err := localCluster.VtctldClientProcess.InitShardPrimary(keyspaceName, shard.Name, cell, primary.TabletUID); err != nil { return 1, err } @@ -457,7 +455,7 @@ func primaryBackup(t *testing.T) { localCluster.VerifyBackupCount(t, shardKsName, 0) - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", "--", "--allow_primary=true", primary.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", primary.Alias) require.Nil(t, err) // We'll restore this on the primary later to test restores using a backup timestamp @@ -477,7 +475,7 @@ func primaryBackup(t *testing.T) { // And only 1 record after we restore using the first backup timestamp cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", "--", "--allow_primary=true", primary.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", primary.Alias) require.Nil(t, err) backups = localCluster.VerifyBackupCount(t, shardKsName, 2) @@ -487,26 +485,25 @@ func primaryBackup(t *testing.T) { // Perform PRS to demote the primary tablet (primary) so that we can do a restore there and verify we don't have the // data from after the older/first backup - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica2.Alias, shardKsName) require.Nil(t, err) // Delete the current primary tablet (replica2) so that the original primary tablet (primary) can be restored from the // older/first backup w/o it replicating the subsequent insert done after the first backup was taken - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary=true", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("DeleteTablets", "--allow-primary", replica2.Alias) require.Nil(t, err) err = replica2.VttabletProcess.TearDown() require.Nil(t, err) // Restore the older/first backup -- using the timestamp we saved -- on the original primary tablet (primary) - err = localCluster.VtctlclientProcess.ExecuteCommand("RestoreFromBackup", "--", "--backup_timestamp", firstBackupTimestamp, primary.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("RestoreFromBackup", "--backup-timestamp", firstBackupTimestamp, primary.Alias) require.Nil(t, err) verifyTabletRestoreStats(t, primary.VttabletProcess.GetVars()) // Re-init the shard -- making the original primary tablet (primary) primary again -- for subsequent tests - err = localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) + err = localCluster.VtctldClientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) require.Nil(t, err) // Verify that we don't have the record created after the older/first backup @@ -528,7 +525,7 @@ func primaryReplicaSameBackup(t *testing.T) { verifyInitialReplication(t) // backup the replica - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) verifyTabletBackupStats(t, replica1.VttabletProcess.GetVars()) @@ -546,9 +543,8 @@ func primaryReplicaSameBackup(t *testing.T) { cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) // Promote replica2 to primary - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica2.Alias, shardKsName) require.Nil(t, err) // insert more data on replica2 (current primary) @@ -566,7 +562,7 @@ func primaryReplicaSameBackup(t *testing.T) { // It is written into the MANIFEST and read back from the MANIFEST. // // Take another backup on the replica. - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) // Insert more data on replica2 (current primary). @@ -596,7 +592,7 @@ func primaryReplicaSameBackupModifiedCompressionEngine(t *testing.T) { time.Sleep(5 * time.Second) // backup the replica - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) verifyTabletBackupStats(t, replica1.VttabletProcess.GetVars()) @@ -621,9 +617,8 @@ func primaryReplicaSameBackupModifiedCompressionEngine(t *testing.T) { cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) // Promote replica2 to primary - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica2.Alias, shardKsName) require.Nil(t, err) // insert more data on replica2 (current primary) @@ -637,9 +632,8 @@ func primaryReplicaSameBackupModifiedCompressionEngine(t *testing.T) { cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 3) // Promote replica1 to primary - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica1.Alias, shardKsName) require.Nil(t, err) // Insert more data on replica1 (current primary). @@ -650,7 +644,7 @@ func primaryReplicaSameBackupModifiedCompressionEngine(t *testing.T) { cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 4) // Now take replica2 backup with gzip (new compressor) - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica2.Alias) require.Nil(t, err) verifyTabletBackupStats(t, replica2.VttabletProcess.GetVars()) @@ -690,7 +684,7 @@ func testRestoreOldPrimary(t *testing.T, method restoreMethod) { time.Sleep(5 * time.Second) // backup the replica - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) verifyTabletBackupStats(t, replica1.VttabletProcess.GetVars()) @@ -700,9 +694,8 @@ func testRestoreOldPrimary(t *testing.T, method restoreMethod) { require.Nil(t, err) // reparent to replica1 - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica1.Alias, shardKsName) require.Nil(t, err) // insert more data to new primary @@ -728,7 +721,7 @@ func restoreUsingRestart(t *testing.T, tablet *cluster.Vttablet) { } func restoreInPlace(t *testing.T, tablet *cluster.Vttablet) { - err := localCluster.VtctlclientProcess.ExecuteCommand("RestoreFromBackup", tablet.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("RestoreFromBackup", tablet.Alias) require.Nil(t, err) } @@ -758,7 +751,7 @@ func restartPrimaryAndReplica(t *testing.T) { err = tablet.VttabletProcess.Setup() require.Nil(t, err) } - err := localCluster.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) + err := localCluster.VtctldClientProcess.InitShardPrimary(keyspaceName, shardName, cell, primary.TabletUID) require.Nil(t, err) } @@ -768,12 +761,12 @@ func stopAllTablets() { tablet.VttabletProcess.TearDown() if tablet.MysqlctldProcess.TabletUID > 0 { tablet.MysqlctldProcess.Stop() - localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", tablet.Alias) + localCluster.VtctldClientProcess.ExecuteCommand("DeleteTablets", "--allow-primary", tablet.Alias) continue } proc, _ := tablet.MysqlctlProcess.StopProcess() mysqlProcs = append(mysqlProcs, proc) - localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", tablet.Alias) + localCluster.VtctldClientProcess.ExecuteCommand("DeleteTablets", "--allow-primary", tablet.Alias) } for _, proc := range mysqlProcs { proc.Wait() @@ -798,7 +791,7 @@ func terminatedRestore(t *testing.T) { checkTabletType(t, replica1.Alias, topodata.TabletType_REPLICA) // backup the replica - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) checkTabletType(t, replica1.Alias, topodata.TabletType_REPLICA) @@ -809,9 +802,8 @@ func terminatedRestore(t *testing.T) { require.Nil(t, err) // reparent to replica1 - err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", - "--keyspace_shard", shardKsName, - "--new_primary", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", + "--new-primary", replica1.Alias, shardKsName) require.Nil(t, err) // insert more data to new primary @@ -823,7 +815,7 @@ func terminatedRestore(t *testing.T) { // If restore fails then the tablet type goes back to original type. checkTabletType(t, primary.Alias, topodata.TabletType_REPLICA) - err = localCluster.VtctlclientProcess.ExecuteCommand("RestoreFromBackup", primary.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("RestoreFromBackup", primary.Alias) require.Nil(t, err) checkTabletType(t, primary.Alias, topodata.TabletType_REPLICA) @@ -865,7 +857,7 @@ func doNotDemoteNewlyPromotedPrimaryIfReparentingDuringBackup(t *testing.T) { checkTabletType(t, primary.Alias, topodata.TabletType_PRIMARY) // now backup - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) }() @@ -876,10 +868,11 @@ func doNotDemoteNewlyPromotedPrimaryIfReparentingDuringBackup(t *testing.T) { checkTabletType(t, primary.Alias, topodata.TabletType_PRIMARY) // now reparent - _, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput( - "PlannedReparentShard", "--", - "--keyspace_shard", fmt.Sprintf("%s/%s", keyspaceName, shardName), - "--new_primary", replica1.Alias) + _, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput( + "PlannedReparentShard", + "--new-primary", replica1.Alias, + fmt.Sprintf("%s/%s", keyspaceName, shardName), + ) require.Nil(t, err) // check that we reparented @@ -908,13 +901,13 @@ func doNotDemoteNewlyPromotedPrimaryIfReparentingDuringBackup(t *testing.T) { func vtctlBackup(t *testing.T, tabletType string) { // StopReplication on replica1. We verify that the replication works fine later in // verifyInitialReplication. So this will also check that VTOrc is running. - err := localCluster.VtctlclientProcess.ExecuteCommand("StopReplication", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("StopReplication", replica1.Alias) require.Nil(t, err) verifyInitialReplication(t) restoreWaitForBackup(t, tabletType, nil, true) - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.Nil(t, err) backups := localCluster.VerifyBackupCount(t, shardKsName, 1) @@ -932,7 +925,7 @@ func vtctlBackup(t *testing.T, tabletType string) { err = replica2.VttabletProcess.TearDown() require.Nil(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", replica2.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("DeleteTablets", replica2.Alias) require.Nil(t, err) _, err = primary.VttabletProcess.QueryTablet("DROP TABLE vt_insert_test", keyspaceName, true) require.Nil(t, err) @@ -978,7 +971,7 @@ func restoreWaitForBackup(t *testing.T, tabletType string, cDetails *Compression } func RemoveBackup(t *testing.T, backupName string) { - err := localCluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backupName) + err := localCluster.VtctldClientProcess.ExecuteCommand("RemoveBackup", shardKsName, backupName) require.Nil(t, err) } @@ -1004,9 +997,9 @@ func verifyRestoreTablet(t *testing.T, tablet *cluster.Vttablet, status string) // We restart replication here because semi-sync will not be set correctly on tablet startup since // we deprecated enable_semi_sync. StartReplication RPC fixes the semi-sync settings by consulting the // durability policies set. - err = localCluster.VtctlclientProcess.ExecuteCommand("StopReplication", tablet.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("StopReplication", tablet.Alias) require.NoError(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand("StartReplication", tablet.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("StartReplication", tablet.Alias) require.NoError(t, err) if tablet.Type == "replica" { @@ -1035,13 +1028,13 @@ func terminateBackup(t *testing.T, alias string) { }() } - args := append([]string{"--server", localCluster.VtctlclientProcess.Server, "--alsologtostderr"}, "Backup", "--", alias) + args := append([]string{"--server", localCluster.VtctldClientProcess.Server, "--alsologtostderr"}, "Backup", alias) tmpProcess := exec.Command( - "vtctlclient", + "vtctldclient", args..., ) - reader, _ := tmpProcess.StderrPipe() + reader, _ := tmpProcess.StdoutPipe() err := tmpProcess.Start() require.Nil(t, err) found := false @@ -1069,13 +1062,13 @@ func terminateRestore(t *testing.T) { }() } - args := append([]string{"--server", localCluster.VtctlclientProcess.Server, "--alsologtostderr"}, "RestoreFromBackup", "--", primary.Alias) + args := append([]string{"--server", localCluster.VtctldClientProcess.Server, "--alsologtostderr"}, "RestoreFromBackup", primary.Alias) tmpProcess := exec.Command( - "vtctlclient", + "vtctldclient", args..., ) - reader, _ := tmpProcess.StderrPipe() + reader, _ := tmpProcess.StdoutPipe() err := tmpProcess.Start() require.Nil(t, err) found := false @@ -1257,13 +1250,10 @@ func waitForNumBackups(t *testing.T, expectNumBackups int) []string { } } -func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incrementalFromPos replication.Position, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) { +func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incrementalFromPos string, expectEmpty bool, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) { numBackups := len(waitForNumBackups(t, -1)) - incrementalFromPosArg := "auto" - if !incrementalFromPos.IsZero() { - incrementalFromPosArg = replication.EncodePosition(incrementalFromPos) - } - output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("Backup", "--incremental-from-pos", incrementalFromPosArg, replica.Alias) + + output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("Backup", "--incremental-from-pos", incrementalFromPos, replica.Alias) if expectError != "" { require.Errorf(t, err, "expected: %v", expectError) require.Contains(t, output, expectError) @@ -1271,24 +1261,30 @@ func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incre } require.NoErrorf(t, err, "output: %v", output) + if expectEmpty { + require.Contains(t, output, mysqlctl.EmptyBackupMessage) + return nil, "" + } + backups := waitForNumBackups(t, numBackups+1) require.NotEmptyf(t, backups, "output: %v", output) verifyTabletBackupStats(t, replica.VttabletProcess.GetVars()) backupName = backups[len(backups)-1] + backupLocation := localCluster.CurrentVTDATAROOT + "/backups/" + shardKsName + "/" + backupName return readManifestFile(t, backupLocation), backupName } -func TestReplicaIncrementalBackup(t *testing.T, replicaIndex int, incrementalFromPos replication.Position, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) { +func TestReplicaIncrementalBackup(t *testing.T, replicaIndex int, incrementalFromPos string, expectEmpty bool, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) { replica := getReplica(t, replicaIndex) - return testReplicaIncrementalBackup(t, replica, incrementalFromPos, expectError) + return testReplicaIncrementalBackup(t, replica, incrementalFromPos, expectEmpty, expectError) } func TestReplicaFullRestore(t *testing.T, replicaIndex int, expectError string) { replica := getReplica(t, replicaIndex) - output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("RestoreFromBackup", replica.Alias) + output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("RestoreFromBackup", replica.Alias) if expectError != "" { require.Errorf(t, err, "expected: %v", expectError) require.Contains(t, output, expectError) @@ -1303,7 +1299,7 @@ func TestReplicaRestoreToPos(t *testing.T, replicaIndex int, restoreToPos replic require.False(t, restoreToPos.IsZero()) restoreToPosArg := replication.EncodePosition(restoreToPos) - output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("RestoreFromBackup", "--", "--restore_to_pos", restoreToPosArg, replica.Alias) + output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("RestoreFromBackup", "--restore-to-pos", restoreToPosArg, replica.Alias) if expectError != "" { require.Errorf(t, err, "expected: %v", expectError) require.Contains(t, output, expectError) diff --git a/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go b/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go index 8b9014e7f8c..6270c023eab 100644 --- a/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go +++ b/go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go @@ -48,6 +48,14 @@ const ( operationFlushAndPurge ) +type incrementalFromPosType int + +const ( + incrementalFromPosPosition incrementalFromPosType = iota + incrementalFromPosAuto + incrementalFromPosBackupName +) + type PITRTestCase struct { Name string SetupType int @@ -106,6 +114,7 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) } var fullBackupPos replication.Position + var lastBackupName string t.Run("full backup", func(t *testing.T) { InsertRowOnPrimary(t, "before-full-backup") waitForReplica(t, 0) @@ -118,6 +127,8 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) pos := replication.EncodePosition(fullBackupPos) backupPositions = append(backupPositions, pos) rowsPerPosition[pos] = len(msgs) + + lastBackupName = manifest.BackupName }) lastBackupPos := fullBackupPos @@ -127,50 +138,63 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) name string writeBeforeBackup bool fromFullPosition bool - autoPosition bool + expectEmpty bool + incrementalFrom incrementalFromPosType expectError string }{ { - name: "first incremental backup", + name: "first incremental backup", + incrementalFrom: incrementalFromPosPosition, + }, + { + name: "empty1", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { - name: "fail1", - expectError: "no binary logs to backup", + name: "empty2", + incrementalFrom: incrementalFromPosAuto, + expectEmpty: true, }, { - name: "fail2", - expectError: "no binary logs to backup", + name: "empty3", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { name: "make writes, succeed", writeBeforeBackup: true, + incrementalFrom: incrementalFromPosPosition, }, { - name: "fail, no binary logs to backup", - expectError: "no binary logs to backup", + name: "empty again", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { name: "make writes again, succeed", writeBeforeBackup: true, + incrementalFrom: incrementalFromPosBackupName, }, { name: "auto position, succeed", writeBeforeBackup: true, - autoPosition: true, + incrementalFrom: incrementalFromPosAuto, }, { - name: "fail auto position, no binary logs to backup", - autoPosition: true, - expectError: "no binary logs to backup", + name: "empty again, based on auto position", + incrementalFrom: incrementalFromPosAuto, + expectEmpty: true, }, { name: "auto position, make writes again, succeed", writeBeforeBackup: true, - autoPosition: true, + incrementalFrom: incrementalFromPosAuto, }, { name: "from full backup position", fromFullPosition: true, + incrementalFrom: incrementalFromPosPosition, }, } var fromFullPositionBackups []string @@ -192,20 +216,31 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) // - auto // - explicit last backup pos // - back in history to the original full backup - var incrementalFromPos replication.Position - if !tc.autoPosition { - incrementalFromPos = lastBackupPos + var incrementalFromPos string + switch tc.incrementalFrom { + case incrementalFromPosAuto: + incrementalFromPos = mysqlctl.AutoIncrementalFromPos + case incrementalFromPosBackupName: + incrementalFromPos = lastBackupName + case incrementalFromPosPosition: + incrementalFromPos = replication.EncodePosition(lastBackupPos) if tc.fromFullPosition { - incrementalFromPos = fullBackupPos + incrementalFromPos = replication.EncodePosition(fullBackupPos) } } // always use same 1st replica - manifest, backupName := TestReplicaIncrementalBackup(t, 0, incrementalFromPos, tc.expectError) + manifest, backupName := TestReplicaIncrementalBackup(t, 0, incrementalFromPos, tc.expectEmpty, tc.expectError) if tc.expectError != "" { return } + if tc.expectEmpty { + assert.Nil(t, manifest) + return + } + require.NotNil(t, manifest) defer func() { lastBackupPos = manifest.Position + lastBackupName = manifest.BackupName }() if tc.fromFullPosition { fromFullPositionBackups = append(fromFullPositionBackups, backupName) @@ -219,8 +254,10 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase) fromPositionIncludingPurged := manifest.FromPosition.GTIDSet.Union(gtidPurgedPos.GTIDSet) expectFromPosition := lastBackupPos.GTIDSet - if !incrementalFromPos.IsZero() { - expectFromPosition = incrementalFromPos.GTIDSet.Union(gtidPurgedPos.GTIDSet) + if tc.incrementalFrom == incrementalFromPosPosition { + pos, err := replication.DecodePosition(incrementalFromPos) + assert.NoError(t, err) + expectFromPosition = pos.GTIDSet.Union(gtidPurgedPos.GTIDSet) } require.Equalf(t, expectFromPosition, fromPositionIncludingPurged, "expected: %v, found: %v, gtid_purged: %v, manifest.Position: %v", expectFromPosition, fromPositionIncludingPurged, gtidPurgedPos, manifest.Position) }) @@ -304,6 +341,7 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes testedBackups := []testedBackupTimestampInfo{} var fullBackupPos replication.Position + var lastBackupName string t.Run("full backup", func(t *testing.T) { insertRowOnPrimary(t, "before-full-backup") waitForReplica(t, 0) @@ -314,6 +352,8 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes // rows := ReadRowsFromReplica(t, 0) testedBackups = append(testedBackups, testedBackupTimestampInfo{len(rows), time.Now()}) + + lastBackupName = manifest.BackupName }) lastBackupPos := fullBackupPos @@ -323,50 +363,63 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes name string writeBeforeBackup bool fromFullPosition bool - autoPosition bool + expectEmpty bool + incrementalFrom incrementalFromPosType expectError string }{ { - name: "first incremental backup", + name: "first incremental backup", + incrementalFrom: incrementalFromPosPosition, }, { - name: "fail1", - expectError: "no binary logs to backup", + name: "empty1", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { - name: "fail2", - expectError: "no binary logs to backup", + name: "empty2", + incrementalFrom: incrementalFromPosAuto, + expectEmpty: true, + }, + { + name: "empty3", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { name: "make writes, succeed", writeBeforeBackup: true, + incrementalFrom: incrementalFromPosPosition, }, { - name: "fail, no binary logs to backup", - expectError: "no binary logs to backup", + name: "empty again", + incrementalFrom: incrementalFromPosPosition, + expectEmpty: true, }, { name: "make writes again, succeed", writeBeforeBackup: true, + incrementalFrom: incrementalFromPosBackupName, }, { name: "auto position, succeed", writeBeforeBackup: true, - autoPosition: true, + incrementalFrom: incrementalFromPosAuto, }, { - name: "fail auto position, no binary logs to backup", - autoPosition: true, - expectError: "no binary logs to backup", + name: "empty again, based on auto position", + incrementalFrom: incrementalFromPosAuto, + expectEmpty: true, }, { name: "auto position, make writes again, succeed", writeBeforeBackup: true, - autoPosition: true, + incrementalFrom: incrementalFromPosAuto, }, { name: "from full backup position", fromFullPosition: true, + incrementalFrom: incrementalFromPosPosition, }, } var fromFullPositionBackups []string @@ -386,18 +439,28 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes // - auto // - explicit last backup pos // - back in history to the original full backup - var incrementalFromPos replication.Position - if !tc.autoPosition { - incrementalFromPos = lastBackupPos + var incrementalFromPos string + switch tc.incrementalFrom { + case incrementalFromPosAuto: + incrementalFromPos = mysqlctl.AutoIncrementalFromPos + case incrementalFromPosBackupName: + incrementalFromPos = lastBackupName + case incrementalFromPosPosition: + incrementalFromPos = replication.EncodePosition(lastBackupPos) if tc.fromFullPosition { - incrementalFromPos = fullBackupPos + incrementalFromPos = replication.EncodePosition(fullBackupPos) } } - manifest, backupName := TestReplicaIncrementalBackup(t, 0, incrementalFromPos, tc.expectError) + manifest, backupName := TestReplicaIncrementalBackup(t, 0, incrementalFromPos, tc.expectEmpty, tc.expectError) if tc.expectError != "" { return } - // We wish to mark the current post-backup timestamp. We will later on retore to this point in time. + if tc.expectEmpty { + assert.Nil(t, manifest) + return + } + require.NotNil(t, manifest) + // We wish to mark the current post-backup timestamp. We will later on restore to this point in time. // However, the restore is up to and _exclusive_ of the timestamp. So for test's sake, we sleep // an extra few milliseconds just to ensure the timestamp we read is strictly after the backup time. // This is basicaly to avoid weird flakiness in CI. @@ -405,6 +468,7 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes testedBackups = append(testedBackups, testedBackupTimestampInfo{len(rowsBeforeBackup), time.Now()}) defer func() { lastBackupPos = manifest.Position + lastBackupName = manifest.BackupName }() if tc.fromFullPosition { fromFullPositionBackups = append(fromFullPositionBackups, backupName) @@ -434,8 +498,10 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes fromPositionIncludingPurged := manifest.FromPosition.GTIDSet.Union(gtidPurgedPos.GTIDSet) expectFromPosition := lastBackupPos.GTIDSet.Union(gtidPurgedPos.GTIDSet) - if !incrementalFromPos.IsZero() { - expectFromPosition = incrementalFromPos.GTIDSet.Union(gtidPurgedPos.GTIDSet) + if tc.incrementalFrom == incrementalFromPosPosition { + pos, err := replication.DecodePosition(incrementalFromPos) + assert.NoError(t, err) + expectFromPosition = pos.GTIDSet.Union(gtidPurgedPos.GTIDSet) } require.Equalf(t, expectFromPosition, fromPositionIncludingPurged, "expected: %v, found: %v, gtid_purged: %v, manifest.Position: %v", expectFromPosition, fromPositionIncludingPurged, gtidPurgedPos, manifest.Position) }) @@ -663,11 +729,11 @@ func ExecTestIncrementalBackupOnTwoTablets(t *testing.T, tcase *PITRTestCase) { lastBackupPos = fullBackupPos case operationIncrementalBackup: - var incrementalFromPos replication.Position // keep zero, we will use "auto" - manifest, _ := TestReplicaIncrementalBackup(t, tc.replicaIndex, incrementalFromPos, tc.expectError) + manifest, _ := TestReplicaIncrementalBackup(t, tc.replicaIndex, "auto", false /* expectEmpty */, tc.expectError) if tc.expectError != "" { return } + require.NotNil(t, manifest) defer func() { lastBackupPos = manifest.Position }() diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index 9c2a29d2eb1..07e8d687f4e 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -53,8 +53,6 @@ var ( ) Engine=InnoDB ` commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", @@ -188,14 +186,14 @@ func TestMain(m *testing.M) { return 1, err } } - if err := localCluster.VtctlclientProcess.InitializeShard(keyspaceName, shard1.Name, shard1Primary.Cell, shard1Primary.TabletUID); err != nil { + if err := localCluster.VtctldClientProcess.InitializeShard(keyspaceName, shard1.Name, shard1Primary.Cell, shard1Primary.TabletUID); err != nil { return 1, err } // run a health check on source replica so it responds to discovery // (for binlog players) and on the source rdonlys (for workers) for _, tablet := range []string{shard1Replica.Alias, shard1Rdonly.Alias} { - if err := localCluster.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet); err != nil { + if err := localCluster.VtctldClientProcess.ExecuteCommand("RunHealthCheck", tablet); err != nil { return 1, err } } @@ -206,7 +204,7 @@ func TestMain(m *testing.M) { } } - if err := localCluster.VtctlclientProcess.InitializeShard(keyspaceName, shard2.Name, shard2Primary.Cell, shard2Primary.TabletUID); err != nil { + if err := localCluster.VtctldClientProcess.InitializeShard(keyspaceName, shard2.Name, shard2Primary.Cell, shard2Primary.TabletUID); err != nil { return 1, err } @@ -214,14 +212,14 @@ func TestMain(m *testing.M) { return 1, err } - if err := localCluster.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchema, tableName)); err != nil { + if err := localCluster.VtctldClientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchema, tableName)); err != nil { return 1, err } - if err := localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName)); err != nil { + if err := localCluster.VtctldClientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName)); err != nil { return 1, err } - _ = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + _ = localCluster.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) return m.Run(), nil }() @@ -239,7 +237,7 @@ func TestAlias(t *testing.T) { insertInitialValues(t) defer deleteInitialValues(t) - err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + err := localCluster.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) require.NoError(t, err) shard1 := localCluster.Keyspaces[0].Shards[0] shard2 := localCluster.Keyspaces[0].Shards[1] @@ -253,11 +251,11 @@ func TestAlias(t *testing.T) { cluster.CheckSrvKeyspace(t, cell2, keyspaceName, expectedPartitions, *localCluster) // Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias - err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", "--", + err = localCluster.VtctldClientProcess.ExecuteCommand("AddCellsAlias", "--cells", allCells, "region_east_coast") require.NoError(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand("UpdateCellsAlias", "--", + err = localCluster.VtctldClientProcess.ExecuteCommand("UpdateCellsAlias", "--cells", allCells, "region_east_coast") require.NoError(t, err) @@ -279,7 +277,7 @@ func TestAlias(t *testing.T) { testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) // now, delete the alias, so that if we run above assertions again, it will fail for replica,rdonly target type - err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteCellsAlias", + err = localCluster.VtctldClientProcess.ExecuteCommand("DeleteCellsAlias", "region_east_coast") require.NoError(t, err) @@ -303,7 +301,7 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { insertInitialValues(t) defer deleteInitialValues(t) - err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + err := localCluster.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) require.NoError(t, err) shard1 := localCluster.Keyspaces[0].Shards[0] shard2 := localCluster.Keyspaces[0].Shards[1] @@ -330,7 +328,7 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) // Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias - err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", "--", + err = localCluster.VtctldClientProcess.ExecuteCommand("AddCellsAlias", "--cells", allCells, "region_east_coast") require.NoError(t, err) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 28a8807cf08..98218bcf3fb 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -41,6 +41,7 @@ import ( "vitess.io/vitess/go/json2" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/syscallutil" "vitess.io/vitess/go/test/endtoend/filelock" "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/log" @@ -420,7 +421,7 @@ func (cluster *LocalProcessCluster) startKeyspace(keyspace Keyspace, shardNames } // Make first tablet as primary - if err = cluster.VtctlclientProcess.InitializeShard(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { + if err = cluster.VtctldClientProcess.InitializeShard(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { log.Errorf("error running InitializeShard on keyspace %v, shard %v: %v", keyspace.Name, shardName, err) return } @@ -440,7 +441,7 @@ func (cluster *LocalProcessCluster) startKeyspace(keyspace Keyspace, shardNames // Apply Schema SQL if keyspace.SchemaSQL != "" { - if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { + if err = cluster.VtctldClientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { log.Errorf("error applying schema: %v, %v", keyspace.SchemaSQL, err) return } @@ -448,7 +449,7 @@ func (cluster *LocalProcessCluster) startKeyspace(keyspace Keyspace, shardNames // Apply VSchema if keyspace.VSchema != "" { - if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { + if err = cluster.VtctldClientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { log.Errorf("error applying vschema: %v, %v", keyspace.VSchema, err) return } @@ -580,7 +581,7 @@ func (cluster *LocalProcessCluster) StartKeyspaceLegacy(keyspace Keyspace, shard } // Make first tablet as primary - if err = cluster.VtctlclientProcess.InitShardPrimary(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { + if err = cluster.VtctldClientProcess.InitShardPrimary(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil { log.Errorf("error running ISM on keyspace %v, shard %v: %v", keyspace.Name, shardName, err) return } @@ -600,7 +601,7 @@ func (cluster *LocalProcessCluster) StartKeyspaceLegacy(keyspace Keyspace, shard // Apply Schema SQL if keyspace.SchemaSQL != "" { - if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { + if err = cluster.VtctldClientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil { log.Errorf("error applying schema: %v, %v", keyspace.SchemaSQL, err) return } @@ -608,7 +609,7 @@ func (cluster *LocalProcessCluster) StartKeyspaceLegacy(keyspace Keyspace, shard // Apply VSchema if keyspace.VSchema != "" { - if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { + if err = cluster.VtctldClientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil { log.Errorf("error applying vschema: %v, %v", keyspace.VSchema, err) return } @@ -764,19 +765,18 @@ func (cluster *LocalProcessCluster) populateVersionInfo() error { return err } +var versionRegex = regexp.MustCompile(`Version: ([0-9]+)\.([0-9]+)\.([0-9]+)`) + func GetMajorVersion(binaryName string) (int, error) { version, err := exec.Command(binaryName, "--version").Output() if err != nil { return 0, err } - versionRegex := regexp.MustCompile(`Version: ([0-9]+)\.([0-9]+)\.([0-9]+)`) v := versionRegex.FindStringSubmatch(string(version)) if len(v) != 4 { return 0, fmt.Errorf("could not parse server version from: %s", version) } - if err != nil { - return 0, fmt.Errorf("could not parse server version from: %s", version) - } + return strconv.Atoi(v[1]) } @@ -1091,7 +1091,7 @@ func (cluster *LocalProcessCluster) waitForMySQLProcessToExit(mysqlctlProcessLis log.Errorf("Error in conversion to integer: %v", err) return } - err = syscall.Kill(pid, syscall.SIGKILL) + err = syscallutil.Kill(pid, syscall.SIGKILL) if err != nil { log.Errorf("Error in killing process: %v", err) } diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 3d442bbb576..9fcefba3892 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -29,6 +29,7 @@ import ( "google.golang.org/grpc" "vitess.io/vitess/go/vt/grpcclient" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" "github.com/buger/jsonparser" @@ -137,7 +138,7 @@ func PanicHandler(t testing.TB) { // ListBackups Lists back preset in shard func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, error) { - output, err := cluster.VtctlclientProcess.ExecuteCommandWithOutput("ListBackups", shardKsName) + output, err := cluster.VtctldClientProcess.ExecuteCommandWithOutput("GetBackups", shardKsName) if err != nil { return nil, err } @@ -164,7 +165,7 @@ func (cluster LocalProcessCluster) RemoveAllBackups(t *testing.T, shardKsName st backups, err := cluster.ListBackups(shardKsName) require.Nil(t, err) for _, backup := range backups { - cluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup) + cluster.VtctldClientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup) } } @@ -498,3 +499,47 @@ func DialVTGate(ctx context.Context, name, addr, username, password string) (*vt vtgateconn.RegisterDialer(dialerName, dialerFunc) return vtgateconn.DialProtocol(ctx, dialerName, addr) } + +// PrintFiles prints the files that are asked for. If no file is specified, all the files are printed. +func PrintFiles(t *testing.T, dir string, files ...string) { + var directories []string + directories = append(directories, dir) + + // Go over the remaining directories to check + for len(directories) > 0 { + // Get one of the directories, and read its contents. + dir = directories[0] + directories = directories[1:] + entries, err := os.ReadDir(dir) + if err != nil { + log.Errorf("Couldn't read directory - %v", dir) + continue + } + for _, entry := range entries { + name := path.Join(dir, entry.Name()) + // For a directory, we add it to our list of directories to check. + if entry.IsDir() { + directories = append(directories, name) + continue + } + // Check if this file should be printed or not. + if len(files) != 0 { + fileFound := false + for _, file := range files { + if strings.EqualFold(entry.Name(), file) { + fileFound = true + break + } + } + if !fileFound { + continue + } + } + // Read and print the file. + res, err := os.ReadFile(name) + require.NoError(t, err) + log.Errorf("READING FILE - %v", name) + log.Errorf("%v", string(res)) + } + } +} diff --git a/go/test/endtoend/cluster/mysqlctl_process.go b/go/test/endtoend/cluster/mysqlctl_process.go index 06808627254..cfc4fc28088 100644 --- a/go/test/endtoend/cluster/mysqlctl_process.go +++ b/go/test/endtoend/cluster/mysqlctl_process.go @@ -30,6 +30,7 @@ import ( "github.com/google/safehtml/template" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/syscallutil" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/tlstest" @@ -215,11 +216,11 @@ func (mysqlctl *MysqlctlProcess) Stop() (err error) { // If we found a valid associated mysqld_safe process then let's kill // it first. if err == nil && mysqldSafePID > 0 { - if err = syscall.Kill(mysqldSafePID, syscall.SIGKILL); err != nil { + if err = syscallutil.Kill(mysqldSafePID, syscall.SIGKILL); err != nil { return err } } - return syscall.Kill(pid, syscall.SIGKILL) + return syscallutil.Kill(pid, syscall.SIGKILL) } // StopProcess executes mysqlctl command to stop mysql instance and returns process reference @@ -252,16 +253,6 @@ func (mysqlctl *MysqlctlProcess) CleanupFiles(tabletUID int) { os.RemoveAll(path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", tabletUID))) } -// Connect returns a new connection to the underlying MySQL server -func (mysqlctl *MysqlctlProcess) Connect(ctx context.Context, username string) (*mysql.Conn, error) { - params := mysql.ConnParams{ - Uname: username, - UnixSocket: path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", mysqlctl.TabletUID), "/mysql.sock"), - } - - return mysql.Connect(ctx, ¶ms) -} - // MysqlCtlProcessInstanceOptionalInit returns a Mysqlctl handle for mysqlctl process // configured with the given Config. func MysqlCtlProcessInstanceOptionalInit(tabletUID int, mySQLPort int, tmpDirectory string, initMySQL bool) (*MysqlctlProcess, error) { diff --git a/go/test/endtoend/cluster/mysqlctld_process.go b/go/test/endtoend/cluster/mysqlctld_process.go index 60d30bc6cc0..08409c1246d 100644 --- a/go/test/endtoend/cluster/mysqlctld_process.go +++ b/go/test/endtoend/cluster/mysqlctld_process.go @@ -92,7 +92,15 @@ func (mysqlctld *MysqlctldProcess) Start() error { "--init_db_sql_file", mysqlctld.InitDBFile) } - errFile, _ := os.Create(path.Join(mysqlctld.LogDirectory, "mysqlctld-stderr.txt")) + err := os.MkdirAll(mysqlctld.LogDirectory, 0755) + if err != nil { + log.Errorf("Failed to create directory for mysqlctld logs: %v", err) + return err + } + errFile, err := os.Create(path.Join(mysqlctld.LogDirectory, "mysqlctld-stderr.txt")) + if err != nil { + log.Errorf("Failed to create directory for mysqlctld stderr: %v", err) + } tempProcess.Stderr = errFile tempProcess.Env = append(tempProcess.Env, os.Environ()...) @@ -103,7 +111,7 @@ func (mysqlctld *MysqlctldProcess) Start() error { log.Infof("%v", strings.Join(tempProcess.Args, " ")) - err := tempProcess.Start() + err = tempProcess.Start() if err != nil { return err } diff --git a/go/test/endtoend/cluster/topo_process.go b/go/test/endtoend/cluster/topo_process.go index 45a2e6586fa..776ed7da27e 100644 --- a/go/test/endtoend/cluster/topo_process.go +++ b/go/test/endtoend/cluster/topo_process.go @@ -145,10 +145,10 @@ func (topo *TopoProcess) SetupEtcd() (err error) { // SetupZookeeper spawns a new zookeeper topo service and initializes it with the defaults. // The service is kept running in the background until TearDown() is called. -func (topo *TopoProcess) SetupZookeeper(cluster *LocalProcessCluster) (err error) { +func (topo *TopoProcess) SetupZookeeper(cluster *LocalProcessCluster) error { host, err := os.Hostname() if err != nil { - return + return err } topo.ZKPorts = fmt.Sprintf("%d:%d:%d", cluster.GetAndReservePort(), cluster.GetAndReservePort(), topo.Port) @@ -160,16 +160,21 @@ func (topo *TopoProcess) SetupZookeeper(cluster *LocalProcessCluster) (err error "init", ) - errFile, _ := os.Create(path.Join(topo.DataDirectory, "topo-stderr.txt")) + err = os.MkdirAll(topo.LogDirectory, 0755) + if err != nil { + log.Errorf("Failed to create log directory for zookeeper: %v", err) + return err + } + errFile, err := os.Create(path.Join(topo.LogDirectory, "topo-stderr.txt")) + if err != nil { + log.Errorf("Failed to create file for zookeeper stderr: %v", err) + return err + } topo.proc.Stderr = errFile topo.proc.Env = append(topo.proc.Env, os.Environ()...) log.Infof("Starting zookeeper with args %v", strings.Join(topo.proc.Args, " ")) - err = topo.proc.Run() - if err != nil { - return - } - return + return topo.proc.Run() } // ConsulConfigs are the configurations that are added the config files which are used by consul @@ -193,13 +198,25 @@ type PortsInfo struct { func (topo *TopoProcess) SetupConsul(cluster *LocalProcessCluster) (err error) { topo.VerifyURL = fmt.Sprintf("http://%s:%d/v1/kv/?keys", topo.Host, topo.Port) - _ = os.MkdirAll(topo.LogDirectory, os.ModePerm) - _ = os.MkdirAll(topo.DataDirectory, os.ModePerm) + err = os.MkdirAll(topo.LogDirectory, os.ModePerm) + if err != nil { + log.Errorf("Failed to create directory for consul logs: %v", err) + return + } + err = os.MkdirAll(topo.DataDirectory, os.ModePerm) + if err != nil { + log.Errorf("Failed to create directory for consul data: %v", err) + return + } configFile := path.Join(os.Getenv("VTDATAROOT"), "consul.json") logFile := path.Join(topo.LogDirectory, "/consul.log") - _, _ = os.Create(logFile) + _, err = os.Create(logFile) + if err != nil { + log.Errorf("Failed to create file for consul logs: %v", err) + return + } var config []byte configs := ConsulConfigs{ @@ -233,7 +250,11 @@ func (topo *TopoProcess) SetupConsul(cluster *LocalProcessCluster) (err error) { "-config-file", configFile, ) - errFile, _ := os.Create(path.Join(topo.DataDirectory, "topo-stderr.txt")) + errFile, err := os.Create(path.Join(topo.LogDirectory, "topo-stderr.txt")) + if err != nil { + log.Errorf("Failed to create file for consul stderr: %v", err) + return + } topo.proc.Stderr = errFile topo.proc.Env = append(topo.proc.Env, os.Environ()...) diff --git a/go/test/endtoend/cluster/vtctld_process.go b/go/test/endtoend/cluster/vtctld_process.go index d0b2e5ab93e..d87427af9b9 100644 --- a/go/test/endtoend/cluster/vtctld_process.go +++ b/go/test/endtoend/cluster/vtctld_process.go @@ -79,7 +79,16 @@ func (vtctld *VtctldProcess) Setup(cell string, extraArgs ...string) (err error) } vtctld.proc.Args = append(vtctld.proc.Args, extraArgs...) - errFile, _ := os.Create(path.Join(vtctld.LogDir, "vtctld-stderr.txt")) + err = os.MkdirAll(vtctld.LogDir, 0755) + if err != nil { + log.Errorf("cannot create log directory for vtctld: %v", err) + return err + } + errFile, err := os.Create(path.Join(vtctld.LogDir, "vtctld-stderr.txt")) + if err != nil { + log.Errorf("cannot create error log file for vtctld: %v", err) + return err + } vtctld.proc.Stderr = errFile vtctld.ErrorLog = errFile.Name() diff --git a/go/test/endtoend/cluster/vtctldclient_process.go b/go/test/endtoend/cluster/vtctldclient_process.go index 52e0f985680..959ab5a93b9 100644 --- a/go/test/endtoend/cluster/vtctldclient_process.go +++ b/go/test/endtoend/cluster/vtctldclient_process.go @@ -22,7 +22,11 @@ import ( "strings" "time" + "vitess.io/vitess/go/json2" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vterrors" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) // VtctldClientProcess is a generic handle for a running vtctldclient command . @@ -93,6 +97,68 @@ func VtctldClientProcessInstance(hostname string, grpcPort int, tmpDirectory str return vtctldclient } +type ApplySchemaParams struct { + DDLStrategy string + MigrationContext string + UUIDs string + CallerID string + BatchSize int +} + +// ApplySchemaWithOutput applies SQL schema to the keyspace +func (vtctldclient *VtctldClientProcess) ApplySchemaWithOutput(keyspace string, sql string, params ApplySchemaParams) (result string, err error) { + args := []string{ + "ApplySchema", + "--sql", sql, + } + if params.MigrationContext != "" { + args = append(args, "--migration-context", params.MigrationContext) + } + if params.DDLStrategy != "" { + args = append(args, "--ddl-strategy", params.DDLStrategy) + } + if params.UUIDs != "" { + args = append(args, "--uuid", params.UUIDs) + } + if params.BatchSize > 0 { + args = append(args, "--batch-size", fmt.Sprintf("%d", params.BatchSize)) + } + if params.CallerID != "" { + args = append(args, "--caller-id", params.CallerID) + } + args = append(args, keyspace) + return vtctldclient.ExecuteCommandWithOutput(args...) +} + +// ApplySchema applies SQL schema to the keyspace +func (vtctldclient *VtctldClientProcess) ApplySchema(keyspace string, sql string) error { + message, err := vtctldclient.ApplySchemaWithOutput(keyspace, sql, ApplySchemaParams{DDLStrategy: "direct -allow-zero-in-date"}) + + return vterrors.Wrap(err, message) +} + +// ApplyVSchema applies vitess schema (JSON format) to the keyspace +func (vtctldclient *VtctldClientProcess) ApplyVSchema(keyspace string, json string) (err error) { + return vtctldclient.ExecuteCommand( + "ApplyVSchema", + "--vschema", json, + keyspace, + ) +} + +// GetSrvKeyspaces returns a mapping of cell to srv keyspace for the given keyspace. +func (vtctldclient *VtctldClientProcess) GetSrvKeyspaces(keyspace string, cells ...string) (ksMap map[string]*topodatapb.SrvKeyspace, err error) { + args := append([]string{"GetSrvKeyspaces", keyspace}, cells...) + out, err := vtctldclient.ExecuteCommandWithOutput(args...) + if err != nil { + return nil, err + } + + ksMap = map[string]*topodatapb.SrvKeyspace{} + err = json2.Unmarshal([]byte(out), &ksMap) + return ksMap, err +} + // PlannedReparentShard executes vtctlclient command to make specified tablet the primary for the shard. func (vtctldclient *VtctldClientProcess) PlannedReparentShard(Keyspace string, Shard string, alias string) (err error) { output, err := vtctldclient.ExecuteCommandWithOutput( @@ -105,6 +171,32 @@ func (vtctldclient *VtctldClientProcess) PlannedReparentShard(Keyspace string, S return err } +// InitializeShard executes vtctldclient command to make specified tablet the primary for the shard. +func (vtctldclient *VtctldClientProcess) InitializeShard(keyspace string, shard string, cell string, uid int) error { + output, err := vtctldclient.ExecuteCommandWithOutput( + "PlannedReparentShard", + fmt.Sprintf("%s/%s", keyspace, shard), + "--wait-replicas-timeout", "31s", + "--new-primary", fmt.Sprintf("%s-%d", cell, uid)) + if err != nil { + log.Errorf("error in PlannedReparentShard output %s, err %s", output, err.Error()) + } + return err +} + +// InitShardPrimary executes vtctldclient command to make specified tablet the primary for the shard. +func (vtctldclient *VtctldClientProcess) InitShardPrimary(keyspace string, shard string, cell string, uid int) error { + output, err := vtctldclient.ExecuteCommandWithOutput( + "InitShardPrimary", + "--force", "--wait-replicas-timeout", "31s", + fmt.Sprintf("%s/%s", keyspace, shard), + fmt.Sprintf("%s-%d", cell, uid)) + if err != nil { + log.Errorf("error in InitShardPrimary output %s, err %s", output, err.Error()) + } + return err +} + // CreateKeyspace executes the vtctl command to create a keyspace func (vtctldclient *VtctldClientProcess) CreateKeyspace(keyspaceName string, sidecarDBName string) (err error) { var output string diff --git a/go/test/endtoend/cluster/vtgate_process.go b/go/test/endtoend/cluster/vtgate_process.go index ab82a32f651..d1877fb89bb 100644 --- a/go/test/endtoend/cluster/vtgate_process.go +++ b/go/test/endtoend/cluster/vtgate_process.go @@ -130,7 +130,11 @@ func (vtgate *VtgateProcess) Setup() (err error) { vtgate.proc.Args = append(vtgate.proc.Args, vtgate.ExtraArgs...) - errFile, _ := os.Create(path.Join(vtgate.LogDir, "vtgate-stderr.txt")) + errFile, err := os.Create(path.Join(vtgate.LogDir, "vtgate-stderr.txt")) + if err != nil { + log.Errorf("cannot create error log file for vtgate: %v", err) + return err + } vtgate.proc.Stderr = errFile vtgate.proc.Env = append(vtgate.proc.Env, os.Environ()...) diff --git a/go/test/endtoend/cluster/vtorc_process.go b/go/test/endtoend/cluster/vtorc_process.go index f80690d8d60..25bbb74c36c 100644 --- a/go/test/endtoend/cluster/vtorc_process.go +++ b/go/test/endtoend/cluster/vtorc_process.go @@ -86,7 +86,16 @@ func (orc *VTOrcProcess) Setup() (err error) { // create the configuration file timeNow := time.Now().UnixNano() - configFile, _ := os.Create(path.Join(orc.LogDir, fmt.Sprintf("orc-config-%d.json", timeNow))) + err = os.MkdirAll(orc.LogDir, 0755) + if err != nil { + log.Errorf("cannot create log directory for vtorc: %v", err) + return err + } + configFile, err := os.Create(path.Join(orc.LogDir, fmt.Sprintf("orc-config-%d.json", timeNow))) + if err != nil { + log.Errorf("cannot create config file for vtorc: %v", err) + return err + } orc.ConfigPath = configFile.Name() // Add the default configurations and print them out @@ -135,7 +144,11 @@ func (orc *VTOrcProcess) Setup() (err error) { if orc.LogFileName == "" { orc.LogFileName = fmt.Sprintf("orc-stderr-%d.txt", timeNow) } - errFile, _ := os.Create(path.Join(orc.LogDir, orc.LogFileName)) + errFile, err := os.Create(path.Join(orc.LogDir, orc.LogFileName)) + if err != nil { + log.Errorf("cannot create error log file for vtorc: %v", err) + return err + } orc.proc.Stderr = errFile orc.proc.Env = append(orc.proc.Env, os.Environ()...) diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index 517f4bf3874..c98ed37afc0 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -76,10 +76,10 @@ type VttabletProcess struct { ServingStatus string DbPassword string DbPort int - VreplicationTabletType string DbFlavor string Charset string ConsolidationsURL string + IsPrimary bool // Extra Args to be set before starting the vttablet process ExtraArgs []string @@ -109,7 +109,6 @@ func (vttablet *VttabletProcess) Setup() (err error) { "--backup_storage_implementation", vttablet.BackupStorageImplementation, "--file_backup_storage_root", vttablet.FileBackupStorageRoot, "--service_map", vttablet.ServiceMap, - "--vreplication_tablet_type", vttablet.VreplicationTabletType, "--db_charset", vttablet.Charset, ) if v, err := GetMajorVersion("vttablet"); err != nil { @@ -450,11 +449,7 @@ func (vttablet *VttabletProcess) CreateDB(keyspace string) error { // QueryTablet lets you execute a query in this tablet and get the result func (vttablet *VttabletProcess) QueryTablet(query string, keyspace string, useDb bool) (*sqltypes.Result, error) { - if !useDb { - keyspace = "" - } - dbParams := NewConnParams(vttablet.DbPort, vttablet.DbPassword, path.Join(vttablet.Directory, "mysql.sock"), keyspace) - conn, err := vttablet.conn(&dbParams) + conn, err := vttablet.TabletConn(keyspace, useDb) if err != nil { return nil, err } @@ -462,6 +457,34 @@ func (vttablet *VttabletProcess) QueryTablet(query string, keyspace string, useD return executeQuery(conn, query) } +// QueryTabletMultiple lets you execute multiple queries -- without any +// results -- against the tablet. +func (vttablet *VttabletProcess) QueryTabletMultiple(queries []string, keyspace string, useDb bool) error { + conn, err := vttablet.TabletConn(keyspace, useDb) + if err != nil { + return err + } + defer conn.Close() + + for _, query := range queries { + log.Infof("Executing query %s (on %s)", query, vttablet.Name) + _, err := executeQuery(conn, query) + if err != nil { + return err + } + } + return nil +} + +// TabletConn opens a MySQL connection on this tablet +func (vttablet *VttabletProcess) TabletConn(keyspace string, useDb bool) (*mysql.Conn, error) { + if !useDb { + keyspace = "" + } + dbParams := NewConnParams(vttablet.DbPort, vttablet.DbPassword, path.Join(vttablet.Directory, "mysql.sock"), keyspace) + return vttablet.conn(&dbParams) +} + func (vttablet *VttabletProcess) defaultConn(dbname string) (*mysql.Conn, error) { dbParams := mysql.ConnParams{ Uname: "vt_dba", @@ -489,6 +512,16 @@ func (vttablet *VttabletProcess) QueryTabletWithDB(query string, dbname string) return executeQuery(conn, query) } +// MultiQueryTabletWithDB lets you execute multiple queries on a specific DB in this tablet. +func (vttablet *VttabletProcess) MultiQueryTabletWithDB(query string, dbname string) error { + conn, err := vttablet.defaultConn(dbname) + if err != nil { + return err + } + defer conn.Close() + return executeMultiQuery(conn, query) +} + // executeQuery will retry the query up to 10 times with a small sleep in between each try. // This allows the tests to be more robust in the face of transient failures. func executeQuery(dbConn *mysql.Conn, query string) (*sqltypes.Result, error) { @@ -513,6 +546,26 @@ func executeQuery(dbConn *mysql.Conn, query string) (*sqltypes.Result, error) { return result, err } +// executeMultiQuery will retry the given multi query up to 10 times with a small sleep in between each try. +// This allows the tests to be more robust in the face of transient failures. +func executeMultiQuery(dbConn *mysql.Conn, query string) (err error) { + retries := 10 + retryDelay := 1 * time.Second + for i := 0; i < retries; i++ { + if i > 0 { + // We only audit from 2nd attempt and onwards, otherwise this is just too verbose. + log.Infof("Executing query %s (attempt %d of %d)", query, (i + 1), retries) + } + err = dbConn.ExecuteFetchMultiDrain(query) + if err == nil { + break + } + time.Sleep(retryDelay) + } + + return err +} + // GetDBVar returns first matching database variable's value func (vttablet *VttabletProcess) GetDBVar(varName string, ksName string) (string, error) { return vttablet.getDBSystemValues("variables", varName, ksName) @@ -534,11 +587,6 @@ func (vttablet *VttabletProcess) getDBSystemValues(placeholder string, value str return "", nil } -// ToggleProfiling enables or disables the configured CPU profiler on this vttablet -func (vttablet *VttabletProcess) ToggleProfiling() error { - return vttablet.proc.Process.Signal(syscall.SIGUSR1) -} - // WaitForVReplicationToCatchup waits for "workflow" to finish copying func (vttablet *VttabletProcess) WaitForVReplicationToCatchup(t testing.TB, workflow, database string, sidecarDBName string, duration time.Duration) { if sidecarDBName == "" { @@ -659,7 +707,6 @@ func VttabletProcessInstance(port, grpcPort, tabletUID int, cell, shard, keyspac ServingStatus: "NOT_SERVING", BackupStorageImplementation: "file", FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), - VreplicationTabletType: "replica", TabletUID: tabletUID, Charset: charset, } diff --git a/go/test/endtoend/cluster/vttablet_process_unix.go b/go/test/endtoend/cluster/vttablet_process_unix.go new file mode 100644 index 00000000000..3f5c76e9988 --- /dev/null +++ b/go/test/endtoend/cluster/vttablet_process_unix.go @@ -0,0 +1,26 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cluster + +import "syscall" + +// ToggleProfiling enables or disables the configured CPU profiler on this vttablet +func (vttablet *VttabletProcess) ToggleProfiling() error { + return vttablet.proc.Process.Signal(syscall.SIGUSR1) +} diff --git a/go/test/endtoend/cluster/vttablet_process_windows.go b/go/test/endtoend/cluster/vttablet_process_windows.go new file mode 100644 index 00000000000..6c233746e8a --- /dev/null +++ b/go/test/endtoend/cluster/vttablet_process_windows.go @@ -0,0 +1,28 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cluster + +import ( + "errors" +) + +// ToggleProfiling enables or disables the configured CPU profiler on this vttablet. +func (vttablet *VttabletProcess) ToggleProfiling() error { + return errors.New("not implemented") +} diff --git a/go/test/endtoend/clustertest/vtctld_test.go b/go/test/endtoend/clustertest/vtctld_test.go index 45643d869b1..c1b341ccd73 100644 --- a/go/test/endtoend/clustertest/vtctld_test.go +++ b/go/test/endtoend/clustertest/vtctld_test.go @@ -128,9 +128,51 @@ func testTabletStatus(t *testing.T) { } func testExecuteAsDba(t *testing.T) { - result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ExecuteFetchAsDba", clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].Alias, `SELECT 1 AS a`) - require.NoError(t, err) - assert.Equal(t, result, oneTableOutput) + tcases := []struct { + query string + result string + expectErr bool + }{ + { + query: "", + expectErr: true, + }, + { + query: "SELECT 1 AS a", + result: oneTableOutput, + }, + { + query: "SELECT 1 AS a; SELECT 1 AS a", + expectErr: true, + }, + { + query: "create table t(id int)", + result: "", + }, + { + query: "create table if not exists t(id int)", + result: "", + }, + { + query: "create table if not exists t(id int); create table if not exists t(id int);", + result: "", + }, + { + query: "create table if not exists t(id int); create table if not exists t(id int); SELECT 1 AS a", + expectErr: true, + }, + } + for _, tcase := range tcases { + t.Run(tcase.query, func(t *testing.T) { + result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ExecuteFetchAsDba", clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].Alias, tcase.query) + if tcase.expectErr { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tcase.result, result) + } + }) + } } func testExecuteAsApp(t *testing.T) { diff --git a/go/test/endtoend/clustertest/vttablet_test.go b/go/test/endtoend/clustertest/vttablet_test.go index 369deb18cfd..5e7d5e27182 100644 --- a/go/test/endtoend/clustertest/vttablet_test.go +++ b/go/test/endtoend/clustertest/vttablet_test.go @@ -51,6 +51,6 @@ func TestDeleteTablet(t *testing.T) { defer cluster.PanicHandler(t) primary := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet() require.NotNil(t, primary) - _, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("DeleteTablet", "--", "--allow_primary", primary.Alias) + _, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("DeleteTablets", "--allow-primary", primary.Alias) require.NoError(t, err) } diff --git a/go/test/endtoend/docker/vttestserver.go b/go/test/endtoend/docker/vttestserver.go index 7f24134a28f..4f86c7616a1 100644 --- a/go/test/endtoend/docker/vttestserver.go +++ b/go/test/endtoend/docker/vttestserver.go @@ -39,7 +39,7 @@ type vttestserver struct { keyspaces []string numShards []int mysqlMaxConnecetions int - port int + basePort int } func newVttestserver(dockerImage string, keyspaces []string, numShards []int, mysqlMaxConnections, port int) *vttestserver { @@ -48,7 +48,7 @@ func newVttestserver(dockerImage string, keyspaces []string, numShards []int, my keyspaces: keyspaces, numShards: numShards, mysqlMaxConnecetions: mysqlMaxConnections, - port: port, + basePort: port, } } @@ -64,13 +64,16 @@ func (v *vttestserver) teardown() { func (v *vttestserver) startDockerImage() error { cmd := exec.Command("docker", "run") cmd.Args = append(cmd.Args, "--name=vttestserver-end2end-test") - cmd.Args = append(cmd.Args, "-p", fmt.Sprintf("%d:33577", v.port)) - cmd.Args = append(cmd.Args, "-e", "PORT=33574") + cmd.Args = append(cmd.Args, "-p", fmt.Sprintf("%d:%d", v.basePort, v.basePort)) + cmd.Args = append(cmd.Args, "-p", fmt.Sprintf("%d:%d", v.basePort+1, v.basePort+1)) + cmd.Args = append(cmd.Args, "-p", fmt.Sprintf("%d:%d", v.basePort+3, v.basePort+3)) + cmd.Args = append(cmd.Args, "-e", fmt.Sprintf("PORT=%d", v.basePort)) cmd.Args = append(cmd.Args, "-e", fmt.Sprintf("KEYSPACES=%s", strings.Join(v.keyspaces, ","))) cmd.Args = append(cmd.Args, "-e", fmt.Sprintf("NUM_SHARDS=%s", strings.Join(convertToStringSlice(v.numShards), ","))) cmd.Args = append(cmd.Args, "-e", "MYSQL_BIND_HOST=0.0.0.0") + cmd.Args = append(cmd.Args, "-e", "VTCOMBO_BIND_HOST=0.0.0.0") cmd.Args = append(cmd.Args, "-e", fmt.Sprintf("MYSQL_MAX_CONNECTIONS=%d", v.mysqlMaxConnecetions)) - cmd.Args = append(cmd.Args, "--health-cmd", "mysqladmin ping -h127.0.0.1 -P33577") + cmd.Args = append(cmd.Args, "--health-cmd", fmt.Sprintf("mysqladmin ping -h127.0.0.1 -P%d", v.basePort+3)) cmd.Args = append(cmd.Args, "--health-interval=5s") cmd.Args = append(cmd.Args, "--health-timeout=2s") cmd.Args = append(cmd.Args, "--health-retries=5") diff --git a/go/test/endtoend/docker/vttestserver_test.go b/go/test/endtoend/docker/vttestserver_test.go index c89f6299f30..e34be52accf 100644 --- a/go/test/endtoend/docker/vttestserver_test.go +++ b/go/test/endtoend/docker/vttestserver_test.go @@ -22,6 +22,7 @@ import ( "os" "testing" + "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" "vitess.io/vitess/go/mysql" @@ -44,7 +45,7 @@ func TestUnsharded(t *testing.T) { dockerImages := []string{vttestserverMysql57image, vttestserverMysql80image} for _, image := range dockerImages { t.Run(image, func(t *testing.T) { - vtest := newVttestserver(image, []string{"unsharded_ks"}, []int{1}, 1000, 33577) + vtest := newVttestserver(image, []string{"unsharded_ks"}, []int{1}, 1000, 33574) err := vtest.startDockerImage() require.NoError(t, err) defer vtest.teardown() @@ -56,7 +57,7 @@ func TestUnsharded(t *testing.T) { ctx := context.Background() vttestParams := mysql.ConnParams{ Host: "localhost", - Port: vtest.port, + Port: vtest.basePort + 3, } conn, err := mysql.Connect(ctx, &vttestParams) require.NoError(t, err) @@ -73,7 +74,7 @@ func TestSharded(t *testing.T) { dockerImages := []string{vttestserverMysql57image, vttestserverMysql80image} for _, image := range dockerImages { t.Run(image, func(t *testing.T) { - vtest := newVttestserver(image, []string{"ks"}, []int{2}, 1000, 33577) + vtest := newVttestserver(image, []string{"ks"}, []int{2}, 1000, 33574) err := vtest.startDockerImage() require.NoError(t, err) defer vtest.teardown() @@ -85,7 +86,7 @@ func TestSharded(t *testing.T) { ctx := context.Background() vttestParams := mysql.ConnParams{ Host: "localhost", - Port: vtest.port, + Port: vtest.basePort + 3, } conn, err := mysql.Connect(ctx, &vttestParams) require.NoError(t, err) @@ -103,7 +104,7 @@ func TestMysqlMaxCons(t *testing.T) { dockerImages := []string{vttestserverMysql57image, vttestserverMysql80image} for _, image := range dockerImages { t.Run(image, func(t *testing.T) { - vtest := newVttestserver(image, []string{"ks"}, []int{2}, 100000, 33577) + vtest := newVttestserver(image, []string{"ks"}, []int{2}, 100000, 33574) err := vtest.startDockerImage() require.NoError(t, err) defer vtest.teardown() @@ -115,7 +116,7 @@ func TestMysqlMaxCons(t *testing.T) { ctx := context.Background() vttestParams := mysql.ConnParams{ Host: "localhost", - Port: vtest.port, + Port: vtest.basePort + 3, } conn, err := mysql.Connect(ctx, &vttestParams) require.NoError(t, err) @@ -125,6 +126,29 @@ func TestMysqlMaxCons(t *testing.T) { } } +// TestVtctldCommands tests that vtctld commands can be run with the docker image. +func TestVtctldCommands(t *testing.T) { + dockerImages := []string{vttestserverMysql57image, vttestserverMysql80image} + for _, image := range dockerImages { + t.Run(image, func(t *testing.T) { + vtest := newVttestserver(image, []string{"long_ks_name"}, []int{2}, 100, 33574) + err := vtest.startDockerImage() + require.NoError(t, err) + defer vtest.teardown() + + // wait for the docker to be setup + err = vtest.waitUntilDockerHealthy(10) + require.NoError(t, err) + + vtctldClient := cluster.VtctldClientProcessInstance("localhost", vtest.basePort+1, os.TempDir()) + res, err := vtctldClient.ExecuteCommandWithOutput("GetKeyspaces") + require.NoError(t, err) + // We verify that the command succeeds, and the keyspace name is present in the output. + require.Contains(t, res, "long_ks_name") + }) + } +} + func TestLargeNumberOfKeyspaces(t *testing.T) { dockerImages := []string{vttestserverMysql57image, vttestserverMysql80image} for _, image := range dockerImages { @@ -136,7 +160,7 @@ func TestLargeNumberOfKeyspaces(t *testing.T) { numShards = append(numShards, 1) } - vtest := newVttestserver(image, keyspaces, numShards, 100000, 33577) + vtest := newVttestserver(image, keyspaces, numShards, 100000, 33574) err := vtest.startDockerImage() require.NoError(t, err) defer vtest.teardown() @@ -148,7 +172,7 @@ func TestLargeNumberOfKeyspaces(t *testing.T) { ctx := context.Background() vttestParams := mysql.ConnParams{ Host: "localhost", - Port: vtest.port, + Port: vtest.basePort + 3, } conn, err := mysql.Connect(ctx, &vttestParams) require.NoError(t, err) diff --git a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go index 725659a5ee1..4c759ff577a 100644 --- a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go +++ b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go @@ -74,7 +74,7 @@ func testReplicationBase(t *testing.T, isClientCertPassed bool) { } // Reparent using SSL (this will also check replication works) - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspace, shardName, clusterInstance.Cell, primaryTablet.TabletUID) + err = clusterInstance.VtctldClientProcess.InitializeShard(keyspace, shardName, clusterInstance.Cell, primaryTablet.TabletUID) if isClientCertPassed { require.NoError(t, err) } else { diff --git a/go/test/endtoend/filelock/filelock.go b/go/test/endtoend/filelock/filelock.go index 05f27c321a8..d37331892d1 100644 --- a/go/test/endtoend/filelock/filelock.go +++ b/go/test/endtoend/filelock/filelock.go @@ -10,7 +10,6 @@ package filelock import ( "errors" "io/fs" - "os" ) // A File provides the minimal set of methods required to lock an open file. @@ -78,22 +77,7 @@ func (lt lockType) String() string { // IsNotSupported returns a boolean indicating whether the error is known to // report that a function is not supported (possibly for a specific input). -// It is satisfied by ErrNotSupported as well as some syscall errors. +// It is satisfied by errors.ErrUnsupported as well as some syscall errors. func IsNotSupported(err error) bool { - return isNotSupported(underlyingError(err)) -} - -var ErrNotSupported = errors.New("operation not supported") - -// underlyingError returns the underlying error for known os error types. -func underlyingError(err error) error { - switch err := err.(type) { - case *fs.PathError: - return err.Err - case *os.LinkError: - return err.Err - case *os.SyscallError: - return err.Err - } - return err + return errors.Is(err, errors.ErrUnsupported) } diff --git a/go/test/endtoend/filelock/filelock_unix.go b/go/test/endtoend/filelock/filelock_unix.go index 23064dae0be..6f73b1bfeea 100644 --- a/go/test/endtoend/filelock/filelock_unix.go +++ b/go/test/endtoend/filelock/filelock_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd + package filelock import ( @@ -36,7 +38,3 @@ func lock(f File, lt lockType) (err error) { func unlock(f File) error { return lock(f, syscall.LOCK_UN) } - -func isNotSupported(err error) bool { - return err == syscall.ENOSYS || err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP || err == ErrNotSupported -} diff --git a/go/test/endtoend/filelock/filelock_windows.go b/go/test/endtoend/filelock/filelock_windows.go new file mode 100644 index 00000000000..34df039b96b --- /dev/null +++ b/go/test/endtoend/filelock/filelock_windows.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows + +package filelock + +import ( + "errors" +) + +type lockType uint32 + +const ( + readLock lockType = 0 + writeLock lockType = 1 +) + +func lock(f File, lt lockType) error { + return errors.New("filelock: not implemented on windows") +} + +func unlock(f File) error { + return errors.New("filelock: not implemented on windows") +} diff --git a/go/test/endtoend/keyspace/keyspace_test.go b/go/test/endtoend/keyspace/keyspace_test.go index 338ad5c8cd2..142e4b4b442 100644 --- a/go/test/endtoend/keyspace/keyspace_test.go +++ b/go/test/endtoend/keyspace/keyspace_test.go @@ -24,6 +24,7 @@ import ( "strings" "testing" + "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/vt/key" "github.com/stretchr/testify/assert" @@ -109,7 +110,7 @@ func TestMain(m *testing.M) { if err := clusterForKSTest.StartKeyspace(*keyspaceSharded, []string{"-80", "80-"}, 1, false); err != nil { return 1 } - if err := clusterForKSTest.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceShardedName); err != nil { + if err := clusterForKSTest.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceShardedName); err != nil { return 1 } @@ -121,7 +122,7 @@ func TestMain(m *testing.M) { if err := clusterForKSTest.StartKeyspace(*keyspaceUnsharded, []string{keyspaceUnshardedName}, 1, false); err != nil { return 1 } - if err := clusterForKSTest.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceUnshardedName); err != nil { + if err := clusterForKSTest.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceUnshardedName); err != nil { return 1 } @@ -228,48 +229,49 @@ func TestGetKeyspace(t *testing.T) { func TestDeleteKeyspace(t *testing.T) { defer cluster.PanicHandler(t) - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateKeyspace", "test_delete_keyspace") - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") + _ = clusterForKSTest.VtctldClientProcess.CreateKeyspace("test_delete_keyspace", sidecar.DefaultName) + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("InitTablet", "--", "--keyspace=test_delete_keyspace", "--shard=0", "zone1-0000000100", "primary") // Can't delete keyspace if there are shards present. - err := clusterForKSTest.VtctlclientProcess.ExecuteCommand("DeleteKeyspace", "test_delete_keyspace") + err := clusterForKSTest.VtctldClientProcess.ExecuteCommand("DeleteKeyspace", "test_delete_keyspace") require.Error(t, err) // Can't delete shard if there are tablets present. - err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("DeleteShard", "--", "--even_if_serving", "test_delete_keyspace/0") + err = clusterForKSTest.VtctldClientProcess.ExecuteCommand("DeleteShards", "--even-if-serving", "test_delete_keyspace/0") require.Error(t, err) // Use recursive DeleteShard to remove tablets. - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("DeleteShard", "--", "--even_if_serving", "--recursive", "test_delete_keyspace/0") + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("DeleteShards", "--even-if-serving", "--recursive", "test_delete_keyspace/0") // Now non-recursive DeleteKeyspace should work. - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("DeleteKeyspace", "test_delete_keyspace") + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("DeleteKeyspace", "test_delete_keyspace") // Start over and this time use recursive DeleteKeyspace to do everything. - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateKeyspace", "test_delete_keyspace") - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") + _ = clusterForKSTest.VtctldClientProcess.CreateKeyspace("test_delete_keyspace", sidecar.DefaultName) + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("InitTablet", "--", "--port=1234", "--bind-address=127.0.0.1", "--keyspace=test_delete_keyspace", "--shard=0", "zone1-0000000100", "primary") // Create the serving/replication entries and check that they exist, // so we can later check they're deleted. - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_delete_keyspace") + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_delete_keyspace") _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetShardReplication", cell, "test_delete_keyspace/0") - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetSrvKeyspace", cell, "test_delete_keyspace") + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("GetSrvKeyspaces", "test_delete_keyspace", cell) // Recursive DeleteKeyspace - _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("DeleteKeyspace", "--", "--recursive", "test_delete_keyspace") + _ = clusterForKSTest.VtctldClientProcess.ExecuteCommand("DeleteKeyspace", "--recursive", "test_delete_keyspace") // Check that everything is gone. - err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetKeyspace", "test_delete_keyspace") + err = clusterForKSTest.VtctldClientProcess.ExecuteCommand("GetKeyspace", "test_delete_keyspace") require.Error(t, err) - err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetShard", "test_delete_keyspace/0") + err = clusterForKSTest.VtctldClientProcess.ExecuteCommand("GetShard", "test_delete_keyspace/0") require.Error(t, err) - err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetTablet", "zone1-0000000100") + err = clusterForKSTest.VtctldClientProcess.ExecuteCommand("GetTablet", "zone1-0000000100") require.Error(t, err) err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetShardReplication", cell, "test_delete_keyspace/0") require.Error(t, err) - err = clusterForKSTest.VtctlclientProcess.ExecuteCommand("GetSrvKeyspace", cell, "test_delete_keyspace") - require.Error(t, err) + ksMap, err := clusterForKSTest.VtctldClientProcess.GetSrvKeyspaces("test_delete_keyspace", cell) + require.NoError(t, err) + require.Empty(t, ksMap[cell]) } // TODO: Fix this test, not running in CI diff --git a/go/test/endtoend/messaging/message_test.go b/go/test/endtoend/messaging/message_test.go index 3082f295055..7e1190c16bb 100644 --- a/go/test/endtoend/messaging/message_test.go +++ b/go/test/endtoend/messaging/message_test.go @@ -393,12 +393,12 @@ func TestReparenting(t *testing.T) { // do planned reparenting, make one replica as primary // and validate client connection count in correspond tablets - clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( - "PlannedReparentShard", "--", - "--keyspace_shard", userKeyspace+"/-80", - "--new_primary", shard0Replica.Alias) + clusterInstance.VtctldClientProcess.ExecuteCommand( + "PlannedReparentShard", + userKeyspace+"/-80", + "--new-primary", shard0Replica.Alias) // validate topology - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Validate") require.Nil(t, err) // Verify connection has migrated. @@ -417,12 +417,12 @@ func TestReparenting(t *testing.T) { stream.Next() // make old primary again as new primary - clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( - "PlannedReparentShard", "--", - "--keyspace_shard", userKeyspace+"/-80", - "--new_primary", shard0Primary.Alias) + clusterInstance.VtctldClientProcess.ExecuteCommand( + "PlannedReparentShard", + userKeyspace+"/-80", + "--new-primary", shard0Primary.Alias) // validate topology - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Validate") require.Nil(t, err) time.Sleep(10 * time.Second) assertClientCount(t, 1, shard0Primary) diff --git a/go/test/endtoend/migration/migration_test.go b/go/test/endtoend/migration/migration_test.go index f0b91e2d6df..eca112e388d 100644 --- a/go/test/endtoend/migration/migration_test.go +++ b/go/test/endtoend/migration/migration_test.go @@ -145,7 +145,7 @@ func TestMigration(t *testing.T) { vt.ExtraArgs = append(vt.ExtraArgs, "--tablet_config", yamlFile) } createKeyspace(t, commerce, []string{"0"}, tabletConfig) - err := clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "commerce") + err := clusterInstance.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", "commerce") require.NoError(t, err) err = clusterInstance.StartVtgate() @@ -221,7 +221,7 @@ func migrate(t *testing.T, fromdb, toks string, tables []string) { "('%s', '%s', %s, '', 9999, 9999, 'primary', 0, 0, 'Running')", tables[0], "vt_"+toks, sqlEscaped.String()) fmt.Printf("VReplicationExec: %s\n", query) vttablet := keyspaces[toks].Shards[0].Vttablets[0].VttabletProcess - err := clusterInstance.VtctlclientProcess.ExecuteCommand("VReplicationExec", vttablet.TabletPath, query) + err := clusterInstance.VtctldClientProcess.ExecuteCommand("VReplicationExec", vttablet.TabletPath, query) require.NoError(t, err) } diff --git a/go/test/endtoend/mysqlctl/mysqlctl_test.go b/go/test/endtoend/mysqlctl/mysqlctl_test.go index 3b28c5bcf30..bdea4d3988c 100644 --- a/go/test/endtoend/mysqlctl/mysqlctl_test.go +++ b/go/test/endtoend/mysqlctl/mysqlctl_test.go @@ -156,6 +156,6 @@ func TestAutoDetect(t *testing.T) { require.Nil(t, err, "error should be nil") // Reparent tablets, which requires flavor detection - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shardName, cell, primaryTablet.TabletUID) + err = clusterInstance.VtctldClientProcess.InitializeShard(keyspaceName, shardName, cell, primaryTablet.TabletUID) require.Nil(t, err, "error should be nil") } diff --git a/go/test/endtoend/mysqlctld/mysqlctld_test.go b/go/test/endtoend/mysqlctld/mysqlctld_test.go index 908a870d6f0..52be2fa4323 100644 --- a/go/test/endtoend/mysqlctld/mysqlctld_test.go +++ b/go/test/endtoend/mysqlctld/mysqlctld_test.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/vt/mysqlctl/mysqlctlclient" + "vitess.io/vitess/go/vt/proto/mysqlctl" "vitess.io/vitess/go/test/endtoend/cluster" ) @@ -158,7 +159,7 @@ func TestAutoDetect(t *testing.T) { require.Nil(t, err, "error should be nil") // Reparent tablets, which requires flavor detection - err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shardName, cell, primaryTablet.TabletUID) + err = clusterInstance.VtctldClientProcess.InitializeShard(keyspaceName, shardName, cell, primaryTablet.TabletUID) require.Nil(t, err, "error should be nil") } @@ -169,3 +170,10 @@ func TestVersionString(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, version) } + +func TestReadBinlogFilesTimestamps(t *testing.T) { + client, err := mysqlctlclient.New("unix", primaryTablet.MysqlctldProcess.SocketFile) + require.NoError(t, err) + _, err = client.ReadBinlogFilesTimestamps(context.Background(), &mysqlctl.ReadBinlogFilesTimestampsRequest{}) + require.ErrorContains(t, err, "empty binlog list in ReadBinlogFilesTimestampsRequest") +} diff --git a/go/test/endtoend/mysqlserver/main_test.go b/go/test/endtoend/mysqlserver/main_test.go index 42b4e6ea235..18b169e33d7 100644 --- a/go/test/endtoend/mysqlserver/main_test.go +++ b/go/test/endtoend/mysqlserver/main_test.go @@ -51,7 +51,7 @@ var ( PARTITION BY HASH( TO_DAYS(created) ) PARTITIONS 10; ` - createProcSQL = `use vt_test_keyspace; + createProcSQL = ` CREATE PROCEDURE testing() BEGIN delete from vt_insert_test; @@ -144,7 +144,7 @@ func TestMain(m *testing.M) { } primaryTabletProcess := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess - if _, err := primaryTabletProcess.QueryTablet(createProcSQL, keyspaceName, false); err != nil { + if _, err := primaryTabletProcess.QueryTablet(createProcSQL, keyspaceName, true); err != nil { return 1, err } diff --git a/go/test/endtoend/mysqlserver/mysql_server_test.go b/go/test/endtoend/mysqlserver/mysql_server_test.go index caed342688d..6b691582c66 100644 --- a/go/test/endtoend/mysqlserver/mysql_server_test.go +++ b/go/test/endtoend/mysqlserver/mysql_server_test.go @@ -116,7 +116,7 @@ func TestTimeout(t *testing.T) { require.Nilf(t, err, "unable to connect mysql: %v", err) defer conn.Close() - _, err = conn.ExecuteFetch("SELECT SLEEP(5);", 1, false) + _, err = conn.ExecuteFetch("SELECT SLEEP(5)", 1, false) require.NotNilf(t, err, "quiry timeout error expected") mysqlErr, ok := err.(*sqlerror.SQLError) require.Truef(t, ok, "invalid error type") @@ -132,7 +132,7 @@ func TestInvalidField(t *testing.T) { require.Nilf(t, err, "unable to connect mysql: %v", err) defer conn.Close() - _, err = conn.ExecuteFetch("SELECT invalid_field from vt_insert_test;", 1, false) + _, err = conn.ExecuteFetch("SELECT invalid_field from vt_insert_test", 1, false) require.NotNil(t, err, "invalid field error expected") mysqlErr, ok := err.(*sqlerror.SQLError) require.Truef(t, ok, "invalid error type") @@ -153,7 +153,7 @@ func TestWarnings(t *testing.T) { require.NoError(t, err) assert.Empty(t, qr.Rows, "number of rows") - qr, err = conn.ExecuteFetch("SHOW WARNINGS;", 1, false) + qr, err = conn.ExecuteFetch("SHOW WARNINGS", 1, false) require.NoError(t, err, "SHOW WARNINGS") assert.EqualValues(t, 1, len(qr.Rows), "number of rows") assert.Contains(t, qr.Rows[0][0].String(), "VARCHAR(\"Warning\")", qr.Rows) @@ -164,7 +164,7 @@ func TestWarnings(t *testing.T) { _, err = conn.ExecuteFetch("SELECT 1 from vt_insert_test limit 1", 1, false) require.NoError(t, err) - qr, err = conn.ExecuteFetch("SHOW WARNINGS;", 1, false) + qr, err = conn.ExecuteFetch("SHOW WARNINGS", 1, false) require.NoError(t, err) assert.Empty(t, qr.Rows) @@ -175,7 +175,7 @@ func TestWarnings(t *testing.T) { _, err = conn.ExecuteFetch("SELECT 1 from vt_insert_test limit 1", 1, false) require.NoError(t, err) - qr, err = conn.ExecuteFetch("SHOW WARNINGS;", 1, false) + qr, err = conn.ExecuteFetch("SHOW WARNINGS", 1, false) require.NoError(t, err) assert.Empty(t, qr.Rows) } diff --git a/go/test/endtoend/onlineddl/ghost/onlineddl_ghost_test.go b/go/test/endtoend/onlineddl/ghost/onlineddl_ghost_test.go index 3dc635c8870..41a9a80086b 100644 --- a/go/test/endtoend/onlineddl/ghost/onlineddl_ghost_test.go +++ b/go/test/endtoend/onlineddl/ghost/onlineddl_ghost_test.go @@ -370,7 +370,7 @@ func testWithInitialSchema(t *testing.T) { for i := 0; i < totalTableCount; i++ { tableName := fmt.Sprintf("vt_onlineddl_test_%02d", i) sqlQuery = fmt.Sprintf(createTable, tableName) - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, sqlQuery) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, sqlQuery) require.Nil(t, err) for _, insert := range insertStatements { @@ -395,7 +395,7 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str } } else { var err error - uuid, err = clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, sqlQuery, cluster.VtctlClientParams{DDLStrategy: ddlStrategy, CallerID: callerID}) + uuid, err = clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, sqlQuery, cluster.ApplySchemaParams{DDLStrategy: ddlStrategy, CallerID: callerID}) assert.NoError(t, err) } uuid = strings.TrimSpace(uuid) diff --git a/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go b/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go index 41cd5b5a1be..672e79c0985 100644 --- a/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go +++ b/go/test/endtoend/onlineddl/revert/onlineddl_revert_test.go @@ -30,6 +30,7 @@ import ( "time" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" @@ -157,14 +158,6 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "5s", "--watch_replication_stream", - // The next flags are deprecated, and we incldue them to verify that they are nonetheless still allowed. - // The values are irrelevant. Just the fact that the flags are allowed in what's important. - // These should be included in v18, and removed in v19. - "--throttle_threshold", "1m", - "--throttle_metrics_query", "select 1 from dual", - "--throttle_metrics_threshold", "1.5", - "--throttle_check_as_check_self=false", - "--throttler-config-via-topo=true", } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", @@ -223,8 +216,10 @@ func testRevertible(t *testing.T) { fkOnlineDDLPossible := false t.Run("check 'rename_table_preserve_foreign_key' variable", func(t *testing.T) { // Online DDL is not possible on vanilla MySQL 8.0 for reasons described in https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/. - // However, Online DDL is made possible in via these changes: https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced - // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. + // However, Online DDL is made possible in via these changes: + // - https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced + // - https://github.com/planetscale/mysql-server/commit/c2f1344a6863518d749f2eb01a4c74ca08a5b889 + // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps3. // Said changes introduce a new global/session boolean variable named 'rename_table_preserve_foreign_key'. It defaults 'false'/0 for backwards compatibility. // When enabled, a `RENAME TABLE` to a FK parent "pins" the children's foreign keys to the table name rather than the table pointer. Which means after the RENAME, // the children will point to the newly instated table rather than the original, renamed table. @@ -362,8 +357,8 @@ func testRevertible(t *testing.T) { }, { name: "expanded: enum", - fromSchema: `id int primary key, e1 enum('a', 'b'), e2 enum('a', 'b'), e3 enum('a', 'b'), e4 enum('a', 'b'), e5 enum('a', 'b'), e6 enum('a', 'b'), e7 enum('a', 'b'), e8 enum('a', 'b')`, - toSchema: `id int primary key, e1 enum('a', 'b'), e2 enum('a'), e3 enum('a', 'b', 'c'), e4 enum('a', 'x'), e5 enum('a', 'x', 'b'), e6 enum('b'), e7 varchar(1), e8 tinyint`, + fromSchema: `id int primary key, e1 enum('a', 'b'), e2 enum('a', 'b'), e3 enum('a', 'b'), e4 enum('a', 'b'), e5 enum('a', 'b'), e6 enum('a', 'b'), e7 enum('a', 'b'), e8 enum('a', 'b')`, + toSchema: `id int primary key, e1 enum('a', 'b'), e2 enum('a'), e3 enum('a', 'b', 'c'), e4 enum('a', 'x'), e5 enum('a', 'x', 'b'), e6 enum('b'), e7 varchar(1), e8 tinyint`, expandedColumnNames: `e3,e4,e5,e6,e7,e8`, }, { @@ -428,7 +423,20 @@ func testRevertible(t *testing.T) { droppedNoDefaultColumnNames := row.AsString("dropped_no_default_column_names", "") expandedColumnNames := row.AsString("expanded_column_names", "") - assert.Equal(t, testcase.removedForeignKeyNames, removeBackticks(removedForeignKeyNames)) + // Online DDL renames constraint names, and keeps the original name as a prefix. + // The name of e.g. "some_fk_2_" might turn into "some_fk_2_518ubnm034rel35l1m0u1dc7m" + expectRemovedForeignKeyNames := strings.Split(testcase.removedForeignKeyNames, ",") + actualRemovedForeignKeyNames := strings.Split(removeBackticks(removedForeignKeyNames), ",") + assert.Equal(t, len(expectRemovedForeignKeyNames), len(actualRemovedForeignKeyNames)) + for _, actualRemovedForeignKeyName := range actualRemovedForeignKeyNames { + found := false + for _, expectRemovedForeignKeyName := range expectRemovedForeignKeyNames { + if strings.HasPrefix(actualRemovedForeignKeyName, expectRemovedForeignKeyName) { + found = true + } + } + assert.Truef(t, found, "unexpected FK name", "%s", actualRemovedForeignKeyName) + } assert.Equal(t, testcase.removedUniqueKeyNames, removeBackticks(removedUniqueKeyNames)) assert.Equal(t, testcase.droppedNoDefaultColumnNames, removeBackticks(droppedNoDefaultColumnNames)) assert.Equal(t, testcase.expandedColumnNames, removeBackticks(expandedColumnNames)) @@ -466,7 +474,8 @@ func testRevertible(t *testing.T) { droppedNoDefaultColumnNames := row.AsString("dropped_no_default_column_names", "") expandedColumnNames := row.AsString("expanded_column_names", "") - assert.Equal(t, "some_fk_2", removeBackticks(removedForeignKeyNames)) + // Online DDL renames constraint names, and keeps the original name as a prefix. The name will be e.g. some_fk_2_518ubnm034rel35l1m0u1dc7m + assert.Contains(t, removeBackticks(removedForeignKeyNames), "some_fk_2") assert.Equal(t, "", removeBackticks(removedUniqueKeyNames)) assert.Equal(t, "", removeBackticks(droppedNoDefaultColumnNames)) assert.Equal(t, "", removeBackticks(expandedColumnNames)) @@ -551,7 +560,7 @@ func testRevert(t *testing.T) { mysqlVersion = onlineddl.GetMySQLVersion(t, clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()) require.NotEmpty(t, mysqlVersion) - _, capableOf, _ := mysql.GetFlavor(mysqlVersion, nil) + capableOf := mysql.ServerVersionCapableOf(mysqlVersion) var uuids []string ddlStrategy := "online" @@ -1033,7 +1042,7 @@ func testRevert(t *testing.T) { require.NotNil(t, row) specialPlan := row.AsString("special_plan", "") artifacts := row.AsString("artifacts", "") - instantDDLCapable, err := capableOf(mysql.InstantDDLFlavorCapability) + instantDDLCapable, err := capableOf(capabilities.InstantDDLFlavorCapability) assert.NoError(t, err) if instantDDLCapable { // instant DDL expected to apply in 8.0 @@ -1050,7 +1059,7 @@ func testRevert(t *testing.T) { t.Run("INSTANT DDL: fail revert", func(t *testing.T) { uuid := testRevertMigration(t, uuids[len(uuids)-1], ddlStrategy) uuids = append(uuids, uuid) - instantDDLCapable, err := capableOf(mysql.InstantDDLFlavorCapability) + instantDDLCapable, err := capableOf(capabilities.InstantDDLFlavorCapability) assert.NoError(t, err) if instantDDLCapable { // instant DDL expected to apply in 8.0, therefore revert is impossible @@ -1185,7 +1194,7 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str } } else { var err error - uuid, err = clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.VtctlClientParams{DDLStrategy: ddlStrategy}) + uuid, err = clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.ApplySchemaParams{DDLStrategy: ddlStrategy}) assert.NoError(t, err) } uuid = strings.TrimSpace(uuid) diff --git a/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go b/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go index fbe6377c1fe..01f7024f59c 100644 --- a/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go +++ b/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go @@ -31,6 +31,7 @@ import ( "time" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" @@ -81,7 +82,7 @@ var ( keyspaceName = "ks" cell = "zone1" schemaChangeDirectory = "" - overrideVtctlParams *cluster.VtctlClientParams + overrideVtctlParams *cluster.ApplySchemaParams ) type WriteMetrics struct { @@ -127,7 +128,8 @@ deletesAttempts=%d, deletesFailures=%d, deletesNoops=%d, deletes=%d, func parseTableName(t *testing.T, sql string) (tableName string) { // ddlStatement could possibly be composed of multiple DDL statements - tokenizer := sqlparser.NewStringTokenizer(sql) + parser := sqlparser.NewTestParser() + tokenizer := parser.NewStringTokenizer(sql) for { stmt, err := sqlparser.ParseNextStrictDDL(tokenizer) if err != nil && errors.Is(err, io.EOF) { @@ -200,6 +202,29 @@ func waitForReadyToComplete(t *testing.T, uuid string, expected bool) { } } +func waitForMessage(t *testing.T, uuid string, messageSubstring string) { + ctx, cancel := context.WithTimeout(context.Background(), normalWaitTime) + defer cancel() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + rs := onlineddl.ReadMigrations(t, &vtParams, uuid) + require.NotNil(t, rs) + for _, row := range rs.Named().Rows { + message := row.AsString("message", "") + if strings.Contains(message, messageSubstring) { + return + } + } + select { + case <-ticker.C: + case <-ctx.Done(): + } + require.NoError(t, ctx.Err()) + } +} + func TestMain(m *testing.M) { defer cluster.PanicHandler(nil) flag.Parse() @@ -310,7 +335,7 @@ func testScheduler(t *testing.T) { mysqlVersion := onlineddl.GetMySQLVersion(t, clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()) require.NotEmpty(t, mysqlVersion) - _, capableOf, _ := mysql.GetFlavor(mysqlVersion, nil) + capableOf := mysql.ServerVersionCapableOf(mysqlVersion) var ( t1uuid string @@ -366,6 +391,9 @@ func testScheduler(t *testing.T) { alterNonexistent = ` ALTER TABLE nonexistent FORCE ` + populateT1Statement = ` + insert into t1_test values (1, 'new_row') + ` ) testReadTimestamp := func(t *testing.T, uuid string, timestampColumn string) (timestamp string) { @@ -490,6 +518,109 @@ func testScheduler(t *testing.T) { onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) }) }) + + t.Run("Postpone completion ALTER", func(t *testing.T) { + t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-completion", "vtgate", "", "", true)) // skip wait + + t.Run("wait for t1 running", func(t *testing.T) { + status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) + fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) + }) + t.Run("check postpone_completion", func(t *testing.T) { + rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) + require.NotNil(t, rs) + for _, row := range rs.Named().Rows { + postponeCompletion := row.AsInt64("postpone_completion", 0) + assert.Equal(t, int64(1), postponeCompletion) + } + }) + t.Run("complete", func(t *testing.T) { + onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) + status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) + fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) + onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) + }) + t.Run("check no postpone_completion", func(t *testing.T) { + rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) + require.NotNil(t, rs) + for _, row := range rs.Named().Rows { + postponeCompletion := row.AsInt64("postpone_completion", 0) + assert.Equal(t, int64(0), postponeCompletion) + } + }) + }) + + forceCutoverCapable, err := capableOf(capabilities.PerformanceSchemaDataLocksTableCapability) // 8.0 + require.NoError(t, err) + if forceCutoverCapable { + t.Run("force_cutover", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), extendedWaitTime*2) + defer cancel() + + t.Run("populate t1_test", func(t *testing.T) { + onlineddl.VtgateExecQuery(t, &vtParams, populateT1Statement, "") + }) + t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-completion", "vtgate", "", "", true)) // skip wait + + t.Run("wait for t1 running", func(t *testing.T) { + status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) + fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) + }) + commitTransactionChan := make(chan any) + transactionErrorChan := make(chan error) + t.Run("locking table rows", func(t *testing.T) { + go runInTransaction(t, ctx, shards[0].Vttablets[0], "select * from t1_test for update", commitTransactionChan, transactionErrorChan) + }) + t.Run("check no force_cutover", func(t *testing.T) { + rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) + require.NotNil(t, rs) + for _, row := range rs.Named().Rows { + forceCutOver := row.AsInt64("force_cutover", 0) + assert.Equal(t, int64(0), forceCutOver) // disabled + } + }) + t.Run("attempt to complete", func(t *testing.T) { + onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) + }) + t.Run("cut-over fail due to timeout", func(t *testing.T) { + waitForMessage(t, t1uuid, "due to context deadline exceeded") + status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusRunning) + fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) + onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) + }) + t.Run("force_cutover", func(t *testing.T) { + onlineddl.CheckForceMigrationCutOver(t, &vtParams, shards, t1uuid, true) + }) + t.Run("check force_cutover", func(t *testing.T) { + rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) + require.NotNil(t, rs) + for _, row := range rs.Named().Rows { + forceCutOver := row.AsInt64("force_cutover", 0) + assert.Equal(t, int64(1), forceCutOver) // enabled + } + }) + t.Run("expect completion", func(t *testing.T) { + status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) + fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) + onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) + }) + t.Run("expect transaction failure", func(t *testing.T) { + select { + case commitTransactionChan <- true: //good + case <-ctx.Done(): + assert.Fail(t, ctx.Err().Error()) + } + // Transaction will now attempt to commit. But we expect our "force_cutover" to have terminated + // the transaction's connection. + select { + case err := <-transactionErrorChan: + assert.ErrorContains(t, err, "broken pipe") + case <-ctx.Done(): + assert.Fail(t, ctx.Err().Error()) + } + }) + }) + } t.Run("ALTER both tables non-concurrent", func(t *testing.T) { t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait @@ -818,7 +949,7 @@ func testScheduler(t *testing.T) { t.Run("Idempotent submission, retry failed migration", func(t *testing.T) { uuid := "00000000_1111_2222_3333_444444444444" - overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, UUIDList: uuid, MigrationContext: "idempotent:1111-2222-3333"} + overrideVtctlParams = &cluster.ApplySchemaParams{DDLStrategy: ddlStrategy, UUIDs: uuid, MigrationContext: "idempotent:1111-2222-3333"} defer func() { overrideVtctlParams = nil }() // create a migration and cancel it. We don't let it complete. We want it in "failed" state t.Run("start and fail migration", func(t *testing.T) { @@ -854,7 +985,7 @@ func testScheduler(t *testing.T) { t.Run("Idempotent submission, retry failed migration in singleton context", func(t *testing.T) { uuid := "00000000_1111_3333_3333_444444444444" ddlStrategy := ddlStrategy + " --singleton-context" - overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, UUIDList: uuid, MigrationContext: "idempotent:1111-3333-3333"} + overrideVtctlParams = &cluster.ApplySchemaParams{DDLStrategy: ddlStrategy, UUIDs: uuid, MigrationContext: "idempotent:1111-3333-3333"} defer func() { overrideVtctlParams = nil }() // create a migration and cancel it. We don't let it complete. We want it in "failed" state t.Run("start and fail migration", func(t *testing.T) { @@ -941,8 +1072,27 @@ func testScheduler(t *testing.T) { }) }) + checkConstraintCapable, err := capableOf(capabilities.CheckConstraintsCapability) // 8.0.16 and above + require.NoError(t, err) + if checkConstraintCapable { + // Constraints + t.Run("CREATE TABLE with CHECK constraint", func(t *testing.T) { + query := `create table with_constraint (id int primary key, check ((id >= 0)))` + uuid := testOnlineDDLStatement(t, createParams(query, ddlStrategy, "vtgate", "chk_", "", false)) + onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) + t.Run("ensure constraint name is rewritten", func(t *testing.T) { + // Since we did not provide a name for the CHECK constraint, MySQL will + // name it `with_constraint_chk_1`. But we expect Online DDL to explicitly + // modify the constraint name, specifically to get rid of the prefix, + // so that we don't get into https://bugs.mysql.com/bug.php?id=107772 situation. + createStatement := getCreateTableStatement(t, shards[0].Vttablets[0], "with_constraint") + assert.NotContains(t, createStatement, "with_constraint_chk") + }) + }) + } + // INSTANT DDL - instantDDLCapable, err := capableOf(mysql.InstantAddLastColumnFlavorCapability) + instantDDLCapable, err := capableOf(capabilities.InstantAddLastColumnFlavorCapability) require.NoError(t, err) if instantDDLCapable { t.Run("INSTANT DDL: postpone-completion", func(t *testing.T) { @@ -2128,7 +2278,7 @@ func testForeignKeys(t *testing.T) { }, { name: "drop foreign key from a child", - sql: "alter table child_table DROP FOREIGN KEY child_parent_fk", + sql: "alter table child_table DROP FOREIGN KEY ", // See "getting child_table constraint name" test step below. allowForeignKeys: true, expectHint: "child_hint", onlyIfFKOnlineDDLPossible: true, @@ -2138,8 +2288,10 @@ func testForeignKeys(t *testing.T) { fkOnlineDDLPossible := false t.Run("check 'rename_table_preserve_foreign_key' variable", func(t *testing.T) { // Online DDL is not possible on vanilla MySQL 8.0 for reasons described in https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/. - // However, Online DDL is made possible in via these changes: https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced - // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. + // However, Online DDL is made possible in via these changes: + // - https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced + // - https://github.com/planetscale/mysql-server/commit/c2f1344a6863518d749f2eb01a4c74ca08a5b889 + // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps3. // Said changes introduce a new global/session boolean variable named 'rename_table_preserve_foreign_key'. It defaults 'false'/0 for backwards compatibility. // When enabled, a `RENAME TABLE` to a FK parent "pins" the children's foreign keys to the table name rather than the table pointer. Which means after the RENAME, // the children will point to the newly instated table rather than the original, renamed table. @@ -2193,6 +2345,19 @@ func testForeignKeys(t *testing.T) { }) } }) + t.Run("getting child_table constraint name", func(t *testing.T) { + // Due to how OnlineDDL works, the name of the foreign key constraint will not be the one we used in the CREATE TABLE statement. + // There's a specific test where we drop said constraint. So speficially for that test (or any similar future tests), we need to dynamically + // evaluate the constraint name. + rs := onlineddl.VtgateExecQuery(t, &vtParams, "select CONSTRAINT_NAME from information_schema.REFERENTIAL_CONSTRAINTS where TABLE_NAME='child_table'", "") + assert.Equal(t, 1, len(rs.Rows)) + row := rs.Named().Row() + assert.NotNil(t, row) + childTableConstraintName := row.AsString("CONSTRAINT_NAME", "") + assert.NotEmpty(t, childTableConstraintName) + testcase.sql = strings.ReplaceAll(testcase.sql, "", childTableConstraintName) + }) + var uuid string t.Run("run migration", func(t *testing.T) { if testcase.allowForeignKeys { @@ -2226,7 +2391,7 @@ func testForeignKeys(t *testing.T) { continue } statement := fmt.Sprintf("DROP TABLE IF EXISTS %s", artifact) - _, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, statement, cluster.VtctlClientParams{DDLStrategy: "direct"}) + _, err := clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, statement, cluster.ApplySchemaParams{DDLStrategy: "direct"}) if err == nil { droppedTables[artifact] = true } @@ -2258,11 +2423,11 @@ func testOnlineDDLStatement(t *testing.T, params *testOnlineDDLStatementParams) } } } else { - vtctlParams := &cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext} + vtctlParams := &cluster.ApplySchemaParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext} if overrideVtctlParams != nil { vtctlParams = overrideVtctlParams } - output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, params.ddlStatement, *vtctlParams) + output, err := clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, params.ddlStatement, *vtctlParams) switch params.expectError { case anyErrorIndicator: if err != nil { @@ -2307,7 +2472,7 @@ func testRevertMigration(t *testing.T, params *testRevertMigrationParams) (uuid } } } else { - output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, revertQuery, cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext}) + output, err := clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, revertQuery, cluster.ApplySchemaParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext}) if params.expectError == "" { assert.NoError(t, err) uuid = output @@ -2328,7 +2493,7 @@ func testRevertMigration(t *testing.T, params *testRevertMigrationParams) (uuid return uuid } -// checkTable checks the number of tables in the first two shards. +// checkTable checks the number of tables in all shards func checkTable(t *testing.T, showTableName string, expectExists bool) bool { expectCount := 0 if expectExists { @@ -2368,3 +2533,31 @@ func getCreateTableStatement(t *testing.T, tablet *cluster.Vttablet, tableName s statement = queryResult.Rows[0][1].ToString() return statement } + +func runInTransaction(t *testing.T, ctx context.Context, tablet *cluster.Vttablet, query string, commitTransactionChan chan any, transactionErrorChan chan error) error { + conn, err := tablet.VttabletProcess.TabletConn(keyspaceName, true) + require.NoError(t, err) + defer conn.Close() + + _, err = conn.ExecuteFetch("begin", 0, false) + require.NoError(t, err) + + _, err = conn.ExecuteFetch(query, 10000, false) + require.NoError(t, err) + + if commitTransactionChan != nil { + // Wait for instruction to commit + select { + case <-commitTransactionChan: + // good + case <-ctx.Done(): + assert.Fail(t, ctx.Err().Error()) + } + } + + _, err = conn.ExecuteFetch("commit", 0, false) + if transactionErrorChan != nil { + transactionErrorChan <- err + } + return err +} diff --git a/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go b/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go index 49e72eda290..e5df3051612 100644 --- a/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go +++ b/go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go @@ -458,7 +458,7 @@ func TestSchemaChange(t *testing.T) { time.Sleep(10 * time.Second) onlineddl.CheckCancelAllMigrations(t, &vtParams, 0) // Validate that invoking CANCEL ALL via vtctl works - onlineddl.CheckCancelAllMigrationsViaVtctl(t, &clusterInstance.VtctlclientProcess, keyspaceName) + onlineddl.CheckCancelAllMigrationsViaVtctld(t, &clusterInstance.VtctldClientProcess, keyspaceName) }) t.Run("cancel all migrations: some migrations to cancel", func(t *testing.T) { // Use VTGate for throttling, issue a `ALTER VITESS_MIGRATION THROTTLE ALL ...` @@ -497,7 +497,7 @@ func TestSchemaChange(t *testing.T) { } wg.Wait() // cancelling via vtctl does not return values. We CANCEL ALL via vtctl, then validate via VTGate that nothing remains to be cancelled. - onlineddl.CheckCancelAllMigrationsViaVtctl(t, &clusterInstance.VtctlclientProcess, keyspaceName) + onlineddl.CheckCancelAllMigrationsViaVtctld(t, &clusterInstance.VtctldClientProcess, keyspaceName) onlineddl.CheckCancelAllMigrations(t, &vtParams, 0) }) @@ -555,7 +555,7 @@ func TestSchemaChange(t *testing.T) { }) t.Run("PRS shard -80", func(t *testing.T) { // migration has started and is throttled. We now run PRS - err := clusterInstance.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", "--keyspace_shard", keyspaceName+"/-80", "--new_primary", reparentTablet.Alias) + err := clusterInstance.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", keyspaceName+"/-80", "--new-primary", reparentTablet.Alias) require.NoError(t, err, "failed PRS: %v", err) rs := onlineddl.VtgateExecQuery(t, &vtParams, "show vitess_tablets", "") onlineddl.PrintQueryResult(os.Stdout, rs) @@ -650,7 +650,7 @@ func TestSchemaChange(t *testing.T) { }) t.Run("PRS shard -80", func(t *testing.T) { // migration has started and completion is postponed. We now PRS - err := clusterInstance.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "--", "--keyspace_shard", keyspaceName+"/-80", "--new_primary", reparentTablet.Alias) + err := clusterInstance.VtctldClientProcess.ExecuteCommand("PlannedReparentShard", keyspaceName+"/-80", "--new-primary", reparentTablet.Alias) require.NoError(t, err, "failed PRS: %v", err) rs := onlineddl.VtgateExecQuery(t, &vtParams, "show vitess_tablets", "") onlineddl.PrintQueryResult(os.Stdout, rs) @@ -905,7 +905,7 @@ func testWithInitialSchema(t *testing.T) { var sqlQuery = "" //nolint for i := 0; i < totalTableCount; i++ { sqlQuery = fmt.Sprintf(createTable, fmt.Sprintf("vt_onlineddl_test_%02d", i)) - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, sqlQuery) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, sqlQuery) require.Nil(t, err) } @@ -923,8 +923,8 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str uuid = row.AsString("uuid", "") } } else { - params := cluster.VtctlClientParams{DDLStrategy: ddlStrategy, UUIDList: providedUUIDList, MigrationContext: providedMigrationContext} - output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, sqlQuery, params) + params := cluster.ApplySchemaParams{DDLStrategy: ddlStrategy, UUIDs: providedUUIDList, MigrationContext: providedMigrationContext} + output, err := clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, sqlQuery, params) if expectError == "" { assert.NoError(t, err) uuid = output diff --git a/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go b/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go index 7f560a24f9e..84c1ea7165c 100644 --- a/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go +++ b/go/test/endtoend/onlineddl/vrepl_stress/onlineddl_vrepl_mini_stress_test.go @@ -29,16 +29,16 @@ import ( "testing" "time" - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/schema" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/onlineddl" "vitess.io/vitess/go/test/endtoend/throttler" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/schema" + "vitess.io/vitess/go/vt/vttablet" ) type WriteMetrics struct { @@ -184,6 +184,9 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "5s", "--watch_replication_stream", + // Test VPlayer batching mode. + fmt.Sprintf("--vreplication_experimental_flags=%d", + vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", @@ -326,11 +329,11 @@ func TestSchemaChange(t *testing.T) { func testWithInitialSchema(t *testing.T) { for _, statement := range cleanupStatements { - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, statement) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, statement) require.Nil(t, err) } // Create the stress table - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, createStatement) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, createStatement) require.Nil(t, err) // Check if table is created @@ -346,7 +349,7 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str } } else { var err error - uuid, err = clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.VtctlClientParams{DDLStrategy: ddlStrategy}) + uuid, err = clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.ApplySchemaParams{DDLStrategy: ddlStrategy}) assert.NoError(t, err) } uuid = strings.TrimSpace(uuid) diff --git a/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go b/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go index bac59241cf2..0db25088bd0 100644 --- a/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go +++ b/go/test/endtoend/onlineddl/vrepl_stress_suite/onlineddl_vrepl_stress_suite_test.go @@ -40,18 +40,18 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/timer" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/schema" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/onlineddl" "vitess.io/vitess/go/test/endtoend/throttler" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/timer" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/schema" + "vitess.io/vitess/go/vt/vttablet" ) type testcase struct { @@ -436,6 +436,9 @@ func TestMain(m *testing.M) { "--migration_check_interval", "5s", "--vstream_packet_size", "4096", // Keep this value small and below 10k to ensure multilple vstream iterations "--watch_replication_stream", + // Test VPlayer batching mode. + fmt.Sprintf("--vreplication_experimental_flags=%d", + vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), } clusterInstance.VtGateExtraArgs = []string{ "--ddl_strategy", "online", @@ -550,10 +553,10 @@ func TestSchemaChange(t *testing.T) { func testWithInitialSchema(t *testing.T) { // Create the stress table for _, statement := range cleanupStatements { - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, statement) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, statement) require.Nil(t, err) } - err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, createStatement) + err := clusterInstance.VtctldClientProcess.ApplySchema(keyspaceName, createStatement) require.Nil(t, err) // Check if table is created @@ -569,7 +572,7 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str } } else { var err error - uuid, err = clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.VtctlClientParams{DDLStrategy: ddlStrategy}) + uuid, err = clusterInstance.VtctldClientProcess.ApplySchemaWithOutput(keyspaceName, alterStatement, cluster.ApplySchemaParams{DDLStrategy: ddlStrategy}) assert.NoError(t, err) } uuid = strings.TrimSpace(uuid) diff --git a/go/test/endtoend/onlineddl/vrepl_suite/onlineddl_vrepl_suite_test.go b/go/test/endtoend/onlineddl/vrepl_suite/onlineddl_vrepl_suite_test.go index c8b87215036..56818069e05 100644 --- a/go/test/endtoend/onlineddl/vrepl_suite/onlineddl_vrepl_suite_test.go +++ b/go/test/endtoend/onlineddl/vrepl_suite/onlineddl_vrepl_suite_test.go @@ -28,6 +28,7 @@ import ( "time" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" @@ -58,8 +59,7 @@ var ( ) const ( - testDataPath = "testdata" - defaultSQLMode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" + testDataPath = "testdata" ) func TestMain(m *testing.M) { @@ -178,7 +178,7 @@ func testSingle(t *testing.T, testName string) { } } - sqlMode := defaultSQLMode + sqlMode := config.DefaultSQLMode if overrideSQLMode, exists := readTestFile(t, testName, "sql_mode"); exists { sqlMode = overrideSQLMode } diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/alter new file mode 100644 index 00000000000..b3e025d40b5 --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/alter @@ -0,0 +1 @@ +modify e enum('red', 'green', 'cyan') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/create.sql new file mode 100644 index 00000000000..cb4e6db0fcd --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-rename/create.sql @@ -0,0 +1,24 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +insert into onlineddl_test values (null, 11, 'red'); +insert into onlineddl_test values (null, 13, 'green'); + +drop event if exists onlineddl_test; +delimiter ;; +create event onlineddl_test + on schedule every 1 second + starts current_timestamp + ends current_timestamp + interval 60 second + on completion not preserve + enable + do +begin + insert into onlineddl_test values (null, 11, 'red'); + insert into onlineddl_test values (null, 13, 'green'); +end ;; diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/alter new file mode 100644 index 00000000000..1d11c9cf5cb --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/alter @@ -0,0 +1 @@ +modify e enum('red', 'green') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/create.sql new file mode 100644 index 00000000000..cb4e6db0fcd --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/enum-truncate/create.sql @@ -0,0 +1,24 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +insert into onlineddl_test values (null, 11, 'red'); +insert into onlineddl_test values (null, 13, 'green'); + +drop event if exists onlineddl_test; +delimiter ;; +create event onlineddl_test + on schedule every 1 second + starts current_timestamp + ends current_timestamp + interval 60 second + on completion not preserve + enable + do +begin + insert into onlineddl_test values (null, 11, 'red'); + insert into onlineddl_test values (null, 13, 'green'); +end ;; diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/alter new file mode 100644 index 00000000000..b3e025d40b5 --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/alter @@ -0,0 +1 @@ +modify e enum('red', 'green', 'cyan') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/create.sql new file mode 100644 index 00000000000..27980b89072 --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/create.sql @@ -0,0 +1,22 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +drop event if exists onlineddl_test; +delimiter ;; +create event onlineddl_test + on schedule every 1 second + starts current_timestamp + ends current_timestamp + interval 60 second + on completion not preserve + enable + do +begin + insert into onlineddl_test values (null, 11, 'red'); + insert into onlineddl_test values (null, 13, 'green'); + insert into onlineddl_test values (null, 17, 'blue'); +end ;; diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/expect_failure b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/expect_failure new file mode 100644 index 00000000000..dfa68652e4d --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-changelog/expect_failure @@ -0,0 +1 @@ +Data truncated for column diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/alter new file mode 100644 index 00000000000..b3e025d40b5 --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/alter @@ -0,0 +1 @@ +modify e enum('red', 'green', 'cyan') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/create.sql new file mode 100644 index 00000000000..248ab90f5fc --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/create.sql @@ -0,0 +1,11 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +insert into onlineddl_test values (null, 11, 'red'); +insert into onlineddl_test values (null, 13, 'green'); +insert into onlineddl_test values (null, 17, 'blue'); diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/expect_failure b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/expect_failure new file mode 100644 index 00000000000..dfa68652e4d --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-rename-copy/expect_failure @@ -0,0 +1 @@ +Data truncated for column diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/alter new file mode 100644 index 00000000000..1d11c9cf5cb --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/alter @@ -0,0 +1 @@ +modify e enum('red', 'green') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/create.sql new file mode 100644 index 00000000000..27980b89072 --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/create.sql @@ -0,0 +1,22 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +drop event if exists onlineddl_test; +delimiter ;; +create event onlineddl_test + on schedule every 1 second + starts current_timestamp + ends current_timestamp + interval 60 second + on completion not preserve + enable + do +begin + insert into onlineddl_test values (null, 11, 'red'); + insert into onlineddl_test values (null, 13, 'green'); + insert into onlineddl_test values (null, 17, 'blue'); +end ;; diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/expect_failure b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/expect_failure new file mode 100644 index 00000000000..dfa68652e4d --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-changelog/expect_failure @@ -0,0 +1 @@ +Data truncated for column diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/alter b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/alter new file mode 100644 index 00000000000..1d11c9cf5cb --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/alter @@ -0,0 +1 @@ +modify e enum('red', 'green') not null diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/create.sql b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/create.sql new file mode 100644 index 00000000000..248ab90f5fc --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/create.sql @@ -0,0 +1,11 @@ +drop table if exists onlineddl_test; +create table onlineddl_test ( + id int auto_increment, + i int not null, + e enum('red', 'green', 'blue') not null, + primary key(id) +) auto_increment=1; + +insert into onlineddl_test values (null, 11, 'red'); +insert into onlineddl_test values (null, 13, 'green'); +insert into onlineddl_test values (null, 17, 'blue'); diff --git a/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/expect_failure b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/expect_failure new file mode 100644 index 00000000000..dfa68652e4d --- /dev/null +++ b/go/test/endtoend/onlineddl/vrepl_suite/testdata/fail-enum-truncate-copy/expect_failure @@ -0,0 +1 @@ +Data truncated for column diff --git a/go/test/endtoend/onlineddl/vtctlutil.go b/go/test/endtoend/onlineddl/vtctlutil.go index 19a6ff79604..52a832f0e1f 100644 --- a/go/test/endtoend/onlineddl/vtctlutil.go +++ b/go/test/endtoend/onlineddl/vtctlutil.go @@ -25,9 +25,9 @@ import ( ) // CheckCancelAllMigrations cancels all pending migrations. There is no validation for affected migrations. -func CheckCancelAllMigrationsViaVtctl(t *testing.T, vtctlclient *cluster.VtctlClientProcess, keyspace string) { +func CheckCancelAllMigrationsViaVtctld(t *testing.T, vtctldclient *cluster.VtctldClientProcess, keyspace string) { cancelQuery := "alter vitess_migration cancel all" - _, err := vtctlclient.ApplySchemaWithOutput(keyspace, cancelQuery, cluster.VtctlClientParams{}) + _, err := vtctldclient.ApplySchemaWithOutput(keyspace, cancelQuery, cluster.ApplySchemaParams{}) assert.NoError(t, err) } diff --git a/go/test/endtoend/onlineddl/vtgate_util.go b/go/test/endtoend/onlineddl/vtgate_util.go index 693523cec48..f2272fcd73e 100644 --- a/go/test/endtoend/onlineddl/vtgate_util.go +++ b/go/test/endtoend/onlineddl/vtgate_util.go @@ -19,7 +19,6 @@ package onlineddl import ( "context" "fmt" - "math" "os" "testing" "time" @@ -57,7 +56,7 @@ func VtgateExecQuery(t *testing.T, vtParams *mysql.ConnParams, query string, exp require.Nil(t, err) defer conn.Close() - qr, err := conn.ExecuteFetch(query, math.MaxInt64, true) + qr, err := conn.ExecuteFetch(query, -1, true) if expectError == "" { require.NoError(t, err) } else { @@ -206,6 +205,21 @@ func CheckLaunchAllMigrations(t *testing.T, vtParams *mysql.ConnParams, expectCo } } +// CheckForceMigrationCutOver marks a migration for forced cut-over, and expects success by counting affected rows. +func CheckForceMigrationCutOver(t *testing.T, vtParams *mysql.ConnParams, shards []cluster.Shard, uuid string, expectPossible bool) { + query, err := sqlparser.ParseAndBind("alter vitess_migration %a force_cutover", + sqltypes.StringBindVariable(uuid), + ) + require.NoError(t, err) + r := VtgateExecQuery(t, vtParams, query, "") + + if expectPossible { + assert.Equal(t, len(shards), int(r.RowsAffected)) + } else { + assert.Equal(t, int(0), int(r.RowsAffected)) + } +} + // CheckMigrationStatus verifies that the migration indicated by given UUID has the given expected status func CheckMigrationStatus(t *testing.T, vtParams *mysql.ConnParams, shards []cluster.Shard, uuid string, expectStatuses ...schema.OnlineDDLStatus) bool { query, err := sqlparser.ParseAndBind("show vitess_migrations like %a", diff --git a/go/test/endtoend/recovery/pitr/binlog_server.go b/go/test/endtoend/recovery/pitr/binlog_server.go index 764af2b57cf..3b78b0d4ad7 100644 --- a/go/test/endtoend/recovery/pitr/binlog_server.go +++ b/go/test/endtoend/recovery/pitr/binlog_server.go @@ -93,14 +93,18 @@ func (bs *binLogServer) start(source mysqlSource) error { bs.proc.Args = append(bs.proc.Args, fmt.Sprintf("-ripple_master_password=%s", source.password)) } - errFile, _ := os.Create(path.Join(bs.dataDirectory, "log.txt")) + errFile, err := os.Create(path.Join(bs.dataDirectory, "log.txt")) + if err != nil { + log.Errorf("cannot create error log file for binlog server: %v", err) + return err + } bs.proc.Stderr = errFile bs.proc.Env = append(bs.proc.Env, os.Environ()...) log.Infof("Running binlog server with command: %v", strings.Join(bs.proc.Args, " ")) - err := bs.proc.Start() + err = bs.proc.Start() if err != nil { return err } diff --git a/go/test/endtoend/recovery/pitr/shardedpitr_test.go b/go/test/endtoend/recovery/pitr/shardedpitr_test.go index d04b5600362..0aed6573337 100644 --- a/go/test/endtoend/recovery/pitr/shardedpitr_test.go +++ b/go/test/endtoend/recovery/pitr/shardedpitr_test.go @@ -89,8 +89,6 @@ var ( } }` commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", @@ -143,7 +141,7 @@ func TestPITRRecovery(t *testing.T) { cluster.VerifyRowsInTabletForTable(t, replica1, keyspaceName, 2, "product") // backup the replica - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) require.NoError(t, err) // check that the backup shows up in the listing @@ -183,10 +181,10 @@ func TestPITRRecovery(t *testing.T) { cluster.VerifyRowsInTabletForTable(t, shard1Replica1, keyspaceName, 4, "product") // take the backup (to simulate the regular backup) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Backup", shard0Replica1.Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Backup", shard0Replica1.Alias) require.NoError(t, err) // take the backup (to simulate the regular backup) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Backup", shard1Replica1.Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Backup", shard1Replica1.Alias) require.NoError(t, err) backups, err := clusterInstance.ListBackups(keyspaceName + "/-80") @@ -297,10 +295,10 @@ func TestPITRRecovery(t *testing.T) { } func performResharding(t *testing.T) { - err := clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) + err := clusterInstance.VtctldClientProcess.ApplyVSchema(keyspaceName, vSchema) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Reshard", "--", "--source_shards=0", "--target_shards=-80,80-", "Create", "ks.reshardWorkflow") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Reshard", "create", "--source-shards=0", "--target-shards=-80,80-", "--target-keyspace", "ks", "--workflow", "reshardWorkflow") require.NoError(t, err) waitTimeout := 30 * time.Second @@ -309,32 +307,32 @@ func performResharding(t *testing.T) { waitForNoWorkflowLag(t, clusterInstance, "ks.reshardWorkflow") - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Reshard", "--", "--tablet_types=rdonly", "SwitchTraffic", "ks.reshardWorkflow") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Reshard", "SwitchTraffic", "--tablet-types=rdonly", "--target-keyspace", "ks", "--workflow", "reshardWorkflow") require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Reshard", "--", "--tablet_types=replica", "SwitchTraffic", "ks.reshardWorkflow") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Reshard", "SwitchTraffic", "--tablet-types=replica", "--target-keyspace", "ks", "--workflow", "reshardWorkflow") require.NoError(t, err) // then serve primary from the split shards - err = clusterInstance.VtctlclientProcess.ExecuteCommand("Reshard", "--", "--tablet_types=primary", "SwitchTraffic", "ks.reshardWorkflow") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("Reshard", "SwitchTraffic", "--tablet-types=primary", "--target-keyspace", "ks", "--workflow", "reshardWorkflow") require.NoError(t, err) // remove the original tablets in the original shard removeTablets(t, []*cluster.Vttablet{primary, replica1, replica2}) for _, tablet := range []*cluster.Vttablet{replica1, replica2} { - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("DeleteTablets", tablet.Alias) require.NoError(t, err) } - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "--", "--allow_primary", primary.Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("DeleteTablets", "--allow-primary", primary.Alias) require.NoError(t, err) // rebuild the serving graph, all mentions of the old shards should be gone - err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "ks") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("RebuildKeyspaceGraph", "ks") require.NoError(t, err) // delete the original shard - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", "ks/0") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("DeleteShards", "ks/0") require.NoError(t, err) // Restart vtgate process @@ -462,13 +460,13 @@ func initializeCluster(t *testing.T) { } } - err = clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shard.Name, cell, primary.TabletUID) + err = clusterInstance.VtctldClientProcess.InitShardPrimary(keyspaceName, shard.Name, cell, primary.TabletUID) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shard0.Name, cell, shard0Primary.TabletUID) + err = clusterInstance.VtctldClientProcess.InitShardPrimary(keyspaceName, shard0.Name, cell, shard0Primary.TabletUID) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shard1.Name, cell, shard1Primary.TabletUID) + err = clusterInstance.VtctldClientProcess.InitShardPrimary(keyspaceName, shard1.Name, cell, shard1Primary.TabletUID) require.NoError(t, err) err = clusterInstance.StartVTOrc(keyspaceName) @@ -499,9 +497,9 @@ func insertRow(t *testing.T, id int, productName string, isSlow bool) { } func createRestoreKeyspace(t *testing.T, timeToRecover, restoreKeyspaceName string) { - output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("CreateKeyspace", "--", - "--keyspace_type=SNAPSHOT", "--base_keyspace="+keyspaceName, - "--snapshot_time", timeToRecover, restoreKeyspaceName) + output, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("CreateKeyspace", + "--type=SNAPSHOT", "--base-keyspace="+keyspaceName, + "--snapshot-timestamp", timeToRecover, restoreKeyspaceName) log.Info(output) require.NoError(t, err) } @@ -558,9 +556,6 @@ func launchRecoveryTablet(t *testing.T, tablet *cluster.Vttablet, binlogServer * "--binlog_user", binlogServer.username, "--binlog_password", binlogServer.password, "--pitr_gtid_lookup_timeout", lookupTimeout, - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", - "--vreplication_tablet_type", "replica", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go index f4db74bbf4e..1ebb7c2647f 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -51,8 +51,6 @@ var ( dbCredentialFile string shardName = "0" commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", @@ -166,7 +164,7 @@ func TestMainImpl(m *testing.M) { if err != nil { return 1, err } - if err := localCluster.VtctlclientProcess.InitializeShard(keyspaceName, shard.Name, cell, primary.TabletUID); err != nil { + if err := localCluster.VtctldClientProcess.InitializeShard(keyspaceName, shard.Name, cell, primary.TabletUID); err != nil { return 1, err } if err := localCluster.StartVTOrc(keyspaceName); err != nil { @@ -208,17 +206,17 @@ func TestRecoveryImpl(t *testing.T) { verifyInitialReplication(t) // take first backup of value = test1 - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err := localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) assert.NoError(t, err) backups := listBackups(t) require.Equal(t, len(backups), 1) assert.Contains(t, backups[0], replica1.Alias) - err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) + err = localCluster.VtctldClientProcess.ApplyVSchema(keyspaceName, vSchema) assert.NoError(t, err) - output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", keyspaceName) + output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("GetVSchema", keyspaceName) assert.NoError(t, err) assert.Contains(t, output, "vt_insert_test") @@ -226,12 +224,12 @@ func TestRecoveryImpl(t *testing.T) { restoreTime := time.Now().UTC() recovery.RestoreTablet(t, localCluster, replica2, recoveryKS1, "0", keyspaceName, commonTabletArg, restoreTime) - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvVSchema", cell) + output, err = localCluster.VtctldClientProcess.ExecuteCommandWithOutput("GetSrvVSchema", cell) assert.NoError(t, err) assert.Contains(t, output, keyspaceName) assert.Contains(t, output, recoveryKS1) - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS1) + output, err = localCluster.VtctldClientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS1) assert.NoError(t, err) assert.Contains(t, output, "vt_insert_test") @@ -279,13 +277,13 @@ func TestRecoveryImpl(t *testing.T) { } // take second backup of value = msgx1 - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", replica1.Alias) assert.NoError(t, err) // restore to first backup recovery.RestoreTablet(t, localCluster, replica3, recoveryKS2, "0", keyspaceName, commonTabletArg, restoreTime) - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS2) + output, err = localCluster.VtctldClientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS2) assert.NoError(t, err) assert.Contains(t, output, "vt_insert_test") diff --git a/go/test/endtoend/reparent/emergencyreparent/ers_test.go b/go/test/endtoend/reparent/emergencyreparent/ers_test.go index 8f6638ecb7e..f0cf4f2cd6a 100644 --- a/go/test/endtoend/reparent/emergencyreparent/ers_test.go +++ b/go/test/endtoend/reparent/emergencyreparent/ers_test.go @@ -233,10 +233,10 @@ func TestERSPromoteRdonly(t *testing.T) { tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets var err error - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", tablets[2].Alias, "rdonly") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", tablets[2].Alias, "rdonly") require.NoError(t, err) utils.ConfirmReplication(t, tablets[0], tablets[1:]) @@ -248,7 +248,7 @@ func TestERSPromoteRdonly(t *testing.T) { out, err := utils.ErsIgnoreTablet(clusterInstance, nil, "30s", "30s", []*cluster.Vttablet{tablets[3]}, false) require.NotNil(t, err, out) - out, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", utils.KeyspaceShard) + out, err = clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetShard", utils.KeyspaceShard) require.NoError(t, err) require.Contains(t, out, `"uid": 101`, "the primary should still be 101 in the shard info") } @@ -288,16 +288,16 @@ func TestPullFromRdonly(t *testing.T) { // make tablets[1] a rdonly tablet. // rename tablet so that the test is not confusing rdonly := tablets[1] - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", rdonly.Alias, "rdonly") + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", rdonly.Alias, "rdonly") require.NoError(t, err) // confirm that all the tablets can replicate successfully right now utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{rdonly, tablets[2], tablets[3]}) // stop replication on the other two tablets - err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopReplication", tablets[2].Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("StopReplication", tablets[2].Alias) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopReplication", tablets[3].Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("StopReplication", tablets[3].Alias) require.NoError(t, err) // stop semi-sync on the primary so that any transaction now added does not require an ack @@ -311,9 +311,9 @@ func TestPullFromRdonly(t *testing.T) { utils.StopTablet(t, tablets[0], true) // start the replication back on the two tablets - err = clusterInstance.VtctlclientProcess.ExecuteCommand("StartReplication", tablets[2].Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("StartReplication", tablets[2].Alias) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("StartReplication", tablets[3].Alias) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("StartReplication", tablets[3].Alias) require.NoError(t, err) // check that tablets[2] and tablets[3] still only has 1 value @@ -349,9 +349,12 @@ func TestNoReplicationStatusAndIOThreadStopped(t *testing.T) { tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) - err := clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", tablets[1].Alias, `STOP SLAVE; RESET SLAVE ALL`) + err := clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[1].Alias, `STOP SLAVE`) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", tablets[3].Alias, `STOP SLAVE IO_THREAD;`) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[1].Alias, `RESET SLAVE ALL`) + require.NoError(t, err) + // + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[3].Alias, `STOP SLAVE IO_THREAD;`) require.NoError(t, err) // Run an additional command in the current primary which will only be acked by tablets[2] and be in its relay log. insertedVal := utils.ConfirmReplication(t, tablets[0], nil) @@ -447,7 +450,7 @@ func TestRecoverWithMultipleFailures(t *testing.T) { utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) // make tablets[1] a rdonly tablet. - err := clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") + err := clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") require.NoError(t, err) // Confirm that replication is still working as intended @@ -475,7 +478,7 @@ func TestERSFailFast(t *testing.T) { utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) // make tablets[1] a rdonly tablet. - err := clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") + err := clusterInstance.VtctldClientProcess.ExecuteCommand("ChangeTabletType", tablets[1].Alias, "rdonly") require.NoError(t, err) // Confirm that replication is still working as intended @@ -514,9 +517,9 @@ func TestReplicationStopped(t *testing.T) { tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) - err := clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", tablets[1].Alias, `STOP SLAVE SQL_THREAD;`) + err := clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[1].Alias, `STOP SLAVE SQL_THREAD;`) require.NoError(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", tablets[2].Alias, `STOP SLAVE;`) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[2].Alias, `STOP SLAVE;`) require.NoError(t, err) // Run an additional command in the current primary which will only be acked by tablets[3] and be in its relay log. insertedVal := utils.ConfirmReplication(t, tablets[0], nil) @@ -525,7 +528,7 @@ func TestReplicationStopped(t *testing.T) { require.Error(t, err, "ERS should fail with 2 replicas having replication stopped") // Start replication back on tablet[1] - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", tablets[1].Alias, `START SLAVE;`) + err = clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", tablets[1].Alias, `START SLAVE;`) require.NoError(t, err) // Failover to tablets[3] again. This time it should succeed out, err := utils.Ers(clusterInstance, tablets[3], "60s", "30s") diff --git a/go/test/endtoend/reparent/newfeaturetest/reparent_test.go b/go/test/endtoend/reparent/newfeaturetest/reparent_test.go index d5f37dc8604..b570509f1a7 100644 --- a/go/test/endtoend/reparent/newfeaturetest/reparent_test.go +++ b/go/test/endtoend/reparent/newfeaturetest/reparent_test.go @@ -131,8 +131,8 @@ func TestChangeTypeWithoutSemiSync(t *testing.T) { utils.RunSQL(ctx, t, "set global super_read_only = 0", tablet) } - utils.RunSQL(ctx, t, "UNINSTALL PLUGIN rpl_semi_sync_slave;", tablet) - utils.RunSQL(ctx, t, "UNINSTALL PLUGIN rpl_semi_sync_master;", tablet) + utils.RunSQL(ctx, t, "UNINSTALL PLUGIN rpl_semi_sync_slave", tablet) + utils.RunSQL(ctx, t, "UNINSTALL PLUGIN rpl_semi_sync_master", tablet) } utils.ValidateTopology(t, clusterInstance, true) diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_test.go index f7afea1431b..6aa5972b928 100644 --- a/go/test/endtoend/reparent/plannedreparent/reparent_test.go +++ b/go/test/endtoend/reparent/plannedreparent/reparent_test.go @@ -98,13 +98,13 @@ func TestPRSWithDrainedLaggingTablet(t *testing.T) { utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) // make tablets[1 lag from the other tablets by setting the delay to a large number - utils.RunSQL(context.Background(), t, `stop slave;CHANGE MASTER TO MASTER_DELAY = 1999;start slave;`, tablets[1]) + utils.RunSQLs(context.Background(), t, []string{`stop slave`, `CHANGE MASTER TO MASTER_DELAY = 1999`, `start slave;`}, tablets[1]) // insert another row in tablets[1 utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[2], tablets[3]}) // assert that there is indeed only 1 row in tablets[1 - res := utils.RunSQL(context.Background(), t, `select msg from vt_insert_test;`, tablets[1]) + res := utils.RunSQL(context.Background(), t, `select msg from vt_insert_test`, tablets[1]) assert.Equal(t, 1, len(res.Rows)) // Perform a graceful reparent operation @@ -217,8 +217,8 @@ func reparentFromOutside(t *testing.T, clusterInstance *cluster.LocalProcessClus if !downPrimary { // commands to stop the current primary - demoteCommands := "SET GLOBAL read_only = ON; FLUSH TABLES WITH READ LOCK; UNLOCK TABLES" - utils.RunSQL(ctx, t, demoteCommands, tablets[0]) + demoteCommands := []string{"SET GLOBAL read_only = ON", "FLUSH TABLES WITH READ LOCK", "UNLOCK TABLES"} + utils.RunSQLs(ctx, t, demoteCommands, tablets[0]) //Get the position of the old primary and wait for the new one to catch up. err := utils.WaitForReplicationPosition(t, tablets[0], tablets[1]) @@ -226,26 +226,33 @@ func reparentFromOutside(t *testing.T, clusterInstance *cluster.LocalProcessClus } // commands to convert a replica to be writable - promoteReplicaCommands := "STOP SLAVE; RESET SLAVE ALL; SET GLOBAL read_only = OFF;" - utils.RunSQL(ctx, t, promoteReplicaCommands, tablets[1]) + promoteReplicaCommands := []string{"STOP SLAVE", "RESET SLAVE ALL", "SET GLOBAL read_only = OFF"} + utils.RunSQLs(ctx, t, promoteReplicaCommands, tablets[1]) // Get primary position _, gtID := cluster.GetPrimaryPosition(t, *tablets[1], utils.Hostname) // tablets[0] will now be a replica of tablets[1 - changeReplicationSourceCommands := fmt.Sprintf("RESET MASTER; RESET SLAVE; SET GLOBAL gtid_purged = '%s';"+ - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1;"+ - "START SLAVE;", gtID, utils.Hostname, tablets[1].MySQLPort) - utils.RunSQL(ctx, t, changeReplicationSourceCommands, tablets[0]) + changeReplicationSourceCommands := []string{ + "RESET MASTER", + "RESET SLAVE", + fmt.Sprintf("SET GLOBAL gtid_purged = '%s'", gtID), + fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1", utils.Hostname, tablets[1].MySQLPort), + } + utils.RunSQLs(ctx, t, changeReplicationSourceCommands, tablets[0]) // Capture time when we made tablets[1 writable baseTime := time.Now().UnixNano() / 1000000000 // tablets[2 will be a replica of tablets[1 - changeReplicationSourceCommands = fmt.Sprintf("STOP SLAVE; RESET MASTER; SET GLOBAL gtid_purged = '%s';"+ - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1;"+ - "START SLAVE;", gtID, utils.Hostname, tablets[1].MySQLPort) - utils.RunSQL(ctx, t, changeReplicationSourceCommands, tablets[2]) + changeReplicationSourceCommands = []string{ + "STOP SLAVE", + "RESET MASTER", + fmt.Sprintf("SET GLOBAL gtid_purged = '%s'", gtID), + fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1", utils.Hostname, tablets[1].MySQLPort), + "START SLAVE", + } + utils.RunSQLs(ctx, t, changeReplicationSourceCommands, tablets[2]) // To test the downPrimary, we kill the old primary first and delete its tablet record if downPrimary { @@ -295,7 +302,7 @@ func TestReparentWithDownReplica(t *testing.T) { // insert data into the new primary, check the connected replica work insertVal = utils.ConfirmReplication(t, tablets[1], []*cluster.Vttablet{tablets[0], tablets[3]}) } else { - assert.Contains(t, out, fmt.Sprintf("TabletManager.PrimaryStatus on %s error", tablets[2].Alias)) + assert.Contains(t, out, fmt.Sprintf("TabletManager.PrimaryStatus on %s", tablets[2].Alias)) // insert data into the old primary, check the connected replica works. The primary tablet shouldn't have changed. insertVal = utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[3]}) } diff --git a/go/test/endtoend/reparent/prscomplex/main_test.go b/go/test/endtoend/reparent/prscomplex/main_test.go index 88276012781..88e3d6c09fa 100644 --- a/go/test/endtoend/reparent/prscomplex/main_test.go +++ b/go/test/endtoend/reparent/prscomplex/main_test.go @@ -63,12 +63,12 @@ func TestMain(m *testing.M) { SchemaSQL: schemaSQL, } clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, - "--queryserver-config-query-timeout=9000", + "--queryserver-config-query-timeout=9000s", "--queryserver-config-pool-size=3", "--queryserver-config-stream-pool-size=3", "--queryserver-config-transaction-cap=2", - "--queryserver-config-transaction-timeout=20", - "--shutdown_grace_period=3", + "--queryserver-config-transaction-timeout=20s", + "--shutdown_grace_period=3s", "--queryserver-config-schema-change-signal=false") err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false) if err != nil { diff --git a/go/test/endtoend/reparent/utils/utils.go b/go/test/endtoend/reparent/utils/utils.go index 4ef13819e85..675648dcf37 100644 --- a/go/test/endtoend/reparent/utils/utils.go +++ b/go/test/endtoend/reparent/utils/utils.go @@ -258,6 +258,21 @@ func getMysqlConnParam(tablet *cluster.Vttablet) mysql.ConnParams { return connParams } +// RunSQLs is used to run SQL commands directly on the MySQL instance of a vttablet. All commands are +// run in a single connection. +func RunSQLs(ctx context.Context, t *testing.T, sqls []string, tablet *cluster.Vttablet) (results []*sqltypes.Result) { + tabletParams := getMysqlConnParam(tablet) + conn, err := mysql.Connect(ctx, &tabletParams) + require.Nil(t, err) + defer conn.Close() + + for _, sql := range sqls { + result := execute(t, conn, sql) + results = append(results, result) + } + return results +} + // RunSQL is used to run a SQL command directly on the MySQL instance of a vttablet func RunSQL(ctx context.Context, t *testing.T, sql string, tablet *cluster.Vttablet) *sqltypes.Result { tabletParams := getMysqlConnParam(tablet) @@ -695,7 +710,7 @@ func SetReplicationSourceFailed(tablet *cluster.Vttablet, prsOut string) bool { // CheckReplicationStatus checks that the replication for sql and io threads is setup as expected func CheckReplicationStatus(ctx context.Context, t *testing.T, tablet *cluster.Vttablet, sqlThreadRunning bool, ioThreadRunning bool) { - res := RunSQL(ctx, t, "show slave status;", tablet) + res := RunSQL(ctx, t, "show slave status", tablet) if ioThreadRunning { require.Equal(t, "Yes", res.Rows[0][10].ToString()) } else { diff --git a/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go b/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go index 2dc79840018..79c3b62ebea 100644 --- a/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go +++ b/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go @@ -53,8 +53,8 @@ var ( ) const ( - testDataPath = "../../onlineddl/vrepl_suite/testdata" - defaultSQLMode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" + testDataPath = "../../onlineddl/vrepl_suite/testdata" + sqlModeAllowsZeroDate = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" ) type testTableSchema struct { @@ -202,7 +202,7 @@ func testSingle(t *testing.T, testName string) { return } - sqlModeQuery := fmt.Sprintf("set @@global.sql_mode='%s'", defaultSQLMode) + sqlModeQuery := fmt.Sprintf("set @@global.sql_mode='%s'", sqlModeAllowsZeroDate) _ = mysqlExec(t, sqlModeQuery, "") _ = mysqlExec(t, "set @@global.event_scheduler=0", "") @@ -345,18 +345,19 @@ func ignoreAutoIncrement(t *testing.T, createTable string) string { func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, allowSchemadiffNormalization bool, hints *schemadiff.DiffHints) { // turn the "from" and "to" create statement strings (which we just read via SHOW CREATE TABLE into sqlparser.CreateTable statement) - fromStmt, err := sqlparser.ParseStrictDDL(fromCreateTable) + env := schemadiff.NewTestEnv() + fromStmt, err := env.Parser().ParseStrictDDL(fromCreateTable) require.NoError(t, err) fromCreateTableStatement, ok := fromStmt.(*sqlparser.CreateTable) require.True(t, ok) - toStmt, err := sqlparser.ParseStrictDDL(toCreateTable) + toStmt, err := env.Parser().ParseStrictDDL(toCreateTable) require.NoError(t, err) toCreateTableStatement, ok := toStmt.(*sqlparser.CreateTable) require.True(t, ok) // The actual diff logic here! - diff, err := schemadiff.DiffTables(fromCreateTableStatement, toCreateTableStatement, hints) + diff, err := schemadiff.DiffTables(env, fromCreateTableStatement, toCreateTableStatement, hints) assert.NoError(t, err) // The diff can be empty or there can be an actual ALTER TABLE statement @@ -385,7 +386,6 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al // the table generated by the test's own ALTER statement? // But wait, there's caveats. - if toCreateTable != resultCreateTable { // schemadiff's ALTER statement can normalize away CHARACTER SET and COLLATION definitions: // when altering a column's CHARTSET&COLLATION into the table's values, schemadiff just strips the @@ -394,20 +394,20 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al // structure is identical. And so we accept that there can be a normalization issue. if allowSchemadiffNormalization { { - stmt, err := sqlparser.ParseStrictDDL(toCreateTable) + stmt, err := env.Parser().ParseStrictDDL(toCreateTable) require.NoError(t, err) createTableStatement, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := schemadiff.NewCreateTableEntity(createTableStatement) + c, err := schemadiff.NewCreateTableEntity(env, createTableStatement) require.NoError(t, err) toCreateTable = c.Create().CanonicalStatementString() } { - stmt, err := sqlparser.ParseStrictDDL(resultCreateTable) + stmt, err := env.Parser().ParseStrictDDL(resultCreateTable) require.NoError(t, err) createTableStatement, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := schemadiff.NewCreateTableEntity(createTableStatement) + c, err := schemadiff.NewCreateTableEntity(env, createTableStatement) require.NoError(t, err) resultCreateTable = c.Create().CanonicalStatementString() } @@ -418,12 +418,12 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al assert.Equal(t, toCreateTable, resultCreateTable, "mismatched table structure. ALTER query was: %s", diffedAlterQuery) // Also, let's see that our diff agrees there's no change: - resultStmt, err := sqlparser.ParseStrictDDL(resultCreateTable) + resultStmt, err := env.Parser().ParseStrictDDL(resultCreateTable) require.NoError(t, err) resultCreateTableStatement, ok := resultStmt.(*sqlparser.CreateTable) require.True(t, ok) - resultDiff, err := schemadiff.DiffTables(toCreateTableStatement, resultCreateTableStatement, hints) + resultDiff, err := schemadiff.DiffTables(env, toCreateTableStatement, resultCreateTableStatement, hints) assert.NoError(t, err) assert.Nil(t, resultDiff) } diff --git a/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go b/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go index 8a3dd4f9b73..96cbab6fcd8 100644 --- a/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go +++ b/go/test/endtoend/tabletgateway/buffer/buffer_test_helpers.go @@ -71,7 +71,7 @@ const ( type threadParams struct { quit bool rpcs int // Number of queries successfully executed. - errors int // Number of failed queries. + errors []error // Errors returned by the queries. waitForNotification chan bool // Channel used to notify the main thread that this thread executed notifyLock sync.Mutex // notifyLock guards the two fields notifyAfterNSuccessfulRpcs/rpcsSoFar. notifyAfterNSuccessfulRpcs int // If 0, notifications are disabled @@ -96,14 +96,14 @@ func (c *threadParams) threadRun(wg *sync.WaitGroup, vtParams *mysql.ConnParams) if c.reservedConn { _, err = conn.ExecuteFetch("set default_week_format = 1", 1000, true) if err != nil { - c.errors++ + c.errors = append(c.errors, err) log.Errorf("error setting default_week_format: %v", err) } } for !c.quit { err = c.executeFunction(c, conn) if err != nil { - c.errors++ + c.errors = append(c.errors, err) log.Errorf("error executing function %s: %v", c.typ, err) } c.rpcs++ @@ -229,7 +229,7 @@ func (bt *BufferingTest) createCluster() (*cluster.LocalProcessCluster, int) { } clusterInstance.VtTabletExtraArgs = []string{ "--health_check_interval", "1s", - "--queryserver-config-transaction-timeout", "20", + "--queryserver-config-transaction-timeout", "20s", } if err := clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { return nil, 1 @@ -241,8 +241,8 @@ func (bt *BufferingTest) createCluster() (*cluster.LocalProcessCluster, int) { "--buffer_window", "10m", "--buffer_max_failover_duration", "10m", "--buffer_min_time_between_failovers", "20m", - "--buffer_implementation", "keyspace_events", "--tablet_refresh_interval", "1s", + "--buffer_drain_concurrency", "4", } clusterInstance.VtGateExtraArgs = append(clusterInstance.VtGateExtraArgs, bt.VtGateExtraArgs...) @@ -343,8 +343,8 @@ func (bt *BufferingTest) Test(t *testing.T) { updateThreadInstance.stop() // Both threads must not see any error - assert.Zero(t, readThreadInstance.errors, "found errors in read queries") - assert.Zero(t, updateThreadInstance.errors, "found errors in tx queries") + assert.Empty(t, readThreadInstance.errors, "found errors in read queries") + assert.Empty(t, updateThreadInstance.errors, "found errors in tx queries") //At least one thread should have been buffered. //This may fail if a failover is too fast. Add retries then. diff --git a/go/test/endtoend/tabletgateway/buffer/reparent/failover_buffer_test.go b/go/test/endtoend/tabletgateway/buffer/reparent/failover_buffer_test.go index d3828eb8166..2be57120050 100644 --- a/go/test/endtoend/tabletgateway/buffer/reparent/failover_buffer_test.go +++ b/go/test/endtoend/tabletgateway/buffer/reparent/failover_buffer_test.go @@ -29,9 +29,9 @@ import ( "vitess.io/vitess/go/vt/log" ) -const ( - demoteQuery = "SET GLOBAL read_only = ON;FLUSH TABLES WITH READ LOCK;UNLOCK TABLES;" - promoteQuery = "STOP SLAVE;RESET SLAVE ALL;SET GLOBAL read_only = OFF;" +var ( + demoteQueries = []string{"SET GLOBAL read_only = ON", "FLUSH TABLES WITH READ LOCK", "UNLOCK TABLES"} + promoteQueries = []string{"STOP SLAVE", "RESET SLAVE ALL", "SET GLOBAL read_only = OFF"} hostname = "localhost" ) @@ -48,7 +48,8 @@ func failoverExternalReparenting(t *testing.T, clusterInstance *cluster.LocalPro replica := clusterInstance.Keyspaces[0].Shards[0].Vttablets[1] oldPrimary := primary newPrimary := replica - primary.VttabletProcess.QueryTablet(demoteQuery, keyspaceUnshardedName, true) + err := primary.VttabletProcess.QueryTabletMultiple(demoteQueries, keyspaceUnshardedName, true) + require.NoError(t, err) // Wait for replica to catch up to primary. cluster.WaitForReplicationPos(t, primary, replica, false, time.Minute) @@ -62,7 +63,8 @@ func failoverExternalReparenting(t *testing.T, clusterInstance *cluster.LocalPro } // Promote replica to new primary. - replica.VttabletProcess.QueryTablet(promoteQuery, keyspaceUnshardedName, true) + err = replica.VttabletProcess.QueryTabletMultiple(promoteQueries, keyspaceUnshardedName, true) + require.NoError(t, err) // Configure old primary to replicate from new primary. @@ -70,11 +72,18 @@ func failoverExternalReparenting(t *testing.T, clusterInstance *cluster.LocalPro // Use 'localhost' as hostname because Travis CI worker hostnames // are too long for MySQL replication. - changeSourceCommands := fmt.Sprintf("RESET SLAVE;SET GLOBAL gtid_slave_pos = '%s';CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d ,MASTER_USER='vt_repl', MASTER_USE_GTID = slave_pos;START SLAVE;", gtID, "localhost", newPrimary.MySQLPort) - oldPrimary.VttabletProcess.QueryTablet(changeSourceCommands, keyspaceUnshardedName, true) + changeSourceCommands := []string{ + "STOP SLAVE", + "RESET MASTER", + fmt.Sprintf("SET GLOBAL gtid_purged = '%s'", gtID), + fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1", "localhost", newPrimary.MySQLPort), + "START SLAVE", + } + err = oldPrimary.VttabletProcess.QueryTabletMultiple(changeSourceCommands, keyspaceUnshardedName, true) + require.NoError(t, err) // Notify the new vttablet primary about the reparent. - err := clusterInstance.VtctlclientProcess.ExecuteCommand("TabletExternallyReparented", newPrimary.Alias) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("TabletExternallyReparented", newPrimary.Alias) require.NoError(t, err) } diff --git a/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go b/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go index c6f7253c791..685c361cef7 100644 --- a/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go +++ b/go/test/endtoend/tabletmanager/tablegc/tablegc_test.go @@ -20,12 +20,15 @@ import ( "flag" "fmt" "os" + "strings" "testing" "time" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet/tabletserver/gc" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/onlineddl" @@ -49,9 +52,9 @@ var ( ) Engine=InnoDB; ` sqlCreateView = ` - create or replace view v1 as select * from t1; + create or replace view v1 as select * from t1 ` - sqlSchema = sqlCreateTable + sqlCreateView + sqlSchema = []string{sqlCreateTable, sqlCreateView} vSchema = ` { @@ -107,7 +110,7 @@ func TestMain(m *testing.M) { // Start keyspace keyspace := &cluster.Keyspace{ Name: keyspaceName, - SchemaSQL: sqlSchema, + SchemaSQL: strings.Join(sqlSchema, ";"), VSchema: vSchema, } @@ -128,19 +131,25 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -func checkTableRows(t *testing.T, tableName string, expect int64) { +func getTableRows(t *testing.T, tableName string) int64 { require.NotEmpty(t, tableName) query := `select count(*) as c from %a` parsed := sqlparser.BuildParsedQuery(query, tableName) rs, err := primaryTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true) require.NoError(t, err) count := rs.Named().Row().AsInt64("c", 0) + return count +} + +func checkTableRows(t *testing.T, tableName string, expect int64) { + count := getTableRows(t, tableName) assert.Equal(t, expect, count) } func populateTable(t *testing.T) { - _, err := primaryTablet.VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true) + err := primaryTablet.VttabletProcess.QueryTabletMultiple(sqlSchema, keyspaceName, true) require.NoError(t, err) + _, err = primaryTablet.VttabletProcess.QueryTablet("delete from t1", keyspaceName, true) require.NoError(t, err) _, err = primaryTablet.VttabletProcess.QueryTablet("insert into t1 (id, value) values (null, md5(rand()))", keyspaceName, true) @@ -158,10 +167,17 @@ func populateTable(t *testing.T) { } // tableExists sees that a given table exists in MySQL -func tableExists(tableExpr string) (exists bool, tableName string, err error) { - query := `select table_name as table_name from information_schema.tables where table_schema=database() and table_name like '%a'` - parsed := sqlparser.BuildParsedQuery(query, tableExpr) - rs, err := primaryTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true) +func tableExists(exprs ...string) (exists bool, tableName string, err error) { + if len(exprs) == 0 { + return false, "", fmt.Errorf("empty table list") + } + var clauses []string + for _, expr := range exprs { + clauses = append(clauses, fmt.Sprintf("table_name like '%s'", expr)) + } + clause := strings.Join(clauses, " or ") + query := fmt.Sprintf(`select table_name as table_name from information_schema.tables where table_schema=database() and (%s)`, clause) + rs, err := primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) if err != nil { return false, "", err } @@ -176,19 +192,18 @@ func validateTableDoesNotExist(t *testing.T, tableExpr string) { defer cancel() ticker := time.NewTicker(time.Second) - var foundTableName string - var exists bool - var err error + defer ticker.Stop() + for { + exists, foundTableName, err := tableExists(tableExpr) + require.NoError(t, err) + if !exists { + return + } select { case <-ticker.C: - exists, foundTableName, err = tableExists(tableExpr) - require.NoError(t, err) - if !exists { - return - } case <-ctx.Done(): - assert.NoError(t, ctx.Err(), "validateTableDoesNotExist timed out, table %v still exists (%v)", tableExpr, foundTableName) + assert.Failf(t, "validateTableDoesNotExist timed out, table %v still exists (%v)", tableExpr, foundTableName) return } } @@ -199,59 +214,84 @@ func validateTableExists(t *testing.T, tableExpr string) { defer cancel() ticker := time.NewTicker(time.Second) - var exists bool - var err error + defer ticker.Stop() + for { + exists, _, err := tableExists(tableExpr) + require.NoError(t, err) + if exists { + return + } select { case <-ticker.C: - exists, _, err = tableExists(tableExpr) - require.NoError(t, err) - if exists { - return - } case <-ctx.Done(): - assert.NoError(t, ctx.Err(), "validateTableExists timed out, table %v still does not exist", tableExpr) + assert.Failf(t, "validateTableExists timed out, table %v still does not exist", tableExpr) return } } } func validateAnyState(t *testing.T, expectNumRows int64, states ...schema.TableGCState) { - for _, state := range states { - expectTableToExist := true - searchExpr := "" - switch state { - case schema.HoldTableGCState: - searchExpr = `\_vt\_HOLD\_%` - case schema.PurgeTableGCState: - searchExpr = `\_vt\_PURGE\_%` - case schema.EvacTableGCState: - searchExpr = `\_vt\_EVAC\_%` - case schema.DropTableGCState: - searchExpr = `\_vt\_DROP\_%` - case schema.TableDroppedGCState: - searchExpr = `\_vt\_%` - expectTableToExist = false - default: - t.Log("Unknown state") - t.Fail() - } - exists, tableName, err := tableExists(searchExpr) - require.NoError(t, err) - - if exists { - if expectNumRows >= 0 { - checkTableRows(t, tableName, expectNumRows) + t.Run(fmt.Sprintf("validateAnyState: expectNumRows=%v, states=%v", expectNumRows, states), func(t *testing.T) { + timeout := gc.NextChecksIntervals[len(gc.NextChecksIntervals)-1] + 5*time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for { + // Attempt validation: + for _, state := range states { + expectTableToExist := true + searchExpr := "" + searchExpr2 := "" + switch state { + case schema.HoldTableGCState: + searchExpr = `\_vt\_HOLD\_%` + searchExpr2 = `\_vt\_hld\_%` + case schema.PurgeTableGCState: + searchExpr = `\_vt\_PURGE\_%` + searchExpr2 = `\_vt\_prg\_%` + case schema.EvacTableGCState: + searchExpr = `\_vt\_EVAC\_%` + searchExpr2 = `\_vt\_evc\_%` + case schema.DropTableGCState: + searchExpr = `\_vt\_DROP\_%` + searchExpr2 = `\_vt\_drp\_%` + case schema.TableDroppedGCState: + searchExpr = `\_vt\_%` + searchExpr2 = `\_vt\_%` + expectTableToExist = false + default: + require.Failf(t, "unknown state", "%v", state) + } + exists, tableName, err := tableExists(searchExpr, searchExpr2) + require.NoError(t, err) + + var foundRows int64 + if exists { + foundRows = getTableRows(t, tableName) + // Now that the table is validated, we can drop it (test cleanup) + dropTable(t, tableName) + } + t.Logf("=== exists: %v, tableName: %v, rows: %v", exists, tableName, foundRows) + if exists == expectTableToExist { + // expectNumRows < 0 means "don't care" + if expectNumRows < 0 || (expectNumRows == foundRows) { + // All conditions are met + return + } + } + } + select { + case <-ticker.C: + case <-ctx.Done(): + assert.Failf(t, "timeout in validateAnyState", " waiting for any of these states: %v, expecting rows: %v", states, expectNumRows) + return } - // Now that the table is validated, we can drop it - dropTable(t, tableName) - } - if exists == expectTableToExist { - // condition met - return } - } - assert.Failf(t, "could not match any of the states", "states=%v", states) + }) } // dropTable drops a table @@ -266,10 +306,10 @@ func TestCapability(t *testing.T) { mysqlVersion := onlineddl.GetMySQLVersion(t, clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()) require.NotEmpty(t, mysqlVersion) - _, capableOf, _ := mysql.GetFlavor(mysqlVersion, nil) + capableOf := mysql.ServerVersionCapableOf(mysqlVersion) require.NotNil(t, capableOf) var err error - fastDropTable, err = capableOf(mysql.FastDropTableFlavorCapability) + fastDropTable, err = capableOf(capabilities.FastDropTableFlavorCapability) require.NoError(t, err) } @@ -279,108 +319,137 @@ func TestPopulateTable(t *testing.T) { validateTableDoesNotExist(t, "no_such_table") } +func generateRenameStatement(newFormat bool, fromTableName string, state schema.TableGCState, tm time.Time) (statement string, toTableName string, err error) { + if newFormat { + return schema.GenerateRenameStatement(fromTableName, state, tm) + } + return schema.GenerateRenameStatementOldFormat(fromTableName, state, tm) +} + func TestHold(t *testing.T) { - populateTable(t) - query, tableName, err := schema.GenerateRenameStatement("t1", schema.HoldTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) - assert.NoError(t, err) + for _, newNameFormat := range []bool{false, true} { + t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) { + populateTable(t) + query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.HoldTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) + assert.NoError(t, err) - _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) - assert.NoError(t, err) + _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) + assert.NoError(t, err) - validateTableDoesNotExist(t, "t1") - validateTableExists(t, tableName) + validateTableDoesNotExist(t, "t1") + validateTableExists(t, tableName) - time.Sleep(tableTransitionExpiration / 2) - { - // Table was created with +10s timestamp, so it should still exist - validateTableExists(t, tableName) + time.Sleep(tableTransitionExpiration / 2) + { + // Table was created with +10s timestamp, so it should still exist + validateTableExists(t, tableName) - checkTableRows(t, tableName, 1024) - } + checkTableRows(t, tableName, 1024) + } - time.Sleep(tableTransitionExpiration) - // We're now both beyond table's timestamp as well as a tableGC interval - validateTableDoesNotExist(t, tableName) - if fastDropTable { - validateAnyState(t, -1, schema.DropTableGCState, schema.TableDroppedGCState) - } else { - validateAnyState(t, -1, schema.PurgeTableGCState, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState) + time.Sleep(tableTransitionExpiration) + // We're now both beyond table's timestamp as well as a tableGC interval + validateTableDoesNotExist(t, tableName) + if fastDropTable { + validateAnyState(t, -1, schema.DropTableGCState, schema.TableDroppedGCState) + } else { + validateAnyState(t, -1, schema.PurgeTableGCState, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState) + } + }) } } func TestEvac(t *testing.T) { - populateTable(t) - query, tableName, err := schema.GenerateRenameStatement("t1", schema.EvacTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) - assert.NoError(t, err) - - _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) - assert.NoError(t, err) - - validateTableDoesNotExist(t, "t1") - - time.Sleep(tableTransitionExpiration / 2) - { - // Table was created with +10s timestamp, so it should still exist - if fastDropTable { - // EVAC state is skipped in mysql 8.0.23 and beyond - validateTableDoesNotExist(t, tableName) - } else { - validateTableExists(t, tableName) - checkTableRows(t, tableName, 1024) - } + for _, newNameFormat := range []bool{false, true} { + t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) { + var tableName string + t.Run("setting up EVAC table", func(t *testing.T) { + populateTable(t) + var query string + var err error + query, tableName, err = generateRenameStatement(newNameFormat, "t1", schema.EvacTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) + assert.NoError(t, err) + + _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) + assert.NoError(t, err) + + validateTableDoesNotExist(t, "t1") + }) + + t.Run("validating before expiration", func(t *testing.T) { + time.Sleep(tableTransitionExpiration / 2) + // Table was created with +10s timestamp, so it should still exist + if fastDropTable { + // EVAC state is skipped in mysql 8.0.23 and beyond + validateTableDoesNotExist(t, tableName) + } else { + validateTableExists(t, tableName) + checkTableRows(t, tableName, 1024) + } + }) + + t.Run("validating rows evacuated", func(t *testing.T) { + // We're now both beyond table's timestamp as well as a tableGC interval + validateTableDoesNotExist(t, tableName) + // Table should be renamed as _vt_DROP_... and then dropped! + validateAnyState(t, 0, schema.DropTableGCState, schema.TableDroppedGCState) + }) + }) } - - time.Sleep(tableTransitionExpiration) - // We're now both beyond table's timestamp as well as a tableGC interval - validateTableDoesNotExist(t, tableName) - // Table should be renamed as _vt_DROP_... and then dropped! - validateAnyState(t, 0, schema.DropTableGCState, schema.TableDroppedGCState) } func TestDrop(t *testing.T) { - populateTable(t) - query, tableName, err := schema.GenerateRenameStatement("t1", schema.DropTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) - assert.NoError(t, err) + for _, newNameFormat := range []bool{false, true} { + t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) { + populateTable(t) + query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.DropTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) + assert.NoError(t, err) - _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) - assert.NoError(t, err) + _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) + assert.NoError(t, err) - validateTableDoesNotExist(t, "t1") + validateTableDoesNotExist(t, "t1") - time.Sleep(tableTransitionExpiration) - time.Sleep(2 * gcCheckInterval) - // We're now both beyond table's timestamp as well as a tableGC interval - validateTableDoesNotExist(t, tableName) + time.Sleep(tableTransitionExpiration) + time.Sleep(2 * gcCheckInterval) + // We're now both beyond table's timestamp as well as a tableGC interval + validateTableDoesNotExist(t, tableName) + }) + } } func TestPurge(t *testing.T) { - populateTable(t) - query, tableName, err := schema.GenerateRenameStatement("t1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) - require.NoError(t, err) + for _, newNameFormat := range []bool{false, true} { + t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) { + populateTable(t) + query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) + require.NoError(t, err) - _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) - require.NoError(t, err) + _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) + require.NoError(t, err) - validateTableDoesNotExist(t, "t1") - if !fastDropTable { - validateTableExists(t, tableName) - checkTableRows(t, tableName, 1024) - } - if !fastDropTable { - time.Sleep(5 * gcPurgeCheckInterval) // wait for table to be purged - } - validateTableDoesNotExist(t, tableName) // whether purged or not, table should at some point transition to next state - if fastDropTable { - // if MySQL supports fast DROP TABLE, TableGC completely skips the PURGE state. Rows are not purged. - validateAnyState(t, 1024, schema.DropTableGCState, schema.TableDroppedGCState) - } else { - validateAnyState(t, 0, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState) + validateTableDoesNotExist(t, "t1") + if !fastDropTable { + validateTableExists(t, tableName) + checkTableRows(t, tableName, 1024) + } + if !fastDropTable { + time.Sleep(5 * gcPurgeCheckInterval) // wait for table to be purged + } + validateTableDoesNotExist(t, tableName) // whether purged or not, table should at some point transition to next state + if fastDropTable { + // if MySQL supports fast DROP TABLE, TableGC completely skips the PURGE state. Rows are not purged. + validateAnyState(t, 1024, schema.DropTableGCState, schema.TableDroppedGCState) + } else { + validateAnyState(t, 0, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState) + } + }) } } func TestPurgeView(t *testing.T) { populateTable(t) - query, tableName, err := schema.GenerateRenameStatement("v1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) + query, tableName, err := generateRenameStatement(true, "v1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration)) require.NoError(t, err) _, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true) diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go index 4fe5a70d125..6212b4a418b 100644 --- a/go/test/endtoend/tabletmanager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -82,7 +82,7 @@ func TestResetReplicationParameters(t *testing.T) { require.NoError(t, err) // Set a replication source on the tablet and start replication - _, err = tablet.VttabletProcess.QueryTablet("stop slave;change master to master_host = 'localhost', master_port = 123;start slave;", keyspaceName, false) + err = tablet.VttabletProcess.QueryTabletMultiple([]string{"stop slave", "change master to master_host = 'localhost', master_port = 123", "start slave"}, keyspaceName, false) require.NoError(t, err) // Check the replica status. diff --git a/go/test/endtoend/topoconncache/main_test.go b/go/test/endtoend/topoconncache/main_test.go index 7cfea8839b0..4c17481ec84 100644 --- a/go/test/endtoend/topoconncache/main_test.go +++ b/go/test/endtoend/topoconncache/main_test.go @@ -48,8 +48,6 @@ var ( ) Engine=InnoDB ` commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", diff --git a/go/test/endtoend/utils/mysql.go b/go/test/endtoend/utils/mysql.go index de8ce40f992..41a70e2dfa4 100644 --- a/go/test/endtoend/utils/mysql.go +++ b/go/test/endtoend/utils/mysql.go @@ -23,25 +23,39 @@ import ( "os" "path" "testing" + "time" "github.com/stretchr/testify/assert" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" ) +const mysqlShutdownTimeout = 1 * time.Minute + // NewMySQL creates a new MySQL server using the local mysqld binary. The name of the database // will be set to `dbName`. SQL queries that need to be executed on the new MySQL instance // can be passed through the `schemaSQL` argument. // The mysql.ConnParams to connect to the new database is returned, along with a function to // teardown the database. func NewMySQL(cluster *cluster.LocalProcessCluster, dbName string, schemaSQL ...string) (mysql.ConnParams, func(), error) { - mysqlParam, _, closer, error := NewMySQLWithMysqld(cluster.GetAndReservePort(), cluster.Hostname, dbName, schemaSQL...) + // Even though we receive schemaSQL as a variadic argument, we ensure to further split it into singular statements. + parser := sqlparser.NewTestParser() + var sqls []string + for _, sql := range schemaSQL { + split, err := parser.SplitStatementToPieces(sql) + if err != nil { + return mysql.ConnParams{}, nil, err + } + sqls = append(sqls, split...) + } + mysqlParam, _, closer, error := NewMySQLWithMysqld(cluster.GetAndReservePort(), cluster.Hostname, dbName, sqls...) return mysqlParam, closer, error } @@ -58,7 +72,7 @@ func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int) ( var cfg dbconfigs.DBConfigs // ensure the DBA username is 'root' instead of the system's default username so that mysqladmin can shutdown cfg.Dba.User = "root" - cfg.InitWithSocket(mycnf.SocketFile) + cfg.InitWithSocket(mycnf.SocketFile, collations.MySQL8()) return mysqlctl.NewMysqld(&cfg), mycnf, nil } @@ -96,7 +110,7 @@ func NewMySQLWithMysqld(port int, hostname, dbName string, schemaSQL ...string) } return params, mysqld, func() { ctx := context.Background() - _ = mysqld.Teardown(ctx, mycnf, true) + _ = mysqld.Teardown(ctx, mycnf, true, mysqlShutdownTimeout) }, nil } @@ -155,7 +169,9 @@ func prepareMySQLWithSchema(params mysql.ConnParams, sql string) error { return nil } -func compareVitessAndMySQLResults(t *testing.T, query string, vtConn *mysql.Conn, vtQr, mysqlQr *sqltypes.Result, compareColumns bool) error { +func compareVitessAndMySQLResults(t *testing.T, query string, vtConn *mysql.Conn, vtQr, mysqlQr *sqltypes.Result, compareColumnNames bool) error { + t.Helper() + if vtQr == nil && mysqlQr == nil { return nil } @@ -168,29 +184,30 @@ func compareVitessAndMySQLResults(t *testing.T, query string, vtConn *mysql.Conn return errors.New("MySQL result is 'nil' while Vitess' is not.\n") } - var errStr string - if compareColumns { - vtColCount := len(vtQr.Fields) - myColCount := len(mysqlQr.Fields) - if vtColCount > 0 && myColCount > 0 { - if vtColCount != myColCount { - t.Errorf("column count does not match: %d vs %d", vtColCount, myColCount) - errStr += fmt.Sprintf("column count does not match: %d vs %d\n", vtColCount, myColCount) - } - - var vtCols []string - var myCols []string - for i, vtField := range vtQr.Fields { - vtCols = append(vtCols, vtField.Name) - myCols = append(myCols, mysqlQr.Fields[i].Name) - } - if !assert.Equal(t, myCols, vtCols, "column names do not match - the expected values are what mysql produced") { - errStr += "column names do not match - the expected values are what mysql produced\n" - errStr += fmt.Sprintf("Not equal: \nexpected: %v\nactual: %v\n", myCols, vtCols) - } + vtColCount := len(vtQr.Fields) + myColCount := len(mysqlQr.Fields) + + if vtColCount != myColCount { + t.Errorf("column count does not match: %d vs %d", vtColCount, myColCount) + } + + if vtColCount > 0 { + var vtCols []string + var myCols []string + for i, vtField := range vtQr.Fields { + myField := mysqlQr.Fields[i] + checkFields(t, myField.Name, vtField, myField) + + vtCols = append(vtCols, vtField.Name) + myCols = append(myCols, myField.Name) + } + + if compareColumnNames && !assert.Equal(t, myCols, vtCols, "column names do not match - the expected values are what mysql produced") { + t.Errorf("column names do not match - the expected values are what mysql produced\nNot equal: \nexpected: %v\nactual: %v\n", myCols, vtCols) } } - stmt, err := sqlparser.Parse(query) + + stmt, err := sqlparser.NewTestParser().Parse(query) if err != nil { t.Error(err) return err @@ -204,7 +221,7 @@ func compareVitessAndMySQLResults(t *testing.T, query string, vtConn *mysql.Conn return nil } - errStr += "Query (" + query + ") results mismatched.\nVitess Results:\n" + errStr := "Query (" + query + ") results mismatched.\nVitess Results:\n" for _, row := range vtQr.Rows { errStr += fmt.Sprintf("%s\n", row) } @@ -224,6 +241,20 @@ func compareVitessAndMySQLResults(t *testing.T, query string, vtConn *mysql.Conn return errors.New(errStr) } +func checkFields(t *testing.T, columnName string, vtField, myField *querypb.Field) { + t.Helper() + if vtField.Type != myField.Type { + t.Errorf("for column %s field types do not match\nNot equal: \nMySQL: %v\nVitess: %v\n", columnName, myField.Type.String(), vtField.Type.String()) + } + + // starting in Vitess 20, decimal types are properly sized in their field information + if BinaryIsAtLeastAtVersion(20, "vtgate") && vtField.Type == sqltypes.Decimal { + if vtField.Decimals != myField.Decimals { + t.Errorf("for column %s field decimals count do not match\nNot equal: \nMySQL: %v\nVitess: %v\n", columnName, myField.Decimals, vtField.Decimals) + } + } +} + func compareVitessAndMySQLErrors(t *testing.T, vtErr, mysqlErr error) { if vtErr != nil && mysqlErr != nil || vtErr == nil && mysqlErr == nil { return diff --git a/go/test/endtoend/utils/utils.go b/go/test/endtoend/utils/utils.go index fa270ba30a0..d9e94911e30 100644 --- a/go/test/endtoend/utils/utils.go +++ b/go/test/endtoend/utils/utils.go @@ -154,6 +154,15 @@ func Exec(t testing.TB, conn *mysql.Conn, query string) *sqltypes.Result { return qr } +// ExecMulti executes the given (potential multi) queries using the given connection. +// The test fails if any of the queries produces an error +func ExecMulti(t testing.TB, conn *mysql.Conn, query string) error { + t.Helper() + err := conn.ExecuteFetchMultiDrain(query) + require.NoError(t, err, "for query: "+query) + return err +} + // ExecCompareMySQL executes the given query against both Vitess and MySQL and compares // the two result set. If there is a mismatch, the difference will be printed and the // test will fail. If the query produces an error in either Vitess or MySQL, the test @@ -236,16 +245,17 @@ func WaitForAuthoritative(t *testing.T, ks, tbl string, readVSchema func() (*int case <-timeout: return fmt.Errorf("schema tracking didn't mark table t2 as authoritative until timeout") default: - time.Sleep(1 * time.Second) res, err := readVSchema() require.NoError(t, err, res) t2Map := getTableT2Map(res, ks, tbl) authoritative, fieldPresent := t2Map["column_list_authoritative"] if !fieldPresent { + time.Sleep(100 * time.Millisecond) continue } authoritativeBool, isBool := authoritative.(bool) if !isBool || !authoritativeBool { + time.Sleep(100 * time.Millisecond) continue } return nil @@ -255,58 +265,78 @@ func WaitForAuthoritative(t *testing.T, ks, tbl string, readVSchema func() (*int // WaitForKsError waits for the ks error field to be populated and returns it. func WaitForKsError(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string) string { + var errString string + WaitForVschemaCondition(t, vtgateProcess, ks, func(t *testing.T, keyspace map[string]interface{}) bool { + ksErr, fieldPresent := keyspace["error"] + if !fieldPresent { + return false + } + var ok bool + errString, ok = ksErr.(string) + return ok + }) + return errString +} + +// WaitForVschemaCondition waits for the condition to be true +func WaitForVschemaCondition(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string, conditionMet func(t *testing.T, keyspace map[string]interface{}) bool) { timeout := time.After(60 * time.Second) for { select { case <-timeout: - t.Fatalf("schema tracking did not find error in '%s'", ks) - return "" + t.Fatalf("schema tracking did not met the condition within the time for keyspace: %s", ks) default: - time.Sleep(1 * time.Second) res, err := vtgateProcess.ReadVSchema() require.NoError(t, err, res) kss := convertToMap(*res)["keyspaces"] ksMap := convertToMap(convertToMap(kss)[ks]) - ksErr, fieldPresent := ksMap["error"] - if !fieldPresent { - break - } - errString, isErr := ksErr.(string) - if !isErr { - break + if conditionMet(t, ksMap) { + return } - return errString + time.Sleep(100 * time.Millisecond) } } } +// WaitForTableDeletions waits for a table to be deleted +func WaitForTableDeletions(ctx context.Context, t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl string) { + WaitForVschemaCondition(t, vtgateProcess, ks, func(t *testing.T, keyspace map[string]interface{}) bool { + tablesMap := keyspace["tables"] + _, isPresent := convertToMap(tablesMap)[tbl] + return !isPresent + }) +} + // WaitForColumn waits for a table's column to be present -func WaitForColumn(t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl, col string) error { +func WaitForColumn(t testing.TB, vtgateProcess cluster.VtgateProcess, ks, tbl, col string) error { timeout := time.After(60 * time.Second) for { select { case <-timeout: return fmt.Errorf("schema tracking did not find column '%s' in table '%s'", col, tbl) default: - time.Sleep(1 * time.Second) res, err := vtgateProcess.ReadVSchema() require.NoError(t, err, res) t2Map := getTableT2Map(res, ks, tbl) authoritative, fieldPresent := t2Map["column_list_authoritative"] if !fieldPresent { - break + time.Sleep(100 * time.Millisecond) + continue } authoritativeBool, isBool := authoritative.(bool) if !isBool || !authoritativeBool { - break + time.Sleep(100 * time.Millisecond) + continue } colMap, exists := t2Map["columns"] if !exists { - break + time.Sleep(100 * time.Millisecond) + continue } colList, isSlice := colMap.([]interface{}) if !isSlice { - break + time.Sleep(100 * time.Millisecond) + continue } for _, c := range colList { colDef, isMap := c.(map[string]interface{}) @@ -317,6 +347,7 @@ func WaitForColumn(t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl, c return nil } } + time.Sleep(100 * time.Millisecond) } } } @@ -330,7 +361,10 @@ func getTableT2Map(res *interface{}, ks, tbl string) map[string]interface{} { } func convertToMap(input interface{}) map[string]interface{} { - output := input.(map[string]interface{}) + output, ok := input.(map[string]interface{}) + if !ok { + return make(map[string]interface{}) + } return output } diff --git a/go/test/endtoend/vault/vault_test.go b/go/test/endtoend/vault/vault_test.go index 9bc5b9cb977..684a374707d 100644 --- a/go/test/endtoend/vault/vault_test.go +++ b/go/test/endtoend/vault/vault_test.go @@ -56,15 +56,13 @@ var ( vtgateUser = "vtgate_user" vtgatePassword = "password123" commonTabletArg = []string{ - "--vreplication_healthcheck_topology_refresh", "1s", - "--vreplication_healthcheck_retry_delay", "1s", "--vreplication_retry_delay", "1s", "--degraded_threshold", "5s", "--lock_tables_timeout", "5s", "--watch_replication_stream", // Frequently reload schema, generating some tablet traffic, // so we can speed up token refresh - "--queryserver-config-schema-reload-time", "5", + "--queryserver-config-schema-reload-time", "5s", "--serving_state_grace_period", "1s"} vaultTabletArg = []string{ "--db-credentials-server", "vault", diff --git a/go/test/endtoend/vreplication/cluster_test.go b/go/test/endtoend/vreplication/cluster_test.go index af93ac40726..7d22d063945 100644 --- a/go/test/endtoend/vreplication/cluster_test.go +++ b/go/test/endtoend/vreplication/cluster_test.go @@ -30,6 +30,8 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/vttablet" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/mysqlctl" @@ -87,11 +89,33 @@ type ClusterConfig struct { vreplicationCompressGTID bool } +// enableGTIDCompression enables GTID compression for the cluster and returns a function +// that can be used to disable it in a defer. +func (cc *ClusterConfig) enableGTIDCompression() func() { + cc.vreplicationCompressGTID = true + return func() { + cc.vreplicationCompressGTID = false + } +} + +// setAllVTTabletExperimentalFlags sets all the experimental flags for vttablet and returns a function +// that can be used to reset them in a defer. +func setAllVTTabletExperimentalFlags() func() { + experimentalArgs := fmt.Sprintf("--vreplication_experimental_flags=%d", + vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching) + oldArgs := extraVTTabletArgs + extraVTTabletArgs = append(extraVTTabletArgs, experimentalArgs) + return func() { + extraVTTabletArgs = oldArgs + } +} + // VitessCluster represents all components within the test cluster type VitessCluster struct { t *testing.T ClusterConfig *ClusterConfig Name string + CellNames []string Cells map[string]*Cell Topo *cluster.TopoProcess Vtctld *cluster.VtctldProcess @@ -115,6 +139,9 @@ type Keyspace struct { VSchema string Schema string SidecarDBName string + + numReplicas int + numRDOnly int } // Shard represents a Vitess shard in a keyspace @@ -332,9 +359,28 @@ func init() { externalClusterConfig = getClusterConfig(1, mainVtDataRoot+"/ext") } +type clusterOptions struct { + cells []string + clusterConfig *ClusterConfig +} + +func getClusterOptions(opts *clusterOptions) *clusterOptions { + if opts == nil { + opts = &clusterOptions{} + } + if opts.cells == nil { + opts.cells = []string{"zone1"} + } + if opts.clusterConfig == nil { + opts.clusterConfig = mainClusterConfig + } + return opts +} + // NewVitessCluster starts a basic cluster with vtgate, vtctld and the topo -func NewVitessCluster(t *testing.T, name string, cellNames []string, clusterConfig *ClusterConfig) *VitessCluster { - vc := &VitessCluster{t: t, Name: name, Cells: make(map[string]*Cell), ClusterConfig: clusterConfig} +func NewVitessCluster(t *testing.T, opts *clusterOptions) *VitessCluster { + opts = getClusterOptions(opts) + vc := &VitessCluster{t: t, Name: t.Name(), CellNames: opts.cells, Cells: make(map[string]*Cell), ClusterConfig: opts.clusterConfig} require.NotNil(t, vc) vc.CleanupDataroot(t, true) @@ -346,32 +392,46 @@ func NewVitessCluster(t *testing.T, name string, cellNames []string, clusterConf err := topo.ManageTopoDir("mkdir", "/vitess/global") require.NoError(t, err) vc.Topo = topo - for _, cellName := range cellNames { + for _, cellName := range opts.cells { err := topo.ManageTopoDir("mkdir", "/vitess/"+cellName) require.NoError(t, err) } - vtctld := cluster.VtctldProcessInstance(vc.ClusterConfig.vtctldPort, vc.ClusterConfig.vtctldGrpcPort, + vc.setupVtctld() + vc.setupVtctl() + vc.setupVtctlClient() + vc.setupVtctldClient() + + return vc +} + +func (vc *VitessCluster) setupVtctld() { + vc.Vtctld = cluster.VtctldProcessInstance(vc.ClusterConfig.vtctldPort, vc.ClusterConfig.vtctldGrpcPort, vc.ClusterConfig.topoPort, vc.ClusterConfig.hostname, vc.ClusterConfig.tmpDir) - vc.Vtctld = vtctld - require.NotNil(t, vc.Vtctld) + require.NotNil(vc.t, vc.Vtctld) // use first cell as `-cell` - vc.Vtctld.Setup(cellNames[0], extraVtctldArgs...) + vc.Vtctld.Setup(vc.CellNames[0], extraVtctldArgs...) +} +func (vc *VitessCluster) setupVtctl() { vc.Vtctl = cluster.VtctlProcessInstance(vc.ClusterConfig.topoPort, vc.ClusterConfig.hostname) - require.NotNil(t, vc.Vtctl) - for _, cellName := range cellNames { + require.NotNil(vc.t, vc.Vtctl) + for _, cellName := range vc.CellNames { vc.Vtctl.AddCellInfo(cellName) - cell, err := vc.AddCell(t, cellName) - require.NoError(t, err) - require.NotNil(t, cell) + cell, err := vc.AddCell(vc.t, cellName) + require.NoError(vc.t, err) + require.NotNil(vc.t, cell) } +} +func (vc *VitessCluster) setupVtctlClient() { vc.VtctlClient = cluster.VtctlClientProcessInstance(vc.ClusterConfig.hostname, vc.Vtctld.GrpcPort, vc.ClusterConfig.tmpDir) - require.NotNil(t, vc.VtctlClient) + require.NotNil(vc.t, vc.VtctlClient) +} + +func (vc *VitessCluster) setupVtctldClient() { vc.VtctldClient = cluster.VtctldClientProcessInstance(vc.ClusterConfig.hostname, vc.Vtctld.GrpcPort, vc.ClusterConfig.tmpDir) - require.NotNil(t, vc.VtctldClient) - return vc + require.NotNil(vc.t, vc.VtctldClient) } // CleanupDataroot deletes the vtdataroot directory. Since we run multiple tests sequentially in a single CI test shard, @@ -385,8 +445,19 @@ func (vc *VitessCluster) CleanupDataroot(t *testing.T, recreate bool) { return } dir := vc.ClusterConfig.vtdataroot - log.Infof("Deleting vtdataroot %s", dir) - err := os.RemoveAll(dir) + // The directory cleanup sometimes fails with a "directory not empty" error as + // everything in the test is shutting down and cleaning up. So we retry a few + // times to deal with that non-problematic and ephemeral issue. + var err error + retries := 3 + for i := 1; i <= retries; i++ { + if err = os.RemoveAll(dir); err == nil { + log.Infof("Deleted vtdataroot %q", dir) + break + } + log.Errorf("Failed to delete vtdataroot (attempt %d of %d) %q: %v", i, retries, dir, err) + time.Sleep(1 * time.Second) + } require.NoError(t, err) if recreate { err = os.Mkdir(dir, 0700) @@ -419,8 +490,14 @@ func (vc *VitessCluster) AddKeyspace(t *testing.T, cells []*Cell, ksName string, cell.Keyspaces[ksName] = keyspace cellsToWatch = cellsToWatch + cell.Name } - require.NoError(t, vc.AddShards(t, cells, keyspace, shards, numReplicas, numRdonly, tabletIDBase, opts)) + for _, cell := range cells { + if len(cell.Vtgates) == 0 { + log.Infof("Starting vtgate") + vc.StartVtgate(t, cell, cellsToWatch) + } + } + require.NoError(t, vc.AddShards(t, cells, keyspace, shards, numReplicas, numRdonly, tabletIDBase, opts)) if schema != "" { if err := vc.VtctlClient.ApplySchema(ksName, schema); err != nil { t.Fatalf(err.Error()) @@ -433,12 +510,6 @@ func (vc *VitessCluster) AddKeyspace(t *testing.T, cells []*Cell, ksName string, } } keyspace.VSchema = vschema - for _, cell := range cells { - if len(cell.Vtgates) == 0 { - log.Infof("Starting vtgate") - vc.StartVtgate(t, cell, cellsToWatch) - } - } err = vc.VtctlClient.ExecuteCommand("RebuildKeyspaceGraph", ksName) require.NoError(t, err) @@ -450,7 +521,7 @@ func (vc *VitessCluster) AddTablet(t testing.TB, cell *Cell, keyspace *Keyspace, tablet := &Tablet{} options := []string{ - "--queryserver-config-schema-reload-time", "5", + "--queryserver-config-schema-reload-time", "5s", "--heartbeat_on_demand_duration", "5s", "--heartbeat_interval", "250ms", } // FIXME: for multi-cell initial schema doesn't seem to load without "--queryserver-config-schema-reload-time" @@ -514,11 +585,11 @@ func (vc *VitessCluster) AddShards(t *testing.T, cells []*Cell, keyspace *Keyspa } } - arrNames := strings.Split(names, ",") - log.Infof("Addshards got %d shards with %+v", len(arrNames), arrNames) - isSharded := len(arrNames) > 1 + shardNames := strings.Split(names, ",") + log.Infof("Addshards got %d shards with %+v", len(shardNames), shardNames) + isSharded := len(shardNames) > 1 primaryTabletUID := 0 - for ind, shardName := range arrNames { + for ind, shardName := range shardNames { tabletID := tabletIDBase + ind*100 tabletIndex := 0 shard := &Shard{Name: shardName, IsSharded: isSharded, Tablets: make(map[string]*Tablet, 1)} @@ -541,10 +612,10 @@ func (vc *VitessCluster) AddShards(t *testing.T, cells []*Cell, keyspace *Keyspa require.NoError(t, err) require.NotNil(t, primary) tabletIndex++ - primary.Vttablet.VreplicationTabletType = "PRIMARY" tablets = append(tablets, primary) dbProcesses = append(dbProcesses, proc) primaryTabletUID = primary.Vttablet.TabletUID + primary.Vttablet.IsPrimary = true } for i := 0; i < numReplicas; i++ { @@ -616,6 +687,12 @@ func (vc *VitessCluster) AddShards(t *testing.T, cells []*Cell, keyspace *Keyspa if err := tablet.Vttablet.Setup(); err != nil { t.Fatalf(err.Error()) } + // Set time_zone to UTC for all tablets. Without this it fails locally on some MacOS setups. + query := "SET GLOBAL time_zone = '+00:00';" + qr, err := tablet.Vttablet.QueryTablet(query, tablet.Vttablet.Keyspace, false) + if err != nil { + t.Fatalf("failed to set time_zone: %v, output: %v", err, qr) + } } } require.NotEqual(t, 0, primaryTabletUID, "Should have created a primary tablet") @@ -624,12 +701,45 @@ func (vc *VitessCluster) AddShards(t *testing.T, cells []*Cell, keyspace *Keyspa log.Infof("Finished creating shard %s", shard.Name) } + for _, shard := range shardNames { + require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, keyspace.Name, shard)) + } + + waitTimeout := 30 * time.Second + vtgate := cells[0].Vtgates[0] + for _, shardName := range shardNames { + shard := keyspace.Shards[shardName] + numReplicas, numRDOnly := 0, 0 + for _, tablet := range shard.Tablets { + switch strings.ToLower(tablet.Vttablet.TabletType) { + case "replica": + numReplicas++ + case "rdonly": + numRDOnly++ + } + } + numReplicas-- // account for primary, which also has replica type + if err := vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", keyspace.Name, shard.Name), 1, waitTimeout); err != nil { + return err + } + if numReplicas > 0 { + if err := vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace.Name, shard.Name), numReplicas, waitTimeout); err != nil { + return err + } + } + if numRDOnly > 0 { + if err := vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspace.Name, shard.Name), numRDOnly, waitTimeout); err != nil { + return err + } + } + } err := vc.VtctlClient.ExecuteCommand("RebuildKeyspaceGraph", keyspace.Name) require.NoError(t, err) log.Infof("Waiting for throttler config to be applied on all shards") - for _, shard := range keyspace.Shards { + for _, shardName := range shardNames { + shard := keyspace.Shards[shardName] for _, tablet := range shard.Tablets { clusterTablet := &cluster.Vttablet{ Alias: tablet.Name, @@ -751,7 +861,7 @@ func (vc *VitessCluster) teardown() { } // TearDown brings down a cluster, deleting processes, removing topo keys -func (vc *VitessCluster) TearDown(t *testing.T) { +func (vc *VitessCluster) TearDown() { if debugMode { return } @@ -768,7 +878,7 @@ func (vc *VitessCluster) TearDown(t *testing.T) { } // some processes seem to hang around for a bit time.Sleep(5 * time.Second) - vc.CleanupDataroot(t, false) + vc.CleanupDataroot(vc.t, false) } func (vc *VitessCluster) getVttabletsInKeyspace(t *testing.T, cell *Cell, ksName string, tabletType string) map[string]*cluster.VttabletProcess { @@ -776,7 +886,7 @@ func (vc *VitessCluster) getVttabletsInKeyspace(t *testing.T, cell *Cell, ksName tablets := make(map[string]*cluster.VttabletProcess) for _, shard := range keyspace.Shards { for _, tablet := range shard.Tablets { - if tablet.Vttablet.GetTabletStatus() == "SERVING" && strings.EqualFold(tablet.Vttablet.VreplicationTabletType, tabletType) { + if tablet.Vttablet.GetTabletStatus() == "SERVING" { log.Infof("Serving status of tablet %s is %s, %s", tablet.Name, tablet.Vttablet.ServingStatus, tablet.Vttablet.GetTabletStatus()) tablets[tablet.Name] = tablet.Vttablet } @@ -796,13 +906,13 @@ func (vc *VitessCluster) getPrimaryTablet(t *testing.T, ksName, shardName string continue } for _, tablet := range shard.Tablets { - if tablet.Vttablet.GetTabletStatus() == "SERVING" && strings.EqualFold(tablet.Vttablet.VreplicationTabletType, "primary") { + if tablet.Vttablet.IsPrimary { return tablet.Vttablet } } } } - require.FailNow(t, "no primary found for %s:%s", ksName, shardName) + require.FailNow(t, "no primary found", "keyspace %s, shard %s", ksName, shardName) return nil } @@ -810,6 +920,13 @@ func (vc *VitessCluster) GetVTGateConn(t *testing.T) *mysql.Conn { return getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) } +func getVTGateConn() (*mysql.Conn, func()) { + vtgateConn := vc.GetVTGateConn(vc.t) + return vtgateConn, func() { + vtgateConn.Close() + } +} + func (vc *VitessCluster) startQuery(t *testing.T, query string) (func(t *testing.T), func(t *testing.T)) { conn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) _, err := conn.ExecuteFetch("begin", 1000, false) diff --git a/go/test/endtoend/vreplication/config_test.go b/go/test/endtoend/vreplication/config_test.go index 0e430548a13..62f20f36e80 100644 --- a/go/test/endtoend/vreplication/config_test.go +++ b/go/test/endtoend/vreplication/config_test.go @@ -60,6 +60,7 @@ create table geom_tbl (id int, g geometry, p point, ls linestring, pg polygon, m create table ` + "`blüb_tbl`" + ` (id int, val1 varchar(20), ` + "`blöb1`" + ` blob, val2 varbinary(20), ` + "`bl@b2`" + ` longblob, txt1 text, blb3 tinyblob, txt2 longtext, blb4 mediumblob, primary key(id)); create table reftable (id int, val1 varchar(20), primary key(id), key(val1)); create table loadtest (id int, name varchar(256), primary key(id), key(name)); +create table nopk (name varchar(128), age int unsigned); ` // These should always be ignored in vreplication internalSchema = ` @@ -94,6 +95,7 @@ create table loadtest (id int, name varchar(256), primary key(id), key(name)); "db_order_test": {}, "vdiff_order": {}, "datze": {}, + "nopk": {}, "reftable": { "type": "reference" } @@ -216,6 +218,14 @@ create table loadtest (id int, name varchar(256), primary key(id), key(name)); } ] }, + "nopk": { + "column_vindexes": [ + { + "columns": ["name"], + "name": "unicode_loose_md5" + } + ] + }, "reftable": { "type": "reference" } diff --git a/go/test/endtoend/vreplication/fk_config_test.go b/go/test/endtoend/vreplication/fk_config_test.go index 5b02aeb62bb..822d44105b4 100644 --- a/go/test/endtoend/vreplication/fk_config_test.go +++ b/go/test/endtoend/vreplication/fk_config_test.go @@ -20,16 +20,24 @@ var ( initialFKSchema = ` create table parent(id int, name varchar(128), primary key(id)) engine=innodb; create table child(id int, parent_id int, name varchar(128), primary key(id), foreign key(parent_id) references parent(id) on delete cascade) engine=innodb; +create view vparent as select * from parent; +create table t1(id int, name varchar(128), primary key(id)) engine=innodb; +create table t2(id int, t1id int, name varchar(128), primary key(id), foreign key(t1id) references t1(id) on delete cascade) engine=innodb; ` initialFKData = ` insert into parent values(1, 'parent1'), (2, 'parent2'); -insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32');` +insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32'); +insert into t1 values(1, 't11'), (2, 't12'); +insert into t2 values(1, 1, 't21'), (2, 1, 't22'), (3, 2, 't23'); +` initialFKSourceVSchema = ` { "tables": { "parent": {}, - "child": {} + "child": {}, + "t1": {}, + "t2": {} } } ` @@ -58,6 +66,22 @@ insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32'); "name": "reverse_bits" } ] + }, + "t1": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + } + ] + }, + "t2": { + "column_vindexes": [ + { + "column": "t1id", + "name": "reverse_bits" + } + ] } } } diff --git a/go/test/endtoend/vreplication/fk_ext_load_generator_test.go b/go/test/endtoend/vreplication/fk_ext_load_generator_test.go new file mode 100644 index 00000000000..12b5871781f --- /dev/null +++ b/go/test/endtoend/vreplication/fk_ext_load_generator_test.go @@ -0,0 +1,503 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vreplication + +import ( + "context" + "fmt" + "math/rand" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/log" +) + +const ( + // Only used when debugging tests. + queryLog = "queries.txt" + + LoadGeneratorStateLoading = "loading" + LoadGeneratorStateRunning = "running" + LoadGeneratorStateStopped = "stopped" + + dataLoadTimeout = 1 * time.Minute + tickInterval = 1 * time.Second + queryTimeout = 1 * time.Minute + + getRandomIdQuery = "SELECT id FROM %s.parent ORDER BY RAND() LIMIT 1" + insertQuery = "INSERT INTO %s.parent (id, name) VALUES (%d, 'name-%d')" + updateQuery = "UPDATE %s.parent SET name = 'rename-%d' WHERE id = %d" + deleteQuery = "DELETE FROM %s.parent WHERE id = %d" + insertChildQuery = "INSERT INTO %s.child (id, parent_id) VALUES (%d, %d)" + insertChildQueryOverrideConstraints = "INSERT /*+ SET_VAR(foreign_key_checks=0) */ INTO %s.child (id, parent_id) VALUES (%d, %d)" +) + +// ILoadGenerator is an interface for load generators that we will use to simulate different types of loads. +type ILoadGenerator interface { + Init(ctx context.Context, vc *VitessCluster) // name & description only for logging. + Teardown() + + // "direct", use direct db connection to primary, only for unsharded keyspace. + // or "vtgate" to use vtgate routing. + // Stop() before calling SetDBStrategy(). + SetDBStrategy(direct, keyspace string) + SetOverrideConstraints(allow bool) // true if load generator can insert rows without FK constraints. + + Keyspace() string + DBStrategy() string // direct or vtgate + State() string // state of load generator (stopped, running) + OverrideConstraints() bool // true if load generator can insert rows without FK constraints. + + Load() error // initial load of data. + Start() error // start populating additional data. + Stop() error // stop populating additional data. + + // Implementation will decide which table to wait for extra rows on. + WaitForAdditionalRows(count int) error + // table == "", implementation will decide which table to get rows from, same table as in WaitForAdditionalRows(). + GetRowCount(table string) (int, error) +} + +var lg ILoadGenerator + +var _ ILoadGenerator = (*SimpleLoadGenerator)(nil) + +type LoadGenerator struct { + ctx context.Context + vc *VitessCluster + state string + dbStrategy string + overrideConstraints bool + keyspace string + tables []string +} + +// SimpleLoadGenerator, which has a single parent table and a single child table for which different types +// of DMLs are run. +type SimpleLoadGenerator struct { + LoadGenerator + currentParentId int + currentChildId int + ch chan bool + runCtx context.Context + runCtxCancel context.CancelFunc +} + +func (lg *SimpleLoadGenerator) SetOverrideConstraints(allow bool) { + lg.overrideConstraints = allow +} + +func (lg *SimpleLoadGenerator) OverrideConstraints() bool { + return lg.overrideConstraints +} + +func (lg *SimpleLoadGenerator) GetRowCount(table string) (int, error) { + vtgateConn, err := lg.getVtgateConn(context.Background()) + if err != nil { + return 0, err + } + defer vtgateConn.Close() + return lg.getNumRows(vtgateConn, table), nil +} + +func (lg *SimpleLoadGenerator) getVtgateConn(ctx context.Context) (*mysql.Conn, error) { + vtParams := mysql.ConnParams{ + Host: lg.vc.ClusterConfig.hostname, + Port: lg.vc.ClusterConfig.vtgateMySQLPort, + Uname: "vt_dba", + } + conn, err := mysql.Connect(ctx, &vtParams) + return conn, err +} + +func (lg *SimpleLoadGenerator) getNumRows(vtgateConn *mysql.Conn, table string) int { + t := lg.vc.t + return getRowCount(t, vtgateConn, table) +} + +func (lg *SimpleLoadGenerator) WaitForAdditionalRows(count int) error { + t := lg.vc.t + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() + numRowsStart := lg.getNumRows(vtgateConn, "parent") + shortCtx, cancel := context.WithTimeout(context.Background(), dataLoadTimeout) + defer cancel() + for { + select { + case <-shortCtx.Done(): + t.Fatalf("Timed out waiting for additional rows in %q table", "parent") + default: + numRows := lg.getNumRows(vtgateConn, "parent") + if numRows >= numRowsStart+count { + return nil + } + time.Sleep(tickInterval) + } + } +} + +func (lg *SimpleLoadGenerator) exec(query string) (*sqltypes.Result, error) { + switch lg.dbStrategy { + case "direct": + // direct is expected to be used only for unsharded keyspaces to simulate an unmanaged keyspace + // that proxies to an external database. + primary := lg.vc.getPrimaryTablet(lg.vc.t, lg.keyspace, "0") + qr, err := primary.QueryTablet(query, lg.keyspace, true) + require.NoError(lg.vc.t, err) + return qr, err + case "vtgate": + return lg.execQueryWithRetry(query) + default: + err := fmt.Errorf("invalid dbStrategy: %v", lg.dbStrategy) + return nil, err + } +} + +// When a workflow switches traffic it is possible to get transient errors from vtgate while executing queries +// due to cluster-level changes. isQueryRetryable() checks for such errors so that tests can wait for such changes +// to complete before proceeding. +func isQueryRetryable(err error) bool { + retryableErrorStrings := []string{ + "retry", + "resharded", + "VT13001", + "Lock wait timeout exceeded", + "errno 2003", + } + for _, e := range retryableErrorStrings { + if strings.Contains(err.Error(), e) { + return true + } + } + return false +} + +func (lg *SimpleLoadGenerator) execQueryWithRetry(query string) (*sqltypes.Result, error) { + ctx, cancel := context.WithTimeout(context.Background(), queryTimeout) + defer cancel() + errCh := make(chan error) + qrCh := make(chan *sqltypes.Result) + var vtgateConn *mysql.Conn + go func() { + var qr *sqltypes.Result + var err error + retry := false + for { + select { + case <-ctx.Done(): + errCh <- fmt.Errorf("query %q did not succeed before the timeout of %s", query, queryTimeout) + return + default: + } + if lg.runCtx != nil && lg.runCtx.Err() != nil { + log.Infof("Load generator run context done, query never completed: %q", query) + errCh <- fmt.Errorf("load generator stopped") + return + } + if retry { + time.Sleep(tickInterval) + } + // We need to parse the error as well as the output of vdiff to determine if the error is retryable, since + // sometimes it is observed that we get the error output as part of vdiff output. + vtgateConn, err = lg.getVtgateConn(ctx) + if err != nil { + if !isQueryRetryable(err) { + errCh <- err + return + } + time.Sleep(tickInterval) + continue + } + qr, err = vtgateConn.ExecuteFetch(query, 1000, false) + vtgateConn.Close() + if err == nil { + qrCh <- qr + return + } + if !isQueryRetryable(err) { + errCh <- err + return + } + retry = true + } + }() + select { + case qr := <-qrCh: + return qr, nil + case err := <-errCh: + log.Infof("query %q failed with error %v", query, err) + return nil, err + } +} + +func (lg *SimpleLoadGenerator) Load() error { + lg.state = LoadGeneratorStateLoading + defer func() { lg.state = LoadGeneratorStateStopped }() + log.Infof("Inserting initial FK data") + var queries = []string{ + "insert into parent values(1, 'parent1'), (2, 'parent2');", + "insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32');", + } + for _, query := range queries { + _, err := lg.exec(query) + require.NoError(lg.vc.t, err) + } + log.Infof("Done inserting initial FK data") + return nil +} + +func (lg *SimpleLoadGenerator) Start() error { + if lg.state == LoadGeneratorStateRunning { + log.Infof("Load generator already running") + return nil + } + lg.state = LoadGeneratorStateRunning + go func() { + defer func() { + lg.state = LoadGeneratorStateStopped + log.Infof("Load generator stopped") + }() + lg.runCtx, lg.runCtxCancel = context.WithCancel(lg.ctx) + defer func() { + lg.runCtx = nil + lg.runCtxCancel = nil + }() + t := lg.vc.t + var err error + log.Infof("Load generator starting") + for i := 0; ; i++ { + if i%1000 == 0 { + // Log occasionally to show that the test is still running. + log.Infof("Load simulation iteration %d", i) + } + select { + case <-lg.ctx.Done(): + log.Infof("Load generator context done") + lg.ch <- true + return + case <-lg.runCtx.Done(): + log.Infof("Load generator run context done") + lg.ch <- true + return + default: + } + op := rand.Intn(100) + switch { + case op < 50: // 50% chance to insert + lg.insert() + case op < 80: // 30% chance to update + lg.update() + default: // 20% chance to delete + lg.delete() + } + require.NoError(t, err) + time.Sleep(1 * time.Millisecond) + } + }() + return nil +} + +func (lg *SimpleLoadGenerator) Stop() error { + if lg.state == LoadGeneratorStateStopped { + log.Infof("Load generator already stopped") + return nil + } + if lg.runCtx != nil && lg.runCtxCancel != nil { + log.Infof("Canceling load generator") + lg.runCtxCancel() + } + // Wait for ch to be closed or we hit a timeout. + timeout := vdiffTimeout + select { + case <-lg.ch: + log.Infof("Load generator stopped") + lg.state = LoadGeneratorStateStopped + return nil + case <-time.After(timeout): + log.Infof("Timed out waiting for load generator to stop") + return fmt.Errorf("timed out waiting for load generator to stop") + } +} + +func (lg *SimpleLoadGenerator) Init(ctx context.Context, vc *VitessCluster) { + lg.ctx = ctx + lg.vc = vc + lg.state = LoadGeneratorStateStopped + lg.currentParentId = 100 + lg.currentChildId = 100 + lg.ch = make(chan bool) + lg.tables = []string{"parent", "child"} +} + +func (lg *SimpleLoadGenerator) Teardown() { + // noop +} + +func (lg *SimpleLoadGenerator) SetDBStrategy(strategy, keyspace string) { + lg.dbStrategy = strategy + lg.keyspace = keyspace +} + +func (lg *SimpleLoadGenerator) Keyspace() string { + return lg.keyspace +} + +func (lg *SimpleLoadGenerator) DBStrategy() string { + return lg.dbStrategy +} + +func (lg *SimpleLoadGenerator) State() string { + return lg.state +} + +func isQueryCancelled(err error) bool { + if err == nil { + return false + } + return strings.Contains(err.Error(), "load generator stopped") +} + +func (lg *SimpleLoadGenerator) insert() { + t := lg.vc.t + currentParentId++ + query := fmt.Sprintf(insertQuery, lg.keyspace, currentParentId, currentParentId) + qr, err := lg.exec(query) + if isQueryCancelled(err) { + return + } + require.NoError(t, err) + require.NotNil(t, qr) + // Insert one or more children, some with valid foreign keys, some without. + for i := 0; i < rand.Intn(4)+1; i++ { + currentChildId++ + if i == 3 && lg.overrideConstraints { + query = fmt.Sprintf(insertChildQueryOverrideConstraints, lg.keyspace, currentChildId, currentParentId+1000000) + lg.exec(query) + } else { + query = fmt.Sprintf(insertChildQuery, lg.keyspace, currentChildId, currentParentId) + lg.exec(query) + } + } +} + +func (lg *SimpleLoadGenerator) getRandomId() int64 { + t := lg.vc.t + qr, err := lg.exec(fmt.Sprintf(getRandomIdQuery, lg.keyspace)) + if isQueryCancelled(err) { + return 0 + } + require.NoError(t, err) + require.NotNil(t, qr) + if len(qr.Rows) == 0 { + return 0 + } + id, err := qr.Rows[0][0].ToInt64() + require.NoError(t, err) + return id +} + +func (lg *SimpleLoadGenerator) update() { + id := lg.getRandomId() + if id == 0 { + return + } + updateQuery := fmt.Sprintf(updateQuery, lg.keyspace, id, id) + _, err := lg.exec(updateQuery) + if isQueryCancelled(err) { + return + } + require.NoError(lg.vc.t, err) +} + +func (lg *SimpleLoadGenerator) delete() { + id := lg.getRandomId() + if id == 0 { + return + } + deleteQuery := fmt.Sprintf(deleteQuery, lg.keyspace, id) + _, err := lg.exec(deleteQuery) + if isQueryCancelled(err) { + return + } + require.NoError(lg.vc.t, err) +} + +// FIXME: following three functions are copied over from vtgate test utility functions in +// `go/test/endtoend/utils/utils.go`. +// We will to refactor and then reuse the same functionality from vtgate tests, in the near future. + +func convertToMap(input interface{}) map[string]interface{} { + output := input.(map[string]interface{}) + return output +} + +func getTableT2Map(res *interface{}, ks, tbl string) map[string]interface{} { + step1 := convertToMap(*res)["keyspaces"] + step2 := convertToMap(step1)[ks] + step3 := convertToMap(step2)["tables"] + tblMap := convertToMap(step3)[tbl] + return convertToMap(tblMap) +} + +// waitForColumn waits for a table's column to be present in the vschema because vtgate's foreign key managed mode +// expects the column to be present in the vschema before it can be used in a foreign key constraint. +func waitForColumn(t *testing.T, vtgateProcess *cluster.VtgateProcess, ks, tbl, col string) error { + timeout := time.After(defaultTimeout) + for { + select { + case <-timeout: + return fmt.Errorf("schema tracking did not find column '%s' in table '%s'", col, tbl) + default: + time.Sleep(defaultTick) + res, err := vtgateProcess.ReadVSchema() + require.NoError(t, err, res) + t2Map := getTableT2Map(res, ks, tbl) + authoritative, fieldPresent := t2Map["column_list_authoritative"] + if !fieldPresent { + break + } + authoritativeBool, isBool := authoritative.(bool) + if !isBool || !authoritativeBool { + break + } + colMap, exists := t2Map["columns"] + if !exists { + break + } + colList, isSlice := colMap.([]interface{}) + if !isSlice { + break + } + for _, c := range colList { + colDef, isMap := c.(map[string]interface{}) + if !isMap { + break + } + if colName, exists := colDef["name"]; exists && colName == col { + log.Infof("Found column '%s' in table '%s' for keyspace '%s'", col, tbl, ks) + return nil + } + } + } + } +} diff --git a/go/test/endtoend/vreplication/fk_ext_test.go b/go/test/endtoend/vreplication/fk_ext_test.go new file mode 100644 index 00000000000..401b99360d8 --- /dev/null +++ b/go/test/endtoend/vreplication/fk_ext_test.go @@ -0,0 +1,443 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vreplication + +import ( + "context" + _ "embed" + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/log" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +const ( + shardStatusWaitTimeout = 30 * time.Second +) + +var ( + //go:embed schema/fkext/source_schema.sql + FKExtSourceSchema string + //go:embed schema/fkext/source_vschema.json + FKExtSourceVSchema string + //go:embed schema/fkext/target1_vschema.json + FKExtTarget1VSchema string + //go:embed schema/fkext/target2_vschema.json + FKExtTarget2VSchema string + //go:embed schema/fkext/materialize_schema.sql + FKExtMaterializeSchema string +) + +type fkextConfigType struct { + *ClusterConfig + sourceKeyspaceName string + target1KeyspaceName string + target2KeyspaceName string + cell string +} + +var fkextConfig *fkextConfigType + +func initFKExtConfig(t *testing.T) { + fkextConfig = &fkextConfigType{ + ClusterConfig: mainClusterConfig, + sourceKeyspaceName: "source", + target1KeyspaceName: "target1", + target2KeyspaceName: "target2", + cell: "zone1", + } +} + +/* +TestFKExt is an end-to-end test for validating the foreign key implementation with respect to, both vreplication +flows and vtgate processing of DMLs for tables with foreign key constraints. It currently: +* Sets up a source keyspace, to simulate the external database, with a parent and child table with a foreign key constraint. +* Creates a target keyspace with two shards, the Vitess keyspace, into which the source data is imported. +* Imports the data using MoveTables. This uses the atomic copy flow +to test that we can import data with foreign key constraints and that data is kept consistent even after the copy phase +since the tables continue to have the FK Constraints. +* Creates a new keyspace with two shards, the Vitess keyspace, into which the data is migrated using MoveTables. +* Materializes the parent and child tables into a different keyspace. +* Reshards the keyspace from 2 shards to 3 shards. +* Reshards the keyspace from 3 shards to 1 shard. + +We drop constraints from the tables from some replicas to simulate a replica that is not doing cascades in +innodb, to confirm that vtgate's fkmanaged mode is working properly. +*/ + +func TestFKExt(t *testing.T) { + setSidecarDBName("_vt") + + // Ensure that there are multiple copy phase cycles per table. + extraVTTabletArgs = append(extraVTTabletArgs, "--vstream_packet_size=256", "--queryserver-config-schema-change-signal") + extraVTGateArgs = append(extraVTGateArgs, "--schema_change_signal=true", "--planner-version", "Gen4") + defer func() { extraVTTabletArgs = nil }() + initFKExtConfig(t) + + cellName := fkextConfig.cell + cells := []string{cellName} + vc = NewVitessCluster(t, &clusterOptions{ + cells: cells, + clusterConfig: fkextConfig.ClusterConfig, + }) + defaultCell := vc.Cells[vc.CellNames[0]] + cell := vc.Cells[cellName] + + defer vc.TearDown() + + sourceKeyspace := fkextConfig.sourceKeyspaceName + vc.AddKeyspace(t, []*Cell{cell}, sourceKeyspace, "0", FKExtSourceVSchema, FKExtSourceSchema, 0, 0, 100, nil) + + verifyClusterHealth(t, vc) + + lg = &SimpleLoadGenerator{} + lg.Init(context.Background(), vc) + lg.SetDBStrategy("vtgate", fkextConfig.sourceKeyspaceName) + if lg.Load() != nil { + t.Fatal("Load failed") + } + if lg.Start() != nil { + t.Fatal("Start failed") + } + t.Run("Import from external db", func(t *testing.T) { + // Import data into vitess from sourceKeyspace to target1Keyspace, both unsharded. + importIntoVitess(t) + }) + + t.Run("MoveTables from unsharded to sharded keyspace", func(t *testing.T) { + // Migrate data from target1Keyspace to the sharded target2Keyspace. Drops constraints from + // replica to simulate a replica that is not doing cascades in innodb to test vtgate's fkmanaged mode. + // The replica with dropped constraints is used as source for the next workflow called in materializeTables(). + moveKeyspace(t) + }) + + t.Run("Materialize parent and copy tables without constraints", func(t *testing.T) { + // Materialize the tables from target2Keyspace to target1Keyspace. Stream only from replicas, one + // shard with constraints dropped. + materializeTables(t) + }) + lg.SetDBStrategy("vtgate", fkextConfig.target2KeyspaceName) + if lg.Start() != nil { + t.Fatal("Start failed") + } + threeShards := "-40,40-c0,c0-" + keyspaceName := fkextConfig.target2KeyspaceName + ks := vc.Cells[fkextConfig.cell].Keyspaces[keyspaceName] + numReplicas := 1 + + t.Run("Reshard keyspace from 2 shards to 3 shards", func(t *testing.T) { + tabletID := 500 + require.NoError(t, vc.AddShards(t, []*Cell{defaultCell}, ks, threeShards, numReplicas, 0, tabletID, nil)) + tablets := make(map[string]*cluster.VttabletProcess) + for i, shard := range strings.Split(threeShards, ",") { + tablets[shard] = vc.Cells[cellName].Keyspaces[keyspaceName].Shards[shard].Tablets[fmt.Sprintf("%s-%d", cellName, tabletID+i*100)].Vttablet + } + sqls := strings.Split(FKExtSourceSchema, "\n") + for _, sql := range sqls { + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ApplySchema", "--", + "--ddl_strategy=direct", "--sql", sql, keyspaceName) + require.NoErrorf(t, err, output) + } + doReshard(t, fkextConfig.target2KeyspaceName, "reshard2to3", "-80,80-", threeShards, tablets) + }) + t.Run("Reshard keyspace from 3 shards to 1 shard", func(t *testing.T) { + tabletID := 800 + shard := "0" + require.NoError(t, vc.AddShards(t, []*Cell{defaultCell}, ks, shard, numReplicas, 0, tabletID, nil)) + tablets := make(map[string]*cluster.VttabletProcess) + tablets[shard] = vc.Cells[cellName].Keyspaces[keyspaceName].Shards[shard].Tablets[fmt.Sprintf("%s-%d", cellName, tabletID)].Vttablet + sqls := strings.Split(FKExtSourceSchema, "\n") + for _, sql := range sqls { + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ApplySchema", "--", + "--ddl_strategy=direct", "--sql", sql, keyspaceName) + require.NoErrorf(t, err, output) + } + doReshard(t, fkextConfig.target2KeyspaceName, "reshard3to1", threeShards, "0", tablets) + }) + lg.Stop() + waitForLowLag(t, fkextConfig.target1KeyspaceName, "mat") + t.Run("Validate materialize counts at end of test", func(t *testing.T) { + validateMaterializeRowCounts(t) + }) + +} + +// checkRowCounts checks that the parent and child tables in the source and target shards have the same number of rows. +func checkRowCounts(t *testing.T, keyspace string, sourceShards, targetShards []string) bool { + sourceTabs := make(map[string]*cluster.VttabletProcess) + targetTabs := make(map[string]*cluster.VttabletProcess) + for _, shard := range sourceShards { + sourceTabs[shard] = vc.getPrimaryTablet(t, keyspace, shard) + } + for _, shard := range targetShards { + targetTabs[shard] = vc.getPrimaryTablet(t, keyspace, shard) + } + + getCount := func(tab *cluster.VttabletProcess, table string) (int64, error) { + qr, err := tab.QueryTablet(fmt.Sprintf("select count(*) from %s", table), keyspace, true) + if err != nil { + return 0, err + } + return qr.Rows[0][0].ToInt64() + } + + var sourceParentCount, sourceChildCount int64 + var targetParentCount, targetChildCount int64 + for _, tab := range sourceTabs { + count, _ := getCount(tab, "parent") + sourceParentCount += count + count, _ = getCount(tab, "child") + sourceChildCount += count + } + for _, tab := range targetTabs { + count, _ := getCount(tab, "parent") + targetParentCount += count + count, _ = getCount(tab, "child") + targetChildCount += count + } + log.Infof("Source parent count: %d, child count: %d, target parent count: %d, child count: %d.", + sourceParentCount, sourceChildCount, targetParentCount, targetChildCount) + if sourceParentCount != targetParentCount || sourceChildCount != targetChildCount { + log.Infof("Row counts do not match for keyspace %s, source shards: %v, target shards: %v", keyspace, sourceShards, targetShards) + return false + } + return true +} + +// compareRowCounts compares the row counts for the parent and child tables in the source and target shards. In addition to vdiffs, +// it is another check to ensure that both tables have the same number of rows in the source and target shards after load generation +// has stopped. +func compareRowCounts(t *testing.T, keyspace string, sourceShards, targetShards []string) error { + log.Infof("Comparing row counts for keyspace %s, source shards: %v, target shards: %v", keyspace, sourceShards, targetShards) + lg.Stop() + defer lg.Start() + if err := waitForCondition("load generator to stop", func() bool { return lg.State() == LoadGeneratorStateStopped }, 10*time.Second); err != nil { + return err + } + if err := waitForCondition("matching row counts", func() bool { return checkRowCounts(t, keyspace, sourceShards, targetShards) }, 30*time.Second); err != nil { + return err + } + + return nil +} + +func doReshard(t *testing.T, keyspace, workflowName, sourceShards, targetShards string, targetTabs map[string]*cluster.VttabletProcess) { + rs := newReshard(vc, &reshardWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: keyspace, + }, + sourceShards: sourceShards, + targetShards: targetShards, + skipSchemaCopy: true, + }, workflowFlavorVtctl) + rs.Create() + waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", keyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) + for _, targetTab := range targetTabs { + catchup(t, targetTab, workflowName, "Reshard") + } + vdiff(t, keyspace, workflowName, fkextConfig.cell, false, true, nil) + rs.SwitchReadsAndWrites() + //if lg.WaitForAdditionalRows(100) != nil { + // t.Fatal("WaitForAdditionalRows failed") + //} + waitForLowLag(t, keyspace, workflowName+"_reverse") + if compareRowCounts(t, keyspace, strings.Split(sourceShards, ","), strings.Split(targetShards, ",")) != nil { + t.Fatal("Row counts do not match") + } + vdiff(t, keyspace, workflowName+"_reverse", fkextConfig.cell, true, false, nil) + + rs.ReverseReadsAndWrites() + //if lg.WaitForAdditionalRows(100) != nil { + // t.Fatal("WaitForAdditionalRows failed") + //} + waitForLowLag(t, keyspace, workflowName) + if compareRowCounts(t, keyspace, strings.Split(targetShards, ","), strings.Split(sourceShards, ",")) != nil { + t.Fatal("Row counts do not match") + } + vdiff(t, keyspace, workflowName, fkextConfig.cell, false, true, nil) + lg.Stop() + + rs.SwitchReadsAndWrites() + rs.Complete() +} + +func areRowCountsEqual(t *testing.T) bool { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() + parentRowCount := getRowCount(t, vtgateConn, "target2.parent") + childRowCount := getRowCount(t, vtgateConn, "target2.child") + parentCopyRowCount := getRowCount(t, vtgateConn, "target1.parent_copy") + childCopyRowCount := getRowCount(t, vtgateConn, "target1.child_copy") + log.Infof("Post-materialize row counts are parent: %d, child: %d, parent_copy: %d, child_copy: %d", + parentRowCount, childRowCount, parentCopyRowCount, childCopyRowCount) + if parentRowCount != parentCopyRowCount || childRowCount != childCopyRowCount { + return false + } + return true +} + +// validateMaterializeRowCounts expects the Load generator to be stopped before calling it. +func validateMaterializeRowCounts(t *testing.T) { + if lg.State() != LoadGeneratorStateStopped { + t.Fatal("Load generator was unexpectedly still running when validateMaterializeRowCounts was called -- this will produce unreliable results.") + } + areRowCountsEqual2 := func() bool { + return areRowCountsEqual(t) + } + require.NoError(t, waitForCondition("row counts to be equal", areRowCountsEqual2, defaultTimeout)) +} + +const fkExtMaterializeSpec = ` +{"workflow": "%s", "source_keyspace": "%s", "target_keyspace": "%s", +"table_settings": [ {"target_table": "parent_copy", "source_expression": "select * from parent" },{"target_table": "child_copy", "source_expression": "select * from child" }], +"tablet_types": "replica"}` + +func materializeTables(t *testing.T) { + wfName := "mat" + err := vc.VtctlClient.ExecuteCommand("ApplySchema", "--", "--ddl_strategy=direct", + "--sql", FKExtMaterializeSchema, fkextConfig.target1KeyspaceName) + require.NoError(t, err, fmt.Sprintf("ApplySchema Error: %s", err)) + materializeSpec := fmt.Sprintf(fkExtMaterializeSpec, "mat", fkextConfig.target2KeyspaceName, fkextConfig.target1KeyspaceName) + err = vc.VtctlClient.ExecuteCommand("Materialize", materializeSpec) + require.NoError(t, err, "Materialize") + tab := vc.getPrimaryTablet(t, fkextConfig.target1KeyspaceName, "0") + catchup(t, tab, wfName, "Materialize") + validateMaterializeRowCounts(t) +} + +func moveKeyspace(t *testing.T) { + targetTabs := newKeyspace(t, fkextConfig.target2KeyspaceName, "-80,80-", FKExtTarget2VSchema, FKExtSourceSchema, 300, 1) + shard := "-80" + tabletId := fmt.Sprintf("%s-%d", fkextConfig.cell, 301) + replicaTab := vc.Cells[fkextConfig.cell].Keyspaces[fkextConfig.target2KeyspaceName].Shards[shard].Tablets[tabletId].Vttablet + dropReplicaConstraints(t, fkextConfig.target2KeyspaceName, replicaTab) + doMoveTables(t, fkextConfig.target1KeyspaceName, fkextConfig.target2KeyspaceName, "move", "replica", targetTabs, false) +} + +func newKeyspace(t *testing.T, keyspaceName, shards, vschema, schema string, tabletId, numReplicas int) map[string]*cluster.VttabletProcess { + tablets := make(map[string]*cluster.VttabletProcess) + cell := vc.Cells[fkextConfig.cell] + vtgate := cell.Vtgates[0] + vc.AddKeyspace(t, []*Cell{cell}, keyspaceName, shards, vschema, schema, numReplicas, 0, tabletId, nil) + err := vc.VtctldClient.ExecuteCommand("RebuildVSchemaGraph") + require.NoError(t, err) + require.NoError(t, waitForColumn(t, vtgate, keyspaceName, "parent", "id")) + require.NoError(t, waitForColumn(t, vtgate, keyspaceName, "child", "parent_id")) + return tablets +} + +func doMoveTables(t *testing.T, sourceKeyspace, targetKeyspace, workflowName, tabletTypes string, targetTabs map[string]*cluster.VttabletProcess, atomicCopy bool) { + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + tabletTypes: tabletTypes, + }, + sourceKeyspace: sourceKeyspace, + atomicCopy: atomicCopy, + }, workflowFlavorRandom) + mt.Create() + + waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", targetKeyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) + + for _, targetTab := range targetTabs { + catchup(t, targetTab, workflowName, "MoveTables") + } + vdiff(t, targetKeyspace, workflowName, fkextConfig.cell, false, true, nil) + lg.Stop() + lg.SetDBStrategy("vtgate", targetKeyspace) + if lg.Start() != nil { + t.Fatal("Start failed") + } + + mt.SwitchReadsAndWrites() + + if lg.WaitForAdditionalRows(100) != nil { + t.Fatal("WaitForAdditionalRows failed") + } + + waitForLowLag(t, sourceKeyspace, workflowName+"_reverse") + vdiff(t, sourceKeyspace, workflowName+"_reverse", fkextConfig.cell, false, true, nil) + if lg.WaitForAdditionalRows(100) != nil { + t.Fatal("WaitForAdditionalRows failed") + } + + mt.ReverseReadsAndWrites() + if lg.WaitForAdditionalRows(100) != nil { + t.Fatal("WaitForAdditionalRows failed") + } + waitForLowLag(t, targetKeyspace, workflowName) + time.Sleep(5 * time.Second) + vdiff(t, targetKeyspace, workflowName, fkextConfig.cell, false, true, nil) + lg.Stop() + mt.SwitchReadsAndWrites() + mt.Complete() + if err := vc.VtctldClient.ExecuteCommand("ApplyRoutingRules", "--rules={}"); err != nil { + t.Fatal(err) + } +} + +func importIntoVitess(t *testing.T) { + targetTabs := newKeyspace(t, fkextConfig.target1KeyspaceName, "0", FKExtTarget1VSchema, FKExtSourceSchema, 200, 1) + doMoveTables(t, fkextConfig.sourceKeyspaceName, fkextConfig.target1KeyspaceName, "import", "primary", targetTabs, true) +} + +const getConstraintsQuery = ` +SELECT CONSTRAINT_NAME, TABLE_NAME +FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +WHERE TABLE_SCHEMA = '%s' AND REFERENCED_TABLE_NAME IS NOT NULL; +` + +// dropReplicaConstraints drops all foreign key constraints on replica tables for a given keyspace/shard. +// We do this so that we can replay binlogs from a replica which is not doing cascades but just replaying +// the binlogs created by the primary. This will confirm that vtgate is doing the cascades correctly. +func dropReplicaConstraints(t *testing.T, keyspaceName string, tablet *cluster.VttabletProcess) { + var dropConstraints []string + require.Equal(t, "replica", strings.ToLower(tablet.TabletType)) + dbName := "vt_" + keyspaceName + qr, err := tablet.QueryTablet(fmt.Sprintf(getConstraintsQuery, dbName), keyspaceName, true) + if err != nil { + t.Fatal(err) + } + for _, row := range qr.Rows { + constraintName := row[0].ToString() + tableName := row[1].ToString() + dropConstraints = append(dropConstraints, fmt.Sprintf("ALTER TABLE `%s`.`%s` DROP FOREIGN KEY `%s`", + dbName, tableName, constraintName)) + } + prefixQueries := []string{ + "set sql_log_bin=0", + "SET @@global.super_read_only=0", + } + suffixQueries := []string{ + "SET @@global.super_read_only=1", + "set sql_log_bin=1", + } + queries := append(prefixQueries, dropConstraints...) + queries = append(queries, suffixQueries...) + require.NoError(t, tablet.QueryTabletMultiple(queries, keyspaceName, true)) +} diff --git a/go/test/endtoend/vreplication/fk_test.go b/go/test/endtoend/vreplication/fk_test.go index 31886864f11..e6133e9e0ff 100644 --- a/go/test/endtoend/vreplication/fk_test.go +++ b/go/test/endtoend/vreplication/fk_test.go @@ -28,49 +28,42 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vttablet" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) +const testWorkflowFlavor = workflowFlavorRandom + // TestFKWorkflow runs a MoveTables workflow with atomic copy for a db with foreign key constraints. // It inserts initial data, then simulates load. We insert both child rows with foreign keys and those without, // i.e. with foreign_key_checks=0. func TestFKWorkflow(t *testing.T) { - // ensure that there are multiple copy phase cycles per table - extraVTTabletArgs = []string{"--vstream_packet_size=256"} + extraVTTabletArgs = []string{ + // Ensure that there are multiple copy phase cycles per table. + "--vstream_packet_size=256", + // Test VPlayer batching mode. + fmt.Sprintf("--vreplication_experimental_flags=%d", + vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), + } defer func() { extraVTTabletArgs = nil }() - cellName := "zone" - cells := []string{cellName} - vc = NewVitessCluster(t, "TestFKWorkflow", cells, mainClusterConfig) + cellName := "zone1" + vc = NewVitessCluster(t, nil) - require.NotNil(t, vc) - allCellNames = cellName - defaultCellName := cellName - defaultCell = vc.Cells[defaultCellName] sourceKeyspace := "fksource" shardName := "0" - defer vc.TearDown(t) + defer vc.TearDown() cell := vc.Cells[cellName] vc.AddKeyspace(t, []*Cell{cell}, sourceKeyspace, shardName, initialFKSourceVSchema, initialFKSchema, 0, 0, 100, sourceKsOpts) - vtgate = cell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKeyspace, shardName) - require.NoError(t, err) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", sourceKeyspace, shardName), 1, 30*time.Second) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() verifyClusterHealth(t, vc) + insertInitialFKData(t) var ls *fkLoadSimulator - - insertInitialFKData(t) withLoad := true // Set it to false to skip load simulation, while debugging var cancel context.CancelFunc var ctx context.Context @@ -86,20 +79,25 @@ func TestFKWorkflow(t *testing.T) { }() go ls.simulateLoad() } + targetKeyspace := "fktarget" targetTabletId := 200 - vc.AddKeyspace(t, []*Cell{cell}, targetKeyspace, shardName, initialFKTargetVSchema, initialFKSchema, 0, 0, targetTabletId, sourceKsOpts) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", targetKeyspace, shardName), 1, 30*time.Second) + vc.AddKeyspace(t, []*Cell{cell}, targetKeyspace, shardName, initialFKTargetVSchema, "", 0, 0, targetTabletId, sourceKsOpts) + + testFKCancel(t, vc) workflowName := "fk" ksWorkflow := fmt.Sprintf("%s.%s", targetKeyspace, workflowName) - mt := newMoveTables(vc, &moveTables{ - workflowName: workflowName, - targetKeyspace: targetKeyspace, + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, sourceKeyspace: sourceKeyspace, atomicCopy: true, - }, moveTablesFlavorRandom) + }, testWorkflowFlavor) mt.Create() waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) @@ -129,10 +127,13 @@ func TestFKWorkflow(t *testing.T) { cancel() <-ch } + mt.Complete() } func insertInitialFKData(t *testing.T) { t.Run("insertInitialFKData", func(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() sourceKeyspace := "fksource" shard := "0" db := fmt.Sprintf("%s:%s", sourceKeyspace, shard) @@ -141,6 +142,9 @@ func insertInitialFKData(t *testing.T) { log.Infof("Done inserting initial FK data") waitForRowCount(t, vtgateConn, db, "parent", 2) waitForRowCount(t, vtgateConn, db, "child", 3) + waitForRowCount(t, vtgateConn, db, "t1", 2) + waitForRowCount(t, vtgateConn, db, "t2", 3) + }) } @@ -268,7 +272,31 @@ func (ls *fkLoadSimulator) delete() { func (ls *fkLoadSimulator) exec(query string) *sqltypes.Result { t := ls.t + vtgateConn, closeConn := getVTGateConn() + defer closeConn() qr := execVtgateQuery(t, vtgateConn, "fksource", query) require.NotNil(t, qr) return qr } + +// testFKCancel confirms that a MoveTables workflow which includes tables with foreign key +// constraints, where the parent table is lexicographically sorted before the child table and +// thus may be dropped first, can be successfully cancelled. +func testFKCancel(t *testing.T, vc *VitessCluster) { + targetKeyspace := "fktarget" + sourceKeyspace := "fksource" + workflowName := "wf2" + ksWorkflow := fmt.Sprintf("%s.%s", targetKeyspace, workflowName) + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + atomicCopy: true, + }, testWorkflowFlavor) + mt.Create() + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + mt.Cancel() +} diff --git a/go/test/endtoend/vreplication/helper_test.go b/go/test/endtoend/vreplication/helper_test.go index d2154f13f1f..54d057fe6e9 100644 --- a/go/test/endtoend/vreplication/helper_test.go +++ b/go/test/endtoend/vreplication/helper_test.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "net/http" + "os" "os/exec" "regexp" "sort" @@ -56,6 +57,11 @@ const ( workflowStateTimeout = 90 * time.Second ) +func setSidecarDBName(dbName string) { + sidecarDBName = dbName + sidecarDBIdentifier = sqlparser.String(sqlparser.NewIdentifierCS(sidecarDBName)) +} + func execMultipleQueries(t *testing.T, conn *mysql.Conn, database string, lines string) { queries := strings.Split(lines, "\n") for _, query := range queries { @@ -65,11 +71,51 @@ func execMultipleQueries(t *testing.T, conn *mysql.Conn, database string, lines execVtgateQuery(t, conn, database, string(query)) } } + +func execQueryWithRetry(t *testing.T, conn *mysql.Conn, query string, timeout time.Duration) *sqltypes.Result { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + ticker := time.NewTicker(defaultTick) + defer ticker.Stop() + + var qr *sqltypes.Result + var err error + for { + qr, err = conn.ExecuteFetch(query, 1000, false) + if err == nil { + return qr + } + select { + case <-ctx.Done(): + require.FailNow(t, fmt.Sprintf("query %q did not succeed before the timeout of %s; last seen result: %v", + query, timeout, qr.Rows)) + case <-ticker.C: + log.Infof("query %q failed with error %v, retrying in %ds", query, err, defaultTick) + } + } +} + func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { qr, err := conn.ExecuteFetch(query, 1000, false) + if err != nil { + log.Errorf("Error executing query: %s: %v", query, err) + } require.NoError(t, err) return qr } +func getConnectionNoError(t *testing.T, hostname string, port int) *mysql.Conn { + vtParams := mysql.ConnParams{ + Host: hostname, + Port: port, + Uname: "vt_dba", + } + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + if err != nil { + return nil + } + return conn +} func getConnection(t *testing.T, hostname string, port int) *mysql.Conn { vtParams := mysql.ConnParams{ @@ -79,7 +125,7 @@ func getConnection(t *testing.T, hostname string, port int) *mysql.Conn { } ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.NoErrorf(t, err, "error connecting to vtgate on %s:%d", hostname, port) return conn } @@ -96,6 +142,19 @@ func execVtgateQuery(t *testing.T, conn *mysql.Conn, database string, query stri return qr } +func execVtgateQueryWithRetry(t *testing.T, conn *mysql.Conn, database string, query string, timeout time.Duration) *sqltypes.Result { + if strings.TrimSpace(query) == "" { + return nil + } + if database != "" { + execQuery(t, conn, "use `"+database+"`;") + } + execQuery(t, conn, "begin") + qr := execQueryWithRetry(t, conn, query, timeout) + execQuery(t, conn, "commit") + return qr +} + func checkHealth(t *testing.T, url string) bool { resp, err := http.Get(url) require.NoError(t, err) @@ -382,7 +441,7 @@ func confirmTablesHaveSecondaryKeys(t *testing.T, tablets []*cluster.VttabletPro require.NotNil(t, res) row := res.Named().Row() tableSchema := row["Create Table"].ToString() - parsedDDL, err := sqlparser.ParseStrictDDL(tableSchema) + parsedDDL, err := sqlparser.NewTestParser().ParseStrictDDL(tableSchema) require.NoError(t, err) createTable, ok := parsedDDL.(*sqlparser.CreateTable) require.True(t, ok) @@ -703,6 +762,13 @@ func isBinlogRowImageNoBlob(t *testing.T, tablet *cluster.VttabletProcess) bool return mode == "noblob" } +func getRowCount(t *testing.T, vtgateConn *mysql.Conn, table string) int { + query := fmt.Sprintf("select count(*) from %s", table) + qr := execVtgateQuery(t, vtgateConn, "", query) + numRows, _ := qr.Rows[0][0].ToInt() + return numRows +} + const ( loadTestBufferingWindowDurationStr = "30s" loadTestPostBufferingInsertWindow = 60 * time.Second // should be greater than loadTestBufferingWindowDurationStr @@ -729,8 +795,6 @@ func (lg *loadGenerator) stop() { log.Infof("Canceling load") lg.cancel() time.Sleep(loadTestWaitForCancel) // wait for cancel to take effect - log.Flush() - } func (lg *loadGenerator) start() { @@ -820,3 +884,51 @@ func (lg *loadGenerator) waitForCount(want int64) { } } } + +// appendToQueryLog is useful when debugging tests. +func appendToQueryLog(msg string) { + file, err := os.OpenFile(queryLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Errorf("Error opening query log file: %v", err) + return + } + defer file.Close() + if _, err := file.WriteString(msg + "\n"); err != nil { + log.Errorf("Error writing to query log file: %v", err) + } +} + +func waitForCondition(name string, condition func() bool, timeout time.Duration) error { + if condition() { + return nil + } + + ticker := time.NewTicker(tickInterval) + defer ticker.Stop() + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + for { + select { + case <-ticker.C: + if condition() { + return nil + } + case <-ctx.Done(): + return fmt.Errorf("%s: waiting for %s", ctx.Err(), name) + } + } +} + +func getCellNames(cells []*Cell) string { + var cellNames []string + if cells == nil { + cells = []*Cell{} + for _, cell := range vc.Cells { + cells = append(cells, cell) + } + } + for _, cell := range cells { + cellNames = append(cellNames, cell.Name) + } + return strings.Join(cellNames, ",") +} diff --git a/go/test/endtoend/vreplication/initial_data_test.go b/go/test/endtoend/vreplication/initial_data_test.go index bf93a040942..23f699563e2 100644 --- a/go/test/endtoend/vreplication/initial_data_test.go +++ b/go/test/endtoend/vreplication/initial_data_test.go @@ -27,6 +27,8 @@ import ( func insertInitialData(t *testing.T) { t.Run("insertInitialData", func(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() log.Infof("Inserting initial data") lines, _ := os.ReadFile("unsharded_init_data.sql") execMultipleQueries(t, vtgateConn, "product:0", string(lines)) @@ -48,6 +50,8 @@ const NumJSONRows = 100 func insertJSONValues(t *testing.T) { // insert null value combinations + vtgateConn, closeConn := getVTGateConn() + defer closeConn() execVtgateQuery(t, vtgateConn, "product:0", "insert into json_tbl(id, j3) values(1, \"{}\")") execVtgateQuery(t, vtgateConn, "product:0", "insert into json_tbl(id, j1, j3) values(2, \"{}\", \"{}\")") execVtgateQuery(t, vtgateConn, "product:0", "insert into json_tbl(id, j2, j3) values(3, \"{}\", \"{}\")") @@ -76,6 +80,8 @@ func insertMoreCustomers(t *testing.T, numCustomers int) { // the number of customer records we are going to // create. The value we get back is the max value // that we reserved. + vtgateConn, closeConn := getVTGateConn() + defer closeConn() maxID := waitForSequenceValue(t, vtgateConn, "product", "customer_seq", numCustomers) // So we need to calculate the first value we reserved // from the max. @@ -95,16 +101,22 @@ func insertMoreCustomers(t *testing.T, numCustomers int) { } func insertMoreProducts(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() sql := "insert into product(pid, description) values(3, 'cpu'),(4, 'camera'),(5, 'mouse');" execVtgateQuery(t, vtgateConn, "product", sql) } func insertMoreProductsForSourceThrottler(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() sql := "insert into product(pid, description) values(103, 'new-cpu'),(104, 'new-camera'),(105, 'new-mouse');" execVtgateQuery(t, vtgateConn, "product", sql) } func insertMoreProductsForTargetThrottler(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() sql := "insert into product(pid, description) values(203, 'new-cpu'),(204, 'new-camera'),(205, 'new-mouse');" execVtgateQuery(t, vtgateConn, "product", sql) } @@ -122,6 +134,8 @@ var blobTableQueries = []string{ } func insertIntoBlobTable(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() for _, query := range blobTableQueries { execVtgateQuery(t, vtgateConn, "product:0", query) } diff --git a/go/test/endtoend/vreplication/materialize_test.go b/go/test/endtoend/vreplication/materialize_test.go index 63205a56c0a..486692a58ba 100644 --- a/go/test/endtoend/vreplication/materialize_test.go +++ b/go/test/endtoend/vreplication/materialize_test.go @@ -20,8 +20,6 @@ import ( "testing" "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/endtoend/cluster" ) const smSchema = ` @@ -63,31 +61,21 @@ const initDataQuery = `insert into ks1.tx(id, typ, val) values (1, 1, 'abc'), (2 // testShardedMaterialize tests a materialize workflow for a sharded cluster (single shard) using comparison filters func testShardedMaterialize(t *testing.T, useVtctldClient bool) { - defaultCellName := "zone1" - allCells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestShardedMaterialize", allCells, mainClusterConfig) + var err error + vc = NewVitessCluster(t, nil) ks1 := "ks1" ks2 := "ks2" - shard := "0" require.NotNil(t, vc) defaultReplicas = 0 // because of CI resource constraints we can only run this test with primary tablets defer func() { defaultReplicas = 1 }() - defer vc.TearDown(t) - - defaultCell = vc.Cells[defaultCellName] + defer vc.TearDown() + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, ks1, "0", smVSchema, smSchema, defaultReplicas, defaultRdonly, 100, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, ks1, shard) - require.NoError(t, err) vc.AddKeyspace(t, []*Cell{defaultCell}, ks2, "0", smVSchema, smSchema, defaultReplicas, defaultRdonly, 200, nil) - err = cluster.WaitForHealthyShard(vc.VtctldClient, ks2, shard) - require.NoError(t, err) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) _, err = vtgateConn.ExecuteFetch(initDataQuery, 0, false) @@ -182,10 +170,8 @@ RETURN id * length(val); ` func testMaterialize(t *testing.T, useVtctldClient bool) { - defaultCellName := "zone1" - allCells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestMaterialize", allCells, mainClusterConfig) + var err error + vc = NewVitessCluster(t, nil) sourceKs := "source" targetKs := "target" shard := "0" @@ -193,20 +179,14 @@ func testMaterialize(t *testing.T, useVtctldClient bool) { defaultReplicas = 0 // because of CI resource constraints we can only run this test with primary tablets defer func() { defaultReplicas = 1 }() - defer vc.TearDown(t) + defer vc.TearDown() - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, sourceKs, "0", smMaterializeVSchemaSource, smMaterializeSchemaSource, defaultReplicas, defaultRdonly, 300, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) - require.NoError(t, err) vc.AddKeyspace(t, []*Cell{defaultCell}, targetKs, "0", smMaterializeVSchemaTarget, smMaterializeSchemaTarget, defaultReplicas, defaultRdonly, 400, nil) - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) - require.NoError(t, err) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) diff --git a/go/test/endtoend/vreplication/migrate_test.go b/go/test/endtoend/vreplication/migrate_test.go index 75ab6a3151b..5d927054000 100644 --- a/go/test/endtoend/vreplication/migrate_test.go +++ b/go/test/endtoend/vreplication/migrate_test.go @@ -25,8 +25,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/test/endtoend/cluster" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) @@ -48,43 +46,36 @@ func insertInitialDataIntoExternalCluster(t *testing.T, conn *mysql.Conn) { // hence the VTDATAROOT env variable gets overwritten. // Each time we need to create vt processes in the "other" cluster we need to set the appropriate VTDATAROOT func TestVtctlMigrate(t *testing.T) { - defaultCellName := "zone1" - cells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestMigrate", cells, mainClusterConfig) + vc = NewVitessCluster(t, nil) - require.NotNil(t, vc, "failed to create VitessCluster") defaultReplicas = 0 defaultRdonly = 0 - defer vc.TearDown(t) + defer vc.TearDown() - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, "product", "0", initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, nil) require.NoError(t, err, "failed to create product keyspace") - err = cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err, "product shard did not become healthy") - vtgate = defaultCell.Vtgates[0] + vtgate := defaultCell.Vtgates[0] require.NotNil(t, vtgate, "failed to get vtgate") - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) insertInitialData(t) + t.Run("VStreamFrom", func(t *testing.T) { + testVStreamFrom(t, vtgate, "product", 2) + }) // create external cluster extCell := "extcell1" - extCells := []string{extCell} - extVc := NewVitessCluster(t, "TestMigrateExternal", extCells, externalClusterConfig) - require.NotNil(t, extVc) - defer extVc.TearDown(t) + extVc := NewVitessCluster(t, &clusterOptions{cells: []string{"extcell1"}, clusterConfig: externalClusterConfig}) + defer extVc.TearDown() extCell2 := extVc.Cells[extCell] extVc.AddKeyspace(t, []*Cell{extCell2}, "rating", "0", initialExternalVSchema, initialExternalSchema, 0, 0, 1000, nil) extVtgate := extCell2.Vtgates[0] require.NotNil(t, extVtgate) - err = cluster.WaitForHealthyShard(extVc.VtctldClient, "rating", "0") - require.NoError(t, err) verifyClusterHealth(t, extVc) extVtgateConn := getConnection(t, extVc.ClusterConfig.hostname, extVc.ClusterConfig.vtgateMySQLPort) insertInitialDataIntoExternalCluster(t, extVtgateConn) @@ -175,26 +166,18 @@ func TestVtctlMigrate(t *testing.T) { // hence the VTDATAROOT env variable gets overwritten. // Each time we need to create vt processes in the "other" cluster we need to set the appropriate VTDATAROOT func TestVtctldMigrate(t *testing.T) { - defaultCellName := "zone1" - cells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestMigrateVtctld", cells, mainClusterConfig) + vc = NewVitessCluster(t, nil) - require.NotNil(t, vc, "failed to create VitessCluster") defaultReplicas = 0 defaultRdonly = 0 - defer vc.TearDown(t) + defer vc.TearDown() - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, "product", "0", initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, nil) require.NoError(t, err, "failed to create product keyspace") - err = cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err, "product shard did not become healthy") - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate, "failed to get vtgate") - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) insertInitialData(t) @@ -202,9 +185,11 @@ func TestVtctldMigrate(t *testing.T) { // create external cluster extCell := "extcell1" extCells := []string{extCell} - extVc := NewVitessCluster(t, t.Name(), extCells, externalClusterConfig) - require.NotNil(t, extVc) - defer extVc.TearDown(t) + extVc := NewVitessCluster(t, &clusterOptions{ + cells: extCells, + clusterConfig: externalClusterConfig, + }) + defer extVc.TearDown() extCell2 := extVc.Cells[extCell] extVc.AddKeyspace(t, []*Cell{extCell2}, "rating", "0", @@ -212,8 +197,6 @@ func TestVtctldMigrate(t *testing.T) { extVtgate := extCell2.Vtgates[0] require.NotNil(t, extVtgate) - err = cluster.WaitForHealthyShard(extVc.VtctldClient, "rating", "0") - require.NoError(t, err) verifyClusterHealth(t, extVc) extVtgateConn := getConnection(t, extVc.ClusterConfig.hostname, extVc.ClusterConfig.vtgateMySQLPort) insertInitialDataIntoExternalCluster(t, extVtgateConn) diff --git a/go/test/endtoend/vreplication/movetables_buffering_test.go b/go/test/endtoend/vreplication/movetables_buffering_test.go index 4e4b7cada97..e853022bfd4 100644 --- a/go/test/endtoend/vreplication/movetables_buffering_test.go +++ b/go/test/endtoend/vreplication/movetables_buffering_test.go @@ -14,14 +14,13 @@ import ( func TestMoveTablesBuffering(t *testing.T) { defaultRdonly = 1 vc = setupMinimalCluster(t) - defer vtgateConn.Close() - defer vc.TearDown(t) + defer vc.TearDown() currentWorkflowType = wrangler.MoveTablesWorkflow setupMinimalCustomerKeyspace(t) tables := "loadtest" err := tstWorkflowExec(t, defaultCellName, workflowName, sourceKs, targetKs, - tables, workflowActionCreate, "", "", "", false) + tables, workflowActionCreate, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) @@ -41,5 +40,4 @@ func TestMoveTablesBuffering(t *testing.T) { lg.stop() log.Infof("TestMoveTablesBuffering: done") - log.Flush() } diff --git a/go/test/endtoend/vreplication/partial_movetables_seq_test.go b/go/test/endtoend/vreplication/partial_movetables_seq_test.go index 6a1ed92cb9c..bb354a5ec01 100644 --- a/go/test/endtoend/vreplication/partial_movetables_seq_test.go +++ b/go/test/endtoend/vreplication/partial_movetables_seq_test.go @@ -20,7 +20,6 @@ import ( "fmt" "strings" "testing" - "time" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" @@ -75,7 +74,7 @@ type vrepTestCase struct { vtgate *cluster.VtgateProcess } -func initPartialMoveTablesComplexTestCase(t *testing.T, name string) *vrepTestCase { +func initPartialMoveTablesComplexTestCase(t *testing.T) *vrepTestCase { const ( seqVSchema = `{ "sharded": false, @@ -122,7 +121,7 @@ func initPartialMoveTablesComplexTestCase(t *testing.T, name string) *vrepTestCa ) tc := &vrepTestCase{ t: t, - testName: name, + testName: t.Name(), keyspaces: make(map[string]*keyspace), defaultCellName: "zone1", workflows: make(map[string]*workflow), @@ -169,18 +168,15 @@ func initPartialMoveTablesComplexTestCase(t *testing.T, name string) *vrepTestCa func (tc *vrepTestCase) teardown() { tc.vtgateConn.Close() - vc.TearDown(tc.t) + vc.TearDown() } func (tc *vrepTestCase) setupCluster() { - cells := []string{"zone1"} - - tc.vc = NewVitessCluster(tc.t, tc.testName, cells, mainClusterConfig) + tc.vc = NewVitessCluster(tc.t, nil) vc = tc.vc // for backward compatibility since vc is used globally in this package require.NotNil(tc.t, tc.vc) tc.setupKeyspaces([]string{"commerce", "seqSrc"}) tc.vtgateConn = getConnection(tc.t, tc.vc.ClusterConfig.hostname, tc.vc.ClusterConfig.vtgateMySQLPort) - vtgateConn = tc.vtgateConn // for backward compatibility since vtgateConn is used globally in this package } func (tc *vrepTestCase) initData() { @@ -211,10 +207,6 @@ func (tc *vrepTestCase) setupKeyspace(ks *keyspace) { tc.vtgate = defaultCell.Vtgates[0] } - for _, shard := range ks.shards { - require.NoError(t, cluster.WaitForHealthyShard(tc.vc.VtctldClient, ks.name, shard)) - require.NoError(t, tc.vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", ks.name, shard), 1, 30*time.Second)) - } } func (tc *vrepTestCase) newWorkflow(typ, workflowName, fromKeyspace, toKeyspace string, options *workflowOptions) *workflow { @@ -239,7 +231,7 @@ func (wf *workflow) create() { currentWorkflowType = wrangler.MoveTablesWorkflow sourceShards := strings.Join(wf.options.sourceShards, ",") err = tstWorkflowExec(t, cell, wf.name, wf.fromKeyspace, wf.toKeyspace, - strings.Join(wf.options.tables, ","), workflowActionCreate, "", sourceShards, "", false) + strings.Join(wf.options.tables, ","), workflowActionCreate, "", sourceShards, "", defaultWorkflowExecOptions) case "reshard": currentWorkflowType = wrangler.ReshardWorkflow sourceShards := strings.Join(wf.options.sourceShards, ",") @@ -248,7 +240,7 @@ func (wf *workflow) create() { targetShards = sourceShards } err = tstWorkflowExec(t, cell, wf.name, wf.fromKeyspace, wf.toKeyspace, - strings.Join(wf.options.tables, ","), workflowActionCreate, "", sourceShards, targetShards, false) + strings.Join(wf.options.tables, ","), workflowActionCreate, "", sourceShards, targetShards, defaultWorkflowExecOptions) default: panic(fmt.Sprintf("unknown workflow type: %s", wf.typ)) } @@ -266,15 +258,15 @@ func (wf *workflow) create() { } func (wf *workflow) switchTraffic() { - require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionSwitchTraffic, "", "", "", false)) + require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionSwitchTraffic, "", "", "", defaultWorkflowExecOptions)) } func (wf *workflow) reverseTraffic() { - require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionReverseTraffic, "", "", "", false)) + require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionReverseTraffic, "", "", "", defaultWorkflowExecOptions)) } func (wf *workflow) complete() { - require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionComplete, "", "", "", false)) + require.NoError(wf.tc.t, tstWorkflowExec(wf.tc.t, wf.tc.defaultCellName, wf.name, wf.fromKeyspace, wf.toKeyspace, "", workflowActionComplete, "", "", "", defaultWorkflowExecOptions)) } // TestPartialMoveTablesWithSequences enhances TestPartialMoveTables by adding an unsharded keyspace which has a @@ -291,7 +283,7 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { extraVTGateArgs = origExtraVTGateArgs }() - tc := initPartialMoveTablesComplexTestCase(t, "TestPartialMoveTablesComplex") + tc := initPartialMoveTablesComplexTestCase(t) defer tc.teardown() var err error @@ -336,6 +328,7 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { shard := "80-" var wf80Dash, wfDash80 *workflow currentCustomerCount = getCustomerCount(t, "before customer2.80-") + vtgateConn, closeConn := getVTGateConn() t.Run("Start MoveTables on customer2.80-", func(t *testing.T) { // Now setup the customer2 keyspace so we can do a partial move tables for one of the two shards: 80-. defaultRdonly = 0 @@ -353,16 +346,17 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { }) currentCustomerCount = getCustomerCount(t, "after customer2.80-/2") - log.Flush() // This query uses an ID that should always get routed to shard 80- - shard80MinusRoutedQuery := "select name from customer where cid = 1 and noexistcol = 'foo'" + shard80DashRoutedQuery := "select name from customer where cid = 1 and noexistcol = 'foo'" // This query uses an ID that should always get routed to shard -80 - shardMinus80RoutedQuery := "select name from customer where cid = 2 and noexistcol = 'foo'" + shardDash80RoutedQuery := "select name from customer where cid = 2 and noexistcol = 'foo'" // Reset any existing vtgate connection state. - vtgateConn.Close() - vtgateConn = getConnection(t, tc.vc.ClusterConfig.hostname, tc.vc.ClusterConfig.vtgateMySQLPort) + closeConn() + + vtgateConn, closeConn = getVTGateConn() + defer closeConn() t.Run("Confirm routing rules", func(t *testing.T) { // Global routing rules should be in place with everything going to the source keyspace (customer). @@ -378,14 +372,14 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { log.Infof("Testing reverse route (target->source) for shard being switched") _, err = vtgateConn.ExecuteFetch("use `customer2:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.80-.primary", "Query was routed to the target before any SwitchTraffic") log.Infof("Testing reverse route (target->source) for shard NOT being switched") _, err = vtgateConn.ExecuteFetch("use `customer2:-80`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.primary", "Query was routed to the target before any SwitchTraffic") @@ -419,22 +413,22 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { t.Run("Validate shard and tablet type routing", func(t *testing.T) { // No shard targeting - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.primary", "Query was routed to the target before partial SwitchTraffic") // Shard targeting _, err = vtgateConn.ExecuteFetch("use `customer2:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") _, err = vtgateConn.ExecuteFetch("use `customer:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") @@ -505,7 +499,7 @@ func TestPartialMoveTablesWithSequences(t *testing.T) { // We switched traffic, so it's the reverse workflow we want to cancel. reverseWf := wf + "_reverse" reverseKs := sourceKs // customer - err = tstWorkflowExec(t, "", reverseWf, "", reverseKs, "", workflowActionCancel, "", "", "", false) + err = tstWorkflowExec(t, "", reverseWf, "", reverseKs, "", workflowActionCancel, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) output, err := tc.vc.VtctlClient.ExecuteCommandWithOutput("Workflow", fmt.Sprintf("%s.%s", reverseKs, reverseWf), "show") @@ -537,6 +531,8 @@ var newCustomerCount = int64(201) var lastCustomerId int64 func getCustomerCount(t *testing.T, msg string) int64 { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() qr := execVtgateQuery(t, vtgateConn, "", "select count(*) from customer") require.NotNil(t, qr) count, err := qr.Rows[0][0].ToInt64() @@ -545,6 +541,8 @@ func getCustomerCount(t *testing.T, msg string) int64 { } func confirmLastCustomerIdHasIncreased(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() qr := execVtgateQuery(t, vtgateConn, "", "select cid from customer order by cid desc limit 1") require.NotNil(t, qr) currentCustomerId, err := qr.Rows[0][0].ToInt64() @@ -554,6 +552,8 @@ func confirmLastCustomerIdHasIncreased(t *testing.T) { } func insertCustomers(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() for i := int64(1); i < newCustomerCount+1; i++ { execVtgateQuery(t, vtgateConn, "customer@primary", fmt.Sprintf("insert into customer(name) values ('name-%d')", currentCustomerCount+i)) } diff --git a/go/test/endtoend/vreplication/partial_movetables_test.go b/go/test/endtoend/vreplication/partial_movetables_test.go index 5583232fbdc..7155dc9be91 100644 --- a/go/test/endtoend/vreplication/partial_movetables_test.go +++ b/go/test/endtoend/vreplication/partial_movetables_test.go @@ -27,7 +27,6 @@ import ( "github.com/tidwall/gjson" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/wrangler" ) // testCancel() starts and cancels a partial MoveTables for one of the shards which will be actually moved later on. @@ -44,13 +43,16 @@ func testCancel(t *testing.T) { table := "customer2" shard := "80-" // start the partial movetables for 80- - mt := newMoveTables(vc, &moveTables{ - workflowName: workflowName, - targetKeyspace: targetKeyspace, + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, sourceKeyspace: sourceKeyspace, tables: table, sourceShards: shard, - }, moveTablesFlavorRandom) + }, workflowFlavorRandom) mt.Create() checkDenyList := func(keyspace string, expected bool) { @@ -76,14 +78,13 @@ func testCancel(t *testing.T) { } -// TestPartialMoveTablesBasic tests partial move tables by moving each -// customer shard -- -80,80- -- once a a time to customer2. -func TestPartialMoveTablesBasic(t *testing.T) { +func testPartialMoveTablesBasic(t *testing.T, flavor workflowFlavor) { + setSidecarDBName("_vt") origDefaultRdonly := defaultRdonly defer func() { defaultRdonly = origDefaultRdonly }() - defaultRdonly = 1 + defaultRdonly = 0 origExtraVTGateArgs := extraVTGateArgs // We need to enable shard routing for partial movetables routing. // And we need to disable schema change tracking in vtgate as we want @@ -100,15 +101,29 @@ func TestPartialMoveTablesBasic(t *testing.T) { extraVTGateArgs = origExtraVTGateArgs }() vc = setupMinimalCluster(t) - defer vtgateConn.Close() - defer vc.TearDown(t) - setupMinimalCustomerKeyspace(t) + defer vc.TearDown() + sourceKeyspace := "product" + targetKeyspace := "customer" + workflowName := "wf1" + targetTabs := setupMinimalCustomerKeyspace(t) + targetTab80Dash := targetTabs["80-"] + targetTabDash80 := targetTabs["-80"] + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + tables: "customer,loadtest,customer2", + }, flavor) + mt.Create() - // Move customer table from unsharded product keyspace to - // sharded customer keyspace. - createMoveTablesWorkflow(t, "customer,loadtest,customer2") - tstWorkflowSwitchReadsAndWrites(t) - tstWorkflowComplete(t) + waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", targetKeyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) + catchup(t, targetTab80Dash, workflowName, "MoveTables") + vdiff(t, targetKeyspace, workflowName, defaultCellName, false, true, nil) + mt.SwitchReadsAndWrites() + mt.Complete() emptyGlobalRoutingRules := "{}\n" @@ -129,20 +144,35 @@ func TestPartialMoveTablesBasic(t *testing.T) { // move tables for one of the two shards: 80-. defaultRdonly = 0 setupCustomer2Keyspace(t) - testCancel(t) - currentWorkflowType = wrangler.MoveTablesWorkflow - wfName := "partial80Dash" - sourceKs := "customer" - targetKs := "customer2" + // We specify the --shards flag for one of the workflows to confirm that both the MoveTables and Workflow commands + // work the same with or without the flag. + workflowExecOptsPartialDash80 := &workflowExecOptions{ + deferSecondaryKeys: true, + shardSubset: "-80", + } + workflowExecOptsPartial80Dash := &workflowExecOptions{ + deferSecondaryKeys: true, + } + var err error + workflowName = "partial80Dash" + sourceKeyspace = "customer" + targetKeyspace = "customer2" shard := "80-" - ksWf := fmt.Sprintf("%s.%s", targetKs, wfName) + tables := "customer,loadtest" + mt80Dash := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + tables: tables, + sourceShards: shard, + }, flavor) + mt80Dash.Create() - // start the partial movetables for 80- - err := tstWorkflowExec(t, defaultCellName, wfName, sourceKs, targetKs, - "customer,loadtest", workflowActionCreate, "", shard, "", false) - require.NoError(t, err) var lg *loadGenerator if runWithLoad { // start load after routing rules are set, otherwise we end up with ambiguous tables lg = newLoadGenerator(t, vc) @@ -151,11 +181,12 @@ func TestPartialMoveTablesBasic(t *testing.T) { }() lg.waitForCount(1000) } + waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", targetKeyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) + catchup(t, targetTab80Dash, workflowName, "MoveTables") + vdiff(t, targetKeyspace, workflowName, defaultCellName, false, true, nil) - targetTab1 = vc.getPrimaryTablet(t, targetKs, shard) - catchup(t, targetTab1, wfName, "Partial MoveTables Customer to Customer2") - vdiffSideBySide(t, ksWf, "") - + vtgateConn, closeConn := getVTGateConn() + defer closeConn() waitForRowCount(t, vtgateConn, "customer", "customer", 3) // customer: all shards waitForRowCount(t, vtgateConn, "customer2", "customer", 3) // customer2: all shards waitForRowCount(t, vtgateConn, "customer2:80-", "customer", 2) // customer2: 80- @@ -179,9 +210,9 @@ func TestPartialMoveTablesBasic(t *testing.T) { } // This query uses an ID that should always get routed to shard 80- - shard80MinusRoutedQuery := "select name from customer where cid = 1 and noexistcol = 'foo'" + shard80DashRoutedQuery := "select name from customer where cid = 1 and noexistcol = 'foo'" // This query uses an ID that should always get routed to shard -80 - shardMinus80RoutedQuery := "select name from customer where cid = 2 and noexistcol = 'foo'" + shardDash80RoutedQuery := "select name from customer where cid = 2 and noexistcol = 'foo'" // reset any existing vtgate connection state vtgateConn.Close() @@ -202,22 +233,19 @@ func TestPartialMoveTablesBasic(t *testing.T) { log.Infof("Testing reverse route (target->source) for shard being switched") _, err = vtgateConn.ExecuteFetch("use `customer2:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.80-.primary", "Query was routed to the target before any SwitchTraffic") log.Infof("Testing reverse route (target->source) for shard NOT being switched") _, err = vtgateConn.ExecuteFetch("use `customer2:-80`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.primary", "Query was routed to the target before any SwitchTraffic") // Switch all traffic for the shard - require.NoError(t, tstWorkflowExec(t, "", wfName, "", targetKs, "", workflowActionSwitchTraffic, "", "", "", false)) - expectedSwitchOutput := fmt.Sprintf("SwitchTraffic was successful for workflow %s.%s\nStart State: Reads Not Switched. Writes Not Switched\nCurrent State: Reads partially switched, for shards: %s. Writes partially switched, for shards: %s\n\n", - targetKs, wfName, shard, shard) - require.Equal(t, expectedSwitchOutput, lastOutput) + mt80Dash.SwitchReadsAndWrites() // Confirm global routing rules -- everything should still be routed // to the source side, customer, globally. @@ -233,69 +261,70 @@ func TestPartialMoveTablesBasic(t *testing.T) { defer vtgateConn.Close() // No shard targeting - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.primary", "Query was routed to the target before partial SwitchTraffic") // Shard targeting _, err = vtgateConn.ExecuteFetch("use `customer2:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") _, err = vtgateConn.ExecuteFetch("use `customer:80-`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.primary", "Query was routed to the source after partial SwitchTraffic") // Tablet type targeting _, err = vtgateConn.ExecuteFetch("use `customer2@replica`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.replica", "Query was routed to the source after partial SwitchTraffic") - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.replica", "Query was routed to the target before partial SwitchTraffic") _, err = vtgateConn.ExecuteFetch("use `customer@replica`", 0, false) require.NoError(t, err) - _, err = vtgateConn.ExecuteFetch(shard80MinusRoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shard80DashRoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer2.80-.replica", "Query was routed to the source after partial SwitchTraffic") - _, err = vtgateConn.ExecuteFetch(shardMinus80RoutedQuery, 0, false) + _, err = vtgateConn.ExecuteFetch(shardDash80RoutedQuery, 0, false) require.Error(t, err) require.Contains(t, err.Error(), "target: customer.-80.replica", "Query was routed to the target before partial SwitchTraffic") - // We cannot Complete a partial move tables at the moment because // it will find that all traffic has (obviously) not been switched. - err = tstWorkflowExec(t, "", wfName, "", targetKs, "", workflowActionComplete, "", "", "", false) + err = tstWorkflowExec(t, "", workflowName, "", targetKs, "", workflowActionComplete, "", "", "", workflowExecOptsPartial80Dash) require.Error(t, err) // Confirm global routing rules: -80 should still be be routed to customer // while 80- should be routed to customer2. require.Equal(t, halfCutoverShardRoutingRules, getShardRoutingRules(t)) - // Now move the other shard: -80 - wfName = "partialDash80" shard = "-80" - ksWf = fmt.Sprintf("%s.%s", targetKs, wfName) - // Start the partial movetables for -80, 80- has already been switched - err = tstWorkflowExec(t, defaultCellName, wfName, sourceKs, targetKs, - "customer,loadtest", workflowActionCreate, "", shard, "", false) - require.NoError(t, err) - targetTab2 := vc.getPrimaryTablet(t, targetKs, shard) - catchup(t, targetTab2, wfName, "Partial MoveTables Customer to Customer2: -80") - vdiffSideBySide(t, ksWf, "") + workflowName = "partialDash80" + mtDash80 := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + tables: tables, + sourceShards: shard, + }, flavor) + mtDash80.Create() - // Switch all traffic for the shard - require.NoError(t, tstWorkflowExec(t, "", wfName, "", targetKs, "", workflowActionSwitchTraffic, "", "", "", false)) - expectedSwitchOutput = fmt.Sprintf("SwitchTraffic was successful for workflow %s.%s\nStart State: Reads partially switched, for shards: 80-. Writes partially switched, for shards: 80-\nCurrent State: All Reads Switched. All Writes Switched\n\n", - targetKs, wfName) - require.Equal(t, expectedSwitchOutput, lastOutput) + waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", targetKeyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) + + catchup(t, targetTabDash80, workflowName, "MoveTables") + vdiff(t, targetKeyspace, workflowName, defaultCellName, false, true, nil) + mtDash80.SwitchReadsAndWrites() // Confirm global routing rules: everything should still be routed // to the source side, customer, globally. @@ -304,27 +333,33 @@ func TestPartialMoveTablesBasic(t *testing.T) { // Confirm shard routing rules: all shards should be routed to the // target side (customer2). require.Equal(t, postCutoverShardRoutingRules, getShardRoutingRules(t)) - lg.stop() // Cancel both reverse workflows (as we've done the cutover), which should // clean up both the global routing rules and the shard routing rules. for _, wf := range []string{"partialDash80", "partial80Dash"} { // We switched traffic, so it's the reverse workflow we want to cancel. + var opts *workflowExecOptions + switch wf { + case "partialDash80": + opts = workflowExecOptsPartialDash80 + case "partial80Dash": + opts = workflowExecOptsPartial80Dash + } reverseWf := wf + "_reverse" - reverseKs := sourceKs // customer - err = tstWorkflowExec(t, "", reverseWf, "", reverseKs, "", workflowActionCancel, "", "", "", false) + reverseKs := sourceKeyspace + err = tstWorkflowExec(t, "", reverseWf, "", reverseKs, "", workflowActionCancel, "", "", "", opts) require.NoError(t, err) - output, err := vc.VtctlClient.ExecuteCommandWithOutput("Workflow", fmt.Sprintf("%s.%s", reverseKs, reverseWf), "show") + output, err := vc.VtctlClient.ExecuteCommandWithOutput("Workflow", "--", "--shards", opts.shardSubset, fmt.Sprintf("%s.%s", reverseKs, reverseWf), "show") require.Error(t, err) require.Contains(t, output, "no streams found") // Delete the original workflow originalKsWf := fmt.Sprintf("%s.%s", targetKs, wf) - _, err = vc.VtctlClient.ExecuteCommandWithOutput("Workflow", originalKsWf, "delete") + _, err = vc.VtctlClient.ExecuteCommandWithOutput("Workflow", "--", "--shards", opts.shardSubset, originalKsWf, "delete") require.NoError(t, err) - output, err = vc.VtctlClient.ExecuteCommandWithOutput("Workflow", originalKsWf, "show") + output, err = vc.VtctlClient.ExecuteCommandWithOutput("Workflow", "--", "--shards", opts.shardSubset, originalKsWf, "show") require.Error(t, err) require.Contains(t, output, "no streams found") } @@ -336,5 +371,15 @@ func TestPartialMoveTablesBasic(t *testing.T) { // Confirm that the shard routing rules are now gone. require.Equal(t, emptyShardRoutingRules, getShardRoutingRules(t)) +} +// TestPartialMoveTablesBasic tests partial move tables by moving each +// customer shard -- -80,80- -- once a a time to customer2. +// We test with both the vtctlclient and vtctldclient flavors. +func TestPartialMoveTablesBasic(t *testing.T) { + for _, flavor := range workflowFlavors { + t.Run(workflowFlavorNames[flavor], func(t *testing.T) { + testPartialMoveTablesBasic(t, flavor) + }) + } } diff --git a/go/test/endtoend/vreplication/performance_test.go b/go/test/endtoend/vreplication/performance_test.go index 9e0ae797e72..6940665c842 100644 --- a/go/test/endtoend/vreplication/performance_test.go +++ b/go/test/endtoend/vreplication/performance_test.go @@ -23,8 +23,6 @@ import ( "time" "vitess.io/vitess/go/test/endtoend/cluster" - - "github.com/stretchr/testify/require" ) func TestReplicationStress(t *testing.T) { @@ -50,23 +48,13 @@ create table customer(cid int, name varbinary(128), meta json default null, typ const sourceKs = "stress_src" const targetKs = "stress_tgt" - allCells := []string{defaultCellName} - allCellNames = defaultCellName - - vc = NewVitessCluster(t, "TestReplicationStress", allCells, mainClusterConfig) - require.NotNil(t, vc) + vc = NewVitessCluster(t, nil) + defer vc.TearDown() - defer vc.TearDown(t) - - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, sourceKs, "0", initialStressVSchema, initialStressSchema, 0, 0, 100, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - - err := cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) diff --git a/go/test/endtoend/vreplication/resharding_workflows_v2_test.go b/go/test/endtoend/vreplication/resharding_workflows_v2_test.go index 338310fdf14..0acb922cd30 100644 --- a/go/test/endtoend/vreplication/resharding_workflows_v2_test.go +++ b/go/test/endtoend/vreplication/resharding_workflows_v2_test.go @@ -40,9 +40,7 @@ const ( targetKs = "customer" ksWorkflow = targetKs + "." + workflowName reverseKsWorkflow = sourceKs + "." + workflowName + "_reverse" - tablesToMove = "customer" defaultCellName = "zone1" - readQuery = "select cid from customer" ) const ( @@ -61,9 +59,19 @@ var ( currentWorkflowType wrangler.VReplicationWorkflowType ) +type workflowExecOptions struct { + deferSecondaryKeys bool + atomicCopy bool + shardSubset string +} + +var defaultWorkflowExecOptions = &workflowExecOptions{ + deferSecondaryKeys: true, +} + func createReshardWorkflow(t *testing.T, sourceShards, targetShards string) error { err := tstWorkflowExec(t, defaultCellName, workflowName, targetKs, targetKs, - "", workflowActionCreate, "", sourceShards, targetShards, false) + "", workflowActionCreate, "", sourceShards, targetShards, defaultWorkflowExecOptions) require.NoError(t, err) waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) confirmTablesHaveSecondaryKeys(t, []*cluster.VttabletProcess{targetTab1}, targetKs, "") @@ -75,10 +83,10 @@ func createReshardWorkflow(t *testing.T, sourceShards, targetShards string) erro func createMoveTablesWorkflow(t *testing.T, tables string) { if tables == "" { - tables = tablesToMove + tables = "customer" } err := tstWorkflowExec(t, defaultCellName, workflowName, sourceKs, targetKs, - tables, workflowActionCreate, "", "", "", false) + tables, workflowActionCreate, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) confirmTablesHaveSecondaryKeys(t, []*cluster.VttabletProcess{targetTab1}, targetKs, tables) @@ -88,10 +96,12 @@ func createMoveTablesWorkflow(t *testing.T, tables string) { } func tstWorkflowAction(t *testing.T, action, tabletTypes, cells string) error { - return tstWorkflowExec(t, cells, workflowName, sourceKs, targetKs, tablesToMove, action, tabletTypes, "", "", false) + return tstWorkflowExec(t, cells, workflowName, sourceKs, targetKs, "customer", action, tabletTypes, "", "", defaultWorkflowExecOptions) } -func tstWorkflowExec(t *testing.T, cells, workflow, sourceKs, targetKs, tables, action, tabletTypes, sourceShards, targetShards string, atomicCopy bool) error { +func tstWorkflowExec(t *testing.T, cells, workflow, sourceKs, targetKs, tables, action, tabletTypes, + sourceShards, targetShards string, options *workflowExecOptions) error { + var args []string if currentWorkflowType == wrangler.MoveTablesWorkflow { args = append(args, "MoveTables") @@ -104,7 +114,7 @@ func tstWorkflowExec(t *testing.T, cells, workflow, sourceKs, targetKs, tables, if BypassLagCheck { args = append(args, "--max_replication_lag_allowed=2542087h") } - if atomicCopy { + if options.atomicCopy { args = append(args, "--atomic-copy") } switch action { @@ -125,11 +135,16 @@ func tstWorkflowExec(t *testing.T, cells, workflow, sourceKs, targetKs, tables, // Test new experimental --defer-secondary-keys flag switch currentWorkflowType { case wrangler.MoveTablesWorkflow, wrangler.MigrateWorkflow, wrangler.ReshardWorkflow: - if !atomicCopy { + + if !options.atomicCopy && options.deferSecondaryKeys { args = append(args, "--defer-secondary-keys") } args = append(args, "--initialize-target-sequences") // Only used for MoveTables } + default: + if options.shardSubset != "" { + args = append(args, "--shards", options.shardSubset) + } } if cells != "" { args = append(args, "--cells", cells) @@ -215,9 +230,12 @@ func validateReadsRoute(t *testing.T, tabletTypes string, tablet *cluster.Vttabl if tabletTypes == "" { tabletTypes = "replica,rdonly" } + vtgateConn, closeConn := getVTGateConn() + defer closeConn() for _, tt := range []string{"replica", "rdonly"} { destination := fmt.Sprintf("%s:%s@%s", tablet.Keyspace, tablet.Shard, tt) if strings.Contains(tabletTypes, tt) { + readQuery := "select * from customer" assertQueryExecutesOnTablet(t, vtgateConn, tablet, destination, readQuery, readQuery) } } @@ -232,6 +250,8 @@ func validateReadsRouteToTarget(t *testing.T, tabletTypes string) { } func validateWritesRouteToSource(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() insertQuery := "insert into customer(name, cid) values('tempCustomer2', 200)" matchInsertQuery := "insert into customer(`name`, cid) values" assertQueryExecutesOnTablet(t, vtgateConn, sourceTab, "customer", insertQuery, matchInsertQuery) @@ -239,6 +259,8 @@ func validateWritesRouteToSource(t *testing.T) { } func validateWritesRouteToTarget(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() insertQuery := "insert into customer(name, cid) values('tempCustomer3', 101)" matchInsertQuery := "insert into customer(`name`, cid) values" assertQueryExecutesOnTablet(t, vtgateConn, targetTab2, "customer", insertQuery, matchInsertQuery) @@ -250,7 +272,7 @@ func validateWritesRouteToTarget(t *testing.T) { func revert(t *testing.T, workflowType string) { switchWrites(t, workflowType, ksWorkflow, true) validateWritesRouteToSource(t) - switchReadsNew(t, workflowType, allCellNames, ksWorkflow, true) + switchReadsNew(t, workflowType, getCellNames(nil), ksWorkflow, true) validateReadsRouteToSource(t, "replica") // cancel the workflow to cleanup @@ -284,8 +306,7 @@ func TestBasicV2Workflows(t *testing.T) { }() vc = setupCluster(t) - defer vtgateConn.Close() - defer vc.TearDown(t) + defer vc.TearDown() // Internal tables like the lifecycle ones for OnlineDDL should be ignored ddlSQL := "ALTER TABLE customer MODIFY cid bigint UNSIGNED" @@ -293,7 +314,6 @@ func TestBasicV2Workflows(t *testing.T) { testMoveTablesV2Workflow(t) testReshardV2Workflow(t) - log.Flush() } func getVtctldGRPCURL() string { @@ -317,19 +337,21 @@ func testVSchemaForSequenceAfterMoveTables(t *testing.T) { // use MoveTables to move customer2 from product to customer using currentWorkflowType = wrangler.MoveTablesWorkflow err := tstWorkflowExec(t, defaultCellName, "wf2", sourceKs, targetKs, - "customer2", workflowActionCreate, "", "", "", false) + "customer2", workflowActionCreate, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) waitForWorkflowState(t, vc, "customer.wf2", binlogdatapb.VReplicationWorkflowState_Running.String()) waitForLowLag(t, "customer", "wf2") err = tstWorkflowExec(t, defaultCellName, "wf2", sourceKs, targetKs, - "", workflowActionSwitchTraffic, "", "", "", false) + "", workflowActionSwitchTraffic, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) err = tstWorkflowExec(t, defaultCellName, "wf2", sourceKs, targetKs, - "", workflowActionComplete, "", "", "", false) + "", workflowActionComplete, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) + vtgateConn, closeConn := getVTGateConn() + defer closeConn() // sanity check output, err := vc.VtctlClient.ExecuteCommandWithOutput("GetVSchema", "product") require.NoError(t, err) @@ -352,16 +374,16 @@ func testVSchemaForSequenceAfterMoveTables(t *testing.T) { // use MoveTables to move customer2 back to product. Note that now the table has an associated sequence err = tstWorkflowExec(t, defaultCellName, "wf3", targetKs, sourceKs, - "customer2", workflowActionCreate, "", "", "", false) + "customer2", workflowActionCreate, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) waitForWorkflowState(t, vc, "product.wf3", binlogdatapb.VReplicationWorkflowState_Running.String()) waitForLowLag(t, "product", "wf3") err = tstWorkflowExec(t, defaultCellName, "wf3", targetKs, sourceKs, - "", workflowActionSwitchTraffic, "", "", "", false) + "", workflowActionSwitchTraffic, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) err = tstWorkflowExec(t, defaultCellName, "wf3", targetKs, sourceKs, - "", workflowActionComplete, "", "", "", false) + "", workflowActionComplete, "", "", "", defaultWorkflowExecOptions) require.NoError(t, err) // sanity check @@ -394,6 +416,8 @@ func testReplicatingWithPKEnumCols(t *testing.T) { // when we re-insert the same row values and ultimately VDiff shows the table as // being identical in both keyspaces. + vtgateConn, closeConn := getVTGateConn() + defer closeConn() // typ is an enum, with soho having a stored and binlogged value of 2 deleteQuery := "delete from customer where cid = 2 and typ = 'soho'" insertQuery := "insert into customer(cid, name, typ, sport, meta) values(2, 'Paül','soho','cricket',convert(x'7b7d' using utf8mb4))" @@ -406,6 +430,8 @@ func testReplicatingWithPKEnumCols(t *testing.T) { } func testReshardV2Workflow(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() currentWorkflowType = wrangler.ReshardWorkflow // create internal tables on the original customer shards that should be @@ -433,6 +459,8 @@ func testReshardV2Workflow(t *testing.T) { } func testMoveTablesV2Workflow(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() currentWorkflowType = wrangler.MoveTablesWorkflow // test basic forward and reverse flows @@ -596,30 +624,17 @@ func testRestOfWorkflow(t *testing.T) { } func setupCluster(t *testing.T) *VitessCluster { - cells := []string{"zone1", "zone2"} - - vc = NewVitessCluster(t, "TestBasicVreplicationWorkflow", cells, mainClusterConfig) - require.NotNil(t, vc) - defaultCellName := "zone1" - allCellNames = defaultCellName - defaultCell = vc.Cells[defaultCellName] + vc = NewVitessCluster(t, &clusterOptions{cells: []string{"zone1", "zone2"}}) zone1 := vc.Cells["zone1"] zone2 := vc.Cells["zone2"] vc.AddKeyspace(t, []*Cell{zone1, zone2}, "product", "0", initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, nil) - vtgate = zone1.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", "product", "0"), 2, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", "product", "0"), 1, 30*time.Second)) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer getVTGateConn() verifyClusterHealth(t, vc) insertInitialData(t) - + defaultCell := vc.Cells[vc.CellNames[0]] sourceTab = vc.Cells[defaultCell.Name].Keyspaces["product"].Shards["0"].Tablets["zone1-100"].Vttablet sourceReplicaTab = vc.Cells[defaultCell.Name].Keyspaces["product"].Shards["0"].Tablets["zone1-101"].Vttablet sourceRdonlyTab = vc.Cells[defaultCell.Name].Keyspaces["product"].Shards["0"].Tablets["zone1-102"].Vttablet @@ -632,12 +647,7 @@ func setupCustomerKeyspace(t *testing.T) { customerVSchema, customerSchema, defaultReplicas, defaultRdonly, 200, nil); err != nil { t.Fatal(err) } - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, "customer", "-80")) - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, "customer", "80-")) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", "customer", "-80"), 2, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", "customer", "80-"), 2, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", "customer", "-80"), 1, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", "customer", "80-"), 1, 30*time.Second)) + defaultCell := vc.Cells[vc.CellNames[0]] custKs := vc.Cells[defaultCell.Name].Keyspaces["customer"] targetTab1 = custKs.Shards["-80"].Tablets["zone1-200"].Vttablet targetTab2 = custKs.Shards["80-"].Tablets["zone1-300"].Vttablet @@ -652,33 +662,17 @@ func setupCustomer2Keyspace(t *testing.T) { customerVSchema, customerSchema, 0, 0, 1200, nil); err != nil { t.Fatal(err) } - for _, c2shard := range c2shards { - err := cluster.WaitForHealthyShard(vc.VtctldClient, c2keyspace, c2shard) - require.NoError(t, err) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", c2keyspace, c2shard), 1, 30*time.Second)) - } } func setupMinimalCluster(t *testing.T) *VitessCluster { - cells := []string{"zone1"} + vc = NewVitessCluster(t, nil) - vc = NewVitessCluster(t, "TestBasicVreplicationWorkflow", cells, mainClusterConfig) - require.NotNil(t, vc) - defaultCellName := "zone1" - allCellNames = defaultCellName - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] zone1 := vc.Cells["zone1"] vc.AddKeyspace(t, []*Cell{zone1}, "product", "0", initialProductVSchema, initialProductSchema, 0, 0, 100, nil) - vtgate = zone1.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", "product", "0"), 1, 30*time.Second)) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) verifyClusterHealth(t, vc) insertInitialData(t) @@ -687,23 +681,24 @@ func setupMinimalCluster(t *testing.T) *VitessCluster { return vc } -func setupMinimalCustomerKeyspace(t *testing.T) { +func setupMinimalCustomerKeyspace(t *testing.T) map[string]*cluster.VttabletProcess { + tablets := make(map[string]*cluster.VttabletProcess) if _, err := vc.AddKeyspace(t, []*Cell{vc.Cells["zone1"]}, "customer", "-80,80-", customerVSchema, customerSchema, 0, 0, 200, nil); err != nil { t.Fatal(err) } - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, "customer", "-80")) - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, "customer", "80-")) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", "customer", "-80"), 1, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", "customer", "80-"), 1, 30*time.Second)) + defaultCell := vc.Cells[vc.CellNames[0]] custKs := vc.Cells[defaultCell.Name].Keyspaces["customer"] targetTab1 = custKs.Shards["-80"].Tablets["zone1-200"].Vttablet targetTab2 = custKs.Shards["80-"].Tablets["zone1-300"].Vttablet + tablets["-80"] = targetTab1 + tablets["80-"] = targetTab2 + return tablets } func TestSwitchReadsWritesInAnyOrder(t *testing.T) { vc = setupCluster(t) - defer vc.TearDown(t) + defer vc.TearDown() moveCustomerTableSwitchFlows(t, []*Cell{vc.Cells["zone1"]}, "zone1") } @@ -735,7 +730,7 @@ func moveCustomerTableSwitchFlows(t *testing.T, cells []*Cell, sourceCellOrAlias catchup(t, targetTab2, workflow, workflowType) vdiffSideBySide(t, ksWorkflow, "") } - + allCellNames := getCellNames(cells) var switchReadsFollowedBySwitchWrites = func() { moveTablesAndWait() @@ -815,16 +810,9 @@ func moveCustomerTableSwitchFlows(t *testing.T, cells []*Cell, sourceCellOrAlias func createAdditionalCustomerShards(t *testing.T, shards string) { ksName := "customer" + defaultCell := vc.Cells[vc.CellNames[0]] keyspace := vc.Cells[defaultCell.Name].Keyspaces[ksName] require.NoError(t, vc.AddShards(t, []*Cell{defaultCell, vc.Cells["zone2"]}, keyspace, shards, defaultReplicas, defaultRdonly, 400, targetKsOpts)) - arrTargetShardNames := strings.Split(shards, ",") - - for _, shardName := range arrTargetShardNames { - err := cluster.WaitForHealthyShard(vc.VtctldClient, ksName, shardName) - require.NoError(t, err) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", ksName, shardName), 2, 30*time.Second)) - require.NoError(t, vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", ksName, shardName), 1, 30*time.Second)) - } custKs := vc.Cells[defaultCell.Name].Keyspaces[ksName] targetTab2 = custKs.Shards["80-c0"].Tablets["zone1-600"].Vttablet targetTab1 = custKs.Shards["40-80"].Tablets["zone1-500"].Vttablet diff --git a/go/test/endtoend/vreplication/schema/fkext/materialize_schema.sql b/go/test/endtoend/vreplication/schema/fkext/materialize_schema.sql new file mode 100644 index 00000000000..6af8ca99b94 --- /dev/null +++ b/go/test/endtoend/vreplication/schema/fkext/materialize_schema.sql @@ -0,0 +1,2 @@ +create table parent_copy(id int, name varchar(128), primary key(id)) engine=innodb; +create table child_copy(id int, parent_id int, name varchar(128), primary key(id)) engine=innodb; \ No newline at end of file diff --git a/go/test/endtoend/vreplication/schema/fkext/source_schema.sql b/go/test/endtoend/vreplication/schema/fkext/source_schema.sql new file mode 100644 index 00000000000..01b788338b6 --- /dev/null +++ b/go/test/endtoend/vreplication/schema/fkext/source_schema.sql @@ -0,0 +1,2 @@ +create table if not exists parent(id int, name varchar(128), primary key(id)) engine=innodb; +create table if not exists child(id int, parent_id int, name varchar(128), primary key(id), foreign key(parent_id) references parent(id) on delete cascade) engine=innodb; \ No newline at end of file diff --git a/go/test/endtoend/vreplication/schema/fkext/source_vschema.json b/go/test/endtoend/vreplication/schema/fkext/source_vschema.json new file mode 100644 index 00000000000..01cde0d643d --- /dev/null +++ b/go/test/endtoend/vreplication/schema/fkext/source_vschema.json @@ -0,0 +1,6 @@ +{ + "tables": { + "parent": {}, + "child": {} + } +} diff --git a/go/test/endtoend/vreplication/schema/fkext/target1_vschema.json b/go/test/endtoend/vreplication/schema/fkext/target1_vschema.json new file mode 100644 index 00000000000..dc89232fbbb --- /dev/null +++ b/go/test/endtoend/vreplication/schema/fkext/target1_vschema.json @@ -0,0 +1,28 @@ +{ + "sharded": false, + "foreignKeyMode": "managed", + "vindexes": { + "reverse_bits": { + "type": "reverse_bits" + } + }, + "tables": { + "parent": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + } + ] + }, + "child": { + "column_vindexes": [ + { + "column": "parent_id", + "name": "reverse_bits" + } + ] + } + + } +} diff --git a/go/test/endtoend/vreplication/schema/fkext/target2_vschema.json b/go/test/endtoend/vreplication/schema/fkext/target2_vschema.json new file mode 100644 index 00000000000..06e851a9007 --- /dev/null +++ b/go/test/endtoend/vreplication/schema/fkext/target2_vschema.json @@ -0,0 +1,43 @@ +{ + "sharded": true, + "foreignKeyMode": "managed", + "vindexes": { + "reverse_bits": { + "type": "reverse_bits" + } + }, + "tables": { + "parent": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + } + ] + }, + "child": { + "column_vindexes": [ + { + "column": "parent_id", + "name": "reverse_bits" + } + ] + }, + "parent_copy": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + } + ] + }, + "child_copy": { + "column_vindexes": [ + { + "column": "parent_id", + "name": "reverse_bits" + } + ] + } + } +} diff --git a/go/test/endtoend/vreplication/sidecardb_test.go b/go/test/endtoend/vreplication/sidecardb_test.go index ef05e051be2..d2a4ec6df07 100644 --- a/go/test/endtoend/vreplication/sidecardb_test.go +++ b/go/test/endtoend/vreplication/sidecardb_test.go @@ -38,7 +38,7 @@ var ddls1, ddls2 []string func init() { sidecarDBTables = []string{"copy_state", "dt_participant", "dt_state", "heartbeat", "post_copy_action", "redo_state", - "redo_statement", "reparent_journal", "resharding_journal", "schema_migrations", "schema_version", "schemacopy", "tables", + "redo_statement", "reparent_journal", "resharding_journal", "schema_migrations", "schema_version", "tables", "vdiff", "vdiff_log", "vdiff_table", "views", "vreplication", "vreplication_log"} numSidecarDBTables = len(sidecarDBTables) ddls1 = []string{ @@ -58,15 +58,8 @@ func prs(t *testing.T, keyspace, shard string) { // TestSidecarDB launches a Vitess cluster and ensures that the expected sidecar tables are created. We also drop/alter // tables and ensure the next tablet init will recreate the sidecar database to the desired schema. func TestSidecarDB(t *testing.T) { - cells := []string{"zone1"} - - vc = NewVitessCluster(t, "TestSidecarDB", cells, mainClusterConfig) - require.NotNil(t, vc) - allCellNames = "zone1" - defaultCellName := "zone1" - defaultCell = vc.Cells[defaultCellName] - - defer vc.TearDown(t) + vc = NewVitessCluster(t, nil) + defer vc.TearDown() keyspace := "product" shard := "0" @@ -74,7 +67,7 @@ func TestSidecarDB(t *testing.T) { cell1 := vc.Cells[defaultCellName] tablet100 := fmt.Sprintf("%s-100", defaultCellName) tablet101 := fmt.Sprintf("%s-101", defaultCellName) - vc.AddKeyspace(t, []*Cell{cell1}, keyspace, shard, initialProductVSchema, initialProductSchema, 1, 0, 100, sourceKsOpts) + vc.AddKeyspace(t, []*Cell{cell1}, keyspace, "0", initialProductVSchema, initialProductSchema, 1, 0, 100, sourceKsOpts) shard0 := vc.Cells[defaultCellName].Keyspaces[keyspace].Shards[shard] tablet100Port := shard0.Tablets[tablet100].Vttablet.Port tablet101Port := shard0.Tablets[tablet101].Vttablet.Port diff --git a/go/test/endtoend/vreplication/time_zone_test.go b/go/test/endtoend/vreplication/time_zone_test.go index 2d0d1eeaf0b..2c0a9a4f5a5 100644 --- a/go/test/endtoend/vreplication/time_zone_test.go +++ b/go/test/endtoend/vreplication/time_zone_test.go @@ -32,31 +32,21 @@ import ( // TestMoveTablesTZ tests the conversion of datetime based on the source timezone passed to the MoveTables workflow func TestMoveTablesTZ(t *testing.T) { - allCellNames = "zone1" - defaultCellName := "zone1" workflow := "tz" sourceKs := "product" targetKs := "customer" - shard := "0" ksWorkflow := fmt.Sprintf("%s.%s", targetKs, workflow) ksReverseWorkflow := fmt.Sprintf("%s.%s_reverse", sourceKs, workflow) - vc = NewVitessCluster(t, "TestCellAliasVreplicationWorkflow", []string{"zone1"}, mainClusterConfig) - require.NotNil(t, vc) - defaultCell = vc.Cells[defaultCellName] + vc = NewVitessCluster(t, nil) + defer vc.TearDown() + defaultCell := vc.Cells[vc.CellNames[0]] cells := []*Cell{defaultCell} - defer vc.TearDown(t) - cell1 := vc.Cells["zone1"] vc.AddKeyspace(t, []*Cell{cell1}, sourceKs, "0", initialProductVSchema, initialProductSchema, 0, 0, 100, sourceKsOpts) - vtgate = cell1.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) - require.NoError(t, err) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) @@ -66,7 +56,7 @@ func TestMoveTablesTZ(t *testing.T) { // it seems to take some time for the mysql server to load time zone info after the tables in mysql db have been populated loadTimeZoneInfo := func(tab *cluster.VttabletProcess, sql, timezone string) { - _, err := tab.QueryTabletWithDB(timeZoneSQL, "mysql") + err := tab.MultiQueryTabletWithDB(timeZoneSQL, "mysql") require.NoError(t, err) timer := time.NewTimer(1 * time.Minute) for { @@ -90,10 +80,6 @@ func TestMoveTablesTZ(t *testing.T) { if _, err := vc.AddKeyspace(t, cells, targetKs, "0", customerVSchema, customerSchema, defaultReplicas, defaultRdonly, 200, targetKsOpts); err != nil { t.Fatal(err) } - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) - require.NoError(t, err) - - defaultCell := vc.Cells["zone1"] custKs := vc.Cells[defaultCell.Name].Keyspaces[targetKs] customerTab := custKs.Shards["0"].Tablets["zone1-200"].Vttablet diff --git a/go/test/endtoend/vreplication/vdiff2_test.go b/go/test/endtoend/vreplication/vdiff2_test.go index 72b09e8fede..08f5bb8926d 100644 --- a/go/test/endtoend/vreplication/vdiff2_test.go +++ b/go/test/endtoend/vreplication/vdiff2_test.go @@ -18,15 +18,27 @@ package vreplication import ( "fmt" + "math" + "runtime" + "strconv" "strings" "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" ) type testCase struct { @@ -40,10 +52,13 @@ type testCase struct { retryInsert string resume bool // test resume functionality with this workflow // If testing resume, what new rows should be diff'd. These rows must have a PK > all initial rows and retry rows. - resumeInsert string - stop bool // test stop functionality with this workflow - testCLIErrors bool // test CLI errors against this workflow (only needs to be done once) - testCLICreateWait bool // test CLI create and wait until done against this workflow (only needs to be done once) + resumeInsert string + stop bool // test stop functionality with this workflow + testCLIErrors bool // test CLI errors against this workflow (only needs to be done once) + testCLICreateWait bool // test CLI create and wait until done against this workflow (only needs to be done once) + testCLIFlagHandling bool // test vtctldclient flag handling from end-to-end + extraVDiffFlags map[string]string + vdiffCount int64 // Keep track of the number of vdiffs created to test the stats } const ( @@ -55,21 +70,25 @@ const ( var testCases = []*testCase{ { - name: "MoveTables/unsharded to two shards", - workflow: "p1c2", - typ: "MoveTables", - sourceKs: "product", - targetKs: "customer", - sourceShards: "0", - targetShards: "-80,80-", - tabletBaseID: 200, - tables: "customer,Lead,Lead-1", - autoRetryError: true, - retryInsert: `insert into customer(cid, name, typ) values(1991234, 'Testy McTester', 'soho')`, - resume: true, - resumeInsert: `insert into customer(cid, name, typ) values(1992234, 'Testy McTester (redux)', 'enterprise')`, - testCLIErrors: true, // test for errors in the simplest workflow - testCLICreateWait: true, // test wait on create feature against simplest workflow + name: "MoveTables/unsharded to two shards", + workflow: "p1c2", + typ: "MoveTables", + sourceKs: "product", + targetKs: "customer", + sourceShards: "0", + targetShards: "-80,80-", + tabletBaseID: 200, + tables: "customer,Lead,Lead-1,nopk", + autoRetryError: true, + retryInsert: `insert into customer(cid, name, typ) values(2005149100, 'Testy McTester', 'soho')`, + resume: true, + resumeInsert: `insert into customer(cid, name, typ) values(2005149200, 'Testy McTester (redux)', 'enterprise')`, + testCLIErrors: true, // test for errors in the simplest workflow + testCLICreateWait: true, // test wait on create feature against simplest workflow + testCLIFlagHandling: true, // test flag handling end-to-end against simplest workflow + extraVDiffFlags: map[string]string{ + "--max-diff-duration": "2s", + }, }, { name: "Reshard Merge/split 2 to 3", @@ -81,9 +100,9 @@ var testCases = []*testCase{ targetShards: "-40,40-a0,a0-", tabletBaseID: 400, autoRetryError: true, - retryInsert: `insert into customer(cid, name, typ) values(1993234, 'Testy McTester Jr', 'enterprise'), (1993235, 'Testy McTester II', 'enterprise')`, + retryInsert: `insert into customer(cid, name, typ) values(2005149300, 'Testy McTester Jr', 'enterprise'), (2005149350, 'Testy McTester II', 'enterprise')`, resume: true, - resumeInsert: `insert into customer(cid, name, typ) values(1994234, 'Testy McTester III', 'enterprise')`, + resumeInsert: `insert into customer(cid, name, typ) values(2005149400, 'Testy McTester III', 'enterprise')`, stop: true, }, { @@ -96,43 +115,48 @@ var testCases = []*testCase{ targetShards: "0", tabletBaseID: 700, autoRetryError: true, - retryInsert: `insert into customer(cid, name, typ) values(1995234, 'Testy McTester IV', 'enterprise')`, + retryInsert: `insert into customer(cid, name, typ) values(2005149500, 'Testy McTester IV', 'enterprise')`, resume: true, - resumeInsert: `insert into customer(cid, name, typ) values(1996234, 'Testy McTester V', 'enterprise'), (1996235, 'Testy McTester VI', 'enterprise')`, + resumeInsert: `insert into customer(cid, name, typ) values(2005149600, 'Testy McTester V', 'enterprise'), (2005149650, 'Testy McTester VI', 'enterprise')`, stop: true, }, } +func checkVDiffCountStat(t *testing.T, tablet *cluster.VttabletProcess, expectedCount int64) { + countStr, err := getDebugVar(t, tablet.Port, []string{"VDiffCount"}) + require.NoError(t, err, "failed to get VDiffCount stat from %s-%d tablet: %v", tablet.Cell, tablet.TabletUID, err) + count, err := strconv.Atoi(countStr) + require.NoError(t, err, "failed to convert VDiffCount stat string to int: %v", err) + require.Equal(t, expectedCount, int64(count), "expected VDiffCount stat to be %d but got %d", expectedCount, count) +} + func TestVDiff2(t *testing.T) { - allCellNames = "zone5,zone1,zone2,zone3,zone4" + cellNames := "zone5,zone1,zone2,zone3,zone4" sourceKs := "product" sourceShards := []string{"0"} targetKs := "customer" targetShards := []string{"-80", "80-"} - // This forces us to use multiple vstream packets even with small test tables - extraVTTabletArgs = []string{"--vstream_packet_size=1"} + extraVTTabletArgs = []string{ + // This forces us to use multiple vstream packets even with small test tables. + "--vstream_packet_size=1", + // Test VPlayer batching mode. + fmt.Sprintf("--vreplication_experimental_flags=%d", + vttablet.VReplicationExperimentalFlagAllowNoBlobBinlogRowImage|vttablet.VReplicationExperimentalFlagOptimizeInserts|vttablet.VReplicationExperimentalFlagVPlayerBatching), + } + + vc = NewVitessCluster(t, &clusterOptions{cells: strings.Split(cellNames, ",")}) + defer vc.TearDown() - vc = NewVitessCluster(t, "TestVDiff2", strings.Split(allCellNames, ","), mainClusterConfig) - require.NotNil(t, vc) zone1 := vc.Cells["zone1"] zone2 := vc.Cells["zone2"] zone3 := vc.Cells["zone3"] - defaultCell = zone1 - - defer vc.TearDown(t) // The primary tablet is only added in the first cell. // We ONLY add primary tablets in this test. _, err := vc.AddKeyspace(t, []*Cell{zone2, zone1, zone3}, sourceKs, strings.Join(sourceShards, ","), initialProductVSchema, initialProductSchema, 0, 0, 100, sourceKsOpts) require.NoError(t, err) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - for _, shard := range sourceShards { - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard)) - } - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := vc.GetVTGateConn(t) defer vtgateConn.Close() verifyClusterHealth(t, vc) @@ -142,15 +166,16 @@ func TestVDiff2(t *testing.T) { query := `insert into customer(cid, name, typ, sport) values(1001, null, 'soho','')` execVtgateQuery(t, vtgateConn, fmt.Sprintf("%s:%s", sourceKs, sourceShards[0]), query) - generateMoreCustomers(t, sourceKs, 100) + generateMoreCustomers(t, sourceKs, 1000) + + // Create rows in the nopk table using the customer names and random ages between 20 and 100. + query = "insert into nopk(name, age) select name, floor(rand()*80)+20 from customer" + execVtgateQuery(t, vtgateConn, fmt.Sprintf("%s:%s", sourceKs, sourceShards[0]), query) // The primary tablet is only added in the first cell. // We ONLY add primary tablets in this test. tks, err := vc.AddKeyspace(t, []*Cell{zone3, zone1, zone2}, targetKs, strings.Join(targetShards, ","), customerVSchema, customerSchema, 0, 0, 200, targetKsOpts) require.NoError(t, err) - for _, shard := range targetShards { - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard)) - } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -158,23 +183,41 @@ func TestVDiff2(t *testing.T) { testWorkflow(t, vc, tc, tks, []*Cell{zone3, zone2, zone1}) }) } + + statsTablet := vc.getPrimaryTablet(t, targetKs, targetShards[0]) + + // We diffed X rows so confirm that the global total is > 0. + countStr, err := getDebugVar(t, statsTablet.Port, []string{"VDiffRowsComparedTotal"}) + require.NoError(t, err, "failed to get VDiffRowsComparedTotal stat from %s-%d tablet: %v", statsTablet.Cell, statsTablet.TabletUID, err) + count, err := strconv.Atoi(countStr) + require.NoError(t, err, "failed to convert VDiffRowsComparedTotal stat string to int: %v", err) + require.Greater(t, count, 0, "expected VDiffRowsComparedTotal stat to be greater than 0 but got %d", count) + + // The VDiffs should all be cleaned up so the VDiffRowsCompared value, which + // is produced from controller info, should be empty. + vdrc, err := getDebugVar(t, statsTablet.Port, []string{"VDiffRowsCompared"}) + require.NoError(t, err, "failed to get VDiffRowsCompared stat from %s-%d tablet: %v", statsTablet.Cell, statsTablet.TabletUID, err) + require.Equal(t, "{}", vdrc, "expected VDiffRowsCompared stat to be empty but got %s", vdrc) } func testWorkflow(t *testing.T, vc *VitessCluster, tc *testCase, tks *Keyspace, cells []*Cell) { + vtgateConn := vc.GetVTGateConn(t) + defer vtgateConn.Close() arrTargetShards := strings.Split(tc.targetShards, ",") if tc.typ == "Reshard" { require.NoError(t, vc.AddShards(t, cells, tks, tc.targetShards, 0, 0, tc.tabletBaseID, targetKsOpts)) - for _, shard := range arrTargetShards { - require.NoError(t, cluster.WaitForHealthyShard(vc.VtctldClient, tc.targetKs, shard)) - } + } ksWorkflow := fmt.Sprintf("%s.%s", tc.targetKs, tc.workflow) + statsShard := arrTargetShards[0] + statsTablet := vc.getPrimaryTablet(t, tc.targetKs, statsShard) var args []string args = append(args, tc.typ, "--") args = append(args, "--source", tc.sourceKs) if tc.typ == "Reshard" { args = append(args, "--source_shards", tc.sourceShards, "--target_shards", tc.targetShards) } + allCellNames := getCellNames(nil) args = append(args, "--cells", allCellNames) args = append(args, "--tables", tc.tables) args = append(args, "Create") @@ -182,12 +225,75 @@ func testWorkflow(t *testing.T, vc *VitessCluster, tc *testCase, tks *Keyspace, err := vc.VtctlClient.ExecuteCommand(args...) require.NoError(t, err) - for _, shard := range arrTargetShards { - tab := vc.getPrimaryTablet(t, tc.targetKs, shard) - catchup(t, tab, tc.workflow, tc.typ) + waitForShardsToCatchup := func() { + for _, shard := range arrTargetShards { + tab := vc.getPrimaryTablet(t, tc.targetKs, shard) + catchup(t, tab, tc.workflow, tc.typ) + } + } + + // Wait for the workflow to finish the copy phase and initially catch up. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + waitForShardsToCatchup() + + if diffDuration, ok := tc.extraVDiffFlags["--max-diff-duration"]; ok { + if !strings.Contains(tc.tables, "customer") { + require.Fail(t, "customer table must be included in the table list to test --max-diff-duration") + } + // Generate enough customer table data so that the table diff gets restarted. + dur, err := time.ParseDuration(diffDuration) + require.NoError(t, err, "could not parse --max-diff-duration %q: %v", diffDuration, err) + seconds := int64(dur.Seconds()) + chunkSize := int64(100000) + // Take the test host/runner vCPU count into account when generating rows. + perVCpuCount := int64(100000) + // Cap it at 1M rows per second so that we will create betweeen 100,000 and 1,000,000 + // rows for each second in the diff duration, depending on the test host vCPU count. + perSecondCount := int64(math.Min(float64(perVCpuCount*int64(runtime.NumCPU())), 1000000)) + totalRowsToCreate := seconds * perSecondCount + log.Infof("Test host has %d vCPUs. Generating %d rows in the customer table to test --max-diff-duration", runtime.NumCPU(), totalRowsToCreate) + for i := int64(0); i < totalRowsToCreate; i += chunkSize { + generateMoreCustomers(t, sourceKs, chunkSize) + } + + // Wait for the workflow to catch up after all the inserts. + waitForShardsToCatchup() + + // This flag is only implemented in vtctldclient. + doVtctldclientVDiff(t, tc.targetKs, tc.workflow, allCellNames, nil, "--max-diff-duration", diffDuration) + + // Confirm that the customer table diff was restarted but not others. + tablet := vc.getPrimaryTablet(t, tc.targetKs, arrTargetShards[0]) + stat, err := getDebugVar(t, tablet.Port, []string{"VDiffRestartedTableDiffsCount"}) + require.NoError(t, err, "failed to get VDiffRestartedTableDiffsCount stat: %v", err) + customerRestarts := gjson.Parse(stat).Get("customer").Int() + require.Greater(t, customerRestarts, int64(0), "expected VDiffRestartedTableDiffsCount stat to be greater than 0 for the customer table, got %d", customerRestarts) + leadRestarts := gjson.Parse(stat).Get("lead").Int() + require.Equal(t, int64(0), leadRestarts, "expected VDiffRestartedTableDiffsCount stat to be 0 for the Lead table, got %d", leadRestarts) + + // Cleanup the created customer records so as not to slow down the rest of the test. + delstmt := fmt.Sprintf("delete from %s.customer order by cid desc limit %d", sourceKs, chunkSize) + for i := int64(0); i < totalRowsToCreate; i += chunkSize { + _, err := vtgateConn.ExecuteFetch(delstmt, int(chunkSize), false) + require.NoError(t, err, "failed to cleanup added customer records: %v", err) + } + // Wait for the workflow to catch up again on the deletes. + waitForShardsToCatchup() + tc.vdiffCount++ // We only did vtctldclient vdiff create + } else { + vdiff(t, tc.targetKs, tc.workflow, allCellNames, true, true, nil) + tc.vdiffCount += 2 // We did vtctlclient AND vtctldclient vdiff create } + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) - vdiff(t, tc.targetKs, tc.workflow, allCellNames, true, true, nil) + // Confirm that the VDiffRowsCompared stat -- which is a running count of the rows + // compared by vdiff per table at the controller level -- works as expected. + vdrc, err := getDebugVar(t, statsTablet.Port, []string{"VDiffRowsCompared"}) + require.NoError(t, err, "failed to get VDiffRowsCompared stat from %s-%d tablet: %v", statsTablet.Cell, statsTablet.TabletUID, err) + uuid, jsout := performVDiff2Action(t, false, ksWorkflow, allCellNames, "show", "last", false, "--verbose") + expect := gjson.Get(jsout, fmt.Sprintf("Reports.customer.%s", statsShard)).Int() + got := gjson.Get(vdrc, fmt.Sprintf("%s.%s.%s", tc.workflow, uuid, "customer")).Int() + require.Equal(t, expect, got, "expected VDiffRowsCompared stat to be %d, but got %d", expect, got) if tc.autoRetryError { testAutoRetryError(t, tc, allCellNames) @@ -197,31 +303,47 @@ func testWorkflow(t *testing.T, vc *VitessCluster, tc *testCase, tks *Keyspace, testResume(t, tc, allCellNames) } - // These are done here so that we have a valid workflow to test the commands against + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) + + // These are done here so that we have a valid workflow to test the commands against. if tc.stop { testStop(t, ksWorkflow, allCellNames) + tc.vdiffCount++ // We did either vtctlclient OR vtctldclient vdiff create } if tc.testCLICreateWait { testCLICreateWait(t, ksWorkflow, allCellNames) + tc.vdiffCount++ // We did either vtctlclient OR vtctldclient vdiff create } if tc.testCLIErrors { testCLIErrors(t, ksWorkflow, allCellNames) } + if tc.testCLIFlagHandling { + testCLIFlagHandling(t, tc.targetKs, tc.workflow, cells[0]) + tc.vdiffCount++ // We did either vtctlclient OR vtctldclient vdiff create + } + + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) testDelete(t, ksWorkflow, allCellNames) + tc.vdiffCount = 0 // All vdiffs are deleted, so reset the count and check + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) - // create another VDiff record to confirm it gets deleted when the workflow is completed + // Create another VDiff record to confirm it gets deleted when the workflow is completed. ts := time.Now() - uuid, _ := performVDiff2Action(t, false, ksWorkflow, allCellNames, "create", "", false) + uuid, _ = performVDiff2Action(t, false, ksWorkflow, allCellNames, "create", "", false) waitForVDiff2ToComplete(t, false, ksWorkflow, allCellNames, uuid, ts) + tc.vdiffCount++ + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) err = vc.VtctlClient.ExecuteCommand(tc.typ, "--", "SwitchTraffic", ksWorkflow) require.NoError(t, err) err = vc.VtctlClient.ExecuteCommand(tc.typ, "--", "Complete", ksWorkflow) require.NoError(t, err) - // confirm the VDiff data is deleted for the workflow + // Confirm the VDiff data is deleted for the workflow. testNoOrphanedData(t, tc.targetKs, tc.workflow, arrTargetShards) + tc.vdiffCount = 0 // All vdiffs are deleted, so reset the count and check + checkVDiffCountStat(t, statsTablet, tc.vdiffCount) } func testCLIErrors(t *testing.T, ksWorkflow, cells string) { @@ -242,6 +364,70 @@ func testCLIErrors(t *testing.T, ksWorkflow, cells string) { }) } +// testCLIFlagHandling tests that the vtctldclient CLI flags are handled correctly +// from vtctldclient->vtctld->vttablet->mysqld. +func testCLIFlagHandling(t *testing.T, targetKs, workflowName string, cell *Cell) { + expectedOptions := &tabletmanagerdatapb.VDiffOptions{ + CoreOptions: &tabletmanagerdatapb.VDiffCoreOptions{ + MaxRows: 999, + MaxExtraRowsToCompare: 777, + AutoRetry: true, + UpdateTableStats: true, + TimeoutSeconds: 60, + MaxDiffSeconds: 333, + }, + PickerOptions: &tabletmanagerdatapb.VDiffPickerOptions{ + SourceCell: "zone1,zone2,zone3,zonefoosource", + TargetCell: "zone1,zone2,zone3,zonefootarget", + TabletTypes: "replica,primary,rdonly", + }, + ReportOptions: &tabletmanagerdatapb.VDiffReportOptions{ + MaxSampleRows: 888, + OnlyPks: true, + }, + } + + t.Run("Client flag handling", func(t *testing.T) { + res, err := vc.VtctldClient.ExecuteCommandWithOutput("vdiff", "--target-keyspace", targetKs, "--workflow", workflowName, + "create", + "--limit", fmt.Sprintf("%d", expectedOptions.CoreOptions.MaxRows), + "--max-report-sample-rows", fmt.Sprintf("%d", expectedOptions.ReportOptions.MaxSampleRows), + "--max-extra-rows-to-compare", fmt.Sprintf("%d", expectedOptions.CoreOptions.MaxExtraRowsToCompare), + "--filtered-replication-wait-time", fmt.Sprintf("%v", time.Duration(expectedOptions.CoreOptions.TimeoutSeconds)*time.Second), + "--max-diff-duration", fmt.Sprintf("%v", time.Duration(expectedOptions.CoreOptions.MaxDiffSeconds)*time.Second), + "--source-cells", expectedOptions.PickerOptions.SourceCell, + "--target-cells", expectedOptions.PickerOptions.TargetCell, + "--tablet-types", expectedOptions.PickerOptions.TabletTypes, + fmt.Sprintf("--update-table-stats=%t", expectedOptions.CoreOptions.UpdateTableStats), + fmt.Sprintf("--auto-retry=%t", expectedOptions.CoreOptions.AutoRetry), + fmt.Sprintf("--only-pks=%t", expectedOptions.ReportOptions.OnlyPks), + "--tablet-types-in-preference-order=false", // So tablet_types should not start with "in_order:", which is the default + "--format=json") // So we can easily grab the UUID + require.NoError(t, err, "vdiff command failed: %s", res) + jsonRes := gjson.Parse(res) + vduuid, err := uuid.Parse(jsonRes.Get("UUID").String()) + require.NoError(t, err, "invalid UUID: %s", jsonRes.Get("UUID").String()) + + // Confirm that the options were passed through and saved correctly. + query := sqlparser.BuildParsedQuery("select options from %s.vdiff where vdiff_uuid = %s", + sidecarDBIdentifier, encodeString(vduuid.String())).Query + tablets := vc.getVttabletsInKeyspace(t, cell, targetKs, "PRIMARY") + require.Greater(t, len(tablets), 0, "no primary tablets found in keyspace %s", targetKs) + tablet := maps.Values(tablets)[0] + qres, err := tablet.QueryTablet(query, targetKs, false) + require.NoError(t, err, "query %q failed: %v", query, err) + require.NotNil(t, qres, "query %q returned nil result", query) // Should never happen + require.Equal(t, 1, len(qres.Rows), "query %q returned %d rows, expected 1", query, len(qres.Rows)) + require.Equal(t, 1, len(qres.Rows[0]), "query %q returned %d columns, expected 1", query, len(qres.Rows[0])) + storedOptions := &tabletmanagerdatapb.VDiffOptions{} + bytes, err := qres.Rows[0][0].ToBytes() + require.NoError(t, err, "failed to convert result %+v to bytes: %v", qres.Rows[0], err) + err = protojson.Unmarshal(bytes, storedOptions) + require.NoError(t, err, "failed to unmarshal result %s to a %T: %v", string(bytes), storedOptions, err) + require.True(t, proto.Equal(expectedOptions, storedOptions), "stored options %v != expected options %v", storedOptions, expectedOptions) + }) +} + func testDelete(t *testing.T, ksWorkflow, cells string) { t.Run("Delete", func(t *testing.T) { // Let's be sure that we have at least 3 unique VDiffs. @@ -302,15 +488,17 @@ func testNoOrphanedData(t *testing.T, keyspace, workflow string, shards []string func testResume(t *testing.T, tc *testCase, cells string) { t.Run("Resume", func(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() ksWorkflow := fmt.Sprintf("%s.%s", tc.targetKs, tc.workflow) - // confirm the last VDiff is in the expected completed state + // Confirm the last VDiff is in the expected completed state. uuid, output := performVDiff2Action(t, false, ksWorkflow, cells, "show", "last", false) jsonOutput := getVDiffInfo(output) require.Equal(t, "completed", jsonOutput.State) - // save the number of rows compared in previous runs + // Save the number of rows compared in previous runs. rowsCompared := jsonOutput.RowsCompared - ogTime := time.Now() // the completed_at should be later than this after resuming + ogTime := time.Now() // The completed_at should be later than this after resuming expectedNewRows := int64(0) if tc.resumeInsert != "" { @@ -323,6 +511,7 @@ func testResume(t *testing.T, tc *testCase, cells string) { // expected number of rows in total (original run and resume) _, _ = performVDiff2Action(t, false, ksWorkflow, cells, "resume", uuid, false) info := waitForVDiff2ToComplete(t, false, ksWorkflow, cells, uuid, ogTime) + require.NotNil(t, info) require.False(t, info.HasMismatch) require.Equal(t, expectedRows, info.RowsCompared) }) @@ -344,18 +533,20 @@ func testStop(t *testing.T, ksWorkflow, cells string) { func testAutoRetryError(t *testing.T, tc *testCase, cells string) { t.Run("Auto retry on error", func(t *testing.T) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() ksWorkflow := fmt.Sprintf("%s.%s", tc.targetKs, tc.workflow) - // confirm the last VDiff is in the expected completed state + // Confirm the last VDiff is in the expected completed state. uuid, output := performVDiff2Action(t, false, ksWorkflow, cells, "show", "last", false) jsonOutput := getVDiffInfo(output) require.Equal(t, "completed", jsonOutput.State) - // save the number of rows compared in the first run + // Save the number of rows compared in the first run. rowsCompared := jsonOutput.RowsCompared - ogTime := time.Now() // the completed_at should be later than this upon retry + ogTime := time.Now() // The completed_at should be later than this upon retry - // create new data since original VDiff run -- if requested -- to confirm that the rows - // compared is cumulative + // Create new data since original VDiff run -- if requested -- to confirm that the rows + // compared is cumulative. expectedNewRows := int64(0) if tc.retryInsert != "" { res := execVtgateQuery(t, vtgateConn, tc.sourceKs, tc.retryInsert) @@ -363,18 +554,19 @@ func testAutoRetryError(t *testing.T, tc *testCase, cells string) { } expectedRows := rowsCompared + expectedNewRows - // update the VDiff to simulate an ephemeral error having occurred + // Update the VDiff to simulate an ephemeral error having occurred. for _, shard := range strings.Split(tc.targetShards, ",") { tab := vc.getPrimaryTablet(t, tc.targetKs, shard) res, err := tab.QueryTabletWithDB(sqlparser.BuildParsedQuery(sqlSimulateError, sidecarDBIdentifier, sidecarDBIdentifier, encodeString(uuid)).Query, "vt_"+tc.targetKs) require.NoError(t, err) - // should have updated the vdiff record and at least one vdiff_table record + // Should have updated the vdiff record and at least one vdiff_table record. require.GreaterOrEqual(t, int(res.RowsAffected), 2) } - // confirm that the VDiff was retried, able to complete, and we compared the expected - // number of rows in total (original run and retry) + // Confirm that the VDiff was retried, able to complete, and we compared the expected + // number of rows in total (original run and retry). info := waitForVDiff2ToComplete(t, false, ksWorkflow, cells, uuid, ogTime) + require.NotNil(t, info) require.False(t, info.HasMismatch) require.Equal(t, expectedRows, info.RowsCompared) }) diff --git a/go/test/endtoend/vreplication/vdiff_helper_test.go b/go/test/endtoend/vreplication/vdiff_helper_test.go index 38ae9273a42..91605bff402 100644 --- a/go/test/endtoend/vreplication/vdiff_helper_test.go +++ b/go/test/endtoend/vreplication/vdiff_helper_test.go @@ -17,6 +17,7 @@ limitations under the License. package vreplication import ( + "context" "fmt" "strings" "testing" @@ -31,7 +32,10 @@ import ( ) const ( - vdiffTimeout = time.Second * 90 // we can leverage auto retry on error with this longer-than-usual timeout + vdiffTimeout = 120 * time.Second // We can leverage auto retry on error with this longer-than-usual timeout + vdiffRetryTimeout = 30 * time.Second + vdiffStatusCheckInterval = 5 * time.Second + vdiffRetryInterval = 5 * time.Second ) var ( @@ -66,6 +70,7 @@ func doVtctlclientVDiff(t *testing.T, keyspace, workflow, cells string, want *ex // update-table-stats is needed in order to test progress reports. uuid, _ := performVDiff2Action(t, true, ksWorkflow, cells, "create", "", false, "--auto-retry", "--update-table-stats") info := waitForVDiff2ToComplete(t, true, ksWorkflow, cells, uuid, time.Time{}) + require.NotNil(t, info) require.Equal(t, workflow, info.Workflow) require.Equal(t, keyspace, info.Keyspace) if want != nil { @@ -85,14 +90,16 @@ func doVtctlclientVDiff(t *testing.T, keyspace, workflow, cells string, want *ex func waitForVDiff2ToComplete(t *testing.T, useVtctlclient bool, ksWorkflow, cells, uuid string, completedAtMin time.Time) *vdiffInfo { var info *vdiffInfo + var jsonStr string first := true previousProgress := vdiff2.ProgressReport{} ch := make(chan bool) go func() { for { - time.Sleep(1 * time.Second) - _, jsonStr := performVDiff2Action(t, useVtctlclient, ksWorkflow, cells, "show", uuid, false) + time.Sleep(vdiffStatusCheckInterval) + _, jsonStr = performVDiff2Action(t, useVtctlclient, ksWorkflow, cells, "show", uuid, false) info = getVDiffInfo(jsonStr) + require.NotNil(t, info) if info.State == "completed" { if !completedAtMin.IsZero() { ca := info.CompletedAt @@ -103,7 +110,7 @@ func waitForVDiff2ToComplete(t *testing.T, useVtctlclient bool, ksWorkflow, cell } ch <- true return - } else if info.State == "started" { // test the progress report + } else if info.State == "started" { // Test the progress report // The ETA should always be in the future -- when we're able to estimate // it -- and the progress percentage should only increase. // The timestamp format allows us to compare them lexicographically. @@ -136,6 +143,7 @@ func waitForVDiff2ToComplete(t *testing.T, useVtctlclient bool, ksWorkflow, cell case <-ch: return info case <-time.After(vdiffTimeout): + log.Errorf("VDiff never completed for UUID %s. Latest output: %s", uuid, jsonStr) require.FailNow(t, fmt.Sprintf("VDiff never completed for UUID %s", uuid)) return nil } @@ -147,13 +155,17 @@ type expectedVDiff2Result struct { hasMismatch bool } -func doVtctldclientVDiff(t *testing.T, keyspace, workflow, cells string, want *expectedVDiff2Result) { +func doVtctldclientVDiff(t *testing.T, keyspace, workflow, cells string, want *expectedVDiff2Result, extraFlags ...string) { ksWorkflow := fmt.Sprintf("%s.%s", keyspace, workflow) t.Run(fmt.Sprintf("vtctldclient vdiff %s", ksWorkflow), func(t *testing.T) { // update-table-stats is needed in order to test progress reports. - uuid, _ := performVDiff2Action(t, false, ksWorkflow, cells, "create", "", false, "--auto-retry", "--update-table-stats") + flags := []string{"--auto-retry", "--update-table-stats"} + if len(extraFlags) > 0 { + flags = append(flags, extraFlags...) + } + uuid, _ := performVDiff2Action(t, false, ksWorkflow, cells, "create", "", false, flags...) info := waitForVDiff2ToComplete(t, false, ksWorkflow, cells, uuid, time.Time{}) - + require.NotNil(t, info) require.Equal(t, workflow, info.Workflow) require.Equal(t, keyspace, info.Keyspace) if want != nil { @@ -186,7 +198,7 @@ func performVDiff2Action(t *testing.T, useVtctlclient bool, ksWorkflow, cells, a args = append(args, extraFlags...) } args = append(args, ksWorkflow, action, actionArg) - output, err = vc.VtctlClient.ExecuteCommandWithOutput(args...) + output, err = execVDiffWithRetry(t, expectError, false, args) log.Infof("vdiff output: %+v (err: %+v)", output, err) if !expectError { require.Nil(t, err) @@ -211,7 +223,8 @@ func performVDiff2Action(t *testing.T, useVtctlclient bool, ksWorkflow, cells, a if actionArg != "" { args = append(args, actionArg) } - output, err = vc.VtctldClient.ExecuteCommandWithOutput(args...) + + output, err = execVDiffWithRetry(t, expectError, true, args) log.Infof("vdiff output: %+v (err: %+v)", output, err) if !expectError { require.NoError(t, err) @@ -226,6 +239,79 @@ func performVDiff2Action(t *testing.T, useVtctlclient bool, ksWorkflow, cells, a return uuid, output } +// During SwitchTraffic, due to changes in the cluster, vdiff can return transient errors. isVDiffRetryable() is used to +// ignore such errors and retry vdiff expecting the condition to be resolved. +func isVDiffRetryable(str string) bool { + for _, s := range []string{"Error while dialing", "failed to connect"} { + if strings.Contains(str, s) { + return true + } + } + return false +} + +type vdiffResult struct { + output string + err error +} + +// execVDiffWithRetry will ignore transient errors that can occur during workflow state changes. +func execVDiffWithRetry(t *testing.T, expectError bool, useVtctldClient bool, args []string) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), vdiffRetryTimeout) + defer cancel() + vdiffResultCh := make(chan vdiffResult) + go func() { + var output string + var err error + retry := false + for { + select { + case <-ctx.Done(): + return + default: + } + if retry { + time.Sleep(vdiffRetryInterval) + } + retry = false + if useVtctldClient { + output, err = vc.VtctldClient.ExecuteCommandWithOutput(args...) + } else { + output, err = vc.VtctlClient.ExecuteCommandWithOutput(args...) + } + if err != nil { + if expectError { + result := vdiffResult{output: output, err: err} + vdiffResultCh <- result + return + } + log.Infof("vdiff error: %s", err) + if isVDiffRetryable(err.Error()) { + retry = true + } else { + result := vdiffResult{output: output, err: err} + vdiffResultCh <- result + return + } + } + if isVDiffRetryable(output) { + retry = true + } + if !retry { + result := vdiffResult{output: output, err: nil} + vdiffResultCh <- result + return + } + } + }() + select { + case <-ctx.Done(): + return "", fmt.Errorf("timed out waiting for vdiff to complete") + case result := <-vdiffResultCh: + return result.output, result.err + } +} + type vdiffInfo struct { Workflow, Keyspace string State, Shards string @@ -260,6 +346,8 @@ func encodeString(in string) string { // generateMoreCustomers creates additional test data for better tests // when needed. func generateMoreCustomers(t *testing.T, keyspace string, numCustomers int64) { + vtgateConn, closeConn := getVTGateConn() + defer closeConn() log.Infof("Generating more test data with an additional %d customers", numCustomers) res := execVtgateQuery(t, vtgateConn, keyspace, "select max(cid) from customer") startingID, _ := res.Rows[0][0].ToInt64() diff --git a/go/test/endtoend/vreplication/vdiff_multiple_movetables_test.go b/go/test/endtoend/vreplication/vdiff_multiple_movetables_test.go index 0f6a9f668d0..a4c25941801 100644 --- a/go/test/endtoend/vreplication/vdiff_multiple_movetables_test.go +++ b/go/test/endtoend/vreplication/vdiff_multiple_movetables_test.go @@ -27,43 +27,26 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) func TestMultipleConcurrentVDiffs(t *testing.T) { - cellName := "zone" - cells := []string{cellName} - vc = NewVitessCluster(t, t.Name(), cells, mainClusterConfig) - - require.NotNil(t, vc) - allCellNames = cellName - defaultCellName := cellName - defaultCell = vc.Cells[defaultCellName] + cellName := "zone1" + vc = NewVitessCluster(t, nil) + defer vc.TearDown() + sourceKeyspace := "product" shardName := "0" - defer vc.TearDown(t) - cell := vc.Cells[cellName] vc.AddKeyspace(t, []*Cell{cell}, sourceKeyspace, shardName, initialProductVSchema, initialProductSchema, 0, 0, 100, sourceKsOpts) - vtgate = cell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKeyspace, shardName) - require.NoError(t, err) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", sourceKeyspace, shardName), 1, 30*time.Second) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() verifyClusterHealth(t, vc) - insertInitialData(t) targetTabletId := 200 targetKeyspace := "customer" vc.AddKeyspace(t, []*Cell{cell}, targetKeyspace, shardName, initialProductVSchema, initialProductSchema, 0, 0, targetTabletId, sourceKsOpts) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", targetKeyspace, shardName), 1, 30*time.Second) index := 1000 var loadCtx context.Context @@ -93,12 +76,16 @@ func TestMultipleConcurrentVDiffs(t *testing.T) { time.Sleep(15 * time.Second) // wait for some rows to be inserted. createWorkflow := func(workflowName, tables string) { - mt := newMoveTables(vc, &moveTables{ - workflowName: workflowName, - targetKeyspace: targetKeyspace, + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + tabletTypes: "primary", + }, sourceKeyspace: sourceKeyspace, tables: tables, - }, moveTablesFlavorVtctld) + }, workflowFlavorVtctld) mt.Create() waitForWorkflowState(t, vc, fmt.Sprintf("%s.%s", targetKeyspace, workflowName), binlogdatapb.VReplicationWorkflowState_Running.String()) catchup(t, targetTab, workflowName, "MoveTables") diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go index 62d174df067..c28118a97cc 100644 --- a/go/test/endtoend/vreplication/vreplication_test.go +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -22,42 +22,38 @@ import ( "io" "net/http" "runtime" + "strconv" "strings" "sync" "testing" "time" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/vtgateconn" - + "github.com/buger/jsonparser" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" - "github.com/buger/jsonparser" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/log" - querypb "vitess.io/vitess/go/vt/proto/query" - throttlebase "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/vtgateconn" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + throttlebase "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base" ) var ( vc *VitessCluster - vtgate *cluster.VtgateProcess - defaultCell *Cell - vtgateConn *mysql.Conn defaultRdonly int defaultReplicas int - allCellNames string sourceKsOpts = make(map[string]string) targetKsOpts = make(map[string]string) httpClient = throttlebase.SetupHTTPClient(time.Second) @@ -122,15 +118,16 @@ func throttlerCheckSelf(tablet *cluster.VttabletProcess, throttlerApp throttlera // NOTE: this is a manual test. It is not executed in the // CI. func TestVReplicationDDLHandling(t *testing.T) { + var err error workflow := "onddl_test" ksWorkflow := fmt.Sprintf("%s.%s", targetKs, workflow) table := "orders" newColumn := "ddltest" cell := "zone1" shard := "0" - vc = NewVitessCluster(t, t.Name(), []string{cell}, mainClusterConfig) - defer vc.TearDown(t) - defaultCell = vc.Cells[cell] + vc = NewVitessCluster(t, nil) + defer vc.TearDown() + defaultCell := vc.Cells[cell] if _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, sourceKs, shard, initialProductVSchema, initialProductSchema, 0, 0, 100, nil); err != nil { t.Fatal(err) @@ -138,15 +135,12 @@ func TestVReplicationDDLHandling(t *testing.T) { if _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, targetKs, shard, "", "", 0, 0, 200, nil); err != nil { t.Fatal(err) } - vtgate = defaultCell.Vtgates[0] + vtgate := defaultCell.Vtgates[0] require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) - require.NoError(t, err) - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) - require.NoError(t, err) + verifyClusterHealth(t, vc) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() sourceTab = vc.getPrimaryTablet(t, sourceKs, shard) targetTab := vc.getPrimaryTablet(t, targetKs, shard) @@ -234,15 +228,15 @@ func TestVreplicationCopyThrottling(t *testing.T) { cell := "zone1" table := "customer" shard := "0" - vc = NewVitessCluster(t, "TestVreplicationCopyThrottling", []string{cell}, mainClusterConfig) - defer vc.TearDown(t) - defaultCell = vc.Cells[cell] + vc = NewVitessCluster(t, nil) + defer vc.TearDown() + defaultCell := vc.Cells[cell] // To test vstreamer source throttling for the MoveTables operation maxSourceTrxHistory := int64(5) extraVTTabletArgs = []string{ // We rely on holding open transactions to generate innodb history so extend the timeout // to avoid flakiness when the CI is very slow. - fmt.Sprintf("--queryserver-config-transaction-timeout=%d", int64(defaultTimeout.Seconds())*3), + fmt.Sprintf("--queryserver-config-transaction-timeout=%s", (defaultTimeout * 3).String()), fmt.Sprintf("--vreplication_copy_phase_max_innodb_history_list_length=%d", maxSourceTrxHistory), parallelInsertWorkers, } @@ -253,12 +247,8 @@ func TestVreplicationCopyThrottling(t *testing.T) { if _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, targetKs, shard, "", "", 0, 0, 200, nil); err != nil { t.Fatal(err) } - vtgate = defaultCell.Vtgates[0] + vtgate := defaultCell.Vtgates[0] require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) - require.NoError(t, err) - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) - require.NoError(t, err) // Confirm that the initial copy table phase does not proceed until the source tablet(s) // have an InnoDB History List length that is less than specified in the tablet's config. @@ -280,6 +270,7 @@ func TestVreplicationCopyThrottling(t *testing.T) { } func TestBasicVreplicationWorkflow(t *testing.T) { + defer setAllVTTabletExperimentalFlags() sourceKsOpts["DBTypeVersion"] = "mysql-8.0" targetKsOpts["DBTypeVersion"] = "mysql-8.0" testBasicVreplicationWorkflow(t, "noblob") @@ -300,12 +291,10 @@ func testBasicVreplicationWorkflow(t *testing.T, binlogRowImage string) { // If limited == true, we only run a limited set of workflows. func testVreplicationWorkflows(t *testing.T, limited bool, binlogRowImage string) { + var err error defaultCellName := "zone1" - allCells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestBasicVreplicationWorkflow", allCells, mainClusterConfig) - - require.NotNil(t, vc) + vc = NewVitessCluster(t, nil) + defer vc.TearDown() // Keep the cluster processes minimal to deal with CI resource constraints defaultReplicas = 0 defaultRdonly = 0 @@ -315,16 +304,11 @@ func testVreplicationWorkflows(t *testing.T, limited bool, binlogRowImage string require.NoError(t, utils.SetBinlogRowImageMode("noblob", vc.ClusterConfig.tmpDir)) defer utils.SetBinlogRowImageMode("", vc.ClusterConfig.tmpDir) } - defer vc.TearDown(t) - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[defaultCellName] vc.AddKeyspace(t, []*Cell{defaultCell}, "product", "0", initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, sourceKsOpts) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) insertInitialData(t) @@ -423,49 +407,14 @@ func TestMoveTablesMariaDBToMySQL(t *testing.T) { testVreplicationWorkflows(t, true /* only do MoveTables */, "") } -func TestMultiCellVreplicationWorkflow(t *testing.T) { - cells := []string{"zone1", "zone2"} - allCellNames = strings.Join(cells, ",") - - vc = NewVitessCluster(t, "TestMultiCellVreplicationWorkflow", cells, mainClusterConfig) - require.NotNil(t, vc) - defaultCellName := "zone1" - defaultCell = vc.Cells[defaultCellName] - keyspace := "product" - shard := "0" - - defer vc.TearDown(t) - - cell1 := vc.Cells["zone1"] - cell2 := vc.Cells["zone2"] - vc.AddKeyspace(t, []*Cell{cell1, cell2}, keyspace, shard, initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, sourceKsOpts) - - vtgate = cell1.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, keyspace, shard) - require.NoError(t, err) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace, shard), 2, 30*time.Second) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() - verifyClusterHealth(t, vc) - insertInitialData(t) - shardCustomer(t, true, []*Cell{cell1, cell2}, cell2.Name, true) - isTableInDenyList(t, vc, "product:0", "customer") - // we tag along this test so as not to create the overhead of creating another cluster - testVStreamCellFlag(t) -} - func TestVStreamFlushBinlog(t *testing.T) { defaultCellName := "zone1" - allCells := []string{defaultCellName} - allCellNames = defaultCellName workflow := "test_vstream_p2c" shard := "0" - vc = NewVitessCluster(t, "TestVStreamBinlogFlush", allCells, mainClusterConfig) + vc = NewVitessCluster(t, nil) require.NotNil(t, vc) - defer vc.TearDown(t) - defaultCell = vc.Cells[defaultCellName] + defer vc.TearDown() + defaultCell := vc.Cells[defaultCellName] // Keep the cluster processes minimal (no rdonly and no replica tablets) // to deal with CI resource constraints. @@ -477,16 +426,8 @@ func TestVStreamFlushBinlog(t *testing.T) { if _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, targetKs, shard, "", "", 0, 0, 200, nil); err != nil { t.Fatal(err) } - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) - require.NoError(t, err) - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) - require.NoError(t, err) verifyClusterHealth(t, vc) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() sourceTab = vc.getPrimaryTablet(t, sourceKs, shard) insertInitialData(t) @@ -502,7 +443,9 @@ func TestVStreamFlushBinlog(t *testing.T) { // Generate a lot of binlog event bytes targetBinlogSize := vstreamer.GetBinlogRotationThreshold() + 1024 - vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := vc.GetVTGateConn(t) + defer vtgateConn.Close() + queryF := "insert into db_order_test (c_uuid, dbstuff, created_at) values ('%d', '%s', now())" for i := 100; i < 10000; i++ { randStr, err := randHex(6500) @@ -577,7 +520,7 @@ func testVStreamCellFlag(t *testing.T) { flags.CellPreference = "onlyspecified" } - ctx2, cancel := context.WithTimeout(ctx, 30*time.Second) + ctx2, cancel := context.WithTimeout(ctx, 10*time.Second) reader, err := conn.VStream(ctx2, topodatapb.TabletType_REPLICA, vgtid, filter, flags) require.NoError(t, err) @@ -621,21 +564,16 @@ func testVStreamCellFlag(t *testing.T) { // We also reuse the setup of this test to validate that the "vstream * from" vtgate query functionality is functional func TestCellAliasVreplicationWorkflow(t *testing.T) { cells := []string{"zone1", "zone2"} - mainClusterConfig.vreplicationCompressGTID = true - defer func() { - mainClusterConfig.vreplicationCompressGTID = false - }() - vc = NewVitessCluster(t, "TestCellAliasVreplicationWorkflow", cells, mainClusterConfig) - require.NotNil(t, vc) - allCellNames = "zone1,zone2" - defaultCellName := "zone1" - defaultCell = vc.Cells[defaultCellName] + defer mainClusterConfig.enableGTIDCompression() + defer setAllVTTabletExperimentalFlags() + vc = NewVitessCluster(t, &clusterOptions{cells: cells}) + defer vc.TearDown() + keyspace := "product" shard := "0" require.NoError(t, utils.SetBinlogRowImageMode("noblob", vc.ClusterConfig.tmpDir)) defer utils.SetBinlogRowImageMode("", vc.ClusterConfig.tmpDir) - defer vc.TearDown(t) cell1 := vc.Cells["zone1"] cell2 := vc.Cells["zone2"] @@ -645,26 +583,21 @@ func TestCellAliasVreplicationWorkflow(t *testing.T) { result, err := vc.VtctlClient.ExecuteCommandWithOutput("AddCellsAlias", "--", "--cells", "zone2", "alias") require.NoError(t, err, "command failed with output: %v", result) - vtgate = cell1.Vtgates[0] - require.NotNil(t, vtgate) - err = cluster.WaitForHealthyShard(vc.VtctldClient, keyspace, shard) - require.NoError(t, err) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace, shard), 2, 30*time.Second) - - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() verifyClusterHealth(t, vc) - insertInitialData(t) + vtgate := cell1.Vtgates[0] t.Run("VStreamFrom", func(t *testing.T) { - testVStreamFrom(t, keyspace, 2) + testVStreamFrom(t, vtgate, keyspace, 2) }) shardCustomer(t, true, []*Cell{cell1, cell2}, "alias", false) + isTableInDenyList(t, vc, "product:0", "customer") + // we tag along this test so as not to create the overhead of creating another cluster + testVStreamCellFlag(t) } // testVStreamFrom confirms that the "vstream * from" endpoint is serving data -func testVStreamFrom(t *testing.T, table string, expectedRowCount int) { +func testVStreamFrom(t *testing.T, vtgate *cluster.VtgateProcess, table string, expectedRowCount int) { ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", @@ -736,11 +669,6 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl if _, err := vc.AddKeyspace(t, cells, "customer", "-80,80-", customerVSchema, customerSchema, defaultReplicas, defaultRdonly, 200, targetKsOpts); err != nil { t.Fatal(err) } - err := cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, "-80") - require.NoError(t, err) - err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, "80-") - require.NoError(t, err) - // Assume we are operating on first cell defaultCell := cells[0] custKs := vc.Cells[defaultCell.Name].Keyspaces["customer"] @@ -760,7 +688,8 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl // The wait in the next code block which checks that customer.dec80 is updated, also confirms that the // blob-related dmls we execute here are vreplicated. insertIntoBlobTable(t) - + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() // Confirm that the 0 scale decimal field, dec80, is replicated correctly dec80Replicated := false execVtgateQuery(t, vtgateConn, sourceKs, "update customer set dec80 = 0") @@ -777,6 +706,12 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl } require.Equal(t, true, dec80Replicated) + // Insert multiple rows in the loadtest table and immediately delete them to confirm that bulk delete + // works the same way with the vplayer optimization enabled and disabled. Currently this optimization + // is disabled by default, but enabled in TestCellAliasVreplicationWorkflow. + execVtgateQuery(t, vtgateConn, sourceKs, "insert into loadtest(id, name) values(10001, 'tempCustomer'), (10002, 'tempCustomer2'), (10003, 'tempCustomer3'), (10004, 'tempCustomer4')") + execVtgateQuery(t, vtgateConn, sourceKs, "delete from loadtest where id > 10000") + // Confirm that all partial query metrics get updated when we are testing the noblob mode. t.Run("validate partial query counts", func(t *testing.T) { if !isBinlogRowImageNoBlob(t, productTab) { @@ -823,8 +758,9 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl } } vdiffSideBySide(t, ksWorkflow, "") - switchReadsDryRun(t, workflowType, allCellNames, ksWorkflow, dryRunResultsReadCustomerShard) - switchReads(t, workflowType, allCellNames, ksWorkflow, false) + cellNames := getCellNames(cells) + switchReadsDryRun(t, workflowType, cellNames, ksWorkflow, dryRunResultsReadCustomerShard) + switchReads(t, workflowType, cellNames, ksWorkflow, false) assertQueryExecutesOnTablet(t, vtgateConn, productTab, "customer", query, query) var commit func(t *testing.T) @@ -868,7 +804,7 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl execVtgateQuery(t, vtgateConn, "customer", "update customer set meta = convert(x'7b7d' using utf8mb4) where cid = 1") if testReverse { // Reverse Replicate - switchReads(t, workflowType, allCellNames, ksWorkflow, true) + switchReads(t, workflowType, cellNames, ksWorkflow, true) printShardPositions(vc, ksShards) switchWrites(t, workflowType, ksWorkflow, true) @@ -888,7 +824,7 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl waitForNoWorkflowLag(t, vc, targetKs, workflow) // Go forward again - switchReads(t, workflowType, allCellNames, ksWorkflow, false) + switchReads(t, workflowType, cellNames, ksWorkflow, false) switchWrites(t, workflowType, ksWorkflow, false) var exists bool @@ -896,7 +832,7 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl require.NoError(t, err, "Error getting denylist for customer:0") require.True(t, exists) - moveTablesAction(t, "Complete", allCellNames, workflow, sourceKs, targetKs, tables) + moveTablesAction(t, "Complete", cellNames, workflow, sourceKs, targetKs, tables) exists, err = isTableInDenyList(t, vc, "product:0", "customer") require.NoError(t, err, "Error getting denylist for customer:0") @@ -941,6 +877,8 @@ func shardCustomer(t *testing.T, testReverse bool, cells []*Cell, sourceCellOrAl func validateRollupReplicates(t *testing.T) { t.Run("validateRollupReplicates", func(t *testing.T) { insertMoreProducts(t) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() waitForRowCount(t, vtgateConn, "product", "rollup", 1) waitForQueryResult(t, vtgateConn, "product:0", "select rollupname, kount from rollup", `[[VARCHAR("total") INT32(5)]]`) @@ -949,6 +887,8 @@ func validateRollupReplicates(t *testing.T) { func reshardCustomer2to4Split(t *testing.T, cells []*Cell, sourceCellOrAlias string) { t.Run("reshardCustomer2to4Split", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() ksName := "customer" counts := map[string]int{"zone1-600": 4, "zone1-700": 5, "zone1-800": 6, "zone1-900": 5} reshard(t, ksName, "customer", "c2c4", "-80,80-", "-40,40-80,80-c0,c0-", @@ -962,6 +902,8 @@ func reshardCustomer2to4Split(t *testing.T, cells []*Cell, sourceCellOrAlias str func reshardMerchant2to3SplitMerge(t *testing.T) { t.Run("reshardMerchant2to3SplitMerge", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() ksName := merchantKeyspace counts := map[string]int{"zone1-1600": 0, "zone1-1700": 2, "zone1-1800": 0} reshard(t, ksName, "merchant", "m2m3", "-80,80-", "-40,40-c0,c0-", @@ -1009,6 +951,8 @@ func reshardMerchant2to3SplitMerge(t *testing.T) { func reshardMerchant3to1Merge(t *testing.T) { t.Run("reshardMerchant3to1Merge", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() ksName := merchantKeyspace counts := map[string]int{"zone1-2000": 3} reshard(t, ksName, "merchant", "m3m1", "-40,40-c0,c0-", "0", @@ -1042,21 +986,18 @@ func reshard(t *testing.T, ksName string, tableName string, workflow string, sou tabletIDBase int, counts map[string]int, dryRunResultSwitchReads, dryRunResultSwitchWrites []string, cells []*Cell, sourceCellOrAlias string, autoIncrementStep int) { t.Run("reshard", func(t *testing.T) { + defaultCell := vc.Cells[vc.CellNames[0]] if cells == nil { cells = []*Cell{defaultCell} } if sourceCellOrAlias == "" { sourceCellOrAlias = defaultCell.Name } + callNames := getCellNames(cells) ksWorkflow := ksName + "." + workflow keyspace := vc.Cells[defaultCell.Name].Keyspaces[ksName] require.NoError(t, vc.AddShards(t, cells, keyspace, targetShards, defaultReplicas, defaultRdonly, tabletIDBase, targetKsOpts)) - arrTargetShardNames := strings.Split(targetShards, ",") - for _, shardName := range arrTargetShardNames { - err := cluster.WaitForHealthyShard(vc.VtctldClient, ksName, shardName) - require.NoError(t, err) - } tablets := vc.getVttabletsInKeyspace(t, defaultCell, ksName, "primary") // Test multi-primary setups, like a Galera cluster, which have auto increment steps > 1. @@ -1080,13 +1021,13 @@ func reshard(t *testing.T, ksName string, tableName string, workflow string, sou restartWorkflow(t, ksWorkflow) vdiffSideBySide(t, ksWorkflow, "") if dryRunResultSwitchReads != nil { - reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", allCellNames, "rdonly,replica", "--dry-run") + reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", callNames, "rdonly,replica", "--dry-run") } - reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", allCellNames, "rdonly,replica") + reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", callNames, "rdonly,replica") if dryRunResultSwitchWrites != nil { - reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", allCellNames, "primary", "--dry-run") + reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", callNames, "primary", "--dry-run") } - reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", allCellNames, "primary") + reshardAction(t, "SwitchTraffic", workflow, ksName, "", "", callNames, "primary") reshardAction(t, "Complete", workflow, ksName, "", "", "", "") for tabletName, count := range counts { if tablets[tabletName] == nil { @@ -1099,6 +1040,9 @@ func reshard(t *testing.T, ksName string, tableName string, workflow string, sou func shardOrders(t *testing.T) { t.Run("shardOrders", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() + defaultCell := vc.Cells[vc.CellNames[0]] workflow := "o2c" cell := defaultCell.Name sourceKs := "product" @@ -1115,7 +1059,7 @@ func shardOrders(t *testing.T) { catchup(t, customerTab1, workflow, workflowType) catchup(t, customerTab2, workflow, workflowType) vdiffSideBySide(t, ksWorkflow, "") - switchReads(t, workflowType, allCellNames, ksWorkflow, false) + switchReads(t, workflowType, strings.Join(vc.CellNames, ","), ksWorkflow, false) switchWrites(t, workflowType, ksWorkflow, false) moveTablesAction(t, "Complete", cell, workflow, sourceKs, targetKs, tables) waitForRowCountInTablet(t, customerTab1, "customer", "orders", 1) @@ -1141,7 +1085,10 @@ func checkThatVDiffFails(t *testing.T, keyspace, workflow string) { func shardMerchant(t *testing.T) { t.Run("shardMerchant", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() workflow := "p2m" + defaultCell := vc.Cells[vc.CellNames[0]] cell := defaultCell.Name sourceKs := "product" targetKs := merchantKeyspace @@ -1150,10 +1097,6 @@ func shardMerchant(t *testing.T) { if _, err := vc.AddKeyspace(t, []*Cell{defaultCell}, merchantKeyspace, "-80,80-", merchantVSchema, "", defaultReplicas, defaultRdonly, 400, targetKsOpts); err != nil { t.Fatal(err) } - err := cluster.WaitForHealthyShard(vc.VtctldClient, merchantKeyspace, "-80") - require.NoError(t, err) - err = cluster.WaitForHealthyShard(vc.VtctldClient, merchantKeyspace, "80-") - require.NoError(t, err) moveTablesAction(t, "Create", cell, workflow, sourceKs, targetKs, tables) merchantKs := vc.Cells[defaultCell.Name].Keyspaces[merchantKeyspace] merchantTab1 := merchantKs.Shards["-80"].Tablets["zone1-400"].Vttablet @@ -1163,7 +1106,7 @@ func shardMerchant(t *testing.T) { catchup(t, merchantTab2, workflow, workflowType) vdiffSideBySide(t, fmt.Sprintf("%s.%s", merchantKeyspace, workflow), "") - switchReads(t, workflowType, allCellNames, ksWorkflow, false) + switchReads(t, workflowType, strings.Join(vc.CellNames, ","), ksWorkflow, false) switchWrites(t, workflowType, ksWorkflow, false) printRoutingRules(t, vc, "After merchant movetables") @@ -1213,9 +1156,10 @@ func materialize(t *testing.T, spec string, useVtctldClient bool) { func materializeProduct(t *testing.T, useVtctldClient bool) { t.Run("materializeProduct", func(t *testing.T) { - // materializing from "product" keyspace to "customer" keyspace + // Materializing from "product" keyspace to "customer" keyspace. workflow := "cproduct" keyspace := "customer" + defaultCell := vc.Cells[vc.CellNames[0]] applyVSchema(t, materializeProductVSchema, keyspace) materialize(t, materializeProductSpec, useVtctldClient) customerTablets := vc.getVttabletsInKeyspace(t, defaultCell, keyspace, "primary") @@ -1226,7 +1170,7 @@ func materializeProduct(t *testing.T, useVtctldClient bool) { productTablets := vc.getVttabletsInKeyspace(t, defaultCell, "product", "primary") t.Run("throttle-app-product", func(t *testing.T) { - // Now, throttle the streamer on source tablets, insert some rows + // Now, throttle the source side component (vstreamer), and insert some rows. for _, tab := range productTablets { body, err := throttleApp(tab, sourceThrottlerAppName) assert.NoError(t, err) @@ -1237,19 +1181,33 @@ func materializeProduct(t *testing.T, useVtctldClient bool) { waitForTabletThrottlingStatus(t, tab, targetThrottlerAppName, throttlerStatusNotThrottled) } insertMoreProductsForSourceThrottler(t) - // To be fair to the test, we give the target time to apply the new changes. We expect it to NOT get them in the first place, - // we expect the additional rows to **not appear** in the materialized view + // To be fair to the test, we give the target time to apply the new changes. We + // expect it to NOT get them in the first place, we expect the additional rows + // to **not appear** in the materialized view. for _, tab := range customerTablets { waitForRowCountInTablet(t, tab, keyspace, workflow, 5) + // Confirm that we updated the stats on the target tablets as expected. + jsVal, err := getDebugVar(t, tab.Port, []string{"VReplicationThrottledCounts"}) + require.NoError(t, err) + require.NotEqual(t, "{}", jsVal) + // The JSON value looks like this: {"cproduct.4.tablet.vstreamer": 2} + vstreamerThrottledCount := gjson.Get(jsVal, fmt.Sprintf(`%s\.*\.tablet\.vstreamer`, workflow)).Int() + require.Greater(t, vstreamerThrottledCount, int64(0)) + // We only need to do this stat check once. + val, err := getDebugVar(t, tab.Port, []string{"VReplicationThrottledCountTotal"}) + require.NoError(t, err) + throttledCount, err := strconv.ParseInt(val, 10, 64) + require.NoError(t, err) + require.GreaterOrEqual(t, throttledCount, vstreamerThrottledCount) } }) t.Run("unthrottle-app-product", func(t *testing.T) { - // unthrottle on source tablets, and expect the rows to show up + // Unthrottle the vstreamer component, and expect the rows to show up. for _, tab := range productTablets { body, err := unthrottleApp(tab, sourceThrottlerAppName) assert.NoError(t, err) assert.Contains(t, body, sourceThrottlerAppName) - // give time for unthrottling to take effect and for target to fetch data + // Give time for unthrottling to take effect and for targets to fetch data. waitForTabletThrottlingStatus(t, tab, sourceThrottlerAppName, throttlerStatusNotThrottled) } for _, tab := range customerTablets { @@ -1258,8 +1216,8 @@ func materializeProduct(t *testing.T, useVtctldClient bool) { }) t.Run("throttle-app-customer", func(t *testing.T) { - // Now, throttle vreplication (vcopier/vapplier) on target tablets, and - // insert some more rows. + // Now, throttle vreplication on the target side (vplayer), and insert some + // more rows. for _, tab := range customerTablets { body, err := throttleApp(tab, targetThrottlerAppName) assert.NoError(t, err) @@ -1274,6 +1232,13 @@ func materializeProduct(t *testing.T, useVtctldClient bool) { // rows to **not appear** in the materialized view. for _, tab := range customerTablets { waitForRowCountInTablet(t, tab, keyspace, workflow, 8) + // Confirm that we updated the stats on the target tablets as expected. + jsVal, err := getDebugVar(t, tab.Port, []string{"VReplicationThrottledCounts"}) + require.NoError(t, err) + require.NotEqual(t, "{}", jsVal) + // The JSON value now looks like this: {"cproduct.4.tablet.vstreamer": 2, "cproduct.4.tablet.vplayer": 4} + vplayerThrottledCount := gjson.Get(jsVal, fmt.Sprintf(`%s\.*\.tablet\.vplayer`, workflow)).Int() + require.Greater(t, vplayerThrottledCount, int64(0)) } }) t.Run("unthrottle-app-customer", func(t *testing.T) { @@ -1294,9 +1259,12 @@ func materializeProduct(t *testing.T, useVtctldClient bool) { func materializeRollup(t *testing.T, useVtctldClient bool) { t.Run("materializeRollup", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() keyspace := "product" workflow := "rollup" applyVSchema(t, materializeSalesVSchema, keyspace) + defaultCell := vc.Cells[vc.CellNames[0]] productTab := vc.Cells[defaultCell.Name].Keyspaces["product"].Shards["0"].Tablets["zone1-100"].Vttablet materialize(t, materializeRollupSpec, useVtctldClient) catchup(t, productTab, workflow, "Materialize") @@ -1308,9 +1276,12 @@ func materializeRollup(t *testing.T, useVtctldClient bool) { func materializeSales(t *testing.T, useVtctldClient bool) { t.Run("materializeSales", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() keyspace := "product" applyVSchema(t, materializeSalesVSchema, keyspace) materialize(t, materializeSalesSpec, useVtctldClient) + defaultCell := vc.Cells[vc.CellNames[0]] productTab := vc.Cells[defaultCell.Name].Keyspaces["product"].Shards["0"].Tablets["zone1-100"].Vttablet catchup(t, productTab, "sales", "Materialize") waitForRowCount(t, vtgateConn, "product", "sales", 2) @@ -1321,8 +1292,11 @@ func materializeSales(t *testing.T, useVtctldClient bool) { func materializeMerchantSales(t *testing.T, useVtctldClient bool) { t.Run("materializeMerchantSales", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() workflow := "msales" materialize(t, materializeMerchantSalesSpec, useVtctldClient) + defaultCell := vc.Cells[vc.CellNames[0]] merchantTablets := vc.getVttabletsInKeyspace(t, defaultCell, merchantKeyspace, "primary") for _, tab := range merchantTablets { catchup(t, tab, workflow, "Materialize") @@ -1335,10 +1309,13 @@ func materializeMerchantSales(t *testing.T, useVtctldClient bool) { func materializeMerchantOrders(t *testing.T, useVtctldClient bool) { t.Run("materializeMerchantOrders", func(t *testing.T) { + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + defer vtgateConn.Close() workflow := "morders" keyspace := merchantKeyspace applyVSchema(t, merchantOrdersVSchema, keyspace) materialize(t, materializeMerchantOrdersSpec, useVtctldClient) + defaultCell := vc.Cells[vc.CellNames[0]] merchantTablets := vc.getVttabletsInKeyspace(t, defaultCell, merchantKeyspace, "primary") for _, tab := range merchantTablets { catchup(t, tab, workflow, "Materialize") @@ -1599,6 +1576,7 @@ func printSwitchWritesExtraDebug(t *testing.T, ksWorkflow, msg string) { log.Infof("------------------- START Extra debug info %s Switch writes %s", msg, ksWorkflow) ksShards := []string{"product/0", "customer/-80", "customer/80-"} printShardPositions(vc, ksShards) + defaultCell := vc.Cells[vc.CellNames[0]] custKs := vc.Cells[defaultCell.Name].Keyspaces["customer"] customerTab1 := custKs.Shards["-80"].Tablets["zone1-200"].Vttablet customerTab2 := custKs.Shards["80-"].Tablets["zone1-300"].Vttablet diff --git a/go/test/endtoend/vreplication/vreplication_vtctldclient_cli_test.go b/go/test/endtoend/vreplication/vreplication_vtctldclient_cli_test.go new file mode 100644 index 00000000000..bfe0c81ebce --- /dev/null +++ b/go/test/endtoend/vreplication/vreplication_vtctldclient_cli_test.go @@ -0,0 +1,252 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vreplication + +import ( + "fmt" + "slices" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/encoding/protojson" + + "vitess.io/vitess/go/test/endtoend/cluster" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// TestVtctldclientCLI tests the vreplication vtctldclient CLI commands, primarily to check that non-standard flags +// are being handled correctly. The other end-to-end tests are expected to test the various common workflows. +func TestVtctldclientCLI(t *testing.T) { + setSidecarDBName("_vt") + var err error + origDefaultRdonly := defaultRdonly + defer func() { + defaultRdonly = origDefaultRdonly + }() + defaultRdonly = 0 + vc = setupMinimalCluster(t) + + err = vc.Vtctl.AddCellInfo("zone2") + require.NoError(t, err) + zone2, err := vc.AddCell(t, "zone2") + require.NoError(t, err) + require.NotNil(t, zone2) + defer vc.TearDown() + + sourceKeyspace := "product" + targetKeyspace := "customer" + var mt iMoveTables + workflowName := "wf1" + targetTabs := setupMinimalCustomerKeyspace(t) + + t.Run("MoveTablesCreateFlags1", func(t *testing.T) { + testMoveTablesFlags1(t, &mt, sourceKeyspace, targetKeyspace, workflowName, targetTabs) + }) + t.Run("MoveTablesCreateFlags2", func(t *testing.T) { + testMoveTablesFlags2(t, &mt, sourceKeyspace, targetKeyspace, workflowName, targetTabs) + }) + t.Run("MoveTablesCompleteFlags", func(t *testing.T) { + testMoveTablesFlags3(t, sourceKeyspace, targetKeyspace, targetTabs) + }) +} + +// Tests several create flags and some complete flags and validates that some of them are set correctly for the workflow. +func testMoveTablesFlags1(t *testing.T, mt *iMoveTables, sourceKeyspace, targetKeyspace, workflowName string, targetTabs map[string]*cluster.VttabletProcess) { + tables := "customer,customer2" + createFlags := []string{"--auto-start=false", "--defer-secondary-keys=false", "--stop-after-copy", + "--no-routing-rules", "--on-ddl", "STOP", "--exclude-tables", "customer2", + "--tablet-types", "primary,rdonly", "--tablet-types-in-preference-order=true", + "--all-cells", + } + completeFlags := []string{"--keep-routing-rules", "--keep-data"} + switchFlags := []string{} + *mt = createMoveTables(t, sourceKeyspace, targetKeyspace, workflowName, tables, createFlags, completeFlags, switchFlags) + (*mt).Show() + moveTablesOutput := (*mt).GetLastOutput() + // Test one set of MoveTable flags. + + workflowOutput, err := vc.VtctldClient.ExecuteCommandWithOutput("Workflow", "--keyspace", "customer", "show", "--workflow", "wf1") + require.NoError(t, err) + var moveTablesResponse vtctldatapb.GetWorkflowsResponse + err = protojson.Unmarshal([]byte(moveTablesOutput), &moveTablesResponse) + require.NoError(t, err) + + var workflowResponse vtctldatapb.GetWorkflowsResponse + err = protojson.Unmarshal([]byte(workflowOutput), &workflowResponse) + require.NoError(t, err) + + moveTablesResponse.Workflows[0].MaxVReplicationTransactionLag = 0 + moveTablesResponse.Workflows[0].MaxVReplicationLag = 0 + workflowResponse.Workflows[0].MaxVReplicationTransactionLag = 0 + workflowResponse.Workflows[0].MaxVReplicationLag = 0 + // also validates that MoveTables Show and Workflow Show return the same output. + require.EqualValues(t, moveTablesResponse.CloneVT(), workflowResponse.CloneVT()) + + // Validate that the flags are set correctly in the database. + validateWorkflow1(t, workflowResponse.Workflows) + // Since we used --no-routing-rules, there should be no routing rules. + confirmNoRoutingRules(t) +} + +// Validates some of the flags created from the previous test. +func testMoveTablesFlags2(t *testing.T, mt *iMoveTables, sourceKeyspace, targetKeyspace, workflowName string, targetTabs map[string]*cluster.VttabletProcess) { + ksWorkflow := fmt.Sprintf("%s.%s", targetKeyspace, workflowName) + (*mt).Start() // Need to start because we set auto-start to false. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Stopped.String()) + confirmNoRoutingRules(t) + for _, tab := range targetTabs { + alias := fmt.Sprintf("zone1-%d", tab.TabletUID) + query := "update _vt.vreplication set source := replace(source, 'stop_after_copy:true', 'stop_after_copy:false') where db_name = 'vt_customer' and workflow = 'wf1'" + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ExecuteFetchAsDba", alias, query) + require.NoError(t, err, output) + } + confirmNoRoutingRules(t) + (*mt).Start() // Need to start because we set stop-after-copy to true. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + (*mt).Stop() // Test stopping workflow. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Stopped.String()) + (*mt).Start() + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + for _, tab := range targetTabs { + catchup(t, tab, workflowName, "MoveTables") + } + (*mt).SwitchReadsAndWrites() + (*mt).Complete() + confirmRoutingRulesExist(t) + // Confirm that --keep-data was honored. + require.True(t, checkTablesExist(t, "zone1-100", []string{"customer", "customer2"})) +} + +// Tests SwitchTraffic and Complete flags +func testMoveTablesFlags3(t *testing.T, sourceKeyspace, targetKeyspace string, targetTabs map[string]*cluster.VttabletProcess) { + for _, tab := range targetTabs { + alias := fmt.Sprintf("zone1-%d", tab.TabletUID) + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ExecuteFetchAsDba", alias, "drop table customer") + require.NoError(t, err, output) + } + createFlags := []string{} + completeFlags := []string{"--rename-tables"} + tables := "customer2" + switchFlags := []string{"--enable-reverse-replication=false"} + mt := createMoveTables(t, sourceKeyspace, targetKeyspace, workflowName, tables, createFlags, completeFlags, switchFlags) + mt.Start() // Need to start because we set stop-after-copy to true. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + mt.Stop() // Test stopping workflow. + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Stopped.String()) + mt.Start() + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + for _, tab := range targetTabs { + catchup(t, tab, workflowName, "MoveTables") + } + mt.SwitchReadsAndWrites() + mt.Complete() + // Confirm that the source tables were renamed. + require.True(t, checkTablesExist(t, "zone1-100", []string{"_customer2_old"})) + require.False(t, checkTablesExist(t, "zone1-100", []string{"customer2"})) +} + +func createMoveTables(t *testing.T, sourceKeyspace, targetKeyspace, workflowName, tables string, + createFlags, completeFlags, switchFlags []string) iMoveTables { + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + tables: tables, + createFlags: createFlags, + completeFlags: completeFlags, + switchFlags: switchFlags, + }, workflowFlavorVtctld) + mt.Create() + return mt +} + +func checkTablesExist(t *testing.T, tabletAlias string, tables []string) bool { + tablesResponse, err := vc.VtctldClient.ExecuteCommandWithOutput("GetSchema", tabletAlias, "--tables", strings.Join(tables, ","), "--table-names-only") + require.NoError(t, err) + tablesFound := strings.Split(tablesResponse, "\n") + for _, table := range tables { + found := false + for _, tableFound := range tablesFound { + if tableFound == table { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func getRoutingRules(t *testing.T) *vschemapb.RoutingRules { + routingRules, err := vc.VtctldClient.ExecuteCommandWithOutput("GetRoutingRules") + require.NoError(t, err) + var routingRulesResponse vschemapb.RoutingRules + err = protojson.Unmarshal([]byte(routingRules), &routingRulesResponse) + require.NoError(t, err) + return &routingRulesResponse +} +func confirmNoRoutingRules(t *testing.T) { + routingRulesResponse := getRoutingRules(t) + require.Zero(t, len(routingRulesResponse.Rules)) +} + +func confirmRoutingRulesExist(t *testing.T) { + routingRulesResponse := getRoutingRules(t) + require.NotZero(t, len(routingRulesResponse.Rules)) +} + +// We only want to validate non-standard attributes that are set by the CLI. The other end-to-end tests validate the rest. +// We also check some of the standard attributes to make sure they are set correctly. +func validateWorkflow1(t *testing.T, workflows []*vtctldatapb.Workflow) { + require.Equal(t, 1, len(workflows)) + wf := workflows[0] + require.Equal(t, "wf1", wf.Name) + require.Equal(t, binlogdatapb.VReplicationWorkflowType_MoveTables.String(), wf.WorkflowType) + require.Equal(t, "None", wf.WorkflowSubType) + require.Equal(t, "customer", wf.Target.Keyspace) + require.Equal(t, 2, len(wf.Target.Shards)) + require.Equal(t, "product", wf.Source.Keyspace) + require.Equal(t, 1, len(wf.Source.Shards)) + require.False(t, wf.DeferSecondaryKeys) + + require.GreaterOrEqual(t, len(wf.ShardStreams), int(1)) + oneStream := maps.Values(wf.ShardStreams)[0] + require.NotNil(t, oneStream) + + stream := oneStream.Streams[0] + require.Equal(t, binlogdatapb.VReplicationWorkflowState_Stopped.String(), stream.State) + require.Equal(t, stream.TabletSelectionPreference, tabletmanagerdatapb.TabletSelectionPreference_INORDER) + require.True(t, slices.Equal([]topodatapb.TabletType{topodatapb.TabletType_PRIMARY, topodatapb.TabletType_RDONLY}, stream.TabletTypes)) + require.True(t, slices.Equal([]string{"zone1", "zone2"}, stream.Cells)) + + bls := stream.BinlogSource + require.Equalf(t, 1, len(bls.Filter.Rules), "Rules are %+v", bls.Filter.Rules) // only customer, customer2 should be excluded + require.Equal(t, binlogdatapb.OnDDLAction_STOP, bls.OnDdl) + require.True(t, bls.StopAfterCopy) +} diff --git a/go/test/endtoend/vreplication/vschema_load_test.go b/go/test/endtoend/vreplication/vschema_load_test.go index a5cac4c68f8..6ca8dcfe472 100644 --- a/go/test/endtoend/vreplication/vschema_load_test.go +++ b/go/test/endtoend/vreplication/vschema_load_test.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -40,23 +39,13 @@ func TestVSchemaChangesUnderLoad(t *testing.T) { extendedTimeout := defaultTimeout * 4 - defaultCellName := "zone1" - allCells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestVSchemaChanges", allCells, mainClusterConfig) + vc = NewVitessCluster(t, nil) + defer vc.TearDown() - require.NotNil(t, vc) - - defer vc.TearDown(t) - - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, "product", "0", initialProductVSchema, initialProductSchema, 1, 0, 100, sourceKsOpts) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, "product", "0") - require.NoError(t, err) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", "product", "0"), 1, 30*time.Second) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + + vtgateConn := vc.GetVTGateConn(t) defer vtgateConn.Close() // ch is used to signal that there is significant data inserted into the tables and when a lot of vschema changes have been applied diff --git a/go/test/endtoend/vreplication/vstream_test.go b/go/test/endtoend/vreplication/vstream_test.go index 5c5e6a80130..dee8243d5e9 100644 --- a/go/test/endtoend/vreplication/vstream_test.go +++ b/go/test/endtoend/vreplication/vstream_test.go @@ -27,7 +27,6 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -43,26 +42,21 @@ import ( // - We ensure that this works through active reparents and doesn't miss any events // - We stream only from the primary and while streaming we reparent to a replica and then back to the original primary func testVStreamWithFailover(t *testing.T, failover bool) { - defaultCellName := "zone1" - cells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestVStreamWithFailover", cells, mainClusterConfig) + vc = NewVitessCluster(t, nil) + defer vc.TearDown() require.NotNil(t, vc) defaultReplicas = 2 defaultRdonly = 0 - defer vc.TearDown(t) - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, "product", "0", initialProductVSchema, initialProductSchema, defaultReplicas, defaultRdonly, 100, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", "product", "0"), 3, 30*time.Second) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() - verifyClusterHealth(t, vc) insertInitialData(t) + vtgate := defaultCell.Vtgates[0] + t.Run("VStreamFrom", func(t *testing.T) { + testVStreamFrom(t, vtgate, "product", 2) + }) ctx := context.Background() vstreamConn, err := vtgateconn.Dial(ctx, fmt.Sprintf("%s:%d", vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateGrpcPort)) if err != nil { @@ -90,6 +84,9 @@ func testVStreamWithFailover(t *testing.T, failover bool) { stopInserting := false id := 0 + vtgateConn := vc.GetVTGateConn(t) + defer vtgateConn.Close() + // first goroutine that keeps inserting rows into table being streamed until some time elapses after second PRS go func() { for { @@ -217,7 +214,12 @@ const vschemaSharded = ` ` func insertRow(keyspace, table string, id int) { - vtgateConn.ExecuteFetch(fmt.Sprintf("use %s;", keyspace), 1000, false) + vtgateConn := getConnectionNoError(vc.t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + // Due to race conditions this call is sometimes made after vtgates have shutdown. In that case just return. + if vtgateConn == nil { + return + } + vtgateConn.ExecuteFetch(fmt.Sprintf("use %s", keyspace), 1000, false) vtgateConn.ExecuteFetch("begin", 1000, false) _, err := vtgateConn.ExecuteFetch(fmt.Sprintf("insert into %s (name) values ('%s%d')", table, table, id), 1000, false) if err != nil { @@ -228,33 +230,25 @@ func insertRow(keyspace, table string, id int) { type numEvents struct { numRowEvents, numJournalEvents int64 - numLessThan80Events, numGreaterThan80Events int64 - numLessThan40Events, numGreaterThan40Events int64 + numDash80Events, num80DashEvents int64 + numDash40Events, num40DashEvents int64 numShard0BeforeReshardEvents, numShard0AfterReshardEvents int64 } // tests the StopOnReshard flag func testVStreamStopOnReshardFlag(t *testing.T, stopOnReshard bool, baseTabletID int) *numEvents { defaultCellName := "zone1" - allCells := []string{"zone1"} - allCellNames = "zone1" - vc = NewVitessCluster(t, "TestVStreamStopOnReshard", allCells, mainClusterConfig) + vc = NewVitessCluster(t, nil) require.NotNil(t, vc) defaultReplicas = 0 // because of CI resource constraints we can only run this test with primary tablets defer func() { defaultReplicas = 1 }() - defer vc.TearDown(t) + defer vc.TearDown() - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, "unsharded", "0", vschemaUnsharded, schemaUnsharded, defaultReplicas, defaultRdonly, baseTabletID+100, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - err := cluster.WaitForHealthyShard(vc.VtctldClient, "unsharded", "0") - require.NoError(t, err) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) - defer vtgateConn.Close() verifyClusterHealth(t, vc) // some initial data @@ -325,13 +319,13 @@ func testVStreamStopOnReshardFlag(t *testing.T, stopOnReshard bool, baseTabletID shard := ev.RowEvent.Shard switch shard { case "-80": - ne.numLessThan80Events++ + ne.numDash80Events++ case "80-": - ne.numGreaterThan80Events++ + ne.num80DashEvents++ case "-40": - ne.numLessThan40Events++ + ne.numDash40Events++ case "40-": - ne.numGreaterThan40Events++ + ne.num40DashEvents++ } ne.numRowEvents++ case binlogdatapb.VEventType_JOURNAL: @@ -385,25 +379,17 @@ func testVStreamStopOnReshardFlag(t *testing.T, stopOnReshard bool, baseTabletID // Validate that we can continue streaming from multiple keyspaces after first copying some tables and then resharding one of the keyspaces // Ensure that there are no missing row events during the resharding process. func testVStreamCopyMultiKeyspaceReshard(t *testing.T, baseTabletID int) numEvents { - defaultCellName := "zone1" - allCellNames = defaultCellName - allCells := []string{allCellNames} - vc = NewVitessCluster(t, "VStreamCopyMultiKeyspaceReshard", allCells, mainClusterConfig) - - require.NotNil(t, vc) + vc = NewVitessCluster(t, nil) ogdr := defaultReplicas defaultReplicas = 0 // because of CI resource constraints we can only run this test with primary tablets defer func(dr int) { defaultReplicas = dr }(ogdr) - defer vc.TearDown(t) + defer vc.TearDown() - defaultCell = vc.Cells[defaultCellName] + defaultCell := vc.Cells[vc.CellNames[0]] vc.AddKeyspace(t, []*Cell{defaultCell}, "unsharded", "0", vschemaUnsharded, schemaUnsharded, defaultReplicas, defaultRdonly, baseTabletID+100, nil) - vtgate = defaultCell.Vtgates[0] - require.NotNil(t, vtgate) - vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.primary", "unsharded", "0"), 1, 30*time.Second) - vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) + vtgateConn := getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) defer vtgateConn.Close() verifyClusterHealth(t, vc) @@ -468,13 +454,13 @@ func testVStreamCopyMultiKeyspaceReshard(t *testing.T, baseTabletID int) numEven ne.numShard0BeforeReshardEvents++ } case "-80": - ne.numLessThan80Events++ + ne.numDash80Events++ case "80-": - ne.numGreaterThan80Events++ + ne.num80DashEvents++ case "-40": - ne.numLessThan40Events++ + ne.numDash40Events++ case "40-": - ne.numGreaterThan40Events++ + ne.num40DashEvents++ } ne.numRowEvents++ case binlogdatapb.VEventType_JOURNAL: @@ -522,7 +508,7 @@ func testVStreamCopyMultiKeyspaceReshard(t *testing.T, baseTabletID int) numEven customerResult := execVtgateQuery(t, vtgateConn, "sharded", "select count(*) from customer") insertedCustomerRows, err := customerResult.Rows[0][0].ToCastInt64() require.NoError(t, err) - require.Equal(t, insertedCustomerRows, ne.numLessThan80Events+ne.numGreaterThan80Events+ne.numLessThan40Events+ne.numGreaterThan40Events) + require.Equal(t, insertedCustomerRows, ne.numDash80Events+ne.num80DashEvents+ne.numDash40Events+ne.num40DashEvents) return ne } @@ -534,20 +520,20 @@ func TestVStreamStopOnReshardTrue(t *testing.T) { ne := testVStreamStopOnReshardFlag(t, true, 1000) require.Greater(t, ne.numJournalEvents, int64(0)) require.NotZero(t, ne.numRowEvents) - require.NotZero(t, ne.numLessThan80Events) - require.NotZero(t, ne.numGreaterThan80Events) - require.Zero(t, ne.numLessThan40Events) - require.Zero(t, ne.numGreaterThan40Events) + require.NotZero(t, ne.numDash80Events) + require.NotZero(t, ne.num80DashEvents) + require.Zero(t, ne.numDash40Events) + require.Zero(t, ne.num40DashEvents) } func TestVStreamStopOnReshardFalse(t *testing.T) { ne := testVStreamStopOnReshardFlag(t, false, 2000) require.Equal(t, int64(0), ne.numJournalEvents) require.NotZero(t, ne.numRowEvents) - require.NotZero(t, ne.numLessThan80Events) - require.NotZero(t, ne.numGreaterThan80Events) - require.NotZero(t, ne.numLessThan40Events) - require.NotZero(t, ne.numGreaterThan40Events) + require.NotZero(t, ne.numDash80Events) + require.NotZero(t, ne.num80DashEvents) + require.NotZero(t, ne.numDash40Events) + require.NotZero(t, ne.num40DashEvents) } func TestVStreamWithKeyspacesToWatch(t *testing.T) { @@ -564,8 +550,8 @@ func TestVStreamCopyMultiKeyspaceReshard(t *testing.T) { require.NotZero(t, ne.numRowEvents) require.NotZero(t, ne.numShard0BeforeReshardEvents) require.NotZero(t, ne.numShard0AfterReshardEvents) - require.NotZero(t, ne.numLessThan80Events) - require.NotZero(t, ne.numGreaterThan80Events) - require.NotZero(t, ne.numLessThan40Events) - require.NotZero(t, ne.numGreaterThan40Events) + require.NotZero(t, ne.numDash80Events) + require.NotZero(t, ne.num80DashEvents) + require.NotZero(t, ne.numDash40Events) + require.NotZero(t, ne.num40DashEvents) } diff --git a/go/test/endtoend/vreplication/wrappers_test.go b/go/test/endtoend/vreplication/wrappers_test.go index 6bd0bbb19d8..5470aeb2bd5 100644 --- a/go/test/endtoend/vreplication/wrappers_test.go +++ b/go/test/endtoend/vreplication/wrappers_test.go @@ -23,53 +23,78 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/wrangler" ) -type moveTablesFlavor int +type iWorkflow interface { + Create() + Show() + SwitchReads() + SwitchWrites() + SwitchReadsAndWrites() + ReverseReadsAndWrites() + Cancel() + Complete() + Flavor() string + GetLastOutput() string + Start() + Stop() +} + +type workflowFlavor int const ( - moveTablesFlavorRandom moveTablesFlavor = iota - moveTablesFlavorVtctl - moveTablesFlavorVtctld + workflowFlavorRandom workflowFlavor = iota + workflowFlavorVtctl + workflowFlavorVtctld ) -var moveTablesFlavors = []moveTablesFlavor{ - moveTablesFlavorVtctl, - moveTablesFlavorVtctld, +var workflowFlavors = []workflowFlavor{ + workflowFlavorVtctl, + workflowFlavorVtctld, +} + +var workflowFlavorNames = map[workflowFlavor]string{ + workflowFlavorVtctl: "vtctl", + workflowFlavorVtctld: "vtctld", } -type moveTables struct { +type workflowInfo struct { vc *VitessCluster workflowName string targetKeyspace string + tabletTypes string +} + +// MoveTables wrappers + +type moveTablesWorkflow struct { + *workflowInfo sourceKeyspace string tables string atomicCopy bool sourceShards string + createFlags []string // currently only used by vtctld + + lastOutput string + completeFlags []string + switchFlags []string } type iMoveTables interface { - Create() - Show() - SwitchReads() - SwitchWrites() - SwitchReadsAndWrites() - ReverseReadsAndWrites() - Cancel() - Complete() - Flavor() string + iWorkflow } -func newMoveTables(vc *VitessCluster, mt *moveTables, flavor moveTablesFlavor) iMoveTables { +func newMoveTables(vc *VitessCluster, mt *moveTablesWorkflow, flavor workflowFlavor) iMoveTables { mt.vc = vc var mt2 iMoveTables - if flavor == moveTablesFlavorRandom { - flavor = moveTablesFlavors[rand.Intn(len(moveTablesFlavors))] + if flavor == workflowFlavorRandom { + flavor = workflowFlavors[rand.Intn(len(workflowFlavors))] } switch flavor { - case moveTablesFlavorVtctl: + case workflowFlavorVtctl: mt2 = newVtctlMoveTables(mt) - case moveTablesFlavorVtctld: + case workflowFlavorVtctld: mt2 = newVtctldMoveTables(mt) default: panic("unreachable") @@ -79,33 +104,31 @@ func newMoveTables(vc *VitessCluster, mt *moveTables, flavor moveTablesFlavor) i } type VtctlMoveTables struct { - *moveTables + *moveTablesWorkflow } func (vmt *VtctlMoveTables) Flavor() string { return "vtctl" } -func newVtctlMoveTables(mt *moveTables) *VtctlMoveTables { +func newVtctlMoveTables(mt *moveTablesWorkflow) *VtctlMoveTables { return &VtctlMoveTables{mt} } func (vmt *VtctlMoveTables) Create() { - log.Infof("vmt is %+v", vmt.vc, vmt.tables) - err := tstWorkflowExec(vmt.vc.t, "", vmt.workflowName, vmt.sourceKeyspace, vmt.targetKeyspace, - vmt.tables, workflowActionCreate, "", vmt.sourceShards, "", vmt.atomicCopy) - require.NoError(vmt.vc.t, err) + currentWorkflowType = wrangler.MoveTablesWorkflow + vmt.exec(workflowActionCreate) } func (vmt *VtctlMoveTables) SwitchReadsAndWrites() { err := tstWorkflowExec(vmt.vc.t, "", vmt.workflowName, vmt.sourceKeyspace, vmt.targetKeyspace, - vmt.tables, workflowActionSwitchTraffic, "", "", "", vmt.atomicCopy) + vmt.tables, workflowActionSwitchTraffic, "", "", "", defaultWorkflowExecOptions) require.NoError(vmt.vc.t, err) } func (vmt *VtctlMoveTables) ReverseReadsAndWrites() { err := tstWorkflowExec(vmt.vc.t, "", vmt.workflowName, vmt.sourceKeyspace, vmt.targetKeyspace, - vmt.tables, workflowActionReverseTraffic, "", "", "", vmt.atomicCopy) + vmt.tables, workflowActionReverseTraffic, "", "", "", defaultWorkflowExecOptions) require.NoError(vmt.vc.t, err) } @@ -114,6 +137,15 @@ func (vmt *VtctlMoveTables) Show() { panic("implement me") } +func (vmt *VtctlMoveTables) exec(action string) { + options := &workflowExecOptions{ + deferSecondaryKeys: false, + atomicCopy: vmt.atomicCopy, + } + err := tstWorkflowExec(vmt.vc.t, "", vmt.workflowName, vmt.sourceKeyspace, vmt.targetKeyspace, + vmt.tables, action, vmt.tabletTypes, vmt.sourceShards, "", options) + require.NoError(vmt.vc.t, err) +} func (vmt *VtctlMoveTables) SwitchReads() { //TODO implement me panic("implement me") @@ -125,23 +157,32 @@ func (vmt *VtctlMoveTables) SwitchWrites() { } func (vmt *VtctlMoveTables) Cancel() { - err := tstWorkflowExec(vmt.vc.t, "", vmt.workflowName, vmt.sourceKeyspace, vmt.targetKeyspace, - vmt.tables, workflowActionCancel, "", "", "", vmt.atomicCopy) - require.NoError(vmt.vc.t, err) + vmt.exec(workflowActionCancel) } func (vmt *VtctlMoveTables) Complete() { - //TODO implement me + vmt.exec(workflowActionComplete) +} + +func (vmt *VtctlMoveTables) GetLastOutput() string { + return vmt.lastOutput +} + +func (vmt *VtctlMoveTables) Start() { + panic("implement me") +} + +func (vmt *VtctlMoveTables) Stop() { panic("implement me") } var _ iMoveTables = (*VtctldMoveTables)(nil) type VtctldMoveTables struct { - *moveTables + *moveTablesWorkflow } -func newVtctldMoveTables(mt *moveTables) *VtctldMoveTables { +func newVtctldMoveTables(mt *moveTablesWorkflow) *VtctldMoveTables { return &VtctldMoveTables{mt} } @@ -152,8 +193,9 @@ func (v VtctldMoveTables) Flavor() string { func (v VtctldMoveTables) exec(args ...string) { args2 := []string{"MoveTables", "--workflow=" + v.workflowName, "--target-keyspace=" + v.targetKeyspace} args2 = append(args2, args...) - if err := vc.VtctldClient.ExecuteCommand(args2...); err != nil { - v.vc.t.Fatalf("failed to create MoveTables workflow: %v", err) + var err error + if v.lastOutput, err = vc.VtctldClient.ExecuteCommandWithOutput(args2...); err != nil { + require.FailNowf(v.vc.t, "failed MoveTables action", "%v: %s", err, v.lastOutput) } } @@ -170,11 +212,14 @@ func (v VtctldMoveTables) Create() { if v.sourceShards != "" { args = append(args, "--source-shards="+v.sourceShards) } + args = append(args, v.createFlags...) v.exec(args...) } func (v VtctldMoveTables) SwitchReadsAndWrites() { - v.exec("SwitchTraffic") + args := []string{"SwitchTraffic"} + args = append(args, v.switchFlags...) + v.exec(args...) } func (v VtctldMoveTables) ReverseReadsAndWrites() { @@ -182,8 +227,7 @@ func (v VtctldMoveTables) ReverseReadsAndWrites() { } func (v VtctldMoveTables) Show() { - //TODO implement me - panic("implement me") + v.exec("Show") } func (v VtctldMoveTables) SwitchReads() { @@ -201,6 +245,198 @@ func (v VtctldMoveTables) Cancel() { } func (v VtctldMoveTables) Complete() { + args := []string{"Complete"} + args = append(args, v.completeFlags...) + v.exec(args...) +} + +func (v VtctldMoveTables) GetLastOutput() string { + return v.lastOutput +} + +func (v VtctldMoveTables) Start() { + v.exec("Start") +} + +func (v VtctldMoveTables) Stop() { + v.exec("Stop") +} + +// Reshard wrappers + +type reshardWorkflow struct { + *workflowInfo + sourceShards string + targetShards string + skipSchemaCopy bool + + lastOutput string +} + +type iReshard interface { + iWorkflow +} + +func newReshard(vc *VitessCluster, rs *reshardWorkflow, flavor workflowFlavor) iReshard { + rs.vc = vc + var rs2 iReshard + if flavor == workflowFlavorRandom { + flavor = workflowFlavors[rand.Intn(len(workflowFlavors))] + } + switch flavor { + case workflowFlavorVtctl: + rs2 = newVtctlReshard(rs) + case workflowFlavorVtctld: + rs2 = newVtctldReshard(rs) + default: + panic("unreachable") + } + log.Infof("Using reshard flavor: %s", rs2.Flavor()) + return rs2 +} + +type VtctlReshard struct { + *reshardWorkflow +} + +func (vrs *VtctlReshard) Flavor() string { + return "vtctl" +} + +func newVtctlReshard(rs *reshardWorkflow) *VtctlReshard { + return &VtctlReshard{rs} +} + +func (vrs *VtctlReshard) Create() { + currentWorkflowType = wrangler.ReshardWorkflow + vrs.exec(workflowActionCreate) +} + +func (vrs *VtctlReshard) SwitchReadsAndWrites() { + vrs.exec(workflowActionSwitchTraffic) +} + +func (vrs *VtctlReshard) ReverseReadsAndWrites() { + vrs.exec(workflowActionReverseTraffic) +} + +func (vrs *VtctlReshard) Show() { + //TODO implement me + panic("implement me") +} + +func (vrs *VtctlReshard) exec(action string) { + options := &workflowExecOptions{} + err := tstWorkflowExec(vrs.vc.t, "", vrs.workflowName, "", vrs.targetKeyspace, + "", action, vrs.tabletTypes, vrs.sourceShards, vrs.targetShards, options) + require.NoError(vrs.vc.t, err) +} + +func (vrs *VtctlReshard) SwitchReads() { + //TODO implement me + panic("implement me") +} + +func (vrs *VtctlReshard) SwitchWrites() { //TODO implement me panic("implement me") } + +func (vrs *VtctlReshard) Cancel() { + vrs.exec(workflowActionCancel) +} + +func (vrs *VtctlReshard) Complete() { + vrs.exec(workflowActionComplete) +} + +func (vrs *VtctlReshard) GetLastOutput() string { + return vrs.lastOutput +} + +func (vrs *VtctlReshard) Start() { + panic("implement me") +} + +func (vrs *VtctlReshard) Stop() { + panic("implement me") +} + +var _ iReshard = (*VtctldReshard)(nil) + +type VtctldReshard struct { + *reshardWorkflow +} + +func newVtctldReshard(rs *reshardWorkflow) *VtctldReshard { + return &VtctldReshard{rs} +} + +func (v VtctldReshard) Flavor() string { + return "vtctld" +} + +func (v VtctldReshard) exec(args ...string) { + args2 := []string{"Reshard", "--workflow=" + v.workflowName, "--target-keyspace=" + v.targetKeyspace} + args2 = append(args2, args...) + if err := vc.VtctldClient.ExecuteCommand(args2...); err != nil { + v.vc.t.Fatalf("failed to create Reshard workflow: %v", err) + } +} + +func (v VtctldReshard) Create() { + args := []string{"Create"} + if v.sourceShards != "" { + args = append(args, "--source-shards="+v.sourceShards) + } + if v.targetShards != "" { + args = append(args, "--target-shards="+v.targetShards) + } + if v.skipSchemaCopy { + args = append(args, "--skip-schema-copy="+strconv.FormatBool(v.skipSchemaCopy)) + } + v.exec(args...) +} + +func (v VtctldReshard) SwitchReadsAndWrites() { + v.exec("SwitchTraffic") +} + +func (v VtctldReshard) ReverseReadsAndWrites() { + v.exec("ReverseTraffic") +} + +func (v VtctldReshard) Show() { + //TODO implement me + panic("implement me") +} + +func (v VtctldReshard) SwitchReads() { + //TODO implement me + panic("implement me") +} + +func (v VtctldReshard) SwitchWrites() { + //TODO implement me + panic("implement me") +} + +func (v VtctldReshard) Cancel() { + v.exec("Cancel") +} + +func (v VtctldReshard) Complete() { + v.exec("Complete") +} + +func (v VtctldReshard) GetLastOutput() string { + return v.lastOutput +} + +func (vrs *VtctldReshard) Start() { + vrs.exec("Start") +} + +func (vrs *VtctldReshard) Stop() { + vrs.exec("Stop") +} diff --git a/go/test/endtoend/vtcombo/recreate/recreate_test.go b/go/test/endtoend/vtcombo/recreate/recreate_test.go index a454adbd7e1..e66edb7688a 100644 --- a/go/test/endtoend/vtcombo/recreate/recreate_test.go +++ b/go/test/endtoend/vtcombo/recreate/recreate_test.go @@ -60,8 +60,7 @@ func TestMain(m *testing.M) { ReplicaCount: 2, }, { - Name: redirected, - ServedFrom: ks1, + Name: redirected, }, } diff --git a/go/test/endtoend/vtcombo/vttest_sample_test.go b/go/test/endtoend/vtcombo/vttest_sample_test.go index 91db0f8a2c0..daeb5e8deb9 100644 --- a/go/test/endtoend/vtcombo/vttest_sample_test.go +++ b/go/test/endtoend/vtcombo/vttest_sample_test.go @@ -48,7 +48,6 @@ var ( vtctldAddr string mysqlAddress string ks1 = "test_keyspace" - redirected = "redirected" jsonTopo = ` { "keyspaces": [ @@ -58,10 +57,6 @@ var ( "rdonlyCount": 1, "replicaCount": 2 }, - { - "name": "redirected", - "servedFrom": "test_keyspace" - }, { "name": "routed", "shards": [{"name": "0"}] @@ -174,15 +169,6 @@ func assertInsertedRowsExist(ctx context.Context, t *testing.T, conn *vtgateconn require.NoError(t, err) assert.Equal(t, rowCount, len(res.Rows)) - - cur = conn.Session(redirected+":-80@replica", nil) - bindVariables = map[string]*querypb.BindVariable{ - "id_start": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(idStart), 10))}, - } - res, err = cur.Execute(ctx, "select * from test_table where id = :id_start", bindVariables) - require.NoError(t, err) - require.Equal(t, 1, len(res.Rows)) - assert.Equal(t, "VARCHAR(\"test1000\")", res.Rows[0][1].String()) } func assertRouting(ctx context.Context, t *testing.T, db *sql.DB) { diff --git a/go/test/endtoend/vtgate/errors_as_warnings/main_test.go b/go/test/endtoend/vtgate/errors_as_warnings/main_test.go index 24cade5b550..71f4a2353f7 100644 --- a/go/test/endtoend/vtgate/errors_as_warnings/main_test.go +++ b/go/test/endtoend/vtgate/errors_as_warnings/main_test.go @@ -153,7 +153,7 @@ func TestScatterErrsAsWarns(t *testing.T) { assertContainsOneOf(t, mode.conn, showQ, expectedWarnings...) // invalid_field should throw error and not warning - _, err = mode.conn.ExecuteFetch("SELECT /*vt+ PLANNER=Gen4 SCATTER_ERRORS_AS_WARNINGS */ invalid_field from t1;", 1, false) + _, err = mode.conn.ExecuteFetch("SELECT /*vt+ PLANNER=Gen4 SCATTER_ERRORS_AS_WARNINGS */ invalid_field from t1", 1, false) require.Error(t, err) serr := sqlerror.NewSQLErrorFromError(err).(*sqlerror.SQLError) require.Equal(t, sqlerror.ERBadFieldError, serr.Number(), serr.Error()) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go index 134b9cfa180..8ff1d660537 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go @@ -28,17 +28,17 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" ) type QueryFormat string const ( SQLQueries QueryFormat = "SQLQueries" + OlapSQLQueries QueryFormat = "OlapSQLQueries" PreparedStatmentQueries QueryFormat = "PreparedStatmentQueries" PreparedStatementPacket QueryFormat = "PreparedStatementPacket" ) @@ -53,6 +53,8 @@ type fuzzer struct { updateShare int concurrency int queryFormat QueryFormat + noFkSetVar bool + fkState *bool // shouldStop is an internal state variable, that tells the fuzzer // whether it should stop or not. @@ -72,7 +74,7 @@ type debugInfo struct { } // newFuzzer creates a new fuzzer struct. -func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare int, deleteShare int, updateShare int, queryFormat QueryFormat) *fuzzer { +func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare int, deleteShare int, updateShare int, queryFormat QueryFormat, fkState *bool) *fuzzer { fz := &fuzzer{ concurrency: concurrency, maxValForId: maxValForId, @@ -81,6 +83,8 @@ func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare i deleteShare: deleteShare, updateShare: updateShare, queryFormat: queryFormat, + fkState: fkState, + noFkSetVar: false, wg: sync.WaitGroup{}, } // Initially the fuzzer thread is stopped. @@ -95,17 +99,17 @@ func (fz *fuzzer) generateQuery() []string { val := rand.Intn(fz.insertShare + fz.updateShare + fz.deleteShare) if val < fz.insertShare { switch fz.queryFormat { - case SQLQueries: - return []string{fz.generateInsertDMLQuery()} + case OlapSQLQueries, SQLQueries: + return []string{fz.generateInsertDMLQuery(getInsertType())} case PreparedStatmentQueries: - return fz.getPreparedInsertQueries() + return fz.getPreparedInsertQueries(getInsertType()) default: panic("Unknown query type") } } if val < fz.insertShare+fz.updateShare { switch fz.queryFormat { - case SQLQueries: + case OlapSQLQueries, SQLQueries: return []string{fz.generateUpdateDMLQuery()} case PreparedStatmentQueries: return fz.getPreparedUpdateQueries() @@ -114,7 +118,7 @@ func (fz *fuzzer) generateQuery() []string { } } switch fz.queryFormat { - case SQLQueries: + case OlapSQLQueries, SQLQueries: return []string{fz.generateDeleteDMLQuery()} case PreparedStatmentQueries: return fz.getPreparedDeleteQueries() @@ -123,31 +127,28 @@ func (fz *fuzzer) generateQuery() []string { } } +func getInsertType() string { + return []string{"insert", "replace"}[rand.Intn(2)] +} + // generateInsertDMLQuery generates an INSERT query from the parameters for the fuzzer. -func (fz *fuzzer) generateInsertDMLQuery() string { +func (fz *fuzzer) generateInsertDMLQuery(insertType string) string { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) tableName := fkTables[tableId] + setVarFkChecksVal := fz.getSetVarFkChecksVal() if tableName == "fk_t20" { colValue := rand.Intn(1 + fz.maxValForCol) col2Value := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, col, col2) values (%v, %v, %v)", tableName, idValue, convertColValueToString(colValue), convertColValueToString(col2Value)) + return fmt.Sprintf("%s %vinto %v (id, col, col2) values (%v, %v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colValue), convertIntValueToString(col2Value)) } else if isMultiColFkTable(tableName) { colaValue := rand.Intn(1 + fz.maxValForCol) colbValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, cola, colb) values (%v, %v, %v)", tableName, idValue, convertColValueToString(colaValue), convertColValueToString(colbValue)) + return fmt.Sprintf("%s %vinto %v (id, cola, colb) values (%v, %v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colaValue), convertIntValueToString(colbValue)) } else { colValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, col) values (%v, %v)", tableName, idValue, convertColValueToString(colValue)) - } -} - -// convertColValueToString converts the given value to a string -func convertColValueToString(value int) string { - if value == 0 { - return "NULL" + return fmt.Sprintf("%s %vinto %v (id, col) values (%v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colValue)) } - return fmt.Sprintf("%d", value) } // generateUpdateDMLQuery generates an UPDATE query from the parameters for the fuzzer. @@ -155,28 +156,63 @@ func (fz *fuzzer) generateUpdateDMLQuery() string { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) tableName := fkTables[tableId] + setVarFkChecksVal := fz.getSetVarFkChecksVal() if tableName == "fk_t20" { - colValue := rand.Intn(1 + fz.maxValForCol) - col2Value := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set col = %v, col2 = %v where id = %v", tableName, convertColValueToString(colValue), convertColValueToString(col2Value), idValue) + colValue := convertIntValueToString(rand.Intn(1 + fz.maxValForCol)) + col2Value := convertIntValueToString(rand.Intn(1 + fz.maxValForCol)) + return fmt.Sprintf("update %v%v set col = %v, col2 = %v where id = %v", setVarFkChecksVal, tableName, colValue, col2Value, idValue) } else if isMultiColFkTable(tableName) { - colaValue := rand.Intn(1 + fz.maxValForCol) - colbValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set cola = %v, colb = %v where id = %v", tableName, convertColValueToString(colaValue), convertColValueToString(colbValue), idValue) + if rand.Intn(2) == 0 { + colaValue := convertIntValueToString(rand.Intn(1 + fz.maxValForCol)) + colbValue := convertIntValueToString(rand.Intn(1 + fz.maxValForCol)) + if fz.concurrency > 1 { + colaValue = fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id") + colbValue = fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id") + } + return fmt.Sprintf("update %v%v set cola = %v, colb = %v where id = %v", setVarFkChecksVal, tableName, colaValue, colbValue, idValue) + } else { + colValue := fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id") + colToUpdate := []string{"cola", "colb"}[rand.Intn(2)] + return fmt.Sprintf("update %v set %v = %v where id = %v", tableName, colToUpdate, colValue, idValue) + } } else { - colValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set col = %v where id = %v", tableName, convertColValueToString(colValue), idValue) + colValue := fz.generateExpression(rand.Intn(4)+1, "col", "id") + return fmt.Sprintf("update %v%v set col = %v where id = %v", setVarFkChecksVal, tableName, colValue, idValue) } } -// generateDeleteDMLQuery generates a DELETE query from the parameters for the fuzzer. -func (fz *fuzzer) generateDeleteDMLQuery() string { +// generateDeleteDMLQuery generates a DELETE query using 1 table from the parameters for the fuzzer. +func (fz *fuzzer) generateSingleDeleteDMLQuery() string { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) - query := fmt.Sprintf("delete from %v where id = %v", fkTables[tableId], idValue) + setVarFkChecksVal := fz.getSetVarFkChecksVal() + delWithLimit := rand.Intn(2) + if delWithLimit == 0 { + return fmt.Sprintf("delete %vfrom %v where id = %v", setVarFkChecksVal, fkTables[tableId], idValue) + } + limitCount := rand.Intn(3) + return fmt.Sprintf("delete %vfrom %v order by id limit %v", setVarFkChecksVal, fkTables[tableId], limitCount) +} + +// generateMultiDeleteDMLQuery generates a DELETE query using 2 tables from the parameters for the fuzzer. +func (fz *fuzzer) generateMultiDeleteDMLQuery() string { + tableId := rand.Intn(len(fkTables)) + tableId2 := rand.Intn(len(fkTables)) + idValue := 1 + rand.Intn(fz.maxValForId) + setVarFkChecksVal := fz.getSetVarFkChecksVal() + query := fmt.Sprintf("delete %v%v from %v join %v using (id) where %v.id = %v", setVarFkChecksVal, fkTables[tableId], fkTables[tableId], fkTables[tableId2], fkTables[tableId], idValue) return query } +// generateDeleteDMLQuery generates a DELETE query from the parameters for the fuzzer. +func (fz *fuzzer) generateDeleteDMLQuery() string { + multiTableDelete := rand.Intn(2) + 1 + if multiTableDelete == 1 { + return fz.generateSingleDeleteDMLQuery() + } + return fz.generateMultiDeleteDMLQuery() +} + // start starts running the fuzzer. func (fz *fuzzer) start(t *testing.T, sharded bool) { // We mark the fuzzer thread to be running now. @@ -199,6 +235,9 @@ func (fz *fuzzer) runFuzzerThread(t *testing.T, sharded bool, fuzzerThreadId int // Create a MySQL Compare that connects to both Vitess and MySQL and runs the queries against both. mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams) require.NoError(t, err) + if fz.fkState != nil { + mcmp.Exec(fmt.Sprintf("SET FOREIGN_KEY_CHECKS=%v", sqlparser.FkChecksStateString(fz.fkState))) + } var vitessDb, mysqlDb *sql.DB if fz.queryFormat == PreparedStatementPacket { // Open another connection to Vitess using the go-sql-driver so that we can send prepared statements as COM_STMT_PREPARE packets. @@ -222,13 +261,16 @@ func (fz *fuzzer) runFuzzerThread(t *testing.T, sharded bool, fuzzerThreadId int _, _ = vitessDb.Exec("use `uks`") } } + if fz.queryFormat == OlapSQLQueries { + _ = utils.Exec(t, mcmp.VtConn, "set workload = olap") + } for { // If fuzzer thread is marked to be stopped, then we should exit this go routine. if fz.shouldStop.Load() == true { return } switch fz.queryFormat { - case SQLQueries, PreparedStatmentQueries: + case OlapSQLQueries, SQLQueries, PreparedStatmentQueries: if fz.generateAndExecuteStatementQuery(t, mcmp) { return } @@ -328,7 +370,7 @@ func (fz *fuzzer) getPreparedDeleteQueries() []string { } // getPreparedInsertQueries gets the list of queries to run for executing an INSERT using prepared statements. -func (fz *fuzzer) getPreparedInsertQueries() []string { +func (fz *fuzzer) getPreparedInsertQueries(insertType string) []string { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) tableName := fkTables[tableId] @@ -336,28 +378,28 @@ func (fz *fuzzer) getPreparedInsertQueries() []string { colValue := rand.Intn(1 + fz.maxValForCol) col2Value := rand.Intn(1 + fz.maxValForCol) return []string{ - "prepare stmt_insert from 'insert into fk_t20 (id, col, col2) values (?, ?, ?)'", + fmt.Sprintf("prepare stmt_insert from '%s into fk_t20 (id, col, col2) values (?, ?, ?)'", insertType), fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @col = %v", convertColValueToString(colValue)), - fmt.Sprintf("SET @col2 = %v", convertColValueToString(col2Value)), + fmt.Sprintf("SET @col = %v", convertIntValueToString(colValue)), + fmt.Sprintf("SET @col2 = %v", convertIntValueToString(col2Value)), "execute stmt_insert using @id, @col, @col2", } } else if isMultiColFkTable(tableName) { colaValue := rand.Intn(1 + fz.maxValForCol) colbValue := rand.Intn(1 + fz.maxValForCol) return []string{ - fmt.Sprintf("prepare stmt_insert from 'insert into %v (id, cola, colb) values (?, ?, ?)'", tableName), + fmt.Sprintf("prepare stmt_insert from '%s into %v (id, cola, colb) values (?, ?, ?)'", insertType, tableName), fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @cola = %v", convertColValueToString(colaValue)), - fmt.Sprintf("SET @colb = %v", convertColValueToString(colbValue)), + fmt.Sprintf("SET @cola = %v", convertIntValueToString(colaValue)), + fmt.Sprintf("SET @colb = %v", convertIntValueToString(colbValue)), "execute stmt_insert using @id, @cola, @colb", } } else { colValue := rand.Intn(1 + fz.maxValForCol) return []string{ - fmt.Sprintf("prepare stmt_insert from 'insert into %v (id, col) values (?, ?)'", tableName), + fmt.Sprintf("prepare stmt_insert from '%s into %v (id, col) values (?, ?)'", insertType, tableName), fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @col = %v", convertColValueToString(colValue)), + fmt.Sprintf("SET @col = %v", convertIntValueToString(colValue)), "execute stmt_insert using @id, @col", } } @@ -374,8 +416,8 @@ func (fz *fuzzer) getPreparedUpdateQueries() []string { return []string{ "prepare stmt_update from 'update fk_t20 set col = ?, col2 = ? where id = ?'", fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @col = %v", convertColValueToString(colValue)), - fmt.Sprintf("SET @col2 = %v", convertColValueToString(col2Value)), + fmt.Sprintf("SET @col = %v", convertIntValueToString(colValue)), + fmt.Sprintf("SET @col2 = %v", convertIntValueToString(col2Value)), "execute stmt_update using @col, @col2, @id", } } else if isMultiColFkTable(tableName) { @@ -384,8 +426,8 @@ func (fz *fuzzer) getPreparedUpdateQueries() []string { return []string{ fmt.Sprintf("prepare stmt_update from 'update %v set cola = ?, colb = ? where id = ?'", tableName), fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @cola = %v", convertColValueToString(colaValue)), - fmt.Sprintf("SET @colb = %v", convertColValueToString(colbValue)), + fmt.Sprintf("SET @cola = %v", convertIntValueToString(colaValue)), + fmt.Sprintf("SET @colb = %v", convertIntValueToString(colbValue)), "execute stmt_update using @cola, @colb, @id", } } else { @@ -393,7 +435,7 @@ func (fz *fuzzer) getPreparedUpdateQueries() []string { return []string{ fmt.Sprintf("prepare stmt_update from 'update %v set col = ? where id = ?'", tableName), fmt.Sprintf("SET @id = %v", idValue), - fmt.Sprintf("SET @col = %v", convertColValueToString(colValue)), + fmt.Sprintf("SET @col = %v", convertIntValueToString(colValue)), "execute stmt_update using @col, @id", } } @@ -403,7 +445,7 @@ func (fz *fuzzer) getPreparedUpdateQueries() []string { func (fz *fuzzer) generateParameterizedQuery() (query string, params []any) { val := rand.Intn(fz.insertShare + fz.updateShare + fz.deleteShare) if val < fz.insertShare { - return fz.generateParameterizedInsertQuery() + return fz.generateParameterizedInsertQuery(getInsertType()) } if val < fz.insertShare+fz.updateShare { return fz.generateParameterizedUpdateQuery() @@ -412,21 +454,21 @@ func (fz *fuzzer) generateParameterizedQuery() (query string, params []any) { } // generateParameterizedInsertQuery generates a parameterized INSERT query for the query format PreparedStatementPacket. -func (fz *fuzzer) generateParameterizedInsertQuery() (query string, params []any) { +func (fz *fuzzer) generateParameterizedInsertQuery(insertType string) (query string, params []any) { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) tableName := fkTables[tableId] if tableName == "fk_t20" { colValue := rand.Intn(1 + fz.maxValForCol) col2Value := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, col, col2) values (?, ?, ?)", tableName), []any{idValue, convertColValueToString(colValue), convertColValueToString(col2Value)} + return fmt.Sprintf("%s into %v (id, col, col2) values (?, ?, ?)", insertType, tableName), []any{idValue, convertIntValueToString(colValue), convertIntValueToString(col2Value)} } else if isMultiColFkTable(tableName) { colaValue := rand.Intn(1 + fz.maxValForCol) colbValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, cola, colb) values (?, ?, ?)", tableName), []any{idValue, convertColValueToString(colaValue), convertColValueToString(colbValue)} + return fmt.Sprintf("%s into %v (id, cola, colb) values (?, ?, ?)", insertType, tableName), []any{idValue, convertIntValueToString(colaValue), convertIntValueToString(colbValue)} } else { colValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("insert into %v (id, col) values (?, ?)", tableName), []any{idValue, convertColValueToString(colValue)} + return fmt.Sprintf("%s into %v (id, col) values (?, ?)", insertType, tableName), []any{idValue, convertIntValueToString(colValue)} } } @@ -438,14 +480,14 @@ func (fz *fuzzer) generateParameterizedUpdateQuery() (query string, params []any if tableName == "fk_t20" { colValue := rand.Intn(1 + fz.maxValForCol) col2Value := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set col = ?, col2 = ? where id = ?", tableName), []any{convertColValueToString(colValue), convertColValueToString(col2Value), idValue} + return fmt.Sprintf("update %v set col = ?, col2 = ? where id = ?", tableName), []any{convertIntValueToString(colValue), convertIntValueToString(col2Value), idValue} } else if isMultiColFkTable(tableName) { colaValue := rand.Intn(1 + fz.maxValForCol) colbValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set cola = ?, colb = ? where id = ?", tableName), []any{convertColValueToString(colaValue), convertColValueToString(colbValue), idValue} + return fmt.Sprintf("update %v set cola = ?, colb = ? where id = ?", tableName), []any{convertIntValueToString(colaValue), convertIntValueToString(colbValue), idValue} } else { colValue := rand.Intn(1 + fz.maxValForCol) - return fmt.Sprintf("update %v set col = ? where id = ?", tableName), []any{convertColValueToString(colValue), idValue} + return fmt.Sprintf("update %v set col = ? where id = ?", tableName), []any{convertIntValueToString(colValue), idValue} } } @@ -456,6 +498,21 @@ func (fz *fuzzer) generateParameterizedDeleteQuery() (query string, params []any return fmt.Sprintf("delete from %v where id = ?", fkTables[tableId]), []any{idValue} } +// getSetVarFkChecksVal generates an optimizer hint to randomly set the foreign key checks to on or off or leave them unaltered. +func (fz *fuzzer) getSetVarFkChecksVal() string { + if fz.concurrency != 1 || fz.noFkSetVar { + return "" + } + val := rand.Intn(3) + if val == 0 { + return "" + } + if val == 1 { + return "/*+ SET_VAR(foreign_key_checks=On) */ " + } + return "/*+ SET_VAR(foreign_key_checks=Off) */ " +} + // TestFkFuzzTest is a fuzzer test that works by querying the database concurrently. // We have a pre-written set of query templates that we will use, but the data in the queries will // be randomly generated. The intent is that we hammer the database as a real-world application would @@ -561,8 +618,7 @@ func TestFkFuzzTest(t *testing.T) { insertShare: 100, deleteShare: 0, updateShare: 0, - }, - { + }, { name: "Single Thread - Balanced Inserts and Deletes", concurrency: 1, timeForTesting: 5 * time.Second, @@ -571,8 +627,7 @@ func TestFkFuzzTest(t *testing.T) { insertShare: 50, deleteShare: 50, updateShare: 0, - }, - { + }, { name: "Single Thread - Balanced Inserts and Updates", concurrency: 1, timeForTesting: 5 * time.Second, @@ -593,6 +648,15 @@ func TestFkFuzzTest(t *testing.T) { updateShare: 50, }, { + name: "Multi Thread - Only Inserts", + concurrency: 30, + timeForTesting: 5 * time.Second, + maxValForCol: 5, + maxValForId: 30, + insertShare: 100, + deleteShare: 0, + updateShare: 0, + }, { name: "Multi Thread - Balanced Inserts, Updates and Deletes", concurrency: 30, timeForTesting: 5 * time.Second, @@ -604,131 +668,97 @@ func TestFkFuzzTest(t *testing.T) { }, } - for _, tt := range testcases { - for _, testSharded := range []bool{false, true} { - for _, queryFormat := range []QueryFormat{SQLQueries, PreparedStatmentQueries, PreparedStatementPacket} { - t.Run(getTestName(tt.name, testSharded)+fmt.Sprintf(" QueryFormat - %v", queryFormat), func(t *testing.T) { - mcmp, closer := start(t) - defer closer() - // Set the correct keyspace to use from VtGates. - if testSharded { - t.Skip("Skip test since we don't have sharded foreign key support yet") - _ = utils.Exec(t, mcmp.VtConn, "use `ks`") - } else { - _ = utils.Exec(t, mcmp.VtConn, "use `uks`") + valTrue := true + valFalse := false + for _, fkState := range []*bool{nil, &valTrue, &valFalse} { + for _, tt := range testcases { + for _, testSharded := range []bool{false, true} { + for _, queryFormat := range []QueryFormat{OlapSQLQueries, SQLQueries, PreparedStatmentQueries, PreparedStatementPacket} { + if fkState != nil && (queryFormat != SQLQueries || tt.concurrency != 1) { + continue } - // Ensure that the Vitess database is originally empty - ensureDatabaseState(t, mcmp.VtConn, true) - ensureDatabaseState(t, mcmp.MySQLConn, true) - - // Create the fuzzer. - fz := newFuzzer(tt.concurrency, tt.maxValForId, tt.maxValForCol, tt.insertShare, tt.deleteShare, tt.updateShare, queryFormat) - - // Start the fuzzer. - fz.start(t, testSharded) - - // Wait for the timeForTesting so that the threads continue to run. - time.Sleep(tt.timeForTesting) - - fz.stop() + t.Run(getTestName(tt.name, testSharded)+fmt.Sprintf(" FkState - %v QueryFormat - %v", sqlparser.FkChecksStateString(fkState), queryFormat), func(t *testing.T) { + mcmp, closer := start(t) + defer closer() + // Set the correct keyspace to use from VtGates. + if testSharded { + t.Skip("Skip test since we don't have sharded foreign key support yet") + _ = utils.Exec(t, mcmp.VtConn, "use `ks`") + } else { + _ = utils.Exec(t, mcmp.VtConn, "use `uks`") + } - // We encountered an error while running the fuzzer. Let's print out the information! - if fz.firstFailureInfo != nil { - log.Errorf("Failing query - %v", fz.firstFailureInfo.queryToFail) - for idx, table := range fkTables { - log.Errorf("MySQL data for %v -\n%v", table, fz.firstFailureInfo.mysqlState[idx].Rows) - log.Errorf("Vitess data for %v -\n%v", table, fz.firstFailureInfo.vitessState[idx].Rows) + // Ensure that the Vitess database is originally empty + ensureDatabaseState(t, mcmp.VtConn, true) + ensureDatabaseState(t, mcmp.MySQLConn, true) + + // Create the fuzzer. + fz := newFuzzer(tt.concurrency, tt.maxValForId, tt.maxValForCol, tt.insertShare, tt.deleteShare, tt.updateShare, queryFormat, fkState) + + // Start the fuzzer. + fz.start(t, testSharded) + + // Wait for the timeForTesting so that the threads continue to run. + totalTime := time.After(tt.timeForTesting) + done := false + for !done { + select { + case <-totalTime: + done = true + case <-time.After(10 * time.Millisecond): + validateReplication(t) + } } - } - // ensure Vitess database has some data. This ensures not all the commands failed. - ensureDatabaseState(t, mcmp.VtConn, false) - // Verify the consistency of the data. - verifyDataIsCorrect(t, mcmp, tt.concurrency) - }) - } - } - } -} + fz.stop() -// ensureDatabaseState ensures that the database is either empty or not. -func ensureDatabaseState(t *testing.T, vtconn *mysql.Conn, empty bool) { - results := collectFkTablesState(vtconn) - isEmpty := true - for _, res := range results { - if len(res.Rows) > 0 { - isEmpty = false - } - } - require.Equal(t, isEmpty, empty) -} + // We encountered an error while running the fuzzer. Let's print out the information! + if fz.firstFailureInfo != nil { + log.Errorf("Failing query - %v", fz.firstFailureInfo.queryToFail) + for idx, table := range fkTables { + log.Errorf("MySQL data for %v -\n%v", table, fz.firstFailureInfo.mysqlState[idx].Rows) + log.Errorf("Vitess data for %v -\n%v", table, fz.firstFailureInfo.vitessState[idx].Rows) + } + } -// verifyDataIsCorrect verifies that the data in MySQL database matches the data in the Vitess database. -func verifyDataIsCorrect(t *testing.T, mcmp utils.MySQLCompare, concurrency int) { - // For single concurrent thread, we run all the queries on both MySQL and Vitess, so we can verify correctness - // by just checking if the data in MySQL and Vitess match. - if concurrency == 1 { - for _, table := range fkTables { - query := fmt.Sprintf("SELECT * FROM %v ORDER BY id", table) - mcmp.Exec(query) - } - } else { - // For higher concurrency, we don't have MySQL data to verify everything is fine, - // so we'll have to do something different. - // We run LEFT JOIN queries on all the parent and child tables linked by foreign keys - // to make sure that nothing is broken in the database. - for _, reference := range fkReferences { - query := fmt.Sprintf("select %v.id from %v left join %v on (%v.col = %v.col) where %v.col is null and %v.col is not null", reference.childTable, reference.childTable, reference.parentTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable) - if isMultiColFkTable(reference.childTable) { - query = fmt.Sprintf("select %v.id from %v left join %v on (%v.cola = %v.cola and %v.colb = %v.colb) where %v.cola is null and %v.cola is not null and %v.colb is not null", reference.childTable, reference.childTable, reference.parentTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable, reference.childTable) - } - res, err := mcmp.VtConn.ExecuteFetch(query, 1000, false) - require.NoError(t, err) - require.Zerof(t, len(res.Rows), "Query %v gave non-empty results", query) - } - } - // We also verify that the results in Primary and Replica table match as is. - for _, keyspace := range clusterInstance.Keyspaces { - for _, shard := range keyspace.Shards { - var primaryTab, replicaTab *cluster.Vttablet - for _, vttablet := range shard.Vttablets { - if vttablet.Type == "primary" { - primaryTab = vttablet - } else { - replicaTab = vttablet + // ensure Vitess database has some data. This ensures not all the commands failed. + ensureDatabaseState(t, mcmp.VtConn, false) + // Verify the consistency of the data. + verifyDataIsCorrect(t, mcmp, tt.concurrency) + }) } } - require.NotNil(t, primaryTab) - require.NotNil(t, replicaTab) - checkReplicationHealthy(t, replicaTab) - cluster.WaitForReplicationPos(t, primaryTab, replicaTab, true, 60.0) - primaryConn, err := utils.GetMySQLConn(primaryTab, fmt.Sprintf("vt_%v", keyspace.Name)) - require.NoError(t, err) - replicaConn, err := utils.GetMySQLConn(replicaTab, fmt.Sprintf("vt_%v", keyspace.Name)) - require.NoError(t, err) - primaryRes := collectFkTablesState(primaryConn) - replicaRes := collectFkTablesState(replicaConn) - verifyDataMatches(t, primaryRes, replicaRes) } } } -// verifyDataMatches verifies that the two list of results are the same. -func verifyDataMatches(t *testing.T, resOne []*sqltypes.Result, resTwo []*sqltypes.Result) { - require.EqualValues(t, len(resTwo), len(resOne), "Res 1 - %v, Res 2 - %v", resOne, resTwo) - for idx, resultOne := range resOne { - resultTwo := resTwo[idx] - require.True(t, resultOne.Equal(resultTwo), "Data for %v doesn't match\nRows 1\n%v\nRows 2\n%v", fkTables[idx], resultOne.Rows, resultTwo.Rows) - } -} - -// collectFkTablesState collects the data stored in the foreign key tables for the given connection. -func collectFkTablesState(conn *mysql.Conn) []*sqltypes.Result { - var tablesData []*sqltypes.Result - for _, table := range fkTables { - query := fmt.Sprintf("SELECT * FROM %v ORDER BY id", table) - res, _ := conn.ExecuteFetch(query, 10000, true) - tablesData = append(tablesData, res) +// BenchmarkFkFuzz benchmarks the performance of Vitess unmanaged, Vitess managed and vanilla MySQL performance on a given set of queries generated by the fuzzer. +func BenchmarkFkFuzz(b *testing.B) { + maxValForCol := 10 + maxValForId := 10 + insertShare := 50 + deleteShare := 50 + updateShare := 50 + numQueries := 1000 + // Wait for schema-tracking to be complete. + waitForSchemaTrackingForFkTables(b) + for i := 0; i < b.N; i++ { + queries, mysqlConn, vtConn, vtUnmanagedConn := setupBenchmark(b, maxValForId, maxValForCol, insertShare, deleteShare, updateShare, numQueries) + + // Now we run the benchmarks! + b.Run("MySQL", func(b *testing.B) { + startBenchmark(b) + runQueries(b, mysqlConn, queries) + }) + + b.Run("Vitess Managed Foreign Keys", func(b *testing.B) { + startBenchmark(b) + runQueries(b, vtConn, queries) + }) + + b.Run("Vitess Unmanaged Foreign Keys", func(b *testing.B) { + startBenchmark(b) + runQueries(b, vtUnmanagedConn, queries) + }) } - return tablesData } diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 46bbc2ed433..b16da5bfabd 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -18,7 +18,9 @@ package foreignkey import ( "context" + "fmt" "io" + "strings" "testing" "time" @@ -27,6 +29,7 @@ import ( "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" + "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/vtgateconn" @@ -167,6 +170,14 @@ func TestUpdateWithFK(t *testing.T) { // Verify the result in u_t2 and u_t3 as well. utils.AssertMatches(t, conn, `select * from u_t2 order by id`, `[[INT64(19) INT64(1234)] [INT64(342) NULL]]`) utils.AssertMatches(t, conn, `select * from u_t3 order by id`, `[[INT64(1) INT64(12)] [INT64(32) INT64(13)]]`) + + // Update with a subquery inside, such that the update is on a foreign key related column. + qr = utils.Exec(t, conn, `update u_t2 set col2 = (select col1 from u_t1 where id = 100) where id = 342`) + assert.EqualValues(t, 1, qr.RowsAffected) + // Verify the result in u_t1, u_t2 and u_t3. + utils.AssertMatches(t, conn, `select * from u_t1 order by id`, `[[INT64(1) INT64(13)] [INT64(10) INT64(12)] [INT64(100) INT64(13)] [INT64(1000) INT64(1234)]]`) + utils.AssertMatches(t, conn, `select * from u_t2 order by id`, `[[INT64(19) INT64(1234)] [INT64(342) INT64(13)]]`) + utils.AssertMatches(t, conn, `select * from u_t3 order by id`, `[[INT64(1) INT64(12)] [INT64(32) INT64(13)]]`) } // TestVstreamForFKBinLog tests that dml queries with fks are written with child row first approach in the binary logs. @@ -669,6 +680,51 @@ func TestFkScenarios(t *testing.T) { assertionQueries: []string{ "select * from fk_t20 order by id", }, + }, { + name: "Multi Table Delete success", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t19(id, col) values (1, 7)", + }, + dmlQuery: "delete fk_t15 from fk_t15 join fk_t17 using id", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t19 order by id", + }, + }, { + name: "Delete with limit success", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t19(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t15 order by id limit 1", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t19 order by id", + }, + }, { + name: "Delete with limit 0 success", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t19(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t15 order by id limit 0", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t19 order by id", + }, }, } @@ -775,6 +831,240 @@ func TestFkScenarios(t *testing.T) { } } +// TestFkQueries is for testing a specific set of queries one after the other. +func TestFkQueries(t *testing.T) { + // Wait for schema-tracking to be complete. + waitForSchemaTrackingForFkTables(t) + // Remove all the foreign key constraints for all the replicas. + // We can then verify that the replica, and the primary have the same data, to ensure + // that none of the queries ever lead to cascades/updates on MySQL level. + for _, ks := range []string{shardedKs, unshardedKs} { + replicas := getReplicaTablets(ks) + for _, replica := range replicas { + removeAllForeignKeyConstraints(t, replica, ks) + } + } + + testcases := []struct { + name string + queries []string + }{ + { + name: "Non-literal update", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "update fk_t10 set col = id + 3", + }, + }, { + name: "Non-literal update with order by", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "update fk_t10 set col = id + 3 order by id desc", + }, + }, { + name: "Non-literal update with order by that require parent and child foreign keys verification - success", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t12 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t13 (id, col) values (1,1),(2,2)", + "update fk_t11 set col = id + 3 where id >= 3", + }, + }, { + name: "Non-literal update with order by that require parent and child foreign keys verification - parent fails", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t12 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "update fk_t11 set col = id + 3", + }, + }, { + name: "Non-literal update with order by that require parent and child foreign keys verification - child fails", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t12 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t13 (id, col) values (1,1),(2,2)", + "update fk_t11 set col = id + 3", + }, + }, { + name: "Single column update in a multi-col table - success", + queries: []string{ + "insert into fk_multicol_t1 (id, cola, colb) values (1, 1, 1), (2, 2, 2)", + "insert into fk_multicol_t2 (id, cola, colb) values (1, 1, 1)", + "update fk_multicol_t1 set colb = 4 + (colb) where id = 2", + }, + }, { + name: "Single column update in a multi-col table - restrict failure", + queries: []string{ + "insert into fk_multicol_t1 (id, cola, colb) values (1, 1, 1), (2, 2, 2)", + "insert into fk_multicol_t2 (id, cola, colb) values (1, 1, 1)", + "update fk_multicol_t1 set colb = 4 + (colb) where id = 1", + }, + }, { + name: "Single column update in multi-col table - cascade and set null", + queries: []string{ + "insert into fk_multicol_t15 (id, cola, colb) values (1, 1, 1), (2, 2, 2)", + "insert into fk_multicol_t16 (id, cola, colb) values (1, 1, 1), (2, 2, 2)", + "insert into fk_multicol_t17 (id, cola, colb) values (1, 1, 1), (2, 2, 2)", + "update fk_multicol_t15 set colb = 4 + (colb) where id = 1", + }, + }, { + name: "Non literal update that evaluates to NULL - restricted", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t13 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "update fk_t10 set col = id + null where id = 1", + }, + }, { + name: "Non literal update that evaluates to NULL - success", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "insert into fk_t12 (id, col) values (1,1),(2,2),(3,3),(4,4),(5,5)", + "update fk_t10 set col = id + null where id = 1", + }, + }, { + name: "Multi column foreign key update with one literal and one non-literal update", + queries: []string{ + "insert into fk_multicol_t15 (id, cola, colb) values (1,1,1),(2,2,2)", + "insert into fk_multicol_t16 (id, cola, colb) values (1,1,1),(2,2,2)", + "update fk_multicol_t15 set cola = 3, colb = (id * 2) - 2", + }, + }, { + name: "Update that sets to 0 and -0 values", + queries: []string{ + "insert into fk_t15 (id, col) values (1,'-0'), (2, '0'), (3, '5'), (4, '-5')", + "insert into fk_t16 (id, col) values (1,'-0'), (2, '0'), (3, '5'), (4, '-5')", + "update fk_t15 set col = col * (col - (col))", + }, + }, + { + name: "Update a child table which doesn't cause an update, but parent doesn't have that value", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2)", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t11 (id, col) values (1,1),(2,2),(5,5)", + "update fk_t11 set col = id where id in (1, 5)", + }, + }, + { + name: "Update a child table from a null to a value that parent doesn't have", + queries: []string{ + "insert into fk_t10 (id, col) values (1,1),(2,2)", + "insert into fk_t11 (id, col) values (1,1),(2,2),(5,NULL)", + "update fk_t11 set col = id where id in (1, 5)", + }, + }, + { + name: "Update on child to 0 when parent has -0", + queries: []string{ + "insert into fk_t15 (id, col) values (2, '-0')", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t16 (id, col) values (3, '5'), (4, '-5')", + "update fk_t16 set col = col * (col - (col)) where id = 3", + "update fk_t16 set col = col * (col - (col)) where id = 4", + }, + }, + { + name: "Multi table delete that uses two tables related by foreign keys", + queries: []string{ + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t10 (id, col) values (1, '5'), (2, NULL), (3, NULL), (4, '4'), (6, '1'), (7, '2')", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t11 (id, col) values (4, '1'), (5, '3'), (7, '22'), (8, '5'), (9, NULL), (10, '3')", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t12 (id, col) values (2, NULL), (3, NULL), (4, '1'), (6, '6'), (8, NULL), (10, '1')", + "insert /*+ SET_VAR(foreign_key_checks=0) */ into fk_t13 (id, col) values (2, '1'), (5, '5'), (7, '5')", + "delete fk_t11 from fk_t11 join fk_t12 using (id) where fk_t11.id = 4", + }, + }, + } + + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + mcmp, closer := start(t) + defer closer() + _ = utils.Exec(t, mcmp.VtConn, "use `uks`") + + // Ensure that the Vitess database is originally empty + ensureDatabaseState(t, mcmp.VtConn, true) + ensureDatabaseState(t, mcmp.MySQLConn, true) + + for _, query := range testcase.queries { + _, _ = mcmp.ExecAllowAndCompareError(query) + if t.Failed() { + break + } + } + + // ensure Vitess database has some data. This ensures not all the commands failed. + ensureDatabaseState(t, mcmp.VtConn, false) + // Verify the consistency of the data. + verifyDataIsCorrect(t, mcmp, 1) + }) + } +} + +// TestShowVschemaKeyspaces verifies the show vschema keyspaces query output for the keyspaces where the foreign keys are +func TestShowVschemaKeyspaces(t *testing.T) { + mcmp, closer := start(t) + conn := mcmp.VtConn + defer closer() + + res := utils.Exec(t, conn, "SHOW VSCHEMA KEYSPACES") + resStr := fmt.Sprintf("%v", res.Rows) + require.Contains(t, resStr, `[VARCHAR("uks") VARCHAR("false") VARCHAR("managed") VARCHAR("")]`) + require.Contains(t, resStr, `[VARCHAR("ks") VARCHAR("true") VARCHAR("managed") VARCHAR("")]`) +} + +// TestFkOneCase is for testing a specific set of queries. On the CI this test won't run since we'll keep the queries empty. +func TestFkOneCase(t *testing.T) { + queries := []string{} + if len(queries) == 0 { + t.Skip("No queries to test") + } + // Wait for schema-tracking to be complete. + waitForSchemaTrackingForFkTables(t) + // Remove all the foreign key constraints for all the replicas. + // We can then verify that the replica, and the primary have the same data, to ensure + // that none of the queries ever lead to cascades/updates on MySQL level. + for _, ks := range []string{shardedKs, unshardedKs} { + replicas := getReplicaTablets(ks) + for _, replica := range replicas { + removeAllForeignKeyConstraints(t, replica, ks) + } + } + + mcmp, closer := start(t) + defer closer() + _ = utils.Exec(t, mcmp.VtConn, "use `uks`") + + // Ensure that the Vitess database is originally empty + ensureDatabaseState(t, mcmp.VtConn, true) + ensureDatabaseState(t, mcmp.MySQLConn, true) + + for _, query := range queries { + if strings.HasPrefix(query, "vexplain") { + res := utils.Exec(t, mcmp.VtConn, query) + log.Errorf("Query %v, Result - %v", query, res.Rows) + continue + } + _, _ = mcmp.ExecAllowAndCompareError(query) + if t.Failed() { + log.Errorf("Query failed - %v", query) + break + } + } + vitessData := collectFkTablesState(mcmp.VtConn) + for idx, table := range fkTables { + log.Errorf("Vitess data for %v -\n%v", table, vitessData[idx].Rows) + } + + // ensure Vitess database has some data. This ensures not all the commands failed. + ensureDatabaseState(t, mcmp.VtConn, false) + // Verify the consistency of the data. + verifyDataIsCorrect(t, mcmp, 1) +} + func TestCyclicFks(t *testing.T) { mcmp, closer := start(t) defer closer() @@ -782,18 +1072,260 @@ func TestCyclicFks(t *testing.T) { // Create a cyclic foreign key constraint. utils.Exec(t, mcmp.VtConn, "alter table fk_t10 add constraint test_cyclic_fks foreign key (col) references fk_t12 (col) on delete cascade on update cascade") - defer func() { - utils.Exec(t, mcmp.VtConn, "alter table fk_t10 drop foreign key test_cyclic_fks") - }() // Wait for schema-tracking to be complete. - ksErr := utils.WaitForKsError(t, clusterInstance.VtgateProcess, unshardedKs) - // Make sure Vschema has the error for cyclic foreign keys. - assert.Contains(t, ksErr, "VT09019: uks has cyclic foreign keys") + errString := utils.WaitForKsError(t, clusterInstance.VtgateProcess, unshardedKs) + assert.Contains(t, errString, "VT09019: keyspace 'uks' has cyclic foreign keys") // Ensure that the Vitess database is originally empty ensureDatabaseState(t, mcmp.VtConn, true) _, err := utils.ExecAllowError(t, mcmp.VtConn, "insert into fk_t10(id, col) values (1, 1)") - require.ErrorContains(t, err, "VT09019: uks has cyclic foreign keys") + require.ErrorContains(t, err, "VT09019: keyspace 'uks' has cyclic foreign keys") + + // Drop the cyclic foreign key constraint. + utils.Exec(t, mcmp.VtConn, "alter table fk_t10 drop foreign key test_cyclic_fks") + + // Wait for schema-tracking to be complete. + utils.WaitForVschemaCondition(t, clusterInstance.VtgateProcess, unshardedKs, func(t *testing.T, keyspace map[string]interface{}) bool { + _, fieldPresent := keyspace["error"] + return !fieldPresent + }) + +} + +func TestReplace(t *testing.T) { + t.Skip("replace engine marked for failure, hence skipping this.") + // Wait for schema-tracking to be complete. + waitForSchemaTrackingForFkTables(t) + // Remove all the foreign key constraints for all the replicas. + // We can then verify that the replica, and the primary have the same data, to ensure + // that none of the queries ever lead to cascades/updates on MySQL level. + for _, ks := range []string{shardedKs, unshardedKs} { + replicas := getReplicaTablets(ks) + for _, replica := range replicas { + removeAllForeignKeyConstraints(t, replica, ks) + } + } + + mcmp1, _ := start(t) + // defer closer1() + _ = utils.Exec(t, mcmp1.VtConn, "use `uks`") + + mcmp2, _ := start(t) + // defer closer2() + _ = utils.Exec(t, mcmp2.VtConn, "use `uks`") + + _ = utils.Exec(t, mcmp1.VtConn, "insert into fk_t2 values(1,5), (2,5)") + + done := false + go func() { + number := 1 + for !done { + query := fmt.Sprintf("replace /* g1q1 - %d */ into fk_t2 values(5,5)", number) + _, _ = utils.ExecAllowError(t, mcmp1.VtConn, query) + number++ + } + }() + + go func() { + number := 1 + for !done { + query := fmt.Sprintf("replace /* q1 - %d */ into fk_t3 values(3,5)", number) + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, query) + + query = fmt.Sprintf("replace /* q2 - %d */ into fk_t3 values(4,5)", number) + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, query) + number++ + } + }() + + totalTime := time.After(1 * time.Minute) + for !done { + select { + case <-totalTime: + done = true + case <-time.After(10 * time.Millisecond): + validateReplication(t) + } + } +} + +func TestReplaceExplicit(t *testing.T) { + t.Skip("explicit delete-insert in transaction fails, hence skipping") + // Wait for schema-tracking to be complete. + waitForSchemaTrackingForFkTables(t) + // Remove all the foreign key constraints for all the replicas. + // We can then verify that the replica, and the primary have the same data, to ensure + // that none of the queries ever lead to cascades/updates on MySQL level. + for _, ks := range []string{shardedKs, unshardedKs} { + replicas := getReplicaTablets(ks) + for _, replica := range replicas { + removeAllForeignKeyConstraints(t, replica, ks) + } + } + + mcmp1, _ := start(t) + // defer closer1() + _ = utils.Exec(t, mcmp1.VtConn, "use `uks`") + + mcmp2, _ := start(t) + // defer closer2() + _ = utils.Exec(t, mcmp2.VtConn, "use `uks`") + + _ = utils.Exec(t, mcmp1.VtConn, "insert into fk_t2 values(1,5), (2,5)") + + done := false + go func() { + number := 0 + for !done { + number++ + _, _ = utils.ExecAllowError(t, mcmp1.VtConn, "begin") + query := fmt.Sprintf("delete /* g1q1 - %d */ from fk_t2 where id = 5", number) + _, err := utils.ExecAllowError(t, mcmp1.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp1.VtConn, "rollback") + continue + } + query = fmt.Sprintf("insert /* g1q1 - %d */ into fk_t2 values(5,5)", number) + _, err = utils.ExecAllowError(t, mcmp1.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp1.VtConn, "rollback") + continue + } + _, _ = utils.ExecAllowError(t, mcmp1.VtConn, "commit") + } + }() + + go func() { + number := 0 + for !done { + number++ + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "begin") + query := fmt.Sprintf("delete /* g1q1 - %d */ from fk_t3 where id = 3 or col = 5", number) + _, err := utils.ExecAllowError(t, mcmp2.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "rollback") + } else { + query = fmt.Sprintf("insert /* g1q1 - %d */ into fk_t3 values(3,5)", number) + _, err = utils.ExecAllowError(t, mcmp2.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "rollback") + } else { + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "commit") + } + } + + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "begin") + query = fmt.Sprintf("delete /* g1q1 - %d */ from fk_t3 where id = 4 or col = 5", number) + _, err = utils.ExecAllowError(t, mcmp2.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "rollback") + continue + } + query = fmt.Sprintf("insert /* g1q1 - %d */ into fk_t3 values(4,5)", number) + _, err = utils.ExecAllowError(t, mcmp2.VtConn, query) + if err != nil { + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "rollback") + continue + } + _, _ = utils.ExecAllowError(t, mcmp2.VtConn, "commit") + } + }() + + totalTime := time.After(1 * time.Minute) + for !done { + select { + case <-totalTime: + done = true + case <-time.After(10 * time.Millisecond): + validateReplication(t) + } + } +} + +// TestReplaceWithFK tests that replace into work as expected when foreign key management is enabled in Vitess. +func TestReplaceWithFK(t *testing.T) { + mcmp, closer := start(t) + conn := mcmp.VtConn + defer closer() + + // replace some data. + _, err := utils.ExecAllowError(t, conn, `replace into t1(id, col) values (1, 1)`) + require.ErrorContains(t, err, "VT12001: unsupported: REPLACE INTO with sharded keyspace (errno 1235) (sqlstate 42000)") + + _ = utils.Exec(t, conn, `use uks`) + + _ = utils.Exec(t, conn, `replace into u_t1(id, col1) values (1, 1), (2, 1)`) + // u_t1: (1,1) (2,1) + + _ = utils.Exec(t, conn, `replace into u_t2(id, col2) values (1, 1), (2, 1)`) + // u_t1: (1,1) (2,1) + // u_t2: (1,1) (2,1) + + _ = utils.Exec(t, conn, `replace into u_t1(id, col1) values (2, 2)`) + // u_t1: (1,1) (2,2) + // u_t2: (1,null) (2,null) + + utils.AssertMatches(t, conn, `select * from u_t1`, `[[INT64(1) INT64(1)] [INT64(2) INT64(2)]]`) + utils.AssertMatches(t, conn, `select * from u_t2`, `[[INT64(1) NULL] [INT64(2) NULL]]`) +} + +// TestInsertWithFKOnDup tests that insertion with on duplicate key update works as expected. +func TestInsertWithFKOnDup(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + utils.Exec(t, mcmp.VtConn, "use `uks`") + + // insert some data. + mcmp.Exec(`insert into u_t1(id, col1) values (100, 1), (200, 2), (300, 3), (400, 4)`) + mcmp.Exec(`insert into u_t2(id, col2) values (1000, 1), (2000, 2), (3000, 3), (4000, 4)`) + + // updating child to an existing value in parent. + mcmp.Exec(`insert into u_t2(id, col2) values (4000, 50) on duplicate key update col2 = 1`) + mcmp.AssertMatches(`select * from u_t2 order by id`, `[[INT64(1000) INT64(1)] [INT64(2000) INT64(2)] [INT64(3000) INT64(3)] [INT64(4000) INT64(1)]]`) + + // updating parent, value not referred in child. + mcmp.Exec(`insert into u_t1(id, col1) values (400, 50) on duplicate key update col1 = values(col1)`) + mcmp.AssertMatches(`select * from u_t1 order by id`, `[[INT64(100) INT64(1)] [INT64(200) INT64(2)] [INT64(300) INT64(3)] [INT64(400) INT64(50)]]`) + mcmp.AssertMatches(`select * from u_t2 order by id`, `[[INT64(1000) INT64(1)] [INT64(2000) INT64(2)] [INT64(3000) INT64(3)] [INT64(4000) INT64(1)]]`) + + // updating parent, child updated to null. + mcmp.Exec(`insert into u_t1(id, col1) values (100, 75) on duplicate key update col1 = values(col1)`) + mcmp.AssertMatches(`select * from u_t1 order by id`, `[[INT64(100) INT64(75)] [INT64(200) INT64(2)] [INT64(300) INT64(3)] [INT64(400) INT64(50)]]`) + mcmp.AssertMatches(`select * from u_t2 order by id`, `[[INT64(1000) NULL] [INT64(2000) INT64(2)] [INT64(3000) INT64(3)] [INT64(4000) NULL]]`) + + // inserting multiple rows in parent, some child rows updated to null. + mcmp.Exec(`insert into u_t1(id, col1) values (100, 42),(600, 2),(300, 24),(200, 2) on duplicate key update col1 = values(col1)`) + mcmp.AssertMatches(`select * from u_t1 order by id`, `[[INT64(100) INT64(42)] [INT64(200) INT64(2)] [INT64(300) INT64(24)] [INT64(400) INT64(50)] [INT64(600) INT64(2)]]`) + mcmp.AssertMatches(`select * from u_t2 order by id`, `[[INT64(1000) NULL] [INT64(2000) INT64(2)] [INT64(3000) NULL] [INT64(4000) NULL]]`) +} + +// TestDDLFk tests that table is created with fk constraint when foreign_key_checks is off. +func TestDDLFk(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + utils.Exec(t, mcmp.VtConn, `use uks`) + + createTableDDLTemp1 := ` +create table temp1(id bigint auto_increment primary key, col varchar(20) not null, +foreign key (col) references temp2(col)) +` + mcmp.Exec(`set foreign_key_checks = off`) + // should be able to create `temp1` table without a `temp2` + mcmp.Exec(createTableDDLTemp1) + + createTableDDLTemp2 := ` +create table temp2(id bigint auto_increment primary key, col varchar(20) not null, key (col)) +` + // now create `temp2` + mcmp.Exec(createTableDDLTemp2) + + // inserting some data with fk constraints on. + mcmp.Exec(`set foreign_key_checks = on`) + mcmp.Exec(`insert into temp2(col) values('a'), ('b'), ('c') `) + mcmp.Exec(`insert into temp1(col) values('a') `) + mcmp.ExecAllowAndCompareError(`insert into temp1(col) values('d') `) } diff --git a/go/test/endtoend/vtgate/foreignkey/main_test.go b/go/test/endtoend/vtgate/foreignkey/main_test.go index dae78ae93a1..483c1d05e80 100644 --- a/go/test/endtoend/vtgate/foreignkey/main_test.go +++ b/go/test/endtoend/vtgate/foreignkey/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package foreignkey import ( + "context" _ "embed" "flag" "fmt" @@ -31,18 +32,17 @@ import ( ) var ( - clusterInstance *cluster.LocalProcessCluster - vtParams mysql.ConnParams - mysqlParams mysql.ConnParams - vtgateGrpcAddress string - shardedKs = "ks" - unshardedKs = "uks" - Cell = "test" - //go:embed sharded_schema.sql - shardedSchemaSQL string - - //go:embed unsharded_schema.sql - unshardedSchemaSQL string + clusterInstance *cluster.LocalProcessCluster + vtParams mysql.ConnParams + mysqlParams mysql.ConnParams + vtgateGrpcAddress string + shardedKs = "ks" + unshardedKs = "uks" + unshardedUnmanagedKs = "unmanaged_uks" + Cell = "test" + + //go:embed schema.sql + schemaSQL string //go:embed sharded_vschema.json shardedVSchema string @@ -50,6 +50,9 @@ var ( //go:embed unsharded_vschema.json unshardedVSchema string + //go:embed unsharded_unmanaged_vschema.json + unshardedUnmanagedVSchema string + fkTables = []string{"fk_t1", "fk_t2", "fk_t3", "fk_t4", "fk_t5", "fk_t6", "fk_t7", "fk_t10", "fk_t11", "fk_t12", "fk_t13", "fk_t15", "fk_t16", "fk_t17", "fk_t18", "fk_t19", "fk_t20", "fk_multicol_t1", "fk_multicol_t2", "fk_multicol_t3", "fk_multicol_t4", "fk_multicol_t5", "fk_multicol_t6", "fk_multicol_t7", @@ -107,7 +110,7 @@ func TestMain(m *testing.M) { // Start keyspace sKs := &cluster.Keyspace{ Name: shardedKs, - SchemaSQL: shardedSchemaSQL, + SchemaSQL: schemaSQL, VSchema: shardedVSchema, } @@ -118,7 +121,7 @@ func TestMain(m *testing.M) { uKs := &cluster.Keyspace{ Name: unshardedKs, - SchemaSQL: unshardedSchemaSQL, + SchemaSQL: schemaSQL, VSchema: unshardedVSchema, } err = clusterInstance.StartUnshardedKeyspace(*uKs, 1, false) @@ -126,6 +129,16 @@ func TestMain(m *testing.M) { return 1 } + unmanagedKs := &cluster.Keyspace{ + Name: unshardedUnmanagedKs, + SchemaSQL: schemaSQL, + VSchema: unshardedUnmanagedVSchema, + } + err = clusterInstance.StartUnshardedKeyspace(*unmanagedKs, 1, false) + if err != nil { + return 1 + } + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildVSchemaGraph") if err != nil { return 1 @@ -142,7 +155,7 @@ func TestMain(m *testing.M) { } vtgateGrpcAddress = fmt.Sprintf("%s:%d", clusterInstance.Hostname, clusterInstance.VtgateGrpcPort) - connParams, closer, err := utils.NewMySQL(clusterInstance, shardedKs, shardedSchemaSQL) + connParams, closer, err := utils.NewMySQL(clusterInstance, shardedKs, schemaSQL) if err != nil { fmt.Println(err) return 1 @@ -159,22 +172,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { require.NoError(t, err) deleteAll := func() { - _ = utils.Exec(t, mcmp.VtConn, "use `ks/-80`") - tables := []string{"t4", "t3", "t2", "t1", "multicol_tbl2", "multicol_tbl1"} - tables = append(tables, fkTables...) - for _, table := range tables { - _, _ = mcmp.ExecAndIgnore("delete /*+ SET_VAR(foreign_key_checks=OFF) */ from " + table) - } - _ = utils.Exec(t, mcmp.VtConn, "use `ks/80-`") - for _, table := range tables { - _, _ = mcmp.ExecAndIgnore("delete /*+ SET_VAR(foreign_key_checks=OFF) */ from " + table) - } - _ = utils.Exec(t, mcmp.VtConn, "use `uks`") - tables = []string{"u_t1", "u_t2", "u_t3"} - tables = append(tables, fkTables...) - for _, table := range tables { - _, _ = mcmp.ExecAndIgnore("delete /*+ SET_VAR(foreign_key_checks=OFF) */ from " + table) - } + clearOutAllData(t, mcmp.VtConn, mcmp.MySQLConn) _ = utils.Exec(t, mcmp.VtConn, "use `ks`") } @@ -186,3 +184,40 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { cluster.PanicHandler(t) } } + +func startBenchmark(b *testing.B) { + ctx := context.Background() + vtConn, err := mysql.Connect(ctx, &vtParams) + require.NoError(b, err) + mysqlConn, err := mysql.Connect(ctx, &mysqlParams) + require.NoError(b, err) + + clearOutAllData(b, vtConn, mysqlConn) +} + +func clearOutAllData(t testing.TB, vtConn *mysql.Conn, mysqlConn *mysql.Conn) { + _ = utils.Exec(t, vtConn, "use `ks/-80`") + tables := []string{"t4", "t3", "t2", "t1", "multicol_tbl2", "multicol_tbl1"} + tables = append(tables, fkTables...) + for _, table := range tables { + _, _ = utils.ExecAllowError(t, vtConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + _, _ = utils.ExecAllowError(t, mysqlConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + } + _ = utils.Exec(t, vtConn, "use `ks/80-`") + for _, table := range tables { + _, _ = utils.ExecAllowError(t, vtConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + _, _ = utils.ExecAllowError(t, mysqlConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + } + _ = utils.Exec(t, vtConn, "use `uks`") + tables = []string{"u_t1", "u_t2", "u_t3"} + tables = append(tables, fkTables...) + for _, table := range tables { + _, _ = utils.ExecAllowError(t, vtConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + _, _ = utils.ExecAllowError(t, mysqlConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + } + _ = utils.Exec(t, vtConn, "use `unmanaged_uks`") + for _, table := range tables { + _, _ = utils.ExecAllowError(t, vtConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + _, _ = utils.ExecAllowError(t, mysqlConn, "delete /*+ SET_VAR(foreign_key_checks=OFF) */ from "+table) + } +} diff --git a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/schema.sql similarity index 95% rename from go/test/endtoend/vtgate/foreignkey/sharded_schema.sql rename to go/test/endtoend/vtgate/foreignkey/schema.sql index c1f511350f2..fd8bec5dc4a 100644 --- a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/schema.sql @@ -73,6 +73,31 @@ create table t6 foreign key (sk, col1) references t5 (sk, col1) on delete restrict on update restrict ) Engine = InnoDB; +create table u_t1 +( + id bigint, + col1 bigint, + index(col1), + primary key (id) +) Engine = InnoDB; + +create table u_t2 +( + id bigint, + col2 bigint, + primary key (id), + foreign key (col2) references u_t1 (col1) on delete set null on update set null +) Engine = InnoDB; + +create table u_t3 +( + id bigint, + col3 bigint, + primary key (id), + foreign key (col3) references u_t1 (col1) on delete cascade on update cascade +) Engine = InnoDB; + + /* * fk_t1 * │ @@ -122,7 +147,7 @@ create table fk_t3 id bigint, col varchar(10), primary key (id), - index(col), + unique index(col), foreign key (col) references fk_t2(col) on delete set null on update set null ) Engine = InnoDB; @@ -184,7 +209,7 @@ create table fk_t10 id bigint, col varchar(10), primary key (id), - index(col) + unique index(col) ) Engine = InnoDB; create table fk_t11 @@ -243,7 +268,7 @@ create table fk_t15 id bigint, col varchar(10), primary key (id), - index(col) + unique index(col) ) Engine = InnoDB; create table fk_t16 @@ -251,7 +276,7 @@ create table fk_t16 id bigint, col varchar(10), primary key (id), - index(col), + unique index(col), foreign key (col) references fk_t15(col) on delete cascade on update cascade ) Engine = InnoDB; @@ -296,6 +321,7 @@ create table fk_t20 foreign key (col2) references fk_t20(col) on delete restrict on update restrict ) Engine = InnoDB; + /* * fk_multicol_t1 * │ @@ -328,16 +354,17 @@ create table fk_multicol_t1 colb varchar(10), cola varchar(10), primary key (id), - index(cola, colb) + index(cola, colb), + unique index(colb) ) Engine = InnoDB; create table fk_multicol_t2 ( id bigint, - colb varchar(10), + colb varchar(10) default 'xyz', cola varchar(10), primary key (id), - index(cola, colb), + unique index(cola, colb), foreign key (cola, colb) references fk_multicol_t1(cola, colb) on delete restrict on update restrict ) Engine = InnoDB; @@ -355,9 +382,10 @@ create table fk_multicol_t4 ( id bigint, colb varchar(10), - cola varchar(10), + cola varchar(10) default 'abcd', primary key (id), index(cola, colb), + unique index(cola), foreign key (cola, colb) references fk_multicol_t3(cola, colb) on delete set null on update set null ) Engine = InnoDB; diff --git a/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go b/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go index e9f0602d235..60eff73b820 100644 --- a/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go +++ b/go/test/endtoend/vtgate/foreignkey/stress/fk_stress_test.go @@ -138,7 +138,8 @@ var ( clusterInstance *cluster.LocalProcessCluster shards []cluster.Shard primary *cluster.Vttablet - replica *cluster.Vttablet + replicaNoFK *cluster.Vttablet + replicaFK *cluster.Vttablet vtParams mysql.ConnParams onlineDDLStrategy = "vitess --unsafe-allow-foreign-keys --cut-over-threshold=15s" @@ -333,7 +334,6 @@ func TestMain(m *testing.M) { "--heartbeat_on_demand_duration", "5s", "--migration_check_interval", "5s", "--watch_replication_stream", - "--vreplication_tablet_type", "primary", } clusterInstance.VtGateExtraArgs = []string{} @@ -351,7 +351,7 @@ func TestMain(m *testing.M) { } // We will use a replica to confirm that vtgate's cascading works correctly. - if err := clusterInstance.StartKeyspace(*keyspace, []string{"1"}, 1, false); err != nil { + if err := clusterInstance.StartKeyspace(*keyspace, []string{"1"}, 2, false); err != nil { return 1, err } @@ -392,14 +392,23 @@ func tabletTestName(t *testing.T, tablet *cluster.Vttablet) string { switch tablet { case primary: return "primary" - case replica: - return "replica" + case replicaNoFK: + return "replicaNoFK" + case replicaFK: + return "replicaFK" default: assert.FailNowf(t, "unknown tablet", "%v, type=%v", tablet.Alias, tablet.Type) } return "" } +func validateReplicationIsHealthy(t *testing.T, tablet *cluster.Vttablet) (result bool) { + t.Run(tabletTestName(t, tablet), func(t *testing.T) { + result = cluster.ValidateReplicationIsHealthy(t, tablet) + }) + return result +} + func getTabletPosition(t *testing.T, tablet *cluster.Vttablet) replication.Position { rs := queryTablet(t, tablet, "select @@gtid_executed as gtid_executed", "") row := rs.Named().Row() @@ -411,17 +420,14 @@ func getTabletPosition(t *testing.T, tablet *cluster.Vttablet) replication.Posit return pos } -func waitForReplicaCatchup(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - primaryPos := getTabletPosition(t, primary) +func waitForReplicaCatchup(t *testing.T, ctx context.Context, replica *cluster.Vttablet, pos replication.Position) { for { replicaPos := getTabletPosition(t, replica) - if replicaPos.GTIDSet.Contains(primaryPos.GTIDSet) { + if replicaPos.GTIDSet.Contains(pos.GTIDSet) { // success return } - if !cluster.ValidateReplicationIsHealthy(t, replica) { + if !validateReplicationIsHealthy(t, replica) { assert.FailNow(t, "replication is broken; not waiting for catchup") return } @@ -435,21 +441,41 @@ func waitForReplicaCatchup(t *testing.T) { } } +func waitForReplicationCatchup(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + primaryPos := getTabletPosition(t, primary) + var wg sync.WaitGroup + for _, replica := range []*cluster.Vttablet{replicaNoFK, replicaFK} { + replica := replica + wg.Add(1) + go func() { + waitForReplicaCatchup(t, ctx, replica, primaryPos) + wg.Done() + }() + } + wg.Wait() +} + func validateMetrics(t *testing.T, tcase *testCase) { for _, workloadTable := range tableNames { t.Run(workloadTable, func(t *testing.T) { t.Run("fk errors", func(t *testing.T) { testSelectTableFKErrors(t, workloadTable, tcase) }) - var primaryRows, replicaRows int64 + var primaryRows, replicaNoFKRows, replicaFKRows int64 t.Run(tabletTestName(t, primary), func(t *testing.T) { primaryRows = testSelectTableMetrics(t, primary, workloadTable, tcase) }) - t.Run(tabletTestName(t, replica), func(t *testing.T) { - replicaRows = testSelectTableMetrics(t, replica, workloadTable, tcase) + t.Run(tabletTestName(t, replicaNoFK), func(t *testing.T) { + replicaNoFKRows = testSelectTableMetrics(t, replicaNoFK, workloadTable, tcase) + }) + t.Run(tabletTestName(t, replicaFK), func(t *testing.T) { + replicaFKRows = testSelectTableMetrics(t, replicaFK, workloadTable, tcase) }) - t.Run("compare primary and replica", func(t *testing.T) { - assert.Equal(t, primaryRows, replicaRows) + t.Run("compare primary and replicas", func(t *testing.T) { + assert.Equal(t, primaryRows, replicaNoFKRows) + assert.Equal(t, primaryRows, replicaFKRows) }) }) } @@ -458,12 +484,16 @@ func validateMetrics(t *testing.T, tcase *testCase) { func TestInitialSetup(t *testing.T) { shards = clusterInstance.Keyspaces[0].Shards require.Equal(t, 1, len(shards)) - require.Equal(t, 2, len(shards[0].Vttablets)) + require.Equal(t, 3, len(shards[0].Vttablets)) // primary, no-fk replica, fk replica primary = shards[0].Vttablets[0] require.NotNil(t, primary) - replica = shards[0].Vttablets[1] - require.NotNil(t, replica) - require.NotEqual(t, primary.Alias, replica.Alias) + replicaNoFK = shards[0].Vttablets[1] + require.NotNil(t, replicaNoFK) + require.NotEqual(t, primary.Alias, replicaNoFK.Alias) + replicaFK = shards[0].Vttablets[2] + require.NotNil(t, replicaFK) + require.NotEqual(t, primary.Alias, replicaFK.Alias) + require.NotEqual(t, replicaNoFK.Alias, replicaFK.Alias) reverseTableNames = slices.Clone(tableNames) slices.Reverse(reverseTableNames) @@ -498,7 +528,8 @@ func ExecuteFKTest(t *testing.T, tcase *testCase) { workloadName = "workload" } testName := fmt.Sprintf("%s/del=%s/upd=%s", workloadName, referenceActionMap[tcase.onDeleteAction], referenceActionMap[tcase.onUpdateAction]) - if tcase.onlineDDLTable != "" { + testOnlineDDL := (tcase.onlineDDLTable != "") + if testOnlineDDL { testName = fmt.Sprintf("%s/ddl=%s", testName, tcase.onlineDDLTable) } if tcase.notes != "" { @@ -525,7 +556,7 @@ func ExecuteFKTest(t *testing.T, tcase *testCase) { baseSleepInterval := 15 * time.Millisecond singleConnectionSleepIntervalNanoseconds := float64(baseSleepInterval.Nanoseconds()) * sleepModifier sleepInterval := time.Duration(int64(singleConnectionSleepIntervalNanoseconds)) - if tcase.onlineDDLTable != "" { + if testOnlineDDL { sleepInterval = sleepInterval * 2 maxConcurrency = max(1, maxConcurrency/2) } @@ -544,7 +575,7 @@ func ExecuteFKTest(t *testing.T, tcase *testCase) { }() } - if tcase.onlineDDLTable != "" { + if testOnlineDDL { t.Run("migrating", func(t *testing.T) { // This only works on patched MySQL hint := tcase.createTableHint @@ -576,18 +607,21 @@ func ExecuteFKTest(t *testing.T, tcase *testCase) { wg.Wait() }) } - t.Run("wait for replica", func(t *testing.T) { - waitForReplicaCatchup(t) + t.Run("wait for replicas", func(t *testing.T) { + waitForReplicationCatchup(t) }) + validateTableDefinitions(t, testOnlineDDL) t.Run("validate metrics", func(t *testing.T) { validateMetrics(t, tcase) }) t.Run("validate replication health", func(t *testing.T) { - cluster.ValidateReplicationIsHealthy(t, replica) + validateReplicationIsHealthy(t, replicaNoFK) + validateReplicationIsHealthy(t, replicaFK) }) t.Run("validate fk", func(t *testing.T) { testFKIntegrity(t, primary, tcase) - testFKIntegrity(t, replica, tcase) + testFKIntegrity(t, replicaNoFK, tcase) + testFKIntegrity(t, replicaFK, tcase) }) }) } @@ -596,20 +630,26 @@ func TestStressFK(t *testing.T) { defer cluster.PanicHandler(t) t.Run("validate replication health", func(t *testing.T) { - cluster.ValidateReplicationIsHealthy(t, replica) + validateReplicationIsHealthy(t, replicaNoFK) + validateReplicationIsHealthy(t, replicaFK) }) runOnlineDDL := false t.Run("check 'rename_table_preserve_foreign_key' variable", func(t *testing.T) { // Online DDL is not possible on vanilla MySQL 8.0 for reasons described in https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/. - // However, Online DDL is made possible in via these changes: https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced - // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. - // Said changes introduce a new global/session boolean variable named 'rename_table_preserve_foreign_key'. It defaults 'false'/0 for backwards compatibility. - // When enabled, a `RENAME TABLE` to a FK parent "pins" the children's foreign keys to the table name rather than the table pointer. Which means after the RENAME, + // However, Online DDL is made possible in via these changes: + // - https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced + // - https://github.com/planetscale/mysql-server/commit/c2f1344a6863518d749f2eb01a4c74ca08a5b889 + // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps3. + // Said changes introduce a new behavior for `RENAME TABLE`. When at least two tables are being renamed in the statement, + // and when at least one table uses internal vitess naming, then a `RENAME TABLE` to a FK parent "pins" the children's + // foreign keys to the table name rather than the table pointer. Which means after the RENAME, // the children will point to the newly instated table rather than the original, renamed table. - // (Note: this applies to a particular type of RENAME where we swap tables, see the above blog post). // For FK children, the MySQL changes simply ignore any Vitess-internal table. // + // The variable 'rename_table_preserve_foreign_key' serves as an indicator to the functionality's availability, + // and at this time changing its value does not change any behavior. + // // In this stress test, we enable Online DDL if the variable 'rename_table_preserve_foreign_key' is present. The Online DDL mechanism will in turn // query for this variable, and manipulate it, when starting the migration and when cutting over. rs, err := primary.VttabletProcess.QueryTablet("show global variables like 'rename_table_preserve_foreign_key'", keyspaceName, false) @@ -691,6 +731,47 @@ func TestStressFK(t *testing.T) { } } +func validateTableDefinitions(t *testing.T, afterOnlineDDL bool) { + t.Run("validate definitions", func(t *testing.T) { + for _, tableName := range []string{childTableName, child2TableName, grandchildTableName} { + t.Run(tableName, func(t *testing.T) { + childFKFollowedParentRenameMsg := "found traces of internal vitess table name, suggesting Online DDL on parent table caused this child table to follow the renames parent. 'rename_table_preserve_foreign_key' should have prevented this" + var primaryStmt string + t.Run(tabletTestName(t, primary), func(t *testing.T) { + primaryStmt = getCreateTableStatement(t, primary, tableName) + assert.NotEmpty(t, primaryStmt) + assert.Contains(t, primaryStmt, "CONSTRAINT") + assert.NotContainsf(t, primaryStmt, "_vrepl", childFKFollowedParentRenameMsg) + assert.NotContainsf(t, primaryStmt, "_vrp_", childFKFollowedParentRenameMsg) + }) + t.Run(tabletTestName(t, replicaFK), func(t *testing.T) { + stmt := getCreateTableStatement(t, replicaFK, tableName) + assert.Contains(t, stmt, "CONSTRAINT") + assert.Equal(t, primaryStmt, stmt) + assert.NotContainsf(t, stmt, "_vrepl", childFKFollowedParentRenameMsg) + assert.NotContainsf(t, stmt, "_vrp_", childFKFollowedParentRenameMsg) + }) + t.Run(tabletTestName(t, replicaNoFK), func(t *testing.T) { + stmt := getCreateTableStatement(t, replicaNoFK, tableName) + // replicaNoFK does not have foreign keys, for the purpose of testing VTGate's cascading + // of foreign key rules. + // However, if we run Online DDL, the table will be swapped at the end of the migration. + // We're not sure here exactly which table has been migrated. Was it this table's parent? + // Or this table itself? Or an unrelated table? In case of Online DDL we don't want to + // validate this replicas' schema, because it could be any one of several outcomes. And + // we don't even care how this replica's schema looks like after the migration. Ths + // schema was inconsistent with the Primary to begin with. We've already tested replicaFK + // for correctness of the schema. + if !afterOnlineDDL { + assert.NotContains(t, stmt, "CONSTRAINT") + assert.NotEqual(t, primaryStmt, stmt) + } + }) + }) + } + }) +} + // createInitialSchema creates the tables from scratch, and drops the foreign key constraints on the replica. func createInitialSchema(t *testing.T, tcase *testCase) { ctx := context.Background() @@ -704,6 +785,13 @@ func createInitialSchema(t *testing.T, tcase *testCase) { require.NoError(t, err) } }) + t.Run("waiting for vschema deletions to apply", func(t *testing.T) { + timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Minute) + defer cancel() + for _, tableName := range tableNames { + utils.WaitForTableDeletions(timeoutCtx, t, clusterInstance.VtgateProcess, keyspaceName, tableName) + } + }) t.Run("creating tables", func(t *testing.T) { // Create the stress tables var b strings.Builder @@ -729,8 +817,8 @@ func createInitialSchema(t *testing.T, tcase *testCase) { require.Nil(t, err) }) } - t.Run("wait for replica", func(t *testing.T) { - waitForReplicaCatchup(t) + t.Run("wait for replication", func(t *testing.T) { + waitForReplicationCatchup(t) }) t.Run("validating tables: vttablet", func(t *testing.T) { // Check if table is created. Checked on tablets. @@ -757,25 +845,12 @@ func createInitialSchema(t *testing.T, tcase *testCase) { t.Run("dropping foreign keys on replica", func(t *testing.T) { for _, statement := range dropConstraintsStatements { - _ = queryTablet(t, replica, "set global super_read_only=0", "") - _ = queryTablet(t, replica, statement, "") - _ = queryTablet(t, replica, "set global super_read_only=1", "") - } - }) - t.Run("validate definitions", func(t *testing.T) { - for _, tableName := range []string{childTableName, child2TableName, grandchildTableName} { - t.Run(tableName, func(t *testing.T) { - t.Run(tabletTestName(t, primary), func(t *testing.T) { - stmt := getCreateTableStatement(t, primary, tableName) - assert.Contains(t, stmt, "CONSTRAINT") - }) - t.Run(tabletTestName(t, replica), func(t *testing.T) { - stmt := getCreateTableStatement(t, replica, tableName) - assert.NotContains(t, stmt, "CONSTRAINT") - }) - }) + _ = queryTablet(t, replicaNoFK, "set global super_read_only=0", "") + _ = queryTablet(t, replicaNoFK, statement, "") + _ = queryTablet(t, replicaNoFK, "set global super_read_only=1", "") } }) + validateTableDefinitions(t, false) } // testOnlineDDLStatement runs an online DDL, ALTER statement @@ -804,7 +879,7 @@ func testOnlineDDLStatement(t *testing.T, alterStatement string, ddlStrategy str } if expectHint != "" { - stmt, err := sqlparser.Parse(alterStatement) + stmt, err := sqlparser.NewTestParser().Parse(alterStatement) require.NoError(t, err) ddlStmt, ok := stmt.(sqlparser.DDLStatement) require.True(t, ok) @@ -915,6 +990,8 @@ func isFKError(err error) bool { return false case sqlerror.ERLockDeadlock: return false // bummer, but deadlocks can happen, it's a legit error. + case sqlerror.ERLockNowait: + return false // For some queries we use NOWAIT. Bummer, but this can happen, it's a legit error. case sqlerror.ERNoReferencedRow, sqlerror.ERRowIsReferenced, sqlerror.ERRowIsReferenced2, diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql deleted file mode 100644 index 3b4496d47fb..00000000000 --- a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql +++ /dev/null @@ -1,472 +0,0 @@ -create table u_t1 -( - id bigint, - col1 bigint, - index(col1), - primary key (id) -) Engine = InnoDB; - -create table u_t2 -( - id bigint, - col2 bigint, - primary key (id), - foreign key (col2) references u_t1 (col1) on delete set null on update set null -) Engine = InnoDB; - -create table u_t3 -( - id bigint, - col3 bigint, - primary key (id), - foreign key (col3) references u_t1 (col1) on delete cascade on update cascade -) Engine = InnoDB; - - -/* - * fk_t1 - * │ - * │ On Delete Restrict - * │ On Update Restrict - * ▼ - * ┌────────────────fk_t2────────────────┐ - * │ │ - * │On Delete Set Null │ On Delete Set Null - * │On Update Set Null │ On Update Set Null - * ▼ ▼ - * fk_t7 fk_t3───────────────────┐ - * │ │ - * │ │ On Delete Set Null - * On Delete Set Null │ │ On Update Set Null - * On Update Set Null │ │ - * ▼ ▼ - * fk_t4 fk_t6 - * │ - * │ - * On Delete Restrict │ - * On Update Restrict │ - * │ - * ▼ - * fk_t5 - */ - -create table fk_t1 -( - id bigint, - col varchar(10), - primary key (id), - index(col) -) Engine = InnoDB; - -create table fk_t2 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t1(col) on delete restrict on update restrict -) Engine = InnoDB; - -create table fk_t3 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t2(col) on delete set null on update set null -) Engine = InnoDB; - -create table fk_t4 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t3(col) on delete set null on update set null -) Engine = InnoDB; - -create table fk_t5 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t4(col) on delete restrict on update restrict -) Engine = InnoDB; - -create table fk_t6 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t3(col) on delete set null on update set null -) Engine = InnoDB; - -create table fk_t7 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t2(col) on delete set null on update set null -) Engine = InnoDB; - -/* - * fk_t10 - * │ - * On Delete Cascade │ - * On Update Cascade │ - * │ - * ▼ - * fk_t11──────────────────┐ - * │ │ - * │ │ On Delete Restrict - * On Delete Cascade │ │ On Update Restrict - * On Update Cascade │ │ - * │ │ - * ▼ ▼ - * fk_t12 fk_t13 - */ - -create table fk_t10 -( - id bigint, - col varchar(10), - primary key (id), - index(col) -) Engine = InnoDB; - -create table fk_t11 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t10(col) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_t12 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t11(col) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_t13 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t11(col) on delete restrict on update restrict -) Engine = InnoDB; - -/* - * fk_t15 - * │ - * │ - * On Delete Cascade │ - * On Update Cascade │ - * │ - * ▼ - * fk_t16 - * │ - * On Delete Set Null │ - * On Update Set Null │ - * │ - * ▼ - * fk_t17──────────────────┐ - * │ │ - * │ │ On Delete Set Null - * On Delete Cascade │ │ On Update Set Null - * On Update Cascade │ │ - * │ │ - * ▼ ▼ - * fk_t18 fk_t19 - */ - -create table fk_t15 -( - id bigint, - col varchar(10), - primary key (id), - index(col) -) Engine = InnoDB; - -create table fk_t16 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t15(col) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_t17 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t16(col) on delete set null on update set null -) Engine = InnoDB; - -create table fk_t18 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t17(col) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_t19 -( - id bigint, - col varchar(10), - primary key (id), - index(col), - foreign key (col) references fk_t17(col) on delete set null on update set null -) Engine = InnoDB; - -/* - Self referenced foreign key from col2 to col in fk_t20 -*/ - -create table fk_t20 -( - id bigint, - col varchar(10), - col2 varchar(10), - primary key (id), - index(col), - foreign key (col2) references fk_t20(col) on delete restrict on update restrict -) Engine = InnoDB; - - -/* - * fk_multicol_t1 - * │ - * │ On Delete Restrict - * │ On Update Restrict - * ▼ - * ┌────────fk_multicol_t2───────────────┐ - * │ │ - * │On Delete Set Null │ On Delete Set Null - * │On Update Set Null │ On Update Set Null - * ▼ ▼ - * fk_multicol_t7 fk_multicol_t3───────────────────┐ - * │ │ - * │ │ On Delete Set Null - * On Delete Set Null │ │ On Update Set Null - * On Update Set Null │ │ - * ▼ ▼ - * fk_multicol_t4 fk_multicol_t6 - * │ - * │ - * On Delete Restrict │ - * On Update Restrict │ - * │ - * ▼ - * fk_multicol_t5 - */ -create table fk_multicol_t1 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb) -) Engine = InnoDB; - -create table fk_multicol_t2 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t1(cola, colb) on delete restrict on update restrict -) Engine = InnoDB; - -create table fk_multicol_t3 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t2(cola, colb) on delete set null on update set null -) Engine = InnoDB; - -create table fk_multicol_t4 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t3(cola, colb) on delete set null on update set null -) Engine = InnoDB; - -create table fk_multicol_t5 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t4(cola, colb) on delete restrict on update restrict -) Engine = InnoDB; - -create table fk_multicol_t6 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t3(cola, colb) on delete set null on update set null -) Engine = InnoDB; - -create table fk_multicol_t7 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t2(cola, colb) on delete set null on update set null -) Engine = InnoDB; - -/* - * fk_multicol_t10 - * │ - * On Delete Cascade │ - * On Update Cascade │ - * │ - * ▼ - * fk_multicol_t11──────────────────┐ - * │ │ - * │ │ On Delete Restrict - * On Delete Cascade │ │ On Update Restrict - * On Update Cascade │ │ - * │ │ - * ▼ ▼ - * fk_multicol_t12 fk_multicol_t13 - */ - -create table fk_multicol_t10 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb) -) Engine = InnoDB; - -create table fk_multicol_t11 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t10(cola, colb) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_multicol_t12 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t11(cola, colb) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_multicol_t13 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t11(cola, colb) on delete restrict on update restrict -) Engine = InnoDB; - -/* - * fk_multicol_t15 - * │ - * │ - * On Delete Cascade │ - * On Update Cascade │ - * │ - * ▼ - * fk_multicol_t16 - * │ - * On Delete Set Null │ - * On Update Set Null │ - * │ - * ▼ - * fk_multicol_t17──────────────────┐ - * │ │ - * │ │ On Delete Set Null - * On Delete Cascade │ │ On Update Set Null - * On Update Cascade │ │ - * │ │ - * ▼ ▼ - * fk_multicol_t18 fk_multicol_t19 - */ - -create table fk_multicol_t15 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb) -) Engine = InnoDB; - -create table fk_multicol_t16 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t15(cola, colb) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_multicol_t17 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t16(cola, colb) on delete set null on update set null -) Engine = InnoDB; - -create table fk_multicol_t18 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t17(cola, colb) on delete cascade on update cascade -) Engine = InnoDB; - -create table fk_multicol_t19 -( - id bigint, - colb varchar(10), - cola varchar(10), - primary key (id), - index(cola, colb), - foreign key (cola, colb) references fk_multicol_t17(cola, colb) on delete set null on update set null -) Engine = InnoDB; diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_unmanaged_vschema.json b/go/test/endtoend/vtgate/foreignkey/unsharded_unmanaged_vschema.json new file mode 100644 index 00000000000..2698e23dac5 --- /dev/null +++ b/go/test/endtoend/vtgate/foreignkey/unsharded_unmanaged_vschema.json @@ -0,0 +1,41 @@ +{ + "sharded": false, + "foreignKeyMode": "unmanaged", + "tables": { + "u_t1": {}, + "u_t2": {}, + "fk_t1": {}, + "fk_t2": {}, + "fk_t3": {}, + "fk_t4": {}, + "fk_t5": {}, + "fk_t6": {}, + "fk_t7": {}, + "fk_t10": {}, + "fk_t11": {}, + "fk_t12": {}, + "fk_t13": {}, + "fk_t15": {}, + "fk_t16": {}, + "fk_t17": {}, + "fk_t18": {}, + "fk_t19": {}, + "fk_t20": {}, + "fk_multicol_t1": {}, + "fk_multicol_t2": {}, + "fk_multicol_t3": {}, + "fk_multicol_t4": {}, + "fk_multicol_t5": {}, + "fk_multicol_t6": {}, + "fk_multicol_t7": {}, + "fk_multicol_t10": {}, + "fk_multicol_t11": {}, + "fk_multicol_t12": {}, + "fk_multicol_t13": {}, + "fk_multicol_t15": {}, + "fk_multicol_t16": {}, + "fk_multicol_t17": {}, + "fk_multicol_t18": {}, + "fk_multicol_t19": {} + } +} \ No newline at end of file diff --git a/go/test/endtoend/vtgate/foreignkey/utils_test.go b/go/test/endtoend/vtgate/foreignkey/utils_test.go index 5e0b4a8a3cc..9d4c53145b0 100644 --- a/go/test/endtoend/vtgate/foreignkey/utils_test.go +++ b/go/test/endtoend/vtgate/foreignkey/utils_test.go @@ -17,17 +17,25 @@ limitations under the License. package foreignkey import ( + "context" "database/sql" "fmt" + "math/rand" + "slices" "strings" "testing" + "time" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) +var supportedOpps = []string{"*", "+", "-"} + // getTestName prepends whether the test is for a sharded keyspace or not to the test name. func getTestName(testName string, testSharded bool) string { if testSharded { @@ -41,9 +49,35 @@ func isMultiColFkTable(tableName string) bool { return strings.Contains(tableName, "multicol") } +func (fz *fuzzer) generateExpression(length int, cols ...string) string { + expr := fz.getColOrInt(cols...) + if length == 1 { + return expr + } + rhsExpr := fz.generateExpression(length-1, cols...) + op := supportedOpps[rand.Intn(len(supportedOpps))] + return fmt.Sprintf("%v %s (%v)", expr, op, rhsExpr) +} + +// getColOrInt gets a column or an integer/NULL literal with equal probability. +func (fz *fuzzer) getColOrInt(cols ...string) string { + if len(cols) == 0 || rand.Intn(2) == 0 { + return convertIntValueToString(rand.Intn(1 + fz.maxValForCol)) + } + return cols[rand.Intn(len(cols))] +} + +// convertIntValueToString converts the given value to a string +func convertIntValueToString(value int) string { + if value == 0 { + return "NULL" + } + return fmt.Sprintf("%d", value) +} + // waitForSchemaTrackingForFkTables waits for schema tracking to have run and seen the tables used // for foreign key tests. -func waitForSchemaTrackingForFkTables(t *testing.T) { +func waitForSchemaTrackingForFkTables(t testing.TB) { err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t1", "col") require.NoError(t, err) err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t18", "col") @@ -56,6 +90,8 @@ func waitForSchemaTrackingForFkTables(t *testing.T) { require.NoError(t, err) err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, unshardedKs, "fk_t11", "col") require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, unshardedUnmanagedKs, "fk_t11", "col") + require.NoError(t, err) } // getReplicaTablets gets all the replica tablets. @@ -142,3 +178,152 @@ func compareVitessAndMySQLErrors(t *testing.T, vtErr, mysqlErr error) { out := fmt.Sprintf("Vitess and MySQL are not erroring the same way.\nVitess error: %v\nMySQL error: %v", vtErr, mysqlErr) t.Error(out) } + +// ensureDatabaseState ensures that the database is either empty or not. +func ensureDatabaseState(t *testing.T, vtconn *mysql.Conn, empty bool) { + results := collectFkTablesState(vtconn) + isEmpty := true + for _, res := range results { + if len(res.Rows) > 0 { + isEmpty = false + } + } + require.Equal(t, isEmpty, empty) +} + +// verifyDataIsCorrect verifies that the data in MySQL database matches the data in the Vitess database. +func verifyDataIsCorrect(t *testing.T, mcmp utils.MySQLCompare, concurrency int) { + // For single concurrent thread, we run all the queries on both MySQL and Vitess, so we can verify correctness + // by just checking if the data in MySQL and Vitess match. + if concurrency == 1 { + for _, table := range fkTables { + query := fmt.Sprintf("SELECT * FROM %v ORDER BY id", table) + mcmp.Exec(query) + } + } else { + // For higher concurrency, we don't have MySQL data to verify everything is fine, + // so we'll have to do something different. + // We run LEFT JOIN queries on all the parent and child tables linked by foreign keys + // to make sure that nothing is broken in the database. + for _, reference := range fkReferences { + query := fmt.Sprintf("select %v.id from %v left join %v on (%v.col = %v.col) where %v.col is null and %v.col is not null", reference.childTable, reference.childTable, reference.parentTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable) + if isMultiColFkTable(reference.childTable) { + query = fmt.Sprintf("select %v.id from %v left join %v on (%v.cola = %v.cola and %v.colb = %v.colb) where %v.cola is null and %v.cola is not null and %v.colb is not null", reference.childTable, reference.childTable, reference.parentTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable, reference.parentTable, reference.childTable, reference.childTable) + } + res, err := mcmp.VtConn.ExecuteFetch(query, 1000, false) + require.NoError(t, err) + require.Zerof(t, len(res.Rows), "Query %v gave non-empty results", query) + } + } + // We also verify that the results in Primary and Replica table match as is. + for _, keyspace := range clusterInstance.Keyspaces { + for _, shard := range keyspace.Shards { + var primaryTab, replicaTab *cluster.Vttablet + for _, vttablet := range shard.Vttablets { + if vttablet.Type == "primary" { + primaryTab = vttablet + } else { + replicaTab = vttablet + } + } + require.NotNil(t, primaryTab) + require.NotNil(t, replicaTab) + checkReplicationHealthy(t, replicaTab) + cluster.WaitForReplicationPos(t, primaryTab, replicaTab, true, 1*time.Minute) + primaryConn, err := utils.GetMySQLConn(primaryTab, fmt.Sprintf("vt_%v", keyspace.Name)) + require.NoError(t, err) + replicaConn, err := utils.GetMySQLConn(replicaTab, fmt.Sprintf("vt_%v", keyspace.Name)) + require.NoError(t, err) + primaryRes := collectFkTablesState(primaryConn) + replicaRes := collectFkTablesState(replicaConn) + verifyDataMatches(t, primaryRes, replicaRes) + } + } +} + +// verifyDataMatches verifies that the two list of results are the same. +func verifyDataMatches(t testing.TB, resOne []*sqltypes.Result, resTwo []*sqltypes.Result) { + require.EqualValues(t, len(resTwo), len(resOne), "Res 1 - %v, Res 2 - %v", resOne, resTwo) + for idx, resultOne := range resOne { + resultTwo := resTwo[idx] + require.True(t, resultOne.Equal(resultTwo), "Data for %v doesn't match\nRows 1\n%v\nRows 2\n%v", fkTables[idx], resultOne.Rows, resultTwo.Rows) + } +} + +// collectFkTablesState collects the data stored in the foreign key tables for the given connection. +func collectFkTablesState(conn *mysql.Conn) []*sqltypes.Result { + var tablesData []*sqltypes.Result + for _, table := range fkTables { + query := fmt.Sprintf("SELECT * FROM %v ORDER BY id", table) + res, _ := conn.ExecuteFetch(query, 10000, true) + tablesData = append(tablesData, res) + } + return tablesData +} + +func validateReplication(t *testing.T) { + for _, keyspace := range clusterInstance.Keyspaces { + for _, shard := range keyspace.Shards { + for _, vttablet := range shard.Vttablets { + if vttablet.Type != "primary" { + checkReplicationHealthy(t, vttablet) + } + } + } + } +} + +// compareResultRows compares the rows of the two results provided. +func compareResultRows(resOne *sqltypes.Result, resTwo *sqltypes.Result) bool { + return slices.EqualFunc(resOne.Rows, resTwo.Rows, func(a, b sqltypes.Row) bool { + return sqltypes.RowEqual(a, b) + }) +} + +// setupBenchmark sets up the benchmark by creating the set of queries that we want to run. It also ensures that the 3 modes (MySQL, Vitess Managed, Vitess Unmanaged) we verify all return the same results after the queries have been executed. +func setupBenchmark(b *testing.B, maxValForId int, maxValForCol int, insertShare int, deleteShare int, updateShare int, numQueries int) ([]string, *mysql.Conn, *mysql.Conn, *mysql.Conn) { + // Clear out all the data to ensure we start with a clean slate. + startBenchmark(b) + // Create a fuzzer to generate and store a certain set of queries. + fz := newFuzzer(1, maxValForId, maxValForCol, insertShare, deleteShare, updateShare, SQLQueries, nil) + fz.noFkSetVar = true + var queries []string + for j := 0; j < numQueries; j++ { + genQueries := fz.generateQuery() + require.Len(b, genQueries, 1) + queries = append(queries, genQueries[0]) + } + + // Connect to MySQL and run all the queries + mysqlConn, err := mysql.Connect(context.Background(), &mysqlParams) + require.NoError(b, err) + // Connect to Vitess managed foreign keys keyspace + vtConn, err := mysql.Connect(context.Background(), &vtParams) + require.NoError(b, err) + utils.Exec(b, vtConn, fmt.Sprintf("use `%v`", unshardedKs)) + // Connect to Vitess unmanaged foreign keys keyspace + vtUnmanagedConn, err := mysql.Connect(context.Background(), &vtParams) + require.NoError(b, err) + utils.Exec(b, vtUnmanagedConn, fmt.Sprintf("use `%v`", unshardedUnmanagedKs)) + + // First we make sure that running all the queries in both the Vitess modes and MySQL gives the same data. + // So we run all the queries and then check that the data in all of them matches. + runQueries(b, mysqlConn, queries) + runQueries(b, vtConn, queries) + runQueries(b, vtUnmanagedConn, queries) + for _, table := range fkTables { + query := fmt.Sprintf("SELECT * FROM %v ORDER BY id", table) + resVitessManaged, _ := vtConn.ExecuteFetch(query, 10000, true) + resMySQL, _ := mysqlConn.ExecuteFetch(query, 10000, true) + resVitessUnmanaged, _ := vtUnmanagedConn.ExecuteFetch(query, 10000, true) + require.True(b, compareResultRows(resVitessManaged, resMySQL), "Results for %v don't match\nVitess Managed\n%v\nMySQL\n%v", table, resVitessManaged, resMySQL) + require.True(b, compareResultRows(resVitessUnmanaged, resMySQL), "Results for %v don't match\nVitess Unmanaged\n%v\nMySQL\n%v", table, resVitessUnmanaged, resMySQL) + } + return queries, mysqlConn, vtConn, vtUnmanagedConn +} + +func runQueries(t testing.TB, conn *mysql.Conn, queries []string) { + for _, query := range queries { + _, _ = utils.ExecAllowError(t, conn, query) + } +} diff --git a/go/test/endtoend/vtgate/godriver/main_test.go b/go/test/endtoend/vtgate/godriver/main_test.go index 492a68662fc..587c189d2ea 100644 --- a/go/test/endtoend/vtgate/godriver/main_test.go +++ b/go/test/endtoend/vtgate/godriver/main_test.go @@ -105,7 +105,7 @@ func TestMain(m *testing.M) { VSchema: VSchema, } clusterInstance.VtTabletExtraArgs = []string{ - "--queryserver-config-transaction-timeout", "3", + "--queryserver-config-transaction-timeout", "3s", } if err := clusterInstance.StartKeyspace(*Keyspace, []string{"-80", "80-"}, 1, false); err != nil { log.Fatal(err.Error()) diff --git a/go/test/endtoend/vtgate/misc_test.go b/go/test/endtoend/vtgate/misc_test.go index 83c41fd7183..4d529da5d17 100644 --- a/go/test/endtoend/vtgate/misc_test.go +++ b/go/test/endtoend/vtgate/misc_test.go @@ -17,16 +17,31 @@ limitations under the License. package vtgate import ( + "context" "fmt" + "sync/atomic" "testing" - - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/test/endtoend/utils" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/utils" ) +func TestInsertOnDuplicateKey(t *testing.T) { + conn, closer := start(t) + defer closer() + + utils.Exec(t, conn, "insert into t11(id, sharding_key, col1, col2, col3) values(1, 2, 'a', 1, 2)") + utils.Exec(t, conn, "insert into t11(id, sharding_key, col1, col2, col3) values(1, 2, 'a', 1, 2) on duplicate key update id=10;") + utils.AssertMatches(t, conn, "select id, sharding_key from t11 where id=10", "[[INT64(10) INT64(2)]]") + +} + func TestInsertNeg(t *testing.T) { conn, closer := start(t) defer closer() @@ -336,6 +351,52 @@ func TestFlush(t *testing.T) { utils.Exec(t, conn, "flush local tables t1, t2") } +// TestFlushLock tests that ftwrl and unlock tables should unblock other session connections to execute the query. +func TestFlushLock(t *testing.T) { + conn, closer := start(t) + defer closer() + + // replica: fail it + utils.Exec(t, conn, "use @replica") + _, err := utils.ExecAllowError(t, conn, "flush tables ks.t1, ks.t2 with read lock") + require.ErrorContains(t, err, "VT09012: FLUSH statement with REPLICA tablet not allowed") + + // primary: should work + utils.Exec(t, conn, "use @primary") + utils.Exec(t, conn, "flush tables ks.t1, ks.t2 with read lock") + + var cnt atomic.Int32 + go func() { + ctx := context.Background() + conn2, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn2.Close() + + cnt.Add(1) + utils.Exec(t, conn2, "select * from ks.t1 for update") + cnt.Add(1) + }() + for cnt.Load() == 0 { + } + // added sleep to let the query execute inside the go routine, which should be blocked. + time.Sleep(1 * time.Second) + require.EqualValues(t, 1, cnt.Load()) + + // unlock it + utils.Exec(t, conn, "unlock tables") + + // now wait for go routine to complete. + timeout := time.After(3 * time.Second) + for cnt.Load() != 2 { + select { + case <-timeout: + t.Fatalf("test timeout waiting for select query to complete") + default: + + } + } +} + func TestShowVariables(t *testing.T) { conn, closer := start(t) defer closer() @@ -730,8 +791,15 @@ func TestJoinWithMergedRouteWithPredicate(t *testing.T) { } func TestRowCountExceed(t *testing.T) { - conn, closer := start(t) - defer closer() + conn, _ := start(t) + defer func() { + cluster.PanicHandler(t) + // needs special delete logic as it exceeds row count. + for i := 50; i <= 300; i += 50 { + utils.Exec(t, conn, fmt.Sprintf("delete from t1 where id1 < %d", i)) + } + conn.Close() + }() for i := 0; i < 250; i++ { utils.Exec(t, conn, fmt.Sprintf("insert into t1 (id1, id2) values (%d, %d)", i, i+1)) @@ -739,3 +807,41 @@ func TestRowCountExceed(t *testing.T) { utils.AssertContainsError(t, conn, "select id1 from t1 where id1 < 1000", `Row count exceeded 100`) } + +func TestLookupErrorMetric(t *testing.T) { + conn, closer := start(t) + defer closer() + + oldErrCount := getVtgateApiErrorCounts(t) + + utils.Exec(t, conn, `insert into t1 values (1,1)`) + _, err := utils.ExecAllowError(t, conn, `insert into t1 values (2,1)`) + require.ErrorContains(t, err, `(errno 1062) (sqlstate 23000)`) + + newErrCount := getVtgateApiErrorCounts(t) + require.EqualValues(t, oldErrCount+1, newErrCount) +} + +func getVtgateApiErrorCounts(t *testing.T) float64 { + apiErr := getVar(t, "VtgateApiErrorCounts") + if apiErr == nil { + return 0 + } + mapErrors := apiErr.(map[string]interface{}) + val, exists := mapErrors["Execute.ks.primary.ALREADY_EXISTS"] + if exists { + return val.(float64) + } + return 0 +} + +func getVar(t *testing.T, key string) interface{} { + vars, err := clusterInstance.VtgateProcess.GetVars() + require.NoError(t, err) + + val, exists := vars[key] + if !exists { + return nil + } + return val +} diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go index ed917efda4c..89b3d0c8c85 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go @@ -73,6 +73,10 @@ func TestAggregateTypes(t *testing.T) { mcmp.AssertMatches("select val1 as a, count(*) from aggr_test group by a order by a", `[[VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)] [VARCHAR("d") INT64(1)] [VARCHAR("e") INT64(2)]]`) mcmp.AssertMatches("select val1 as a, count(*) from aggr_test group by a order by 2, a", `[[VARCHAR("b") INT64(1)] [VARCHAR("d") INT64(1)] [VARCHAR("a") INT64(2)] [VARCHAR("c") INT64(2)] [VARCHAR("e") INT64(2)]]`) mcmp.AssertMatches("select sum(val1) from aggr_test", `[[FLOAT64(0)]]`) + t.Run("Average for sharded keyspaces", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches("select avg(val1) from aggr_test", `[[FLOAT64(0)]]`) + }) } func TestGroupBy(t *testing.T) { @@ -172,6 +176,16 @@ func TestAggrOnJoin(t *testing.T) { mcmp.AssertMatches("select a.val1 from aggr_test a join t3 t on a.val2 = t.id7 group by a.val1 having count(*) = 4", `[[VARCHAR("a")]]`) + + t.Run("Average in join for sharded", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches(`select avg(a1.val2), avg(a2.val2) from aggr_test a1 join aggr_test a2 on a1.val2 = a2.id join t3 t on a2.val2 = t.id7`, + "[[DECIMAL(1.5000) DECIMAL(1.0000)]]") + + mcmp.AssertMatches(`select a1.val1, avg(a1.val2) from aggr_test a1 join aggr_test a2 on a1.val2 = a2.id join t3 t on a2.val2 = t.id7 group by a1.val1`, + `[[VARCHAR("a") DECIMAL(1.0000)] [VARCHAR("b") DECIMAL(1.0000)] [VARCHAR("c") DECIMAL(3.0000)]]`) + }) + } func TestNotEqualFilterOnScatter(t *testing.T) { @@ -314,22 +328,32 @@ func TestAggOnTopOfLimit(t *testing.T) { for _, workload := range []string{"oltp", "olap"} { t.Run(workload, func(t *testing.T) { utils.Exec(t, mcmp.VtConn, fmt.Sprintf("set workload = '%s'", workload)) - mcmp.AssertMatches(" select count(*) from (select id, val1 from aggr_test where val2 < 4 limit 2) as x", "[[INT64(2)]]") - mcmp.AssertMatches(" select count(val1) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2)]]") - mcmp.AssertMatches(" select count(*) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(2)]]") - mcmp.AssertMatches(" select count(val1) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(1)]]") - mcmp.AssertMatches(" select count(val2) from (select id, val2 from aggr_test where val2 is null limit 2) as x", "[[INT64(0)]]") - mcmp.AssertMatches(" select val1, count(*) from (select id, val1 from aggr_test where val2 < 4 order by val1 limit 2) as x group by val1", `[[NULL INT64(1)] [VARCHAR("a") INT64(1)]]`) - mcmp.AssertMatchesNoOrder(" select val1, count(val2) from (select val1, val2 from aggr_test limit 8) as x group by val1", `[[NULL INT64(1)] [VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)]]`) + mcmp.AssertMatches("select count(*) from (select id, val1 from aggr_test where val2 < 4 limit 2) as x", "[[INT64(2)]]") + mcmp.AssertMatches("select count(val1) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2)]]") + mcmp.AssertMatches("select count(*) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(2)]]") + mcmp.AssertMatches("select count(val1) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(1)]]") + mcmp.AssertMatches("select count(val2) from (select id, val2 from aggr_test where val2 is null limit 2) as x", "[[INT64(0)]]") + mcmp.AssertMatches("select val1, count(*) from (select id, val1 from aggr_test where val2 < 4 order by val1 limit 2) as x group by val1", `[[NULL INT64(1)] [VARCHAR("a") INT64(1)]]`) + mcmp.AssertMatchesNoOrder("select val1, count(val2) from (select val1, val2 from aggr_test limit 8) as x group by val1", `[[NULL INT64(1)] [VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)]]`) + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches("select avg(val2) from (select id, val2 from aggr_test where val2 is null limit 2) as x", "[[NULL]]") + mcmp.AssertMatchesNoOrder("select val1, avg(val2) from (select val1, val2 from aggr_test limit 8) as x group by val1", `[[NULL DECIMAL(2.0000)] [VARCHAR("a") DECIMAL(3.5000)] [VARCHAR("b") DECIMAL(1.0000)] [VARCHAR("c") DECIMAL(3.5000)]]`) + }) // mysql returns FLOAT64(0), vitess returns DECIMAL(0) - mcmp.AssertMatchesNoCompare(" select count(*), sum(val1) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2) FLOAT64(0)]]", "[[INT64(2) FLOAT64(0)]]") - mcmp.AssertMatches(" select count(val1), sum(id) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2) DECIMAL(7)]]") - mcmp.AssertMatches(" select count(*), sum(id) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(2) DECIMAL(14)]]") - mcmp.AssertMatches(" select count(val1), sum(id) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(1) DECIMAL(14)]]") - mcmp.AssertMatches(" select count(val2), sum(val2) from (select id, val2 from aggr_test where val2 is null limit 2) as x", "[[INT64(0) NULL]]") - mcmp.AssertMatches(" select val1, count(*), sum(id) from (select id, val1 from aggr_test where val2 < 4 order by val1 limit 2) as x group by val1", `[[NULL INT64(1) DECIMAL(7)] [VARCHAR("a") INT64(1) DECIMAL(2)]]`) - mcmp.AssertMatchesNoOrder(" select val1, count(val2), sum(val2) from (select val1, val2 from aggr_test limit 8) as x group by val1", `[[NULL INT64(1) DECIMAL(2)] [VARCHAR("a") INT64(2) DECIMAL(7)] [VARCHAR("b") INT64(1) DECIMAL(1)] [VARCHAR("c") INT64(2) DECIMAL(7)]]`) + mcmp.AssertMatches("select count(val1), sum(id) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2) DECIMAL(7)]]") + mcmp.AssertMatches("select count(*), sum(id) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(2) DECIMAL(14)]]") + mcmp.AssertMatches("select count(val1), sum(id) from (select id, val1 from aggr_test where val2 is null limit 2) as x", "[[INT64(1) DECIMAL(14)]]") + mcmp.AssertMatches("select count(val2), sum(val2) from (select id, val2 from aggr_test where val2 is null limit 2) as x", "[[INT64(0) NULL]]") + mcmp.AssertMatches("select val1, count(*), sum(id) from (select id, val1 from aggr_test where val2 < 4 order by val1 limit 2) as x group by val1", `[[NULL INT64(1) DECIMAL(7)] [VARCHAR("a") INT64(1) DECIMAL(2)]]`) + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches("select count(*), sum(val1), avg(val1) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2) FLOAT64(0) FLOAT64(0)]]") + mcmp.AssertMatches("select count(val1), sum(id), avg(id) from (select id, val1 from aggr_test where val2 < 4 order by val1 desc limit 2) as x", "[[INT64(2) DECIMAL(7) DECIMAL(3.5000)]]") + mcmp.AssertMatchesNoOrder("select val1, count(val2), sum(val2), avg(val2) from (select val1, val2 from aggr_test limit 8) as x group by val1", + `[[NULL INT64(1) DECIMAL(2) DECIMAL(2.0000)] [VARCHAR("a") INT64(2) DECIMAL(7) DECIMAL(3.5000)] [VARCHAR("b") INT64(1) DECIMAL(1) DECIMAL(1.0000)] [VARCHAR("c") INT64(2) DECIMAL(7) DECIMAL(3.5000)]]`) + }) }) } } @@ -345,6 +369,11 @@ func TestEmptyTableAggr(t *testing.T) { mcmp.AssertMatches(" select count(*) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[INT64(0)]]") mcmp.AssertMatches(" select t1.`name`, count(*) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo' group by t1.`name`", "[]") mcmp.AssertMatches(" select t1.`name`, count(*) from t1 inner join t2 on (t1.t1_id = t2.id) where t1.value = 'foo' group by t1.`name`", "[]") + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches(" select count(t1.value) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[INT64(0)]]") + mcmp.AssertMatches(" select avg(t1.value) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[NULL]]") + }) }) } @@ -355,8 +384,13 @@ func TestEmptyTableAggr(t *testing.T) { utils.Exec(t, mcmp.VtConn, fmt.Sprintf("set workload = %s", workload)) mcmp.AssertMatches(" select count(*) from t1 inner join t2 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[INT64(0)]]") mcmp.AssertMatches(" select count(*) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[INT64(0)]]") - mcmp.AssertMatches(" select t1.`name`, count(*) from t1 inner join t2 on (t1.t1_id = t2.id) where t1.value = 'foo' group by t1.`name`", "[]") mcmp.AssertMatches(" select t1.`name`, count(*) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo' group by t1.`name`", "[]") + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches(" select count(t1.value) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[INT64(0)]]") + mcmp.AssertMatches(" select avg(t1.value) from t2 inner join t1 on (t1.t1_id = t2.id) where t1.value = 'foo'", "[[NULL]]") + mcmp.AssertMatches(" select t1.`name`, count(*) from t1 inner join t2 on (t1.t1_id = t2.id) where t1.value = 'foo' group by t1.`name`", "[]") + }) }) } @@ -399,6 +433,37 @@ func TestAggregateLeftJoin(t *testing.T) { mcmp.AssertMatches("SELECT sum(t1.shardkey) FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.id", `[[DECIMAL(1)]]`) mcmp.AssertMatches("SELECT sum(t2.shardkey) FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.id", `[[DECIMAL(1)]]`) mcmp.AssertMatches("SELECT count(*) FROM t2 LEFT JOIN t1 ON t1.t1_id = t2.id WHERE IFNULL(t1.name, 'NOTSET') = 'r'", `[[INT64(1)]]`) + + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches("SELECT avg(t1.shardkey) FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.id", `[[DECIMAL(0.5000)]]`) + mcmp.AssertMatches("SELECT avg(t2.shardkey) FROM t1 LEFT JOIN t2 ON t1.t1_id = t2.id", `[[DECIMAL(1.0000)]]`) + aggregations := []string{ + "count(t1.shardkey)", + "count(t2.shardkey)", + "sum(t1.shardkey)", + "sum(t2.shardkey)", + "avg(t1.shardkey)", + "avg(t2.shardkey)", + "count(*)", + } + + grouping := []string{ + "t1.t1_id", + "t1.shardKey", + "t1.value", + "t2.id", + "t2.shardKey", + } + + // quickly construct a big number of left join aggregation queries that have to be executed using the hash join + for _, agg := range aggregations { + for _, gb := range grouping { + query := fmt.Sprintf("SELECT %s FROM t1 LEFT JOIN (select id, shardkey from t2 limit 100) as t2 ON t1.t1_id = t2.id group by %s", agg, gb) + mcmp.Exec(query) + } + } + }) } // TestScalarAggregate tests validates that only count is returned and no additional field is returned.gst @@ -426,6 +491,10 @@ func TestScalarAggregate(t *testing.T) { mcmp.Exec("insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'A',1), (3,'b',1), (4,'c',3), (5,'c',4)") mcmp.AssertMatches("select count(distinct val1) from aggr_test", `[[INT64(3)]]`) + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.AssertMatches("select avg(val1) from aggr_test", `[[FLOAT64(0)]]`) + }) } func TestAggregationRandomOnAnAggregatedValue(t *testing.T) { @@ -482,6 +551,31 @@ func TestComplexAggregation(t *testing.T) { mcmp.Exec(`SELECT shardkey + MIN(t1_id)+MAX(t1_id) FROM t1 GROUP BY shardkey`) mcmp.Exec(`SELECT name+COUNT(t1_id)+1 FROM t1 GROUP BY name`) mcmp.Exec(`SELECT COUNT(*)+shardkey+MIN(t1_id)+1+MAX(t1_id)*SUM(t1_id)+1+name FROM t1 GROUP BY shardkey, name`) + t.Run("Average in sharded query", func(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp.Exec(`SELECT COUNT(t1_id)+MAX(shardkey)+AVG(t1_id) FROM t1`) + }) +} + +func TestJoinAggregation(t *testing.T) { + // This is new functionality in Vitess 20 + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into t1(t1_id, `name`, `value`, shardkey) values(1,'a1','foo',100), (2,'b1','foo',200), (3,'c1','foo',300), (4,'a1','foo',100), (5,'d1','toto',200), (6,'c1','tata',893), (7,'a1','titi',2380), (8,'b1','tete',12833), (9,'e1','yoyo',783493)") + + mcmp.Exec(`insert into bet_logs(id, merchant_game_id, bet_amount, game_id) values + (1, 1, 22.5, 40), (2, 1, 15.3, 40), + (3, 2, 22.5, 40), (4, 2, 15.3, 40), + (5, 3, 22.5, 40), (6, 3, 15.3, 40), + (7, 3, 22.5, 40), (8, 4, 15.3, 40) +`) + + mcmp.Exec("set @@sql_mode = ' '") + mcmp.Exec(`SELECT t1.name, SUM(b.bet_amount) AS bet_amount FROM bet_logs as b LEFT JOIN t1 ON b.merchant_game_id = t1.t1_id GROUP BY b.merchant_game_id`) + mcmp.Exec(`SELECT t1.name, CAST(SUM(b.bet_amount) AS DECIMAL(20,6)) AS bet_amount FROM bet_logs as b LEFT JOIN t1 ON b.merchant_game_id = t1.t1_id GROUP BY b.merchant_game_id`) } // TestGroupConcatAggregation tests the group_concat function with vitess doing the aggregation. @@ -501,6 +595,12 @@ func TestGroupConcatAggregation(t *testing.T) { compareRow(t, mQr, vtQr, nil, []int{0}) mQr, vtQr = mcmp.ExecNoCompare(`SELECT group_concat(value), t1.name FROM t1, t2 group by t1.name`) compareRow(t, mQr, vtQr, []int{1}, []int{0}) + if versionMet := utils.BinaryIsAtLeastAtVersion(19, "vtgate"); !versionMet { + // skipping + return + } + mQr, vtQr = mcmp.ExecNoCompare(`SELECT group_concat(name, value) FROM t1`) + compareRow(t, mQr, vtQr, nil, []int{0}) } func compareRow(t *testing.T, mRes *sqltypes.Result, vtRes *sqltypes.Result, grpCols []int, fCols []int) { @@ -540,6 +640,7 @@ func TestDistinctAggregation(t *testing.T) { tcases := []struct { query string expectedErr string + minVersion int }{{ query: `SELECT COUNT(DISTINCT value), SUM(DISTINCT shardkey) FROM t1`, expectedErr: "VT12001: unsupported: only one DISTINCT aggregation is allowed in a SELECT: sum(distinct shardkey) (errno 1235) (sqlstate 42000)", @@ -553,10 +654,15 @@ func TestDistinctAggregation(t *testing.T) { }, { query: `SELECT a.value, SUM(DISTINCT b.t1_id), min(DISTINCT a.t1_id) FROM t1 a, t1 b group by a.value`, }, { - query: `SELECT distinct count(*) from t1, (select distinct count(*) from t1) as t2`, + minVersion: 19, + query: `SELECT count(distinct name, shardkey) from t1`, }} for _, tc := range tcases { + if versionMet := utils.BinaryIsAtLeastAtVersion(tc.minVersion, "vtgate"); !versionMet { + // skipping + continue + } mcmp.Run(tc.query, func(mcmp *utils.MySQLCompare) { _, err := mcmp.ExecAllowError(tc.query) if tc.expectedErr == "" { diff --git a/go/test/endtoend/vtgate/queries/aggregation/distinct_test.go b/go/test/endtoend/vtgate/queries/aggregation/distinct_test.go index 0a06190923c..3ec27dae6a6 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/distinct_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/distinct_test.go @@ -46,9 +46,9 @@ func TestDistinctIt(t *testing.T) { mcmp.AssertMatchesNoOrder("select distinct id from aggr_test", `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(5)] [INT64(4)] [INT64(6)] [INT64(7)] [INT64(8)]]`) if utils.BinaryIsAtLeastAtVersion(17, "vtgate") { - mcmp.AssertMatches("select /*vt+ PLANNER=Gen4 */ distinct val1 from aggr_test order by val1 desc", `[[VARCHAR("e")] [VARCHAR("d")] [VARCHAR("c")] [VARCHAR("b")] [VARCHAR("a")]]`) - mcmp.AssertMatchesNoOrder("select /*vt+ PLANNER=Gen4 */ distinct val1, count(*) from aggr_test group by val1", `[[VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)] [VARCHAR("d") INT64(1)] [VARCHAR("e") INT64(2)]]`) - mcmp.AssertMatchesNoOrder("select /*vt+ PLANNER=Gen4 */ distinct val1+val2 from aggr_test", `[[NULL] [FLOAT64(1)] [FLOAT64(3)] [FLOAT64(4)]]`) - mcmp.AssertMatchesNoOrder("select /*vt+ PLANNER=Gen4 */ distinct count(*) from aggr_test group by val1", `[[INT64(2)] [INT64(1)]]`) + mcmp.AssertMatches("select distinct val1 from aggr_test order by val1 desc", `[[VARCHAR("e")] [VARCHAR("d")] [VARCHAR("c")] [VARCHAR("b")] [VARCHAR("a")]]`) + mcmp.AssertMatchesNoOrder("select distinct val1, count(*) from aggr_test group by val1", `[[VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)] [VARCHAR("d") INT64(1)] [VARCHAR("e") INT64(2)]]`) + mcmp.AssertMatchesNoOrder("select distinct val1+val2 from aggr_test", `[[NULL] [FLOAT64(1)] [FLOAT64(3)] [FLOAT64(4)]]`) + mcmp.AssertMatchesNoOrder("select distinct count(*) from aggr_test group by val1", `[[INT64(2)] [INT64(1)]]`) } } diff --git a/go/test/endtoend/vtgate/queries/aggregation/schema.sql b/go/test/endtoend/vtgate/queries/aggregation/schema.sql index e1489b4bd21..49956b98302 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/schema.sql +++ b/go/test/endtoend/vtgate/queries/aggregation/schema.sql @@ -96,4 +96,12 @@ CREATE TABLE dept ( loc VARCHAR(13), PRIMARY KEY (deptno) ) Engine = InnoDB - COLLATE = utf8mb4_general_ci; \ No newline at end of file + COLLATE = utf8mb4_general_ci; + +CREATE TABLE bet_logs ( + id bigint unsigned NOT NULL, + merchant_game_id bigint unsigned NOT NULL, + bet_amount DECIMAL(20, 8), + game_id bigint, + PRIMARY KEY (id) +) ENGINE InnoDB; diff --git a/go/test/endtoend/vtgate/queries/aggregation/vschema.json b/go/test/endtoend/vtgate/queries/aggregation/vschema.json index 050202aed81..6c3cddf4436 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/vschema.json +++ b/go/test/endtoend/vtgate/queries/aggregation/vschema.json @@ -147,6 +147,14 @@ "name": "hash" } ] + }, + "bet_logs": { + "column_vindexes": [ + { + "column": "id", + "name": "hash" + } + ] } } } \ No newline at end of file diff --git a/go/test/endtoend/vtgate/queries/derived/cte_test.go b/go/test/endtoend/vtgate/queries/derived/cte_test.go index 677a5dba653..61ddf5d6661 100644 --- a/go/test/endtoend/vtgate/queries/derived/cte_test.go +++ b/go/test/endtoend/vtgate/queries/derived/cte_test.go @@ -18,9 +18,12 @@ package misc import ( "testing" + + "vitess.io/vitess/go/test/endtoend/utils" ) func TestCTEWithOrderByLimit(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") mcmp, closer := start(t) defer closer() @@ -28,6 +31,7 @@ func TestCTEWithOrderByLimit(t *testing.T) { } func TestCTEAggregationOnRHS(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") mcmp, closer := start(t) defer closer() @@ -36,6 +40,7 @@ func TestCTEAggregationOnRHS(t *testing.T) { } func TestCTERemoveInnerOrderBy(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") mcmp, closer := start(t) defer closer() @@ -43,6 +48,7 @@ func TestCTERemoveInnerOrderBy(t *testing.T) { } func TestCTEWithHaving(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") mcmp, closer := start(t) defer closer() @@ -53,6 +59,7 @@ func TestCTEWithHaving(t *testing.T) { } func TestCTEColumns(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") mcmp, closer := start(t) defer closer() diff --git a/go/test/endtoend/vtgate/queries/derived/derived_test.go b/go/test/endtoend/vtgate/queries/derived/derived_test.go index c3360ee4135..80ae36633e1 100644 --- a/go/test/endtoend/vtgate/queries/derived/derived_test.go +++ b/go/test/endtoend/vtgate/queries/derived/derived_test.go @@ -52,7 +52,7 @@ func TestDerivedTableWithOrderByLimit(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("select /*vt+ PLANNER=Gen4 */ music.id from music join (select id,name from user order by id limit 2) as d on music.user_id = d.id") + mcmp.Exec("select music.id from music join (select id,name from user order by id limit 2) as d on music.user_id = d.id") } func TestDerivedAggregationOnRHS(t *testing.T) { @@ -60,14 +60,14 @@ func TestDerivedAggregationOnRHS(t *testing.T) { defer closer() mcmp.Exec("set sql_mode = ''") - mcmp.Exec("select /*vt+ PLANNER=Gen4 */ d.a from music join (select id, count(*) as a from user) as d on music.user_id = d.id group by 1") + mcmp.Exec("select d.a from music join (select id, count(*) as a from user) as d on music.user_id = d.id group by 1") } func TestDerivedRemoveInnerOrderBy(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("select /*vt+ PLANNER=Gen4 */ count(*) from (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) as toto") + mcmp.Exec("select count(*) from (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) as toto") } func TestDerivedTableWithHaving(t *testing.T) { @@ -76,7 +76,7 @@ func TestDerivedTableWithHaving(t *testing.T) { mcmp.Exec("set sql_mode = ''") // For the given query, we can get any id back, because we aren't grouping by it. - mcmp.AssertMatchesAnyNoCompare("select /*vt+ PLANNER=Gen4 */ * from (select id from user having count(*) >= 1) s", + mcmp.AssertMatchesAnyNoCompare("select * from (select id from user having count(*) >= 1) s", "[[INT64(1)]]", "[[INT64(2)]]", "[[INT64(3)]]", "[[INT64(4)]]", "[[INT64(5)]]") } @@ -84,6 +84,32 @@ func TestDerivedTableColumns(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.AssertMatches(`SELECT /*vt+ PLANNER=gen4 */ t.id FROM (SELECT id FROM user) AS t(id) ORDER BY t.id DESC`, + mcmp.AssertMatches(`SELECT t.id FROM (SELECT id FROM user) AS t(id) ORDER BY t.id DESC`, `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`) } + +// TestDerivedTablesWithLimit tests queries where we have to limit the right hand side of the join. +// We do this by not using the apply join we usually use, and instead use the hash join engine primitive +// These tests exercise these situations +func TestDerivedTablesWithLimit(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + // We need full type info before planning this, so we wait for the schema tracker + require.NoError(t, + utils.WaitForAuthoritative(t, keyspaceName, "user", clusterInstance.VtgateProcess.ReadVSchema)) + + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into user(id, name) values(6,'pikachu')") + + mcmp.AssertMatchesNoOrder( + `SELECT u.id, m.id FROM + (SELECT id, name FROM user LIMIT 10) AS u JOIN + (SELECT id, user_id FROM music LIMIT 10) as m on u.id = m.user_id`, + `[[INT64(1) INT64(1)] [INT64(5) INT64(2)] [INT64(1) INT64(3)] [INT64(2) INT64(4)] [INT64(3) INT64(5)] [INT64(5) INT64(7)] [INT64(4) INT64(6)]]`) + + mcmp.AssertMatchesNoOrder( + `SELECT u.id, m.id FROM user AS u LEFT JOIN + (SELECT id, user_id FROM music LIMIT 10) as m on u.id = m.user_id`, + `[[INT64(1) INT64(1)] [INT64(5) INT64(2)] [INT64(1) INT64(3)] [INT64(2) INT64(4)] [INT64(3) INT64(5)] [INT64(5) INT64(7)] [INT64(4) INT64(6)] [INT64(6) NULL]]`) +} diff --git a/go/test/endtoend/vtgate/queries/derived/schema.sql b/go/test/endtoend/vtgate/queries/derived/schema.sql index cf608028ed5..3cb8619d93b 100644 --- a/go/test/endtoend/vtgate/queries/derived/schema.sql +++ b/go/test/endtoend/vtgate/queries/derived/schema.sql @@ -1,13 +1,13 @@ create table user ( - id bigint, + id bigint, name varchar(255), primary key (id) ) Engine = InnoDB; create table music ( - id bigint, + id bigint, user_id bigint, primary key (id) ) Engine = InnoDB; diff --git a/go/test/endtoend/vtgate/queries/dml/dml_test.go b/go/test/endtoend/vtgate/queries/dml/dml_test.go index 52a64acaa56..561d73f44d5 100644 --- a/go/test/endtoend/vtgate/queries/dml/dml_test.go +++ b/go/test/endtoend/vtgate/queries/dml/dml_test.go @@ -20,6 +20,9 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/endtoend/utils" ) func TestMultiEqual(t *testing.T) { @@ -39,3 +42,254 @@ func TestMultiEqual(t *testing.T) { qr = mcmp.Exec("delete from user_tbl where (id, region_id) in ((1,1), (2,4))") assert.EqualValues(t, 1, qr.RowsAffected) } + +// TestMultiTableDelete executed multi-table delete queries +func TestMultiTableDelete(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + + mcmp, closer := start(t) + defer closer() + + // initial rows + mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)") + mcmp.Exec("insert into oevent_tbl(oid, ename) values (1,'a'), (2,'b'), (3,'a'), (4,'c')") + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("c")]]`) + + // multi table delete + qr := mcmp.Exec(`delete o from order_tbl o join oevent_tbl ev where o.oid = ev.oid and ev.ename = 'a'`) + assert.EqualValues(t, 2, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("c")]]`) + + qr = mcmp.Exec(`delete o from order_tbl o join oevent_tbl ev where o.cust_no = ev.oid`) + assert.EqualValues(t, 1, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("c")]]`) +} + +// TestDeleteWithLimit executed delete queries with limit +func TestDeleteWithLimit(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + + mcmp, closer := start(t) + defer closer() + + // initial rows + mcmp.Exec("insert into s_tbl(id, num) values (1,10), (2,10), (3,10), (4,20), (5,5), (6,15), (7,17), (8,80)") + mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)") + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(1) INT64(10)] [INT64(2) INT64(10)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // delete with limit + qr := mcmp.Exec(`delete from s_tbl order by num, id limit 3`) + require.EqualValues(t, 3, qr.RowsAffected) + + qr = mcmp.Exec(`delete from order_tbl where region_id = 1 limit 1`) + require.EqualValues(t, 1, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + // 2 rows matches but limit is 1, so any one of the row can remain in table. + mcmp.AssertMatchesAnyNoCompare(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // delete with limit + qr = mcmp.Exec(`delete from s_tbl where num < 20 limit 2`) + require.EqualValues(t, 2, qr.RowsAffected) + + qr = mcmp.Exec(`delete from order_tbl limit 5`) + require.EqualValues(t, 3, qr.RowsAffected) + + // check rows + // 3 rows matches `num < 20` but limit is 2 so any one of them can remain in the table. + mcmp.AssertMatchesAnyNoCompare(`select id, num from s_tbl order by id`, + `[[INT64(4) INT64(20)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`, + `[[INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(8) INT64(80)]]`, + `[[INT64(4) INT64(20)] [INT64(6) INT64(15)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[]`) + + // remove all rows + mcmp.Exec(`delete from s_tbl`) + mcmp.Exec(`delete from order_tbl limit 5`) + + // try with limit again on empty table. + qr = mcmp.Exec(`delete from s_tbl where num < 20 limit 2`) + require.EqualValues(t, 0, qr.RowsAffected) + + qr = mcmp.Exec(`delete from order_tbl limit 5`) + require.EqualValues(t, 0, qr.RowsAffected) + +} + +// TestUpdateWithLimit executed update queries with limit +func TestUpdateWithLimit(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + + mcmp, closer := start(t) + defer closer() + + // initial rows + mcmp.Exec("insert into s_tbl(id, num) values (1,10), (2,10), (3,10), (4,20), (5,5), (6,15), (7,17), (8,80)") + mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)") + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(1) INT64(10)] [INT64(2) INT64(10)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // update with limit + qr := mcmp.Exec(`update s_tbl set num = 12 order by num, id limit 3`) + require.EqualValues(t, 3, qr.RowsAffected) + + qr = mcmp.Exec(`update order_tbl set cust_no = 12 where region_id = 1 limit 1`) + require.EqualValues(t, 1, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(1) INT64(12)] [INT64(2) INT64(12)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(12)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + // 2 rows matches but limit is 1, so any one of the row can be modified in the table. + mcmp.AssertMatchesAnyNoCompare(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(12)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(12)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // update with limit + qr = mcmp.Exec(`update s_tbl set num = 32 where num > 17 limit 1`) + require.EqualValues(t, 1, qr.RowsAffected) + + qr = mcmp.Exec(`update order_tbl set cust_no = cust_no + 10 limit 5`) + require.EqualValues(t, 4, qr.RowsAffected) + + // check rows + // 2 rows matches `num > 17` but limit is 1 so any one of them will be updated. + mcmp.AssertMatchesAnyNoCompare(`select id, num from s_tbl order by id`, + `[[INT64(1) INT64(12)] [INT64(2) INT64(12)] [INT64(3) INT64(10)] [INT64(4) INT64(32)] [INT64(5) INT64(12)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`, + `[[INT64(1) INT64(12)] [INT64(2) INT64(12)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(12)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(32)]]`) + mcmp.AssertMatchesAnyNoCompare(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(22)] [INT64(1) INT64(2) INT64(12)] [INT64(2) INT64(3) INT64(15)] [INT64(2) INT64(4) INT64(65)]]`, + `[[INT64(1) INT64(1) INT64(14)] [INT64(1) INT64(2) INT64(22)] [INT64(2) INT64(3) INT64(15)] [INT64(2) INT64(4) INT64(65)]]`) + + // trying with zero limit. + qr = mcmp.Exec(`update s_tbl set num = 44 limit 0`) + require.EqualValues(t, 0, qr.RowsAffected) + + qr = mcmp.Exec(`update order_tbl set oid = 44 limit 0`) + require.EqualValues(t, 0, qr.RowsAffected) + + // trying with limit with no-matching row. + qr = mcmp.Exec(`update s_tbl set num = 44 where id > 100 limit 2`) + require.EqualValues(t, 0, qr.RowsAffected) + + qr = mcmp.Exec(`update order_tbl set oid = 44 where region_id > 100 limit 2`) + require.EqualValues(t, 0, qr.RowsAffected) + +} + +// TestMultiTableUpdate executed multi-table update queries +func TestMultiTableUpdate(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + + mcmp, closer := start(t) + defer closer() + + // initial rows + mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)") + mcmp.Exec("insert into oevent_tbl(oid, ename) values (1,'a'), (2,'b'), (3,'a'), (4,'c')") + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("c")]]`) + + // multi table delete + qr := mcmp.Exec(`update order_tbl o join oevent_tbl ev on o.oid = ev.oid set ev.ename = 'a' where ev.oid > 3`) + assert.EqualValues(t, 1, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("a")]]`) + + qr = mcmp.Exec(`update order_tbl o, oevent_tbl ev set ev.ename = 'xyz' where o.cust_no = ev.oid`) + assert.EqualValues(t, 2, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`, + `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("xyz")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("xyz")]]`) +} + +// TestDeleteWithSubquery executed delete queries with subqueries +func TestDeleteWithSubquery(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + + mcmp, closer := start(t) + defer closer() + + // initial rows + mcmp.Exec("insert into s_tbl(id, num) values (1,10), (2,10), (3,10), (4,20), (5,5), (6,15), (7,17), (8,80)") + mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)") + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(1) INT64(10)] [INT64(2) INT64(10)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // delete with subquery on s_tbl + qr := mcmp.Exec(`delete from s_tbl where id in (select oid from order_tbl)`) + require.EqualValues(t, 4, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`) + + // delete with subquery on order_tbl + qr = mcmp.Exec(`delete from order_tbl where cust_no > (select num from s_tbl where id = 7)`) + require.EqualValues(t, 1, qr.RowsAffected) + + // check rows + mcmp.AssertMatches(`select id, num from s_tbl order by id`, + `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)]]`) + + // delete with subquery from same table (fails on mysql) - subquery get's merged so fails for vitess + _, err := mcmp.ExecAllowAndCompareError(`delete from s_tbl where id in (select id from s_tbl)`) + require.ErrorContains(t, err, "You can't specify target table 's_tbl' for update in FROM clause (errno 1093) (sqlstate HY000)") + + // delete with subquery from same table (fails on mysql) - subquery not merged so passes for vitess + qr = utils.Exec(t, mcmp.VtConn, `delete from order_tbl where region_id in (select cust_no from order_tbl)`) + require.EqualValues(t, 1, qr.RowsAffected) + + // check rows + utils.AssertMatches(t, mcmp.VtConn, `select id, num from s_tbl order by id`, + `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`) + utils.AssertMatches(t, mcmp.VtConn, `select region_id, oid, cust_no from order_tbl order by oid`, + `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)]]`) +} diff --git a/go/test/endtoend/vtgate/queries/dml/insert_test.go b/go/test/endtoend/vtgate/queries/dml/insert_test.go index 867b3b46fc8..80d0602b898 100644 --- a/go/test/endtoend/vtgate/queries/dml/insert_test.go +++ b/go/test/endtoend/vtgate/queries/dml/insert_test.go @@ -21,7 +21,9 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -68,11 +70,20 @@ func TestFailureInsertSelect(t *testing.T) { // primary key same mcmp.AssertContainsError("insert into s_tbl(id, num) select id, num*20 from s_tbl where id = 1", `AlreadyExists desc = Duplicate entry '1' for key`) - // lookup key same (does not fail on MySQL as there is no lookup, and we have not put unique contrains on num column) - utils.AssertContainsError(t, mcmp.VtConn, "insert into s_tbl(id, num) select id*20, num from s_tbl where id = 1", `lookup.Create: Code: ALREADY_EXISTS`) - // mismatch column count - mcmp.AssertContainsError("insert into s_tbl(id, num) select 100,200,300", `column count does not match value count at row 1`) - mcmp.AssertContainsError("insert into s_tbl(id, num) select 100", `column count does not match value count at row 1`) + // lookup key same (does not fail on MySQL as there is no lookup, and we have not put unique constraint on num column) + vtgateVersion, err := cluster.GetMajorVersion("vtgate") + require.NoError(t, err) + if vtgateVersion >= 19 { + utils.AssertContainsError(t, mcmp.VtConn, "insert into s_tbl(id, num) select id*20, num from s_tbl where id = 1", `(errno 1062) (sqlstate 23000)`) + // mismatch column count + mcmp.AssertContainsError("insert into s_tbl(id, num) select 100,200,300", `column count does not match value count with the row`) + mcmp.AssertContainsError("insert into s_tbl(id, num) select 100", `column count does not match value count with the row`) + } else { + utils.AssertContainsError(t, mcmp.VtConn, "insert into s_tbl(id, num) select id*20, num from s_tbl where id = 1", `lookup.Create: Code: ALREADY_EXISTS`) + // mismatch column count + mcmp.AssertContainsError("insert into s_tbl(id, num) select 100,200,300", `column count does not match value count at row 1`) + mcmp.AssertContainsError("insert into s_tbl(id, num) select 100", `column count does not match value count at row 1`) + } }) } } @@ -298,7 +309,7 @@ func TestIgnoreInsertSelect(t *testing.T) { mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,100),(1,2,200),(1,3,300)") // inserting same rows, throws error. - mcmp.AssertContainsError("insert into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl", `lookup.Create: Code: ALREADY_EXISTS`) + mcmp.AssertContainsError("insert into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl", `(errno 1062) (sqlstate 23000)`) // inserting same rows with ignore qr := mcmp.Exec("insert ignore into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl") assert.EqualValues(t, 0, qr.RowsAffected) @@ -336,7 +347,7 @@ func TestIgnoreInsertSelectOlapMode(t *testing.T) { mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,100),(1,2,200),(1,3,300)") // inserting same rows, throws error. - mcmp.AssertContainsError("insert into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl", `lookup.Create: Code: ALREADY_EXISTS`) + mcmp.AssertContainsError("insert into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl", `(errno 1062) (sqlstate 23000)`) // inserting same rows with ignore qr := mcmp.Exec("insert ignore into order_tbl(region_id, oid, cust_no) select region_id, oid, cust_no from order_tbl") assert.EqualValues(t, 0, qr.RowsAffected) diff --git a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go index e33daf061bc..5ba9877bf5f 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go +++ b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go @@ -21,14 +21,12 @@ import ( "fmt" "testing" - "vitess.io/vitess/go/test/endtoend/utils" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/utils" ) func start(t *testing.T) (utils.MySQLCompare, func()) { @@ -223,6 +221,7 @@ func TestInfrSchemaAndUnionAll(t *testing.T) { } func TestTypeORMQuery(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") // This test checks that we can run queries similar to the ones that the TypeORM framework uses require.NoError(t, @@ -231,7 +230,7 @@ func TestTypeORMQuery(t *testing.T) { mcmp, closer := start(t) defer closer() - query := `SELECT kcu.TABLE_NAME, kcu.COLUMN_NAME, cols.DATA_TYPE + utils.AssertMatchesAny(t, mcmp.VtConn, `SELECT kcu.TABLE_NAME, kcu.COLUMN_NAME, cols.DATA_TYPE FROM (SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu WHERE kcu.TABLE_SCHEMA = 'ks' @@ -251,9 +250,51 @@ FROM (SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME WHERE cols.TABLE_SCHEMA = 'ks' AND cols.TABLE_NAME = 't7_xxhash') cols ON kcu.TABLE_SCHEMA = cols.TABLE_SCHEMA AND kcu.TABLE_NAME = cols.TABLE_NAME AND - kcu.COLUMN_NAME = cols.COLUMN_NAME` - utils.AssertMatchesAny(t, mcmp.VtConn, query, + kcu.COLUMN_NAME = cols.COLUMN_NAME`, `[[VARBINARY("t1") VARCHAR("id1") BLOB("bigint")] [VARBINARY("t7_xxhash") VARCHAR("uid") BLOB("varchar")]]`, `[[VARCHAR("t1") VARCHAR("id1") BLOB("bigint")] [VARCHAR("t7_xxhash") VARCHAR("uid") BLOB("varchar")]]`, ) + + utils.AssertMatchesAny(t, mcmp.VtConn, ` +SELECT * +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = 'ks' AND TABLE_NAME = 't1' +UNION +SELECT * +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = 'ks' AND TABLE_NAME = 't2'; +`, + `[[VARBINARY("def") VARBINARY("vt_ks") VARBINARY("t1") VARCHAR("id1") UINT32(1) NULL VARCHAR("NO") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("PRI") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARBINARY("def") VARBINARY("vt_ks") VARBINARY("t1") VARCHAR("id2") UINT32(2) NULL VARCHAR("YES") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARBINARY("def") VARBINARY("vt_ks") VARBINARY("t2") VARCHAR("id") UINT32(1) NULL VARCHAR("NO") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("PRI") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARBINARY("def") VARBINARY("vt_ks") VARBINARY("t2") VARCHAR("value") UINT32(2) NULL VARCHAR("YES") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL]]`, + `[[VARCHAR("def") VARCHAR("vt_ks") VARCHAR("t1") VARCHAR("id1") UINT32(1) NULL VARCHAR("NO") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("PRI") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARCHAR("def") VARCHAR("vt_ks") VARCHAR("t1") VARCHAR("id2") UINT32(2) NULL VARCHAR("YES") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARCHAR("def") VARCHAR("vt_ks") VARCHAR("t2") VARCHAR("id") UINT32(1) NULL VARCHAR("NO") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("PRI") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL] [VARCHAR("def") VARCHAR("vt_ks") VARCHAR("t2") VARCHAR("value") UINT32(2) NULL VARCHAR("YES") BLOB("bigint") NULL NULL UINT64(19) UINT64(0) NULL NULL NULL BLOB("bigint") VARBINARY("") VARCHAR("") VARCHAR("select,insert,update,references") BLOB("") BLOB("") NULL]]`, + ) +} + +func TestJoinWithSingleShardQueryOnRHS(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + // This test checks that we can run queries like this, where the RHS is a single shard query + mcmp, closer := start(t) + defer closer() + + query := `SELECT + c.column_name as column_name, + c.data_type as data_type, + c.table_name as table_name, + c.table_schema as table_schema +FROM + information_schema.columns c + JOIN ( + SELECT + table_name + FROM + information_schema.tables + WHERE + table_schema != 'information_schema' + LIMIT + 1 + ) AS tables ON tables.table_name = c.table_name +ORDER BY + c.table_name` + + res := utils.Exec(t, mcmp.VtConn, query) + require.NotEmpty(t, res.Rows) } diff --git a/go/test/endtoend/vtgate/queries/informationschema/schema.sql b/go/test/endtoend/vtgate/queries/informationschema/schema.sql index 1fc9949406b..ad324cffd1a 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/schema.sql +++ b/go/test/endtoend/vtgate/queries/informationschema/schema.sql @@ -5,6 +5,13 @@ create table t1 primary key (id1) ) Engine = InnoDB; +create table t2 +( + id bigint, + value bigint, + primary key (id) +) Engine = InnoDB; + create table t1_id2_idx ( id2 bigint, diff --git a/go/test/endtoend/vtgate/queries/informationschema/vschema.json b/go/test/endtoend/vtgate/queries/informationschema/vschema.json index eec57e9970d..4fa5af75f49 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/vschema.json +++ b/go/test/endtoend/vtgate/queries/informationschema/vschema.json @@ -4,7 +4,7 @@ "hash": { "type": "hash" }, - "unicode_loose_xxhash" : { + "unicode_loose_xxhash": { "type": "unicode_loose_xxhash" }, "t1_id2_idx": { @@ -40,6 +40,14 @@ } ] }, + "t2": { + "column_vindexes": [ + { + "column": "id", + "name": "hash" + } + ] + }, "t3_id7_idx": { "column_vindexes": [ { diff --git a/go/test/endtoend/vtgate/queries/kill/main_test.go b/go/test/endtoend/vtgate/queries/kill/main_test.go index 836603c91ee..cc74be24336 100644 --- a/go/test/endtoend/vtgate/queries/kill/main_test.go +++ b/go/test/endtoend/vtgate/queries/kill/main_test.go @@ -134,7 +134,8 @@ func dropData(t *testing.T) { defer conn.Close() utils.Exec(t, conn, "drop table if exists test") - utils.Exec(t, conn, schema) + utils.Exec(t, conn, "drop table if exists test_idx") + utils.ExecMulti(t, conn, schema) } func getRandomString(size int) string { diff --git a/go/test/endtoend/vtgate/queries/misc/misc_test.go b/go/test/endtoend/vtgate/queries/misc/misc_test.go index 0fdee1b88a1..0b7671c753d 100644 --- a/go/test/endtoend/vtgate/queries/misc/misc_test.go +++ b/go/test/endtoend/vtgate/queries/misc/misc_test.go @@ -36,7 +36,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { require.NoError(t, err) deleteAll := func() { - tables := []string{"t1", "uks.unsharded"} + tables := []string{"t1", "tbl", "unq_idx", "nonunq_idx", "uks.unsharded"} for _, table := range tables { _, _ = mcmp.ExecAndIgnore("delete from " + table) } @@ -59,8 +59,25 @@ func TestBitVals(t *testing.T) { mcmp.AssertMatches(`select b'1001', 0x9, B'010011011010'`, `[[VARBINARY("\t") VARBINARY("\t") VARBINARY("\x04\xda")]]`) mcmp.AssertMatches(`select b'1001', 0x9, B'010011011010' from t1`, `[[VARBINARY("\t") VARBINARY("\t") VARBINARY("\x04\xda")]]`) - mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010'`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[INT64(10) UINT64(11) INT64(1245)]]`) - mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010' from t1`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[INT64(10) UINT64(11) INT64(1245)]]`) + vtgateVersion, err := cluster.GetMajorVersion("vtgate") + require.NoError(t, err) + if vtgateVersion >= 19 { + mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010'`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[INT64(10) UINT64(11) INT64(1245)]]`) + mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010' from t1`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[INT64(10) UINT64(11) INT64(1245)]]`) + } else { + mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010'`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[UINT64(10) UINT64(11) UINT64(1245)]]`) + mcmp.AssertMatchesNoCompare(`select 1 + b'1001', 2 + 0x9, 3 + B'010011011010' from t1`, `[[INT64(10) UINT64(11) INT64(1245)]]`, `[[UINT64(10) UINT64(11) UINT64(1245)]]`) + } +} + +// TestTimeFunctionWithPrecision tests that inserting data with NOW(1) works as intended. +func TestTimeFunctionWithPrecision(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into t1(id1, id2) values (1, NOW(1))") + mcmp.Exec("insert into t1(id1, id2) values (2, NOW(2))") + mcmp.Exec("insert into t1(id1, id2) values (3, NOW())") } func TestHexVals(t *testing.T) { @@ -120,6 +137,41 @@ func TestCast(t *testing.T) { mcmp.AssertMatches("select cast('3.2' as unsigned)", `[[UINT64(3)]]`) } +// TestVindexHints tests that vindex hints work as intended. +func TestVindexHints(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into tbl(id, unq_col, nonunq_col) values (1,0,10), (2,10,10), (3,4,20), (4,30,20), (5,40,10)") + mcmp.AssertMatches("select id, unq_col, nonunq_col from tbl where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", "[[INT64(2) INT64(10) INT64(10)]]") + + // Verify that without any vindex hints, the query plan uses a hash vindex. + res, err := mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false) + require.NoError(t, err) + require.Contains(t, fmt.Sprintf("%v", res.Rows), "hash") + + // Now we make the query explicitly use the unique lookup vindex. + // We make sure the query still works. + res, err = mcmp.VtConn.ExecuteFetch("select id, unq_col, nonunq_col from tbl USE VINDEX (unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false) + require.NoError(t, err) + require.EqualValues(t, fmt.Sprintf("%v", res.Rows), "[[INT64(2) INT64(10) INT64(10)]]") + // Verify that we are using the unq_vdx, that we requested explicitly. + res, err = mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl USE VINDEX (unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false) + require.NoError(t, err) + require.Contains(t, fmt.Sprintf("%v", res.Rows), "unq_vdx") + + // Now we make the query explicitly refuse two of the three vindexes. + // We make sure the query still works. + res, err = mcmp.VtConn.ExecuteFetch("select id, unq_col, nonunq_col from tbl IGNORE VINDEX (hash, unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false) + require.NoError(t, err) + require.EqualValues(t, fmt.Sprintf("%v", res.Rows), "[[INT64(2) INT64(10) INT64(10)]]") + // Verify that we are using the nonunq_vdx, which is the only one left to be used. + res, err = mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl IGNORE VINDEX (hash, unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false) + require.NoError(t, err) + require.Contains(t, fmt.Sprintf("%v", res.Rows), "nonunq_vdx") +} + func TestOuterJoinWithPredicate(t *testing.T) { mcmp, closer := start(t) defer closer() @@ -267,3 +319,36 @@ func TestAnalyze(t *testing.T) { }) } } + +// TestTransactionModeVar executes SELECT on `transaction_mode` variable +func TestTransactionModeVar(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + + mcmp, closer := start(t) + defer closer() + + tcases := []struct { + setStmt string + expRes string + }{{ + expRes: `[[VARCHAR("MULTI")]]`, + }, { + setStmt: `set transaction_mode = single`, + expRes: `[[VARCHAR("SINGLE")]]`, + }, { + setStmt: `set transaction_mode = multi`, + expRes: `[[VARCHAR("MULTI")]]`, + }, { + setStmt: `set transaction_mode = twopc`, + expRes: `[[VARCHAR("TWOPC")]]`, + }} + + for _, tcase := range tcases { + t.Run(tcase.setStmt, func(t *testing.T) { + if tcase.setStmt != "" { + utils.Exec(t, mcmp.VtConn, tcase.setStmt) + } + utils.AssertMatches(t, mcmp.VtConn, "select @@transaction_mode", tcase.expRes) + }) + } +} diff --git a/go/test/endtoend/vtgate/queries/misc/schema.sql b/go/test/endtoend/vtgate/queries/misc/schema.sql index ceac0c07e6d..6fd57b9183d 100644 --- a/go/test/endtoend/vtgate/queries/misc/schema.sql +++ b/go/test/endtoend/vtgate/queries/misc/schema.sql @@ -2,4 +2,28 @@ create table if not exists t1( id1 bigint, id2 bigint, primary key(id1) -) Engine=InnoDB; \ No newline at end of file +) Engine=InnoDB; + +create table unq_idx +( + unq_col bigint, + keyspace_id varbinary(20), + primary key (unq_col) +) Engine = InnoDB; + +create table nonunq_idx +( + nonunq_col bigint, + id bigint, + keyspace_id varbinary(20), + primary key (nonunq_col, id) +) Engine = InnoDB; + +create table tbl +( + id bigint, + unq_col bigint, + nonunq_col bigint, + primary key (id), + unique (unq_col) +) Engine = InnoDB; diff --git a/go/test/endtoend/vtgate/queries/misc/vschema.json b/go/test/endtoend/vtgate/queries/misc/vschema.json index 60aa2bc9c07..f56b1fc1b36 100644 --- a/go/test/endtoend/vtgate/queries/misc/vschema.json +++ b/go/test/endtoend/vtgate/queries/misc/vschema.json @@ -3,6 +3,26 @@ "vindexes": { "hash": { "type": "hash" + }, + "unq_vdx": { + "type": "consistent_lookup_unique", + "params": { + "table": "unq_idx", + "from": "unq_col", + "to": "keyspace_id", + "ignore_nulls": "true" + }, + "owner": "tbl" + }, + "nonunq_vdx": { + "type": "consistent_lookup", + "params": { + "table": "nonunq_idx", + "from": "nonunq_col,id", + "to": "keyspace_id", + "ignore_nulls": "true" + }, + "owner": "tbl" } }, "tables": { @@ -13,6 +33,41 @@ "name": "hash" } ] + }, + "tbl": { + "column_vindexes": [ + { + "column": "id", + "name": "hash" + }, + { + "column": "unq_col", + "name": "unq_vdx" + }, + { + "columns": [ + "nonunq_col", + "id" + ], + "name": "nonunq_vdx" + } + ] + }, + "unq_idx": { + "column_vindexes": [ + { + "column": "unq_col", + "name": "hash" + } + ] + }, + "nonunq_idx": { + "column_vindexes": [ + { + "column": "nonunq_col", + "name": "hash" + } + ] } } } \ No newline at end of file diff --git a/go/test/endtoend/vtgate/queries/normalize/normalize_test.go b/go/test/endtoend/vtgate/queries/normalize/normalize_test.go index b6495443a8e..735a26fc00c 100644 --- a/go/test/endtoend/vtgate/queries/normalize/normalize_test.go +++ b/go/test/endtoend/vtgate/queries/normalize/normalize_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/utils" "vitess.io/vitess/go/mysql" @@ -40,6 +41,11 @@ func TestNormalizeAllFields(t *testing.T) { insertQuery := `insert into t1 values (1, "chars", "variable chars", x'73757265', 0x676F, 0.33, 9.99, 1, "1976-06-08", "small", "b", "{\"key\":\"value\"}", point(1,5), b'011', 0b0101)` normalizedInsertQuery := `insert into t1 values (:vtg1 /* INT64 */, :vtg2 /* VARCHAR */, :vtg3 /* VARCHAR */, :vtg4 /* HEXVAL */, :vtg5 /* HEXNUM */, :vtg6 /* DECIMAL */, :vtg7 /* DECIMAL */, :vtg8 /* INT64 */, :vtg9 /* VARCHAR */, :vtg10 /* VARCHAR */, :vtg11 /* VARCHAR */, :vtg12 /* VARCHAR */, point(:vtg13 /* INT64 */, :vtg14 /* INT64 */), :vtg15 /* BITNUM */, :vtg16 /* BITNUM */)` + vtgateVersion, err := cluster.GetMajorVersion("vtgate") + require.NoError(t, err) + if vtgateVersion < 19 { + normalizedInsertQuery = `insert into t1 values (:vtg1 /* INT64 */, :vtg2 /* VARCHAR */, :vtg3 /* VARCHAR */, :vtg4 /* HEXVAL */, :vtg5 /* HEXNUM */, :vtg6 /* DECIMAL */, :vtg7 /* DECIMAL */, :vtg8 /* INT64 */, :vtg9 /* VARCHAR */, :vtg10 /* VARCHAR */, :vtg11 /* VARCHAR */, :vtg12 /* VARCHAR */, point(:vtg13 /* INT64 */, :vtg14 /* INT64 */), :vtg15 /* HEXNUM */, :vtg16 /* HEXNUM */)` + } selectQuery := "select * from t1" utils.Exec(t, conn, insertQuery) qr := utils.Exec(t, conn, selectQuery) diff --git a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go index dd48a09fec7..43f800ee24c 100644 --- a/go/test/endtoend/vtgate/queries/orderby/orderby_test.go +++ b/go/test/endtoend/vtgate/queries/orderby/orderby_test.go @@ -68,7 +68,7 @@ func TestOrderBy(t *testing.T) { mcmp.AssertMatches("select id1, id2 from t4 order by id1 desc", `[[INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(5) VARCHAR("test")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(2) VARCHAR("Abc")] [INT64(1) VARCHAR("a")]]`) // test ordering of complex column if utils.BinaryIsAtLeastAtVersion(17, "vtgate") { - mcmp.AssertMatches("select /*vt+ PLANNER=Gen4 */ id1, id2 from t4 order by reverse(id2) desc", `[[INT64(5) VARCHAR("test")] [INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(2) VARCHAR("Abc")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(1) VARCHAR("a")]]`) + mcmp.AssertMatches("select id1, id2 from t4 order by reverse(id2) desc", `[[INT64(5) VARCHAR("test")] [INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(2) VARCHAR("Abc")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(1) VARCHAR("a")]]`) } defer func() { @@ -80,6 +80,6 @@ func TestOrderBy(t *testing.T) { mcmp.AssertMatches("select id1, id2 from t4 order by id2 desc", `[[INT64(5) VARCHAR("test")] [INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(2) VARCHAR("Abc")] [INT64(1) VARCHAR("a")]]`) mcmp.AssertMatches("select id1, id2 from t4 order by id1 desc", `[[INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(5) VARCHAR("test")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(2) VARCHAR("Abc")] [INT64(1) VARCHAR("a")]]`) if utils.BinaryIsAtLeastAtVersion(17, "vtgate") { - mcmp.AssertMatches("select /*vt+ PLANNER=Gen4 */ id1, id2 from t4 order by reverse(id2) desc", `[[INT64(5) VARCHAR("test")] [INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(2) VARCHAR("Abc")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(1) VARCHAR("a")]]`) + mcmp.AssertMatches("select id1, id2 from t4 order by reverse(id2) desc", `[[INT64(5) VARCHAR("test")] [INT64(8) VARCHAR("F")] [INT64(7) VARCHAR("e")] [INT64(6) VARCHAR("d")] [INT64(2) VARCHAR("Abc")] [INT64(4) VARCHAR("c")] [INT64(3) VARCHAR("b")] [INT64(1) VARCHAR("a")]]`) } } diff --git a/go/test/endtoend/vtgate/queries/random/simplifier_test.go b/go/test/endtoend/vtgate/queries/random/simplifier_test.go index 478ee355d34..3f3f78017cd 100644 --- a/go/test/endtoend/vtgate/queries/random/simplifier_test.go +++ b/go/test/endtoend/vtgate/queries/random/simplifier_test.go @@ -22,6 +22,7 @@ import ( "testing" "vitess.io/vitess/go/test/vschemawrapper" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/require" @@ -36,22 +37,22 @@ func TestSimplifyResultsMismatchedQuery(t *testing.T) { t.Skip("Skip CI") var queries []string - queries = append(queries, "select /*vt+ PLANNER=Gen4 */ (68 - -16) / case false when -45 then 3 when 28 then -43 else -62 end as crandom0 from dept as tbl0, (select /*vt+ PLANNER=Gen4 */ distinct not not false and count(*) from emp as tbl0, emp as tbl1 where tbl1.ename) as tbl1 limit 1", - "select /*vt+ PLANNER=Gen4 */ distinct case true when 'burro' then 'trout' else 'elf' end < case count(distinct true) when 'bobcat' then 'turkey' else 'penguin' end from dept as tbl0, emp as tbl1 where 'spider'", - "select /*vt+ PLANNER=Gen4 */ distinct sum(distinct tbl1.deptno) from dept as tbl0, emp as tbl1 where tbl0.deptno and tbl1.comm in (12, tbl0.deptno, case false when 67 then -17 when -78 then -35 end, -76 >> -68)", - "select /*vt+ PLANNER=Gen4 */ count(*) + 1 from emp as tbl0 order by count(*) desc", - "select /*vt+ PLANNER=Gen4 */ count(2 >> tbl2.mgr), sum(distinct tbl2.empno <=> 15) from emp as tbl0 left join emp as tbl2 on -32", - "select /*vt+ PLANNER=Gen4 */ sum(case false when true then tbl1.deptno else -154 / 132 end) as caggr1 from emp as tbl0, dept as tbl1", - "select /*vt+ PLANNER=Gen4 */ tbl1.dname as cgroup0, tbl1.dname as cgroup1 from dept as tbl0, dept as tbl1 group by tbl1.dname, tbl1.deptno order by tbl1.deptno desc", - "select /*vt+ PLANNER=Gen4 */ tbl0.ename as cgroup1 from emp as tbl0 group by tbl0.job, tbl0.ename having sum(tbl0.mgr) = sum(tbl0.mgr) order by tbl0.job desc, tbl0.ename asc limit 8", - "select /*vt+ PLANNER=Gen4 */ distinct count(*) as caggr1 from dept as tbl0, emp as tbl1 group by tbl1.sal having max(tbl1.comm) != true", - "select /*vt+ PLANNER=Gen4 */ distinct sum(tbl1.loc) as caggr0 from dept as tbl0, dept as tbl1 group by tbl1.deptno having max(tbl1.dname) <= 1", - "select /*vt+ PLANNER=Gen4 */ min(tbl0.deptno) as caggr0 from dept as tbl0, emp as tbl1 where case when false then tbl0.dname end group by tbl1.comm", - "select /*vt+ PLANNER=Gen4 */ count(*) as caggr0, 1 as crandom0 from dept as tbl0, emp as tbl1 where 1 = 0", - "select /*vt+ PLANNER=Gen4 */ count(*) as caggr0, 1 as crandom0 from dept as tbl0, emp as tbl1 where 'octopus'", - "select /*vt+ PLANNER=Gen4 */ distinct 'octopus' as crandom0 from dept as tbl0, emp as tbl1 where tbl0.deptno = tbl1.empno having count(*) = count(*)", - "select /*vt+ PLANNER=Gen4 */ max(tbl0.deptno) from dept as tbl0 right join emp as tbl1 on tbl0.deptno = tbl1.empno and tbl0.deptno = tbl1.deptno group by tbl0.deptno", - "select /*vt+ PLANNER=Gen4 */ count(tbl1.comm) from emp as tbl1 right join emp as tbl2 on tbl1.mgr = tbl2.sal") + queries = append(queries, "select (68 - -16) / case false when -45 then 3 when 28 then -43 else -62 end as crandom0 from dept as tbl0, (select distinct not not false and count(*) from emp as tbl0, emp as tbl1 where tbl1.ename) as tbl1 limit 1", + "select distinct case true when 'burro' then 'trout' else 'elf' end < case count(distinct true) when 'bobcat' then 'turkey' else 'penguin' end from dept as tbl0, emp as tbl1 where 'spider'", + "select distinct sum(distinct tbl1.deptno) from dept as tbl0, emp as tbl1 where tbl0.deptno and tbl1.comm in (12, tbl0.deptno, case false when 67 then -17 when -78 then -35 end, -76 >> -68)", + "select count(*) + 1 from emp as tbl0 order by count(*) desc", + "select count(2 >> tbl2.mgr), sum(distinct tbl2.empno <=> 15) from emp as tbl0 left join emp as tbl2 on -32", + "select sum(case false when true then tbl1.deptno else -154 / 132 end) as caggr1 from emp as tbl0, dept as tbl1", + "select tbl1.dname as cgroup0, tbl1.dname as cgroup1 from dept as tbl0, dept as tbl1 group by tbl1.dname, tbl1.deptno order by tbl1.deptno desc", + "select tbl0.ename as cgroup1 from emp as tbl0 group by tbl0.job, tbl0.ename having sum(tbl0.mgr) = sum(tbl0.mgr) order by tbl0.job desc, tbl0.ename asc limit 8", + "select distinct count(*) as caggr1 from dept as tbl0, emp as tbl1 group by tbl1.sal having max(tbl1.comm) != true", + "select distinct sum(tbl1.loc) as caggr0 from dept as tbl0, dept as tbl1 group by tbl1.deptno having max(tbl1.dname) <= 1", + "select min(tbl0.deptno) as caggr0 from dept as tbl0, emp as tbl1 where case when false then tbl0.dname end group by tbl1.comm", + "select count(*) as caggr0, 1 as crandom0 from dept as tbl0, emp as tbl1 where 1 = 0", + "select count(*) as caggr0, 1 as crandom0 from dept as tbl0, emp as tbl1 where 'octopus'", + "select distinct 'octopus' as crandom0 from dept as tbl0, emp as tbl1 where tbl0.deptno = tbl1.empno having count(*) = count(*)", + "select max(tbl0.deptno) from dept as tbl0 right join emp as tbl1 on tbl0.deptno = tbl1.empno and tbl0.deptno = tbl1.deptno group by tbl0.deptno", + "select count(tbl1.comm) from emp as tbl1 right join emp as tbl2 on tbl1.mgr = tbl2.sal") for _, query := range queries { var simplified string @@ -88,13 +89,14 @@ func simplifyResultsMismatchedQuery(t *testing.T, query string) string { formal, err := vindexes.LoadFormal("svschema.json") require.NoError(t, err) - vSchema := vindexes.BuildVSchema(formal) + vSchema := vindexes.BuildVSchema(formal, sqlparser.NewTestParser()) vSchemaWrapper := &vschemawrapper.VSchemaWrapper{ V: vSchema, Version: planbuilder.Gen4, + Env: vtenv.NewTestEnv(), } - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) simplified := simplifier.SimplifyStatement( diff --git a/go/test/endtoend/vtgate/queries/reference/reference_test.go b/go/test/endtoend/vtgate/queries/reference/reference_test.go index 75efc840880..9c2508a889f 100644 --- a/go/test/endtoend/vtgate/queries/reference/reference_test.go +++ b/go/test/endtoend/vtgate/queries/reference/reference_test.go @@ -114,7 +114,7 @@ func TestReferenceRouting(t *testing.T) { utils.AssertMatches( t, conn, - `SELECT /*vt+ PLANNER=gen4 */ COUNT(zd.id) + `SELECT COUNT(zd.id) FROM delivery_failure df JOIN zip_detail zd ON zd.id = df.zip_detail_id WHERE zd.id = 3`, `[[INT64(0)]]`, diff --git a/go/test/endtoend/vtgate/queries/subquery/subquery_test.go b/go/test/endtoend/vtgate/queries/subquery/subquery_test.go index 01cc7b2ee54..59dc42de060 100644 --- a/go/test/endtoend/vtgate/queries/subquery/subquery_test.go +++ b/go/test/endtoend/vtgate/queries/subquery/subquery_test.go @@ -19,12 +19,11 @@ package subquery import ( "testing" - "vitess.io/vitess/go/test/endtoend/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/utils" ) func start(t *testing.T) (utils.MySQLCompare, func()) { @@ -58,6 +57,26 @@ func TestSubqueriesHasValues(t *testing.T) { mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id1 FROM t1 WHERE id1 > 10) ORDER BY id2`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)] [INT64(5)] [INT64(6)]]`) } +func TestNotINQueries(t *testing.T) { + // Tests NOT IN where the RHS contains all rows, some rows and no rows + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into t1(id1, id2) values (0,1),(1,2),(2,3),(3,4),(4,5),(5,6)") + // no matching rows + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id1 FROM t1 WHERE id1 > 10) ORDER BY id2`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)] [INT64(5)] [INT64(6)]]`) + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id2 FROM t1 WHERE id2 > 10) ORDER BY id2`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)] [INT64(5)] [INT64(6)]]`) + + // some matching rows + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id1 FROM t1 WHERE id1 > 3) ORDER BY id2`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id2 FROM t1 WHERE id2 > 3) ORDER BY id2`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + + // all rows matching + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id1 FROM t1) ORDER BY id2`, `[]`) + mcmp.AssertMatches(`SELECT id2 FROM t1 WHERE id1 NOT IN (SELECT id2 FROM t1) ORDER BY id2`, `[[INT64(1)]]`) + +} + // Test only supported in >= v16.0.0 func TestSubqueriesExists(t *testing.T) { utils.SkipIfBinaryIsBelowVersion(t, 16, "vtgate") @@ -97,7 +116,6 @@ func TestSubqueryInINClause(t *testing.T) { } func TestSubqueryInUpdate(t *testing.T) { - utils.SkipIfBinaryIsBelowVersion(t, 14, "vtgate") mcmp, closer := start(t) defer closer() @@ -106,13 +124,12 @@ func TestSubqueryInUpdate(t *testing.T) { utils.Exec(t, conn, `insert into t1(id1, id2) values (1, 10), (2, 20), (3, 30), (4, 40), (5, 50)`) utils.Exec(t, conn, `insert into t2(id3, id4) values (1, 3), (2, 4)`) utils.AssertMatches(t, conn, `SELECT id2, keyspace_id FROM t1_id2_idx WHERE id2 IN (2,10)`, `[[INT64(10) VARBINARY("\x16k@\xb4J\xbaK\xd6")]]`) - utils.Exec(t, conn, `update /*vt+ PLANNER=gen4 */ t1 set id2 = (select count(*) from t2) where id1 = 1`) + utils.Exec(t, conn, `update t1 set id2 = (select count(*) from t2) where id1 = 1`) utils.AssertMatches(t, conn, `SELECT id2 FROM t1 WHERE id1 = 1`, `[[INT64(2)]]`) utils.AssertMatches(t, conn, `SELECT id2, keyspace_id FROM t1_id2_idx WHERE id2 IN (2,10)`, `[[INT64(2) VARBINARY("\x16k@\xb4J\xbaK\xd6")]]`) } func TestSubqueryInReference(t *testing.T) { - utils.SkipIfBinaryIsBelowVersion(t, 14, "vtgate") mcmp, closer := start(t) defer closer() @@ -141,3 +158,33 @@ func TestSubqueryInReference(t *testing.T) { mcmp.AssertMatches(`select (select id1 from t1 where id2 = 30)`, `[[INT64(3)]]`) mcmp.AssertMatches(`select (select id1 from t1 where id2 = 9)`, `[[NULL]]`) } + +// TestSubqueryInAggregation validates that subquery work inside aggregation functions. +func TestSubqueryInAggregation(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vtgate") + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into t1(id1, id2) values(0,0),(1,1)") + mcmp.Exec("insert into t2(id3, id4) values(1,2),(5,7)") + mcmp.Exec(`SELECT max((select min(id2) from t1)) FROM t2`) + mcmp.Exec(`SELECT max((select group_concat(id1, id2) from t1 where id1 = 1)) FROM t1 where id1 = 1`) + mcmp.Exec(`SELECT max((select min(id2) from t1 where id2 = 1)) FROM dual`) + mcmp.Exec(`SELECT max((select min(id2) from t1)) FROM t2 where id4 = 7`) + + // This fails as the planner adds `weight_string` method which make the query fail on MySQL. + // mcmp.Exec(`SELECT max((select min(id2) from t1 where t1.id1 = t.id1)) FROM t1 t`) +} + +// TestSubqueryInDerivedTable tests that subqueries and derived tables +// are handled correctly when there are joins inside the derived table +func TestSubqueryInDerivedTable(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate") + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("INSERT INTO t1 (id1, id2) VALUES (1, 100), (2, 200), (3, 300), (4, 400), (5, 500);") + mcmp.Exec("INSERT INTO t2 (id3, id4) VALUES (10, 1), (20, 2), (30, 3), (40, 4), (50, 99)") + mcmp.Exec(`select t.a from (select t1.id2, t2.id3, (select id2 from t1 order by id2 limit 1) as a from t1 join t2 on t1.id1 = t2.id4) t`) + mcmp.Exec(`SELECT COUNT(*) FROM (SELECT DISTINCT t1.id1 FROM t1 JOIN t2 ON t1.id1 = t2.id4) dt`) +} diff --git a/go/test/endtoend/vtgate/queries/timeout/main_test.go b/go/test/endtoend/vtgate/queries/timeout/main_test.go index d71dc55ef46..c265e824e88 100644 --- a/go/test/endtoend/vtgate/queries/timeout/main_test.go +++ b/go/test/endtoend/vtgate/queries/timeout/main_test.go @@ -63,8 +63,8 @@ func TestMain(m *testing.M) { clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-config-max-result-size", "1000000", - "--queryserver-config-query-timeout", "200", - "--queryserver-config-query-pool-timeout", "200") + "--queryserver-config-query-timeout", "200s", + "--queryserver-config-query-pool-timeout", "200s") // Start Unsharded keyspace ukeyspace := &cluster.Keyspace{ Name: uks, diff --git a/go/test/endtoend/vtgate/queries/union/union_test.go b/go/test/endtoend/vtgate/queries/union/union_test.go index d382d039f02..898f8b1d659 100644 --- a/go/test/endtoend/vtgate/queries/union/union_test.go +++ b/go/test/endtoend/vtgate/queries/union/union_test.go @@ -73,6 +73,17 @@ func TestUnionDistinct(t *testing.T) { t.Skip() mcmp.AssertMatches("select 1 from dual where 1 IN (select 1 as col union select 2)", "[[INT64(1)]]") }) + if utils.BinaryIsAtLeastAtVersion(19, "vtgate") { + mcmp.AssertMatches(`SELECT 1 from t1 UNION SELECT 2 from t1`, `[[INT64(1)] [INT64(2)]]`) + mcmp.AssertMatches(`SELECT 5 from t1 UNION SELECT 6 from t1`, `[[INT64(5)] [INT64(6)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT id2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT 1 from t1 UNION SELECT id2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT 5 from t1 UNION SELECT id2 from t1`, `[[INT64(5)] [INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT 2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT 5 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)] [INT64(5)]]`) + mcmp.Exec(`select curdate() from t1 union select 3 from t1`) + mcmp.Exec(`select curdate() from t1 union select id1 from t1`) + } }) } diff --git a/go/test/endtoend/vtgate/readafterwrite/raw_test.go b/go/test/endtoend/vtgate/readafterwrite/raw_test.go index 56f9b3a44cb..0549a9b06b0 100644 --- a/go/test/endtoend/vtgate/readafterwrite/raw_test.go +++ b/go/test/endtoend/vtgate/readafterwrite/raw_test.go @@ -119,7 +119,7 @@ func TestMain(m *testing.M) { VSchema: vSchema, } clusterInstance.VtTabletExtraArgs = []string{ - "--queryserver-config-transaction-timeout", "5", + "--queryserver-config-transaction-timeout", "5s", } if err := clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, false); err != nil { return 1 diff --git a/go/test/endtoend/vtgate/reservedconn/main_test.go b/go/test/endtoend/vtgate/reservedconn/main_test.go index cc76e7a3b46..528182a82e2 100644 --- a/go/test/endtoend/vtgate/reservedconn/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/main_test.go @@ -133,7 +133,7 @@ func runAllTests(m *testing.M) int { SchemaSQL: sqlSchema, VSchema: vSchema, } - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "5"} + clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "5s"} if enableSettingsPool { clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-enable-settings-pool") } diff --git a/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go b/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go index f97d96ef89a..b66bb15dbd5 100644 --- a/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go +++ b/go/test/endtoend/vtgate/reservedconn/reconnect2/main_test.go @@ -96,7 +96,7 @@ func runAllTests(m *testing.M) int { SchemaSQL: sqlSchema, VSchema: vSchema, } - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "5"} + clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "5s"} if enableSettingsPool { clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-enable-settings-pool") } diff --git a/go/test/endtoend/vtgate/schema.sql b/go/test/endtoend/vtgate/schema.sql index a883a26519f..4c9ed46fe9a 100644 --- a/go/test/endtoend/vtgate/schema.sql +++ b/go/test/endtoend/vtgate/schema.sql @@ -155,3 +155,13 @@ create table t10_id_to_keyspace_id_idx keyspace_id varbinary(10), primary key (id) ) Engine = InnoDB; + +create table t11 +( + id bigint, + sharding_key bigint, + col1 varchar(50), + col2 int, + col3 int, + primary key (id) +) Engine = InnoDB; \ No newline at end of file diff --git a/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go b/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go index b89b0916e37..3bb4f6dfd9f 100644 --- a/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go +++ b/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_test.go @@ -129,6 +129,8 @@ func TestVSchemaTrackerInit(t *testing.T) { 100*time.Millisecond, 60*time.Second, "initial table list not complete") + + utils.AssertMatches(t, conn, "SHOW VSCHEMA KEYSPACES", `[[VARCHAR("ks") VARCHAR("false") VARCHAR("unmanaged") VARCHAR("")]]`) } // TestVSchemaTrackerKeyspaceReInit tests that the vschema tracker diff --git a/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go b/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go index 1c9f4b0b6e2..8f8050bebe1 100644 --- a/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go +++ b/go/test/endtoend/vtgate/schematracker/sharded/st_sharded_test.go @@ -192,6 +192,12 @@ func TestInitAndUpdate(t *testing.T) { 30*time.Second, "initial table list not complete") + if vtgateVersion >= 19 { + utils.AssertMatches(t, conn, + "SHOW VSCHEMA KEYSPACES", + `[[VARCHAR("ks") VARCHAR("true") VARCHAR("unmanaged") VARCHAR("")]]`) + } + // Init _ = utils.Exec(t, conn, "create table test_sc (id bigint primary key)") expected = `[[VARCHAR("dual")] [VARCHAR("t2")] [VARCHAR("t2_id4_idx")] [VARCHAR("t8")] [VARCHAR("test_sc")]]` diff --git a/go/test/endtoend/vtgate/schematracker/sharded_prs/st_sharded_test.go b/go/test/endtoend/vtgate/schematracker/sharded_prs/st_sharded_test.go index 3ff0b61b482..6ff8e69bb52 100644 --- a/go/test/endtoend/vtgate/schematracker/sharded_prs/st_sharded_test.go +++ b/go/test/endtoend/vtgate/schematracker/sharded_prs/st_sharded_test.go @@ -209,7 +209,6 @@ func TestMain(m *testing.M) { func TestAddColumn(t *testing.T) { defer cluster.PanicHandler(t) - utils.SkipIfBinaryIsBelowVersion(t, 14, "vtgate") ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.NoError(t, err) diff --git a/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go b/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go index 1a37dfb5cf7..257dd7238f3 100644 --- a/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go +++ b/go/test/endtoend/vtgate/schematracker/unsharded/st_unsharded_test.go @@ -120,12 +120,7 @@ func TestNewUnshardedTable(t *testing.T) { require.NoError(t, err) defer conn.Close() - vtgateVersion, err := cluster.GetMajorVersion("vtgate") - require.NoError(t, err) - expected := `[[VARCHAR("dual")] [VARCHAR("main")]]` - if vtgateVersion >= 17 { - expected = `[[VARCHAR("main")]]` - } + expected := `[[VARCHAR("main")]]` // ensuring our initial table "main" is in the schema utils.AssertMatchesWithTimeout(t, conn, @@ -138,10 +133,7 @@ func TestNewUnshardedTable(t *testing.T) { // create a new table which is not part of the VSchema utils.Exec(t, conn, `create table new_table_tracked(id bigint, name varchar(100), primary key(id)) Engine=InnoDB`) - expected = `[[VARCHAR("dual")] [VARCHAR("main")] [VARCHAR("new_table_tracked")]]` - if vtgateVersion >= 17 { - expected = `[[VARCHAR("main")] [VARCHAR("new_table_tracked")]]` - } + expected = `[[VARCHAR("main")] [VARCHAR("new_table_tracked")]]` // waiting for the vttablet's schema_reload interval to kick in utils.AssertMatchesWithTimeout(t, conn, @@ -176,10 +168,7 @@ func TestNewUnshardedTable(t *testing.T) { utils.Exec(t, conn, `drop table new_table_tracked`) // waiting for the vttablet's schema_reload interval to kick in - expected = `[[VARCHAR("dual")] [VARCHAR("main")]]` - if vtgateVersion >= 17 { - expected = `[[VARCHAR("main")]]` - } + expected = `[[VARCHAR("main")]]` utils.AssertMatchesWithTimeout(t, conn, "SHOW VSCHEMA TABLES", expected, @@ -187,3 +176,55 @@ func TestNewUnshardedTable(t *testing.T) { 30*time.Second, "new_table_tracked not in vschema tables") } + +// TestCaseSensitiveSchemaTracking tests that schema tracking is case-sensitive. +// This test only works on Linux (and not on Windows and Mac) since it has a case-sensitive file system, so it allows +// creating two tables having the same name differing only in casing, but other operating systems don't. +// More information at https://dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html#:~:text=Table%20names%20are%20stored%20in,lowercase%20on%20storage%20and%20lookup. +func TestCaseSensitiveSchemaTracking(t *testing.T) { + utils.SkipIfBinaryIsBelowVersion(t, 19, "vttablet") + defer cluster.PanicHandler(t) + + // create a sql connection + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + // ensuring our initial table "main" is in the schema + utils.AssertMatchesWithTimeout(t, conn, + "SHOW VSCHEMA TABLES", + `[[VARCHAR("main")]]`, + 100*time.Millisecond, + 30*time.Second, + "initial tables not found in vschema") + + // Now we create two tables with the same name differing only in casing t1 and T1. + // For both of them we'll have different schema's and verify that we can read the data after schema tracking kicks in. + utils.Exec(t, conn, `create table t1(id bigint, primary key(id)) Engine=InnoDB`) + utils.Exec(t, conn, `create table T1(col bigint, col2 bigint, primary key(col)) Engine=InnoDB`) + + // Wait for schema tracking to be caught up + utils.AssertMatchesWithTimeout(t, conn, + "SHOW VSCHEMA TABLES", + `[[VARCHAR("T1")] [VARCHAR("main")] [VARCHAR("t1")]]`, + 100*time.Millisecond, + 30*time.Second, + "schema tracking didn't track both the tables") + + // Run DMLs + utils.Exec(t, conn, `insert into t1(id) values(0),(1)`) + utils.Exec(t, conn, `insert into T1(col, col2) values(0,0),(1,1)`) + + // Verify the tables are queryable + utils.AssertMatchesWithTimeout(t, conn, + `select * from t1`, `[[INT64(0)] [INT64(1)]]`, + 100*time.Millisecond, + 30*time.Second, + "could not query expected rows in t1 through vtgate") + utils.AssertMatchesWithTimeout(t, conn, + `select * from T1`, `[[INT64(0) INT64(0)] [INT64(1) INT64(1)]]`, + 100*time.Millisecond, + 30*time.Second, + "could not query expected rows in T1 through vtgate") +} diff --git a/go/test/endtoend/vtgate/unsharded/main_test.go b/go/test/endtoend/vtgate/unsharded/main_test.go index f772fabecc1..461a3c73b35 100644 --- a/go/test/endtoend/vtgate/unsharded/main_test.go +++ b/go/test/endtoend/vtgate/unsharded/main_test.go @@ -97,53 +97,53 @@ CREATE TABLE allDefaults ( } ` - createProcSQL = `use vt_customer; + createProcSQL = []string{` CREATE PROCEDURE sp_insert() BEGIN insert into allDefaults () values (); END; - +`, ` CREATE PROCEDURE sp_delete() BEGIN delete from allDefaults; END; - +`, ` CREATE PROCEDURE sp_multi_dml() BEGIN insert into allDefaults () values (); delete from allDefaults; END; - +`, ` CREATE PROCEDURE sp_variable() BEGIN insert into allDefaults () values (); SELECT min(id) INTO @myvar FROM allDefaults; DELETE FROM allDefaults WHERE id = @myvar; END; - +`, ` CREATE PROCEDURE sp_select() BEGIN SELECT * FROM allDefaults; END; - +`, ` CREATE PROCEDURE sp_all() BEGIN insert into allDefaults () values (); select * from allDefaults; delete from allDefaults; END; - +`, ` CREATE PROCEDURE in_parameter(IN val int) BEGIN insert into allDefaults(id) values(val); END; - +`, ` CREATE PROCEDURE out_parameter(OUT val int) BEGIN insert into allDefaults(id) values (128); select 128 into val from dual; END; -` +`} ) var enableSettingsPool bool @@ -179,7 +179,7 @@ func runAllTests(m *testing.M) int { SchemaSQL: SchemaSQL, VSchema: VSchema, } - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "3", "--queryserver-config-max-result-size", "30"} + clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-transaction-timeout", "3s", "--queryserver-config-max-result-size", "30"} if enableSettingsPool { clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--queryserver-enable-settings-pool") } @@ -196,7 +196,7 @@ func runAllTests(m *testing.M) int { } primaryTablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess - if _, err := primaryTablet.QueryTablet(createProcSQL, KeyspaceName, false); err != nil { + if err := primaryTablet.QueryTabletMultiple(createProcSQL, KeyspaceName, true); err != nil { log.Fatal(err.Error()) return 1 } @@ -332,13 +332,11 @@ func TestCallProcedure(t *testing.T) { utils.AssertMatches(t, conn, "show warnings", `[[VARCHAR("Warning") UINT16(1235) VARCHAR("'CALL' not supported in sharded mode")]]`) - _, err = conn.ExecuteFetch(`CALL sp_select()`, 1000, true) - require.Error(t, err) - require.Contains(t, err.Error(), "Multi-Resultset not supported in stored procedure") + err = conn.ExecuteFetchMultiDrain(`CALL sp_select()`) + require.ErrorContains(t, err, "Multi-Resultset not supported in stored procedure") - _, err = conn.ExecuteFetch(`CALL sp_all()`, 1000, true) - require.Error(t, err) - require.Contains(t, err.Error(), "Multi-Resultset not supported in stored procedure") + err = conn.ExecuteFetchMultiDrain(`CALL sp_all()`) + require.ErrorContains(t, err, "Multi-Resultset not supported in stored procedure") qr = utils.Exec(t, conn, `CALL sp_delete()`) require.GreaterOrEqual(t, 1, int(qr.RowsAffected)) diff --git a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go index 83e20d9aa31..3251668e155 100644 --- a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go +++ b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go @@ -25,8 +25,8 @@ import ( "vitess.io/vitess/go/test/endtoend/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gotest.tools/assert" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/test/endtoend/cluster" diff --git a/go/test/endtoend/vtgate/vschema.json b/go/test/endtoend/vtgate/vschema.json index 8d16beec2a6..07b6e76550f 100644 --- a/go/test/endtoend/vtgate/vschema.json +++ b/go/test/endtoend/vtgate/vschema.json @@ -1,11 +1,10 @@ - { "sharded": true, "vindexes": { - "unicode_loose_xxhash" : { + "unicode_loose_xxhash": { "type": "unicode_loose_xxhash" }, - "unicode_loose_md5" : { + "unicode_loose_md5": { "type": "unicode_loose_md5" }, "hash": { @@ -159,7 +158,10 @@ "name": "hash" }, { - "columns": ["id2", "id1"], + "columns": [ + "id2", + "id1" + ], "name": "t4_id2_vdx" } ] @@ -179,7 +181,10 @@ "name": "hash" }, { - "columns": ["id2", "id1"], + "columns": [ + "id2", + "id1" + ], "name": "t6_id2_vdx" } ] @@ -301,6 +306,14 @@ "name": "hash" } ] + }, + "t11": { + "column_vindexes": [ + { + "column": "sharding_key", + "name": "hash" + } + ] } } } diff --git a/go/test/endtoend/vtorc/general/vtorc_test.go b/go/test/endtoend/vtorc/general/vtorc_test.go index 244cd364e7c..f7deffe20b3 100644 --- a/go/test/endtoend/vtorc/general/vtorc_test.go +++ b/go/test/endtoend/vtorc/general/vtorc_test.go @@ -189,9 +189,13 @@ func TestVTOrcRepairs(t *testing.T) { t.Run("ReplicationFromOtherReplica", func(t *testing.T) { // point replica at otherReplica - changeReplicationSourceCommand := fmt.Sprintf("STOP SLAVE; RESET SLAVE ALL;"+ - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1; START SLAVE", utils.Hostname, otherReplica.MySQLPort) - _, err := utils.RunSQL(t, changeReplicationSourceCommand, replica, "") + changeReplicationSourceCommands := []string{ + "STOP SLAVE", + "RESET SLAVE ALL", + fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1", utils.Hostname, otherReplica.MySQLPort), + "START SLAVE", + } + err := utils.RunSQLs(t, changeReplicationSourceCommands, replica, "") require.NoError(t, err) // wait until the source port is set back correctly by vtorc @@ -204,10 +208,13 @@ func TestVTOrcRepairs(t *testing.T) { t.Run("CircularReplication", func(t *testing.T) { // change the replication source on the primary - changeReplicationSourceCommands := fmt.Sprintf("STOP SLAVE; RESET SLAVE ALL;"+ - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1;"+ - "START SLAVE;", replica.VttabletProcess.TabletHostname, replica.MySQLPort) - _, err := utils.RunSQL(t, changeReplicationSourceCommands, curPrimary, "") + changeReplicationSourceCommands := []string{ + "STOP SLAVE", + "RESET SLAVE ALL", + fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1", replica.VttabletProcess.TabletHostname, replica.MySQLPort), + "START SLAVE", + } + err := utils.RunSQLs(t, changeReplicationSourceCommands, curPrimary, "") require.NoError(t, err) // wait for curPrimary to reach stable state diff --git a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go index 180f367d7fb..e226e8d13ae 100644 --- a/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go +++ b/go/test/endtoend/vtorc/primaryfailure/primary_failure_test.go @@ -438,9 +438,8 @@ func TestLostRdonlyOnPrimaryFailure(t *testing.T) { // check that replication is setup correctly utils.CheckReplication(t, clusterInfo, curPrimary, []*cluster.Vttablet{rdonly, aheadRdonly, replica}, 15*time.Second) - // revoke super privileges from vtorc on replica and rdonly so that it is unable to repair the replication - utils.ChangePrivileges(t, `REVOKE SUPER ON *.* FROM 'orc_client_user'@'%'`, replica, "orc_client_user") - utils.ChangePrivileges(t, `REVOKE SUPER ON *.* FROM 'orc_client_user'@'%'`, rdonly, "orc_client_user") + // disable recoveries on vtorc so that it is unable to repair the replication + utils.DisableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // stop replication on the replica and rdonly. err := clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommand("StopReplication", replica.Alias) @@ -467,9 +466,8 @@ func TestLostRdonlyOnPrimaryFailure(t *testing.T) { utils.PermanentlyRemoveVttablet(clusterInfo, curPrimary) }() - // grant super privileges back to vtorc on replica and rdonly so that it can repair - utils.ChangePrivileges(t, `GRANT SUPER ON *.* TO 'orc_client_user'@'%'`, replica, "orc_client_user") - utils.ChangePrivileges(t, `GRANT SUPER ON *.* TO 'orc_client_user'@'%'`, rdonly, "orc_client_user") + // enable recoveries back on vtorc so that it can repair + utils.EnableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // vtorc must promote the lagging replica and not the rdonly, since it has a MustNotPromoteRule promotion rule utils.CheckPrimaryTablet(t, clusterInfo, replica, true) @@ -667,8 +665,8 @@ func TestDownPrimaryPromotionRuleWithLag(t *testing.T) { // newly started tablet does not replicate from anyone yet, we will allow vtorc to fix this too utils.CheckReplication(t, clusterInfo, curPrimary, []*cluster.Vttablet{crossCellReplica, replica, rdonly}, 25*time.Second) - // revoke super privileges from vtorc on crossCellReplica so that it is unable to repair the replication - utils.ChangePrivileges(t, `REVOKE SUPER ON *.* FROM 'orc_client_user'@'%'`, crossCellReplica, "orc_client_user") + // disable recoveries for vtorc so that it is unable to repair the replication. + utils.DisableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // stop replication on the crossCellReplica. err := clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommand("StopReplication", crossCellReplica.Alias) @@ -684,8 +682,8 @@ func TestDownPrimaryPromotionRuleWithLag(t *testing.T) { err = clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommand("StartReplication", crossCellReplica.Alias) require.NoError(t, err) - // grant super privileges back to vtorc on crossCellReplica so that it can repair - utils.ChangePrivileges(t, `GRANT SUPER ON *.* TO 'orc_client_user'@'%'`, crossCellReplica, "orc_client_user") + // enable recoveries back on vtorc so that it can repair + utils.EnableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // assert that the crossCellReplica is indeed lagging and does not have the new insertion by checking the count of rows in the table out, err := utils.RunSQL(t, "SELECT * FROM vt_insert_test", crossCellReplica, "vt_ks") @@ -748,8 +746,8 @@ func TestDownPrimaryPromotionRuleWithLagCrossCenter(t *testing.T) { // newly started tablet does not replicate from anyone yet, we will allow vtorc to fix this too utils.CheckReplication(t, clusterInfo, curPrimary, []*cluster.Vttablet{crossCellReplica, replica, rdonly}, 25*time.Second) - // revoke super privileges from vtorc on replica so that it is unable to repair the replication - utils.ChangePrivileges(t, `REVOKE SUPER ON *.* FROM 'orc_client_user'@'%'`, replica, "orc_client_user") + // disable recoveries from vtorc so that it is unable to repair the replication + utils.DisableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // stop replication on the replica. err := clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommand("StopReplication", replica.Alias) @@ -765,8 +763,8 @@ func TestDownPrimaryPromotionRuleWithLagCrossCenter(t *testing.T) { err = clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommand("StartReplication", replica.Alias) require.NoError(t, err) - // grant super privileges back to vtorc on replica so that it can repair - utils.ChangePrivileges(t, `GRANT SUPER ON *.* TO 'orc_client_user'@'%'`, replica, "orc_client_user") + // enable recoveries back on vtorc so that it can repair + utils.EnableGlobalRecoveries(t, clusterInfo.ClusterInstance.VTOrcProcesses[0]) // assert that the replica is indeed lagging and does not have the new insertion by checking the count of rows in the table out, err := utils.RunSQL(t, "SELECT * FROM vt_insert_test", replica, "vt_ks") diff --git a/go/test/endtoend/vtorc/utils/utils.go b/go/test/endtoend/vtorc/utils/utils.go index 07b5b016fcc..11294319658 100644 --- a/go/test/endtoend/vtorc/utils/utils.go +++ b/go/test/endtoend/vtorc/utils/utils.go @@ -683,21 +683,6 @@ func PermanentlyRemoveVttablet(clusterInfo *VTOrcClusterInfo, tablet *cluster.Vt } } -// ChangePrivileges is used to change the privileges of the given user. These commands are executed such that they are not replicated -func ChangePrivileges(t *testing.T, sql string, tablet *cluster.Vttablet, user string) { - _, err := RunSQL(t, "SET sql_log_bin = OFF;"+sql+";SET sql_log_bin = ON;", tablet, "") - require.NoError(t, err) - - res, err := RunSQL(t, fmt.Sprintf("SELECT id FROM INFORMATION_SCHEMA.PROCESSLIST WHERE user = '%s'", user), tablet, "") - require.NoError(t, err) - for _, row := range res.Rows { - id, err := row[0].ToInt64() - require.NoError(t, err) - _, err = RunSQL(t, fmt.Sprintf("kill %d", id), tablet, "") - require.NoError(t, err) - } -} - // ResetPrimaryLogs is used reset the binary logs func ResetPrimaryLogs(t *testing.T, curPrimary *cluster.Vttablet) { _, err := RunSQL(t, "FLUSH BINARY LOGS", curPrimary, "") @@ -1121,3 +1106,19 @@ func PrintVTOrcLogsOnFailure(t *testing.T, clusterInstance *cluster.LocalProcess log.Errorf("%s", string(content)) } } + +// EnableGlobalRecoveries enables global recoveries for the given VTOrc. +func EnableGlobalRecoveries(t *testing.T, vtorc *cluster.VTOrcProcess) { + status, resp, err := MakeAPICall(t, vtorc, "/api/enable-global-recoveries") + require.NoError(t, err) + assert.Equal(t, 200, status) + assert.Equal(t, "Global recoveries enabled\n", resp) +} + +// DisableGlobalRecoveries disables global recoveries for the given VTOrc. +func DisableGlobalRecoveries(t *testing.T, vtorc *cluster.VTOrcProcess) { + status, resp, err := MakeAPICall(t, vtorc, "/api/disable-global-recoveries") + require.NoError(t, err) + assert.Equal(t, 200, status) + assert.Equal(t, "Global recoveries disabled\n", resp) +} diff --git a/go/test/fuzzing/ast_fuzzer.go b/go/test/fuzzing/ast_fuzzer.go index 118f044ea66..5951a0da9eb 100644 --- a/go/test/fuzzing/ast_fuzzer.go +++ b/go/test/fuzzing/ast_fuzzer.go @@ -36,11 +36,11 @@ func FuzzEqualsSQLNode(data []byte) int { if err != nil { return 0 } - inA, err := sqlparser.Parse(query1) + inA, err := sqlparser.NewTestParser().Parse(query1) if err != nil { return 0 } - inB, err := sqlparser.Parse(query2) + inB, err := sqlparser.NewTestParser().Parse(query2) if err != nil { return 0 } diff --git a/go/test/fuzzing/parser_fuzzer.go b/go/test/fuzzing/parser_fuzzer.go index 67b8a30ef00..04a37e6dbcb 100644 --- a/go/test/fuzzing/parser_fuzzer.go +++ b/go/test/fuzzing/parser_fuzzer.go @@ -42,7 +42,7 @@ func FuzzNormalizer(data []byte) int { } func FuzzParser(data []byte) int { - _, err := sqlparser.Parse(string(data)) + _, err := sqlparser.NewTestParser().Parse(string(data)) if err != nil { return 0 } @@ -55,7 +55,7 @@ func FuzzNodeFormat(data []byte) int { if err != nil { return 0 } - node, err := sqlparser.Parse(query) + node, err := sqlparser.NewTestParser().Parse(query) if err != nil { return 0 } @@ -69,6 +69,6 @@ func FuzzNodeFormat(data []byte) int { } func FuzzSplitStatementToPieces(data []byte) int { - _, _ = sqlparser.SplitStatementToPieces(string(data)) + _, _ = sqlparser.NewTestParser().SplitStatementToPieces(string(data)) return 1 } diff --git a/go/test/fuzzing/tabletserver_schema_fuzzer.go b/go/test/fuzzing/tabletserver_schema_fuzzer.go index 67bb36e52ed..5ee680952cc 100644 --- a/go/test/fuzzing/tabletserver_schema_fuzzer.go +++ b/go/test/fuzzing/tabletserver_schema_fuzzer.go @@ -17,9 +17,13 @@ import ( "context" "sync" "testing" + "time" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -57,14 +61,14 @@ func FuzzLoadTable(data []byte) int { func newTestLoadTable(tableName, comment string, db *fakesqldb.DB) (*schema.Table, error) { ctx := context.Background() - appParams := db.ConnParams() - dbaParams := db.ConnParams() + appParams := dbconfigs.New(db.ConnParams()) + dbaParams := dbconfigs.New(db.ConnParams()) cfg := tabletenv.ConnPoolConfig{ - Size: 2, + Size: 2, + IdleTimeout: 10 * time.Second, } - _ = cfg.IdleTimeoutSeconds.Set("10s") - connPool := connpool.NewPool(tabletenv.NewEnv(nil, "SchemaTest"), "", cfg) + connPool := connpool.NewPool(tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "SchemaTest"), "", cfg) connPool.Open(appParams, dbaParams, appParams) conn, err := connPool.Get(ctx, nil) if err != nil { @@ -72,5 +76,5 @@ func newTestLoadTable(tableName, comment string, db *fakesqldb.DB) (*schema.Tabl } defer conn.Recycle() - return schema.LoadTable(conn, "fakesqldb", tableName, "BASE_TABLE", comment) + return schema.LoadTable(conn, "fakesqldb", tableName, "BASE_TABLE", comment, collations.MySQL8()) } diff --git a/go/test/fuzzing/vt_schema_fuzzer.go b/go/test/fuzzing/vt_schema_fuzzer.go index 2092eac866a..79a30d3394a 100644 --- a/go/test/fuzzing/vt_schema_fuzzer.go +++ b/go/test/fuzzing/vt_schema_fuzzer.go @@ -26,7 +26,7 @@ import ( // FuzzOnlineDDLFromCommentedStatement implements a fuzzer // that targets schema.OnlineDDLFromCommentedStatement func FuzzOnlineDDLFromCommentedStatement(data []byte) int { - stmt, err := sqlparser.Parse(string(data)) + stmt, err := sqlparser.NewTestParser().Parse(string(data)) if err != nil { return 0 } @@ -75,7 +75,7 @@ func FuzzNewOnlineDDLs(data []byte) int { return 0 } - onlineDDLs, err := schema.NewOnlineDDLs(keyspace, sql, ddlStmt, ddlStrategySetting, requestContext) + onlineDDLs, err := schema.NewOnlineDDLs(sql, ddlStmt, ddlStrategySetting, requestContext, keyspace) if err != nil { return 0 } diff --git a/go/test/fuzzing/vtctl_fuzzer.go b/go/test/fuzzing/vtctl_fuzzer.go index 82fdaa572de..cfd19d1ee46 100644 --- a/go/test/fuzzing/vtctl_fuzzer.go +++ b/go/test/fuzzing/vtctl_fuzzer.go @@ -24,6 +24,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vtctl" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/vttablet/tmclienttest" "vitess.io/vitess/go/vt/wrangler" @@ -180,7 +181,7 @@ func Fuzz(data []byte) int { // Add params to the command commandSlice = append(commandSlice, args...) - _ = vtctl.RunCommand(ctx, wrangler.New(logger, topo, tmc), commandSlice) + _ = vtctl.RunCommand(ctx, wrangler.New(vtenv.NewTestEnv(), logger, topo, tmc), commandSlice) command++ } diff --git a/go/test/vschemawrapper/vschema_wrapper.go b/go/test/vschemawrapper/vschema_wrapper.go index 78cf0f8d41c..0f8b47a9804 100644 --- a/go/test/vschemawrapper/vschema_wrapper.go +++ b/go/test/vschemawrapper/vschema_wrapper.go @@ -30,6 +30,7 @@ import ( vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" @@ -40,14 +41,16 @@ import ( var _ plancontext.VSchema = (*VSchemaWrapper)(nil) type VSchemaWrapper struct { - V *vindexes.VSchema - Keyspace *vindexes.Keyspace - TabletType_ topodatapb.TabletType - Dest key.Destination - SysVarEnabled bool - Version plancontext.PlannerVersion - EnableViews bool - TestBuilder func(query string, vschema plancontext.VSchema, keyspace string) (*engine.Plan, error) + V *vindexes.VSchema + Keyspace *vindexes.Keyspace + TabletType_ topodatapb.TabletType + Dest key.Destination + SysVarEnabled bool + ForeignKeyChecksState *bool + Version plancontext.PlannerVersion + EnableViews bool + TestBuilder func(query string, vschema plancontext.VSchema, keyspace string) (*engine.Plan, error) + Env *vtenv.Environment } func (vw *VSchemaWrapper) GetPrepareData(stmtName string) *vtgatepb.PrepareData { @@ -81,7 +84,7 @@ func (vw *VSchemaWrapper) PlanPrepareStatement(ctx context.Context, query string if err != nil { return nil, nil, err } - stmt, _, err := sqlparser.Parse2(query) + stmt, _, err := vw.Env.Parser().Parse2(query) if err != nil { return nil, nil, err } @@ -122,7 +125,11 @@ func (vw *VSchemaWrapper) GetSrvVschema() *vschemapb.SrvVSchema { } func (vw *VSchemaWrapper) ConnCollation() collations.ID { - return collations.CollationUtf8mb3ID + return vw.Env.CollationEnv().DefaultConnectionCharset() +} + +func (vw *VSchemaWrapper) Environment() *vtenv.Environment { + return vw.Env } func (vw *VSchemaWrapper) PlannerWarning(_ string) { @@ -140,6 +147,10 @@ func (vw *VSchemaWrapper) KeyspaceError(keyspace string) error { return nil } +func (vw *VSchemaWrapper) GetForeignKeyChecksState() *bool { + return vw.ForeignKeyChecksState +} + func (vw *VSchemaWrapper) AllKeyspace() ([]*vindexes.Keyspace, error) { if vw.Keyspace == nil { return nil, vterrors.VT13001("keyspace not available") diff --git a/go/textutil/strings_test.go b/go/textutil/strings_test.go index 9ea175909fd..2b43166831c 100644 --- a/go/textutil/strings_test.go +++ b/go/textutil/strings_test.go @@ -20,6 +20,9 @@ import ( "testing" "github.com/stretchr/testify/assert" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) func TestSplitDelimitedList(t *testing.T) { @@ -65,6 +68,12 @@ func TestSplitUnescape(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, elems) } + { + s := "invalid%2" + elems, err := SplitUnescape(s, ",") + assert.Error(t, err) + assert.Equal(t, []string{}, elems) + } } func TestSingleWordCamel(t *testing.T) { @@ -108,3 +117,94 @@ func TestSingleWordCamel(t *testing.T) { }) } } + +func TestValueIsSimulatedNull(t *testing.T) { + tt := []struct { + name string + val interface{} + isNull bool + }{ + { + name: "case string false", + val: "test", + isNull: false, + }, + { + name: "case string true", + val: SimulatedNullString, + isNull: true, + }, + { + name: "case []string true", + val: []string{SimulatedNullString}, + isNull: true, + }, + { + name: "case []string false", + val: []string{SimulatedNullString, SimulatedNullString}, + isNull: false, + }, + { + name: "case binlogdatapb.OnDDLAction true", + val: binlogdatapb.OnDDLAction(SimulatedNullInt), + isNull: true, + }, + { + name: "case int true", + val: SimulatedNullInt, + isNull: true, + }, + { + name: "case int32 true", + val: int32(SimulatedNullInt), + isNull: true, + }, + { + name: "case int64 true", + val: int64(SimulatedNullInt), + isNull: true, + }, + { + name: "case []topodatapb.TabletType true", + val: []topodatapb.TabletType{topodatapb.TabletType(SimulatedNullInt)}, + isNull: true, + }, + { + name: "case binlogdatapb.VReplicationWorkflowState true", + val: binlogdatapb.VReplicationWorkflowState(SimulatedNullInt), + isNull: true, + }, + { + name: "case default", + val: float64(1), + isNull: false, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + isNull := ValueIsSimulatedNull(tc.val) + assert.Equal(t, tc.isNull, isNull) + }) + } +} + +func TestTitle(t *testing.T) { + tt := []struct { + s string + expect string + }{ + {s: "hello world", expect: "Hello World"}, + {s: "snake_case", expect: "Snake_case"}, + {s: "TITLE CASE", expect: "TITLE CASE"}, + {s: "HelLo wOrLd", expect: "HelLo WOrLd"}, + {s: "", expect: ""}, + } + + for _, tc := range tt { + t.Run(tc.s, func(t *testing.T) { + title := Title(tc.s) + assert.Equal(t, tc.expect, title) + }) + } +} diff --git a/go/textutil/template_test.go b/go/textutil/template_test.go new file mode 100644 index 00000000000..077a6f6a72d --- /dev/null +++ b/go/textutil/template_test.go @@ -0,0 +1,51 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package textutil + +import ( + "testing" + "text/template" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestExecuteTemplate(t *testing.T) { + tmplText := "Result: {{.Value}}" + inputData := struct{ Value string }{Value: "Test"} + tmpl, err := template.New("test").Parse(tmplText) + require.NoError(t, err) + + result, err := ExecuteTemplate(tmpl, inputData) + require.NoError(t, err) + + expectedResult := "Result: Test" + assert.Equal(t, expectedResult, result) + +} + +func TestExecuteTemplateWithError(t *testing.T) { + templText := "{{.UndefinedVariable}}" + invalidInput := struct{ Name string }{Name: "foo"} + + tmpl, err := template.New("test").Parse(templText) + require.NoError(t, err) + + result, err := ExecuteTemplate(tmpl, invalidInput) + assert.Error(t, err) + assert.Equal(t, "", result) +} diff --git a/go/timer/randticker_flaky_test.go b/go/timer/randticker_test.go similarity index 100% rename from go/timer/randticker_flaky_test.go rename to go/timer/randticker_test.go diff --git a/go/timer/suspendable_ticker.go b/go/timer/suspendable_ticker.go index 5257626b85f..f2694a5cab3 100644 --- a/go/timer/suspendable_ticker.go +++ b/go/timer/suspendable_ticker.go @@ -28,7 +28,7 @@ type SuspendableTicker struct { // C is user facing C chan time.Time - suspended int64 + suspended atomic.Bool } // NewSuspendableTicker creates a new suspendable ticker, indicating whether the ticker should start @@ -39,7 +39,7 @@ func NewSuspendableTicker(d time.Duration, initiallySuspended bool) *Suspendable C: make(chan time.Time), } if initiallySuspended { - s.suspended = 1 + s.suspended.Store(true) } go s.loop() return s @@ -48,12 +48,12 @@ func NewSuspendableTicker(d time.Duration, initiallySuspended bool) *Suspendable // Suspend stops sending time events on the channel C // time events sent during suspended time are lost func (s *SuspendableTicker) Suspend() { - atomic.StoreInt64(&s.suspended, 1) + s.suspended.Store(true) } // Resume re-enables time events on channel C func (s *SuspendableTicker) Resume() { - atomic.StoreInt64(&s.suspended, 0) + s.suspended.Store(false) } // Stop completely stops the timer, like time.Timer @@ -64,15 +64,23 @@ func (s *SuspendableTicker) Stop() { // TickNow generates a tick at this point in time. It may block // if nothing consumes the tick. func (s *SuspendableTicker) TickNow() { - if atomic.LoadInt64(&s.suspended) == 0 { + if !s.suspended.Load() { // not suspended s.C <- time.Now() } } +// TickAfter generates a tick after given duration has passed. +// It runs asynchronously and returns immediately. +func (s *SuspendableTicker) TickAfter(d time.Duration) { + time.AfterFunc(d, func() { + s.TickNow() + }) +} + func (s *SuspendableTicker) loop() { for t := range s.ticker.C { - if atomic.LoadInt64(&s.suspended) == 0 { + if !s.suspended.Load() { // not suspended s.C <- t } diff --git a/go/timer/suspendable_ticker_test.go b/go/timer/suspendable_ticker_test.go new file mode 100644 index 00000000000..1c7cf65edcf --- /dev/null +++ b/go/timer/suspendable_ticker_test.go @@ -0,0 +1,144 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package timer + +import ( + "context" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +const ( + fastTickerInterval = 10 * time.Millisecond +) + +func TestInitiallySuspended(t *testing.T) { + ctx := context.Background() + t.Run("true", func(t *testing.T) { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + ticker := NewSuspendableTicker(fastTickerInterval, true) + defer ticker.Stop() + select { + case <-ticker.C: + assert.Fail(t, "unexpected tick. Was supposed to be suspended") + case <-ctx.Done(): + return + } + }) + t.Run("false", func(t *testing.T) { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + ticker := NewSuspendableTicker(fastTickerInterval, false) + defer ticker.Stop() + select { + case <-ticker.C: + return + case <-ctx.Done(): + assert.Fail(t, "unexpected timeout. Expected tick") + } + }) +} + +func TestSuspendableTicker(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ticker := NewSuspendableTicker(fastTickerInterval, false) + defer ticker.Stop() + + var ticks atomic.Int64 + go func() { + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + ticks.Add(1) + } + } + }() + t.Run("ticks running", func(t *testing.T) { + time.Sleep(time.Second) + after := ticks.Load() + assert.Greater(t, after, int64(10)) // should be about 100 + }) + t.Run("ticks suspended", func(t *testing.T) { + ticker.Suspend() + before := ticks.Load() + time.Sleep(time.Second) + after := ticks.Load() + assert.Less(t, after-before, int64(10)) + }) + t.Run("ticks resumed", func(t *testing.T) { + ticker.Resume() + before := ticks.Load() + time.Sleep(time.Second) + after := ticks.Load() + assert.Greater(t, after-before, int64(10)) + }) + t.Run("ticker stopped", func(t *testing.T) { + ticker.Stop() + before := ticks.Load() + time.Sleep(time.Second) + after := ticks.Load() + assert.Less(t, after-before, int64(10)) + }) +} + +func TestSuspendableTickerTick(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ticker := NewSuspendableTicker(time.Hour, false) + defer ticker.Stop() + + var ticks atomic.Int64 + go func() { + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + ticks.Add(1) + } + } + }() + t.Run("nothing going on", func(t *testing.T) { + time.Sleep(time.Second) + after := ticks.Load() + assert.Zero(t, after) + }) + t.Run("tick now", func(t *testing.T) { + before := ticks.Load() + ticker.TickNow() + time.Sleep(time.Second) + after := ticks.Load() + assert.Equal(t, int64(1), after-before) + }) + t.Run("tick after", func(t *testing.T) { + before := ticks.Load() + ticker.TickAfter(2 * time.Second) + time.Sleep(time.Second) + after := ticks.Load() + assert.Zero(t, after-before) + time.Sleep(3 * time.Second) + after = ticks.Load() + assert.Equal(t, int64(1), after-before) + }) +} diff --git a/go/timer/timer_flaky_test.go b/go/timer/timer_test.go similarity index 100% rename from go/timer/timer_flaky_test.go rename to go/timer/timer_test.go diff --git a/go/tools/asthelpergen/asthelpergen.go b/go/tools/asthelpergen/asthelpergen.go index 1811ff72511..3f59fdb3ece 100644 --- a/go/tools/asthelpergen/asthelpergen.go +++ b/go/tools/asthelpergen/asthelpergen.go @@ -29,7 +29,6 @@ import ( "golang.org/x/tools/go/packages" "vitess.io/vitess/go/textutil" - "vitess.io/vitess/go/tools/codegen" ) diff --git a/go/tools/codegen/common_test.go b/go/tools/codegen/common_test.go new file mode 100644 index 00000000000..fd2ef0035d9 --- /dev/null +++ b/go/tools/codegen/common_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package codegen + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/tools/go/packages" +) + +func TestCheckErrors(t *testing.T) { + tests := []struct { + name string + loaded []*packages.Package + expectedErr string + }{ + { + name: "Empty packages", + loaded: []*packages.Package{}, + expectedErr: "", + }, + { + name: "Non-empty packages", + loaded: []*packages.Package{ + { + Errors: []packages.Error{ + { + Pos: "", + Msg: "New error", + Kind: 7, + }, + { + Pos: "1:7", + Msg: "New error", + Kind: 7, + }, + }, + }, + }, + expectedErr: "found 2 error(s) when loading Go packages:", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := CheckErrors(tt.loaded, GeneratedInSqlparser) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} + +func TestGeneratedInSqlParser(t *testing.T) { + tests := []struct { + name string + fileName string + expectedOutput bool + }{ + { + name: "Empty file name", + fileName: "", + expectedOutput: false, + }, + { + name: "Random file name", + fileName: "random", + expectedOutput: false, + }, + { + name: "ast_format_fast.go", + fileName: "ast_format_fast.go", + expectedOutput: true, + }, + { + name: "ast_equals.go", + fileName: "ast_equals.go", + expectedOutput: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expectedOutput, GeneratedInSqlparser(tt.fileName)) + }) + } +} diff --git a/go/tools/codegen/goimports_test.go b/go/tools/codegen/goimports_test.go new file mode 100644 index 00000000000..25883073859 --- /dev/null +++ b/go/tools/codegen/goimports_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package codegen + +import ( + "testing" + + "github.com/dave/jennifer/jen" + "github.com/stretchr/testify/require" +) + +func TestFormatGenFile(t *testing.T) { + tests := []struct { + name string + file *jen.File + expectedErr string + }{ + { + name: "some-file", + file: jen.NewFile("some-file"), + expectedErr: "Error 1:13: expected ';', found '-' while formatting source", + }, + { + name: "random", + file: jen.NewFile("random"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := FormatJenFile(tt.file) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} + +func TestGoImports(t *testing.T) { + err := GoImports("") + require.ErrorContains(t, err, "exit status 2") +} + +func TestSaveJenFile(t *testing.T) { + tests := []struct { + name string + filePath string + file *jen.File + expectedErr string + }{ + { + name: "Empty file path", + filePath: "", + file: jen.NewFile("random"), + expectedErr: "open : no such file or directory", + }, + { + name: "Non empty file path", + filePath: "random", + file: jen.NewFile("random"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := SaveJenFile(tt.filePath, tt.file) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} diff --git a/go/tools/release-notes/release_notes.go b/go/tools/release-notes/release_notes.go index 1673d6a5160..1a8105a6f38 100644 --- a/go/tools/release-notes/release_notes.go +++ b/go/tools/release-notes/release_notes.go @@ -17,7 +17,6 @@ limitations under the License. package main import ( - "bytes" "encoding/json" "fmt" "log" @@ -79,9 +78,7 @@ type ( } ) -var ( - releaseNotesPath = `changelog/` -) +var releaseNotesPath = `changelog/` const ( releaseNotesPathGitHub = `https://github.com/vitessio/vitess/blob/main/` @@ -141,7 +138,7 @@ func (rn *releaseNote) generate(rnFile, changelogFile *os.File) error { // Generate the release notes rn.PathToChangeLogFileOnGH = releaseNotesPathGitHub + path.Join(rn.SubDirPath, "changelog.md") if rnFile == nil { - rnFile, err = os.OpenFile(path.Join(rn.SubDirPath, "release_notes.md"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + rnFile, err = os.OpenFile(path.Join(rn.SubDirPath, "release_notes.md"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } @@ -155,7 +152,7 @@ func (rn *releaseNote) generate(rnFile, changelogFile *os.File) error { // Generate the changelog if changelogFile == nil { - changelogFile, err = os.OpenFile(path.Join(rn.SubDirPath, "changelog.md"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + changelogFile, err = os.OpenFile(path.Join(rn.SubDirPath, "changelog.md"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } @@ -304,11 +301,11 @@ func getStringForPullRequestInfos(prPerType prsByType) (string, error) { data := createSortedPrTypeSlice(prPerType) t := template.Must(template.New("markdownTemplatePR").Parse(markdownTemplatePR)) - buff := bytes.Buffer{} - if err := t.ExecuteTemplate(&buff, "markdownTemplatePR", data); err != nil { + var buf strings.Builder + if err := t.ExecuteTemplate(&buf, "markdownTemplatePR", data); err != nil { return "", err } - return buff.String(), nil + return buf.String(), nil } func getStringForKnownIssues(issues []knownIssue) (string, error) { @@ -316,11 +313,11 @@ func getStringForKnownIssues(issues []knownIssue) (string, error) { return "", nil } t := template.Must(template.New("markdownTemplateKnownIssues").Parse(markdownTemplateKnownIssues)) - buff := bytes.Buffer{} - if err := t.ExecuteTemplate(&buff, "markdownTemplateKnownIssues", issues); err != nil { + var buf strings.Builder + if err := t.ExecuteTemplate(&buf, "markdownTemplateKnownIssues", issues); err != nil { return "", err } - return buff.String(), nil + return buf.String(), nil } func groupAndStringifyPullRequest(pris []pullRequestInformation) (string, error) { @@ -336,9 +333,7 @@ func groupAndStringifyPullRequest(pris []pullRequestInformation) (string, error) } func main() { - var ( - versionName, summaryFile string - ) + var versionName, summaryFile string pflag.StringVarP(&versionName, "version", "v", "", "name of the version (has to be the following format: v11.0.0)") pflag.StringVarP(&summaryFile, "summary", "s", "", "readme file on which there is a summary of the release") pflag.Parse() diff --git a/go/tools/releases/releases_test.go b/go/tools/releases/releases_test.go new file mode 100644 index 00000000000..19b3f7df88e --- /dev/null +++ b/go/tools/releases/releases_test.go @@ -0,0 +1,89 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetDirs(t *testing.T) { + tests := []struct { + name string + currentDir dir + expectedErr string + }{ + { + name: "Empty dir", + currentDir: dir{}, + expectedErr: "open : no such file or directory", + }, + { + name: "Non empty dir", + currentDir: dir{ + Path: "./", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := getDirs(tt.currentDir) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} + +func TestExecReadMeTemplateWithDir(t *testing.T) { + tests := []struct { + name string + template string + currentDir dir + expectedErr string + }{ + { + name: "Empty dir and empty template", + currentDir: dir{}, + template: "", + expectedErr: "", + }, + { + name: "Invaild directory path", + currentDir: dir{ + Path: `\./`, + }, + template: "", + expectedErr: `open \./README.md: no such file or directory`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := execReadMeTemplateWithDir(tt.currentDir, tt.template) + if tt.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.expectedErr) + } + }) + } +} diff --git a/go/trace/fake_test.go b/go/trace/fake_test.go new file mode 100644 index 00000000000..d7d01333202 --- /dev/null +++ b/go/trace/fake_test.go @@ -0,0 +1,33 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trace + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNoopTracingServer(t *testing.T) { + factoryFunc := tracingBackendFactories["noop"] + tracingSvc, closer, err := factoryFunc("value") + require.NoError(t, err) + require.NoError(t, closer.Close()) + span, err := tracingSvc.NewFromString("parent", "label") + require.NoError(t, err) + require.Empty(t, span) +} diff --git a/go/trace/logger_test.go b/go/trace/logger_test.go new file mode 100644 index 00000000000..cb414515fa5 --- /dev/null +++ b/go/trace/logger_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trace + +import ( + "io" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +// If captureStdout is false, it will capture the outut of +// os.Stderr +func captureOutput(t *testing.T, f func(), captureStdout bool) string { + oldVal := os.Stderr + if captureStdout { + oldVal = os.Stdout + } + t.Cleanup(func() { + // Ensure reset even if deferred function panics + if captureStdout { + os.Stdout = oldVal + } else { + os.Stderr = oldVal + } + }) + + r, w, _ := os.Pipe() + if captureStdout { + os.Stdout = w + } else { + os.Stderr = w + } + + f() + + w.Close() + got, _ := io.ReadAll(r) + + return string(got) +} + +func TestLoggerLogAndError(t *testing.T) { + logger := traceLogger{} + + // Test Error() output + output := captureOutput(t, func() { + logger.Error("This is an error message") + }, false) + assert.Contains(t, output, "This is an error message") + + // Test Log() output + output = captureOutput(t, func() { + logger.Log("This is an log message") + }, false) + assert.Contains(t, output, "This is an log message") +} diff --git a/go/trace/opentracing_test.go b/go/trace/opentracing_test.go index 104545fe657..4a1dad369d9 100644 --- a/go/trace/opentracing_test.go +++ b/go/trace/opentracing_test.go @@ -17,12 +17,14 @@ limitations under the License. package trace import ( + "context" "encoding/base64" "encoding/json" "testing" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestExtractMapFromString(t *testing.T) { @@ -47,3 +49,29 @@ func TestErrorConditions(t *testing.T) { _, err = extractMapFromString("this is not base64") // malformed base64 assert.Error(t, err) } + +func TestNewClientSpan(t *testing.T) { + svc := openTracingService{ + Tracer: &fakeTracer{}, + } + clientSpan := svc.NewClientSpan(nil, "test-svc", "test-label") + require.NotEmpty(t, clientSpan) + + clientSpan = svc.New(clientSpan, "client-span") + require.NotEmpty(t, clientSpan) + + spanFromCtx, ok := svc.FromContext(context.Background()) + require.False(t, ok) + require.Nil(t, spanFromCtx) + + ctx := svc.NewContext(context.TODO(), clientSpan) + require.NotNil(t, ctx) + clientSpan.Finish() + + spanFromCtx, ok = svc.FromContext(ctx) + require.True(t, ok) + require.NotEmpty(t, spanFromCtx) + + ctx = svc.NewContext(context.TODO(), &mockSpan{}) + require.Nil(t, ctx) +} diff --git a/go/trace/plugin_datadog_test.go b/go/trace/plugin_datadog_test.go new file mode 100644 index 00000000000..4dc43a80c1e --- /dev/null +++ b/go/trace/plugin_datadog_test.go @@ -0,0 +1,39 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trace + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/require" +) + +func TestGetOpenTracingTracer(t *testing.T) { + tracer := datadogTracer{ + actual: opentracing.GlobalTracer(), + } + require.Equal(t, opentracing.GlobalTracer(), tracer.GetOpenTracingTracer()) +} + +func TestNewDataDogTracerHostAndPortNotSet(t *testing.T) { + tracingSvc, closer, err := newDatadogTracer("svc") + expectedErr := "need host and port to datadog agent to use datadog tracing" + require.ErrorContains(t, err, expectedErr) + require.Nil(t, tracingSvc) + require.Nil(t, closer) +} diff --git a/go/trace/plugin_jaeger_test.go b/go/trace/plugin_jaeger_test.go new file mode 100644 index 00000000000..0deab36c7ce --- /dev/null +++ b/go/trace/plugin_jaeger_test.go @@ -0,0 +1,35 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trace + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewJaegerTracerFromEnv(t *testing.T) { + tracingSvc, closer, err := newJagerTracerFromEnv("noop") + require.NoError(t, err) + require.NotEmpty(t, tracingSvc) + require.NotEmpty(t, closer) + + tracingSvc, closer, err = newJagerTracerFromEnv("") + require.ErrorContains(t, err, "no service name provided") + require.Empty(t, tracingSvc) + require.Empty(t, closer) +} diff --git a/go/trace/trace_test.go b/go/trace/trace_test.go index c98a47167a8..6b5bdf491bb 100644 --- a/go/trace/trace_test.go +++ b/go/trace/trace_test.go @@ -22,7 +22,9 @@ import ( "io" "testing" + "github.com/opentracing/opentracing-go" "github.com/spf13/viper" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "vitess.io/vitess/go/viperutil/vipertest" @@ -68,13 +70,80 @@ func TestRegisterService(t *testing.T) { } } +func TestNewFromString(t *testing.T) { + tests := []struct { + parent string + label string + context context.Context + expectedLog string + isPresent bool + expectedErr string + }{ + { + parent: "", + label: "empty parent", + context: context.TODO(), + expectedLog: "", + isPresent: true, + expectedErr: "parent is empty", + }, + { + parent: "parent", + label: "non-empty parent", + expectedLog: "[key: sql-statement-type values:non-empty parent]\n", + context: context.Background(), + isPresent: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.label, func(t *testing.T) { + span, ctx, err := NewFromString(context.Background(), tt.parent, tt.label) + if tt.expectedErr == "" { + require.NoError(t, err) + require.NotEmpty(t, span) + require.Equal(t, tt.context, ctx) + + got := captureOutput(t, func() { + AnnotateSQL(span, &fakeStringer{tt.label}) + }, true) + + require.Equal(t, tt.expectedLog, got) + } else { + require.ErrorContains(t, err, tt.expectedErr) + require.Nil(t, span) + require.Nil(t, ctx) + } + + copySpan := CopySpan(context.TODO(), tt.context) + if tt.isPresent { + require.Equal(t, tt.context, copySpan) + } else { + require.Equal(t, context.TODO(), copySpan) + } + }) + } +} + +func TestNilCloser(t *testing.T) { + nc := nilCloser{} + require.Nil(t, nc.Close()) +} + type fakeTracer struct { name string log []string } +func (f *fakeTracer) GetOpenTracingTracer() opentracing.Tracer { + return opentracing.GlobalTracer() +} + func (f *fakeTracer) NewFromString(parent, label string) (Span, error) { - panic("implement me") + if parent == "" { + return &mockSpan{tracer: f}, fmt.Errorf("parent is empty") + } + return &mockSpan{tracer: f}, nil } func (f *fakeTracer) New(parent Span, label string) Span { @@ -84,7 +153,10 @@ func (f *fakeTracer) New(parent Span, label string) Span { } func (f *fakeTracer) FromContext(ctx context.Context) (Span, bool) { - return nil, false + if ctx == context.Background() { + return nil, false + } + return &mockSpan{}, true } func (f *fakeTracer) NewContext(parent context.Context, span Span) context.Context { @@ -113,4 +185,13 @@ func (m *mockSpan) Finish() { func (m *mockSpan) Annotate(key string, value any) { m.tracer.log = append(m.tracer.log, fmt.Sprintf("key: %v values:%v", key, value)) + fmt.Println(m.tracer.log) +} + +type fakeStringer struct { + str string +} + +func (fs *fakeStringer) String() string { + return fs.str } diff --git a/go/trace/utils_test.go b/go/trace/utils_test.go new file mode 100644 index 00000000000..63bbcfa1528 --- /dev/null +++ b/go/trace/utils_test.go @@ -0,0 +1,41 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package trace + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLogErrorsWhenClosing(t *testing.T) { + logFunc := LogErrorsWhenClosing(&fakeCloser{}) + + got := captureOutput(t, func() { + logFunc() + }, false) + + require.Contains(t, string(got), "test error") +} + +type fakeCloser struct { +} + +func (fc *fakeCloser) Close() error { + return fmt.Errorf("test error") +} diff --git a/go/unicode2/unicode.go b/go/unicode2/unicode.go index bb1942289fe..126c32fc3cd 100644 --- a/go/unicode2/unicode.go +++ b/go/unicode2/unicode.go @@ -117,7 +117,7 @@ func sortIter(t []tableIndex) { } } -// next16 finds the ranged to be added to the table. If ranges overlap between +// next16 finds the range to be added to the table. If ranges overlap between // multiple tables it clips the result to a non-overlapping range if the // elements are not fully subsumed. It returns a zero range if there are no more // ranges. diff --git a/go/unicode2/unicode_test.go b/go/unicode2/unicode_test.go new file mode 100644 index 00000000000..e955d677b49 --- /dev/null +++ b/go/unicode2/unicode_test.go @@ -0,0 +1,267 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package unicode2 + +import ( + "testing" + "unicode" + + "github.com/stretchr/testify/assert" +) + +func TestMerge(t *testing.T) { + // Test for no range tables + rt := Merge() + assert.Equal(t, &unicode.RangeTable{}, rt) + + testCases := []struct { + rt1 *unicode.RangeTable + rt2 *unicode.RangeTable + expected *unicode.RangeTable + }{ + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 67, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 68, Hi: 71, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 2001, Hi: 3000, Stride: 1}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 71, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 3000, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 70, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2100, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 68, Hi: 72, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 2000, Hi: 3100, Stride: 1}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 72, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 3100, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 65, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1000, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 70, Hi: 70, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 2000, Hi: 2001, Stride: 1}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 70, Stride: 5}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1000, Stride: 1}, + {Lo: 2000, Hi: 2001, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 65, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1000, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 70, Hi: 71, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 2000, Hi: 2000, Stride: 1}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 65, Stride: 1}, + {Lo: 70, Hi: 71, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1000}, + }, + LatinOffset: 2, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 68, Stride: 1}, + {Lo: 100, Hi: 104, Stride: 2}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1004, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 69, Hi: 75, Stride: 2}, + }, + R32: []unicode.Range32{ + {Lo: 1005, Hi: 2000, Stride: 2}, + {Lo: 2003, Hi: 2006, Stride: 3}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 69, Stride: 1}, + {Lo: 71, Hi: 75, Stride: 2}, + {Lo: 100, Hi: 104, Stride: 2}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1005, Stride: 1}, + {Lo: 1007, Hi: 2000, Stride: 2}, + {Lo: 2003, Hi: 2006, Stride: 3}, + }, + LatinOffset: 3, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 78, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 75, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1500, Stride: 1}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 78, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 78, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 75, Stride: 8}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1500, Stride: 3}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 78, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 2000, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + { + rt1: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 78, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1500, Stride: 1}, + }, + }, + rt2: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 79, Hi: 84, Stride: 8}, + }, + R32: []unicode.Range32{ + {Lo: 1501, Hi: 2000, Stride: 500}, + }, + }, + expected: &unicode.RangeTable{ + R16: []unicode.Range16{ + {Lo: 65, Hi: 79, Stride: 1}, + }, + R32: []unicode.Range32{ + {Lo: 1000, Hi: 1501, Stride: 1}, + }, + LatinOffset: 1, + }, + }, + } + + for _, tc := range testCases { + rt := Merge(tc.rt1, tc.rt2) + + assert.Equal(t, tc.expected, rt) + } +} diff --git a/go/viperutil/internal/sync/sync.go b/go/viperutil/internal/sync/sync.go index 6608569d86c..6bee1a14e72 100644 --- a/go/viperutil/internal/sync/sync.go +++ b/go/viperutil/internal/sync/sync.go @@ -147,6 +147,7 @@ func (v *Viper) Watch(ctx context.Context, static *viper.Viper, minWaitInterval v.disk.SetConfigFile(cfg) if err := v.disk.ReadInConfig(); err != nil { + cancel() return nil, err } diff --git a/go/vt/binlog/binlog_streamer.go b/go/vt/binlog/binlog_streamer.go index abbf73ba506..d62fcc3a915 100644 --- a/go/vt/binlog/binlog_streamer.go +++ b/go/vt/binlog/binlog_streamer.go @@ -251,8 +251,8 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog var statements []FullBinlogStatement var format mysql.BinlogFormat var gtid replication.GTID - var pos = bls.startPos - var autocommit = true + pos := bls.startPos + autocommit := true var err error // Remember the RBR state. @@ -723,7 +723,7 @@ func (bls *Streamer) appendDeletes(statements []FullBinlogStatement, tce *tableC } // writeValuesAsSQL is a helper method to print the values as SQL in the -// provided bytes.Buffer. It also returns the value for the keyspaceIDColumn, +// provided strings.Builder. It also returns the value for the keyspaceIDColumn, // and the array of values for the PK, if necessary. func writeValuesAsSQL(sql *sqlparser.TrackedBuffer, tce *tableCacheEntry, rs *mysql.Rows, rowIndex int, getPK bool) (sqltypes.Value, []sqltypes.Value, error) { valueIndex := 0 @@ -794,7 +794,7 @@ func writeValuesAsSQL(sql *sqlparser.TrackedBuffer, tce *tableCacheEntry, rs *my } // writeIdentifiersAsSQL is a helper method to print the identifies as SQL in the -// provided bytes.Buffer. It also returns the value for the keyspaceIDColumn, +// provided strings.Builder. It also returns the value for the keyspaceIDColumn, // and the array of values for the PK, if necessary. func writeIdentifiersAsSQL(sql *sqlparser.TrackedBuffer, tce *tableCacheEntry, rs *mysql.Rows, rowIndex int, getPK bool) (sqltypes.Value, []sqltypes.Value, error) { valueIndex := 0 diff --git a/go/vt/binlog/binlog_streamer_rbr_test.go b/go/vt/binlog/binlog_streamer_rbr_test.go index d8481ca0665..1678b086719 100644 --- a/go/vt/binlog/binlog_streamer_rbr_test.go +++ b/go/vt/binlog/binlog_streamer_rbr_test.go @@ -53,7 +53,7 @@ func TestStreamerParseRBREvents(t *testing.T) { }, { Name: "message", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, }) @@ -302,7 +302,7 @@ func TestStreamerParseRBRNameEscapes(t *testing.T) { }, { Name: "delete", Type: querypb.Type_VARCHAR, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, }) diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 6d689bc5436..6bdc2d70d2d 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -34,14 +34,13 @@ import ( "time" "github.com/spf13/pflag" - - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/mysql/sqlerror" - "google.golang.org/protobuf/proto" "vitess.io/vitess/go/history" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/log" @@ -60,8 +59,12 @@ var ( // BlplQuery is the key for the stats map. BlplQuery = "Query" + // BlplMultiQuery is the key for the stats map. + BlplMultiQuery = "MultiQuery" // BlplTransaction is the key for the stats map. BlplTransaction = "Transaction" + // BlplBatchTransaction is the key for the stats map. + BlplBatchTransaction = "BatchTransaction" ) // Stats is the internal stats of a player. It is a different @@ -84,13 +87,15 @@ type Stats struct { State atomic.Value - PhaseTimings *stats.Timings - QueryTimings *stats.Timings - QueryCount *stats.CountersWithSingleLabel - CopyRowCount *stats.Counter - CopyLoopCount *stats.Counter - ErrorCounts *stats.CountersWithMultiLabels - NoopQueryCount *stats.CountersWithSingleLabel + PhaseTimings *stats.Timings + QueryTimings *stats.Timings + QueryCount *stats.CountersWithSingleLabel + BulkQueryCount *stats.CountersWithSingleLabel + TrxQueryBatchCount *stats.CountersWithSingleLabel + CopyRowCount *stats.Counter + CopyLoopCount *stats.Counter + ErrorCounts *stats.CountersWithMultiLabels + NoopQueryCount *stats.CountersWithSingleLabel VReplicationLags *stats.Timings VReplicationLagRates *stats.Rates @@ -100,6 +105,8 @@ type Stats struct { PartialQueryCount *stats.CountersWithMultiLabels PartialQueryCacheSize *stats.CountersWithMultiLabels + + ThrottledCounts *stats.CountersWithMultiLabels // By throttler and component } // RecordHeartbeat updates the time the last heartbeat from vstreamer was seen @@ -157,6 +164,8 @@ func NewStats() *Stats { bps.PhaseTimings = stats.NewTimings("", "", "Phase") bps.QueryTimings = stats.NewTimings("", "", "Phase") bps.QueryCount = stats.NewCountersWithSingleLabel("", "", "Phase", "") + bps.BulkQueryCount = stats.NewCountersWithSingleLabel("", "", "Statement", "") + bps.TrxQueryBatchCount = stats.NewCountersWithSingleLabel("", "", "Statement", "") bps.CopyRowCount = stats.NewCounter("", "") bps.CopyLoopCount = stats.NewCounter("", "") bps.ErrorCounts = stats.NewCountersWithMultiLabels("", "", []string{"type"}) @@ -167,6 +176,7 @@ func NewStats() *Stats { bps.TableCopyTimings = stats.NewTimings("", "", "Table") bps.PartialQueryCacheSize = stats.NewCountersWithMultiLabels("", "", []string{"type"}) bps.PartialQueryCount = stats.NewCountersWithMultiLabels("", "", []string{"type"}) + bps.ThrottledCounts = stats.NewCountersWithMultiLabels("", "", []string{"throttler", "component"}) return bps } @@ -362,13 +372,14 @@ func (blp *BinlogPlayer) applyEvents(ctx context.Context) error { if backoff == throttler.NotThrottled { break } + blp.blplStats.ThrottledCounts.Add([]string{"trx", "binlogplayer"}, 1) // We don't bother checking for context cancellation here because the // sleep will block only up to 1 second. (Usually, backoff is 1s / rate // e.g. a rate of 1000 TPS results into a backoff of 1 ms.) time.Sleep(backoff) } - // get the response + // Get the response. response, err := stream.Recv() // Check context before checking error, because canceled // contexts could be wrapped as regular errors. @@ -598,6 +609,7 @@ func ReadVRSettings(dbClient DBClient, uid int32) (VRSettings, error) { // the _vt.vreplication table. func CreateVReplication(workflow string, source *binlogdatapb.BinlogSource, position string, maxTPS, maxReplicationLag, timeUpdated int64, dbName string, workflowType binlogdatapb.VReplicationWorkflowType, workflowSubType binlogdatapb.VReplicationWorkflowSubType, deferSecondaryKeys bool) string { + protoutil.SortBinlogSourceTables(source) return fmt.Sprintf("insert into _vt.vreplication "+ "(workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type, defer_secondary_keys) "+ "values (%v, %v, %v, %v, %v, %v, 0, '%v', %v, %d, %d, %v)", @@ -608,6 +620,7 @@ func CreateVReplication(workflow string, source *binlogdatapb.BinlogSource, posi // CreateVReplicationState returns a statement to create a stopped vreplication. func CreateVReplicationState(workflow string, source *binlogdatapb.BinlogSource, position string, state binlogdatapb.VReplicationWorkflowState, dbName string, workflowType binlogdatapb.VReplicationWorkflowType, workflowSubType binlogdatapb.VReplicationWorkflowSubType) string { + protoutil.SortBinlogSourceTables(source) return fmt.Sprintf("insert into _vt.vreplication "+ "(workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type) "+ "values (%v, %v, %v, %v, %v, %v, 0, '%v', %v, %d, %d)", @@ -652,13 +665,6 @@ func GenerateUpdateTimeThrottled(uid int32, timeThrottledUnix int64, componentTh return fmt.Sprintf("update _vt.vreplication set time_updated=%v, time_throttled=%v, component_throttled='%v' where id=%v", timeThrottledUnix, timeThrottledUnix, componentThrottled, uid), nil } -// StartVReplication returns a statement to start the replication. -func StartVReplication(uid int32) string { - return fmt.Sprintf( - "update _vt.vreplication set state='%v', stop_pos=NULL where id=%v", - binlogdatapb.VReplicationWorkflowState_Running.String(), uid) -} - // StartVReplicationUntil returns a statement to start the replication with a stop position. func StartVReplicationUntil(uid int32, pos string) string { return fmt.Sprintf( diff --git a/go/vt/binlog/binlogplayer/dbclient.go b/go/vt/binlog/binlogplayer/dbclient.go index f9cd03691a5..bc96e690b76 100644 --- a/go/vt/binlog/binlogplayer/dbclient.go +++ b/go/vt/binlog/binlogplayer/dbclient.go @@ -19,6 +19,7 @@ package binlogplayer import ( "context" "fmt" + "strings" "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/mysql" @@ -38,12 +39,14 @@ type DBClient interface { Rollback() error Close() ExecuteFetch(query string, maxrows int) (qr *sqltypes.Result, err error) + ExecuteFetchMulti(query string, maxrows int) (qrs []*sqltypes.Result, err error) } // dbClientImpl is a real DBClient backed by a mysql connection. type dbClientImpl struct { dbConfig dbconfigs.Connector dbConn *mysql.Conn + parser *sqlparser.Parser } // dbClientImplWithSidecarDBReplacement is a DBClient implementation @@ -55,14 +58,15 @@ type dbClientImplWithSidecarDBReplacement struct { } // NewDBClient creates a DBClient instance -func NewDBClient(params dbconfigs.Connector) DBClient { +func NewDBClient(params dbconfigs.Connector, parser *sqlparser.Parser) DBClient { if sidecar.GetName() != sidecar.DefaultName { return &dbClientImplWithSidecarDBReplacement{ - dbClientImpl{dbConfig: params}, + dbClientImpl{dbConfig: params, parser: parser}, } } return &dbClientImpl{ dbConfig: params, + parser: parser, } } @@ -140,11 +144,47 @@ func (dc *dbClientImpl) ExecuteFetch(query string, maxrows int) (*sqltypes.Resul return mqr, nil } +func (dc *dbClientImpl) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + results := make([]*sqltypes.Result, 0) + mqr, more, err := dc.dbConn.ExecuteFetchMulti(query, maxrows, true) + if err != nil { + dc.handleError(err) + return nil, err + } + results = append(results, mqr) + for more { + mqr, more, _, err = dc.dbConn.ReadQueryResult(maxrows, false) + if err != nil { + dc.handleError(err) + return nil, err + } + results = append(results, mqr) + } + return results, nil +} + func (dcr *dbClientImplWithSidecarDBReplacement) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, error) { // Replace any provided sidecar database qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) + uq, err := dcr.parser.ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } return dcr.dbClientImpl.ExecuteFetch(uq, maxrows) } + +func (dcr *dbClientImplWithSidecarDBReplacement) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + // Replace any provided sidecar database qualifiers with the correct one. + qps, err := dcr.parser.SplitStatementToPieces(query) + if err != nil { + return nil, err + } + for i, qp := range qps { + uq, err := dcr.parser.ReplaceTableQualifiers(qp, sidecar.DefaultName, sidecar.GetName()) + if err != nil { + return nil, err + } + qps[i] = uq + } + + return dcr.dbClientImpl.ExecuteFetchMulti(strings.Join(qps, ";"), maxrows) +} diff --git a/go/vt/binlog/binlogplayer/fake_dbclient.go b/go/vt/binlog/binlogplayer/fake_dbclient.go index 186722cf12f..750f35b3fe3 100644 --- a/go/vt/binlog/binlogplayer/fake_dbclient.go +++ b/go/vt/binlog/binlogplayer/fake_dbclient.go @@ -80,3 +80,7 @@ func (dc *fakeDBClient) ExecuteFetch(query string, maxrows int) (qr *sqltypes.Re } return nil, fmt.Errorf("unexpected: %v", query) } + +func (dc *fakeDBClient) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + return make([]*sqltypes.Result, 0), nil +} diff --git a/go/vt/binlog/binlogplayer/mock_dbclient.go b/go/vt/binlog/binlogplayer/mock_dbclient.go index d64c4d40146..abc170ed493 100644 --- a/go/vt/binlog/binlogplayer/mock_dbclient.go +++ b/go/vt/binlog/binlogplayer/mock_dbclient.go @@ -25,6 +25,7 @@ import ( "time" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/sqlparser" ) const mockClientUNameFiltered = "Filtered" @@ -41,6 +42,7 @@ type MockDBClient struct { done chan struct{} invariants map[string]*sqltypes.Result Tag string + parser *sqlparser.Parser } type mockExpect struct { @@ -83,15 +85,17 @@ func NewMockDBClient(t *testing.T) *MockDBClient { "set @@session.sql_mode": {}, "set sql_mode": {}, }, + parser: sqlparser.NewTestParser(), } } // NewMockDbaClient returns a new DBClientMock with the default "Dba" UName. func NewMockDbaClient(t *testing.T) *MockDBClient { return &MockDBClient{ - t: t, - UName: mockClientUNameDba, - done: make(chan struct{}), + t: t, + UName: mockClientUNameDba, + done: make(chan struct{}), + parser: sqlparser.NewTestParser(), } } @@ -224,3 +228,19 @@ func (dc *MockDBClient) ExecuteFetch(query string, maxrows int) (qr *sqltypes.Re } return result.result, result.err } + +func (dc *MockDBClient) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + queries, err := dc.parser.SplitStatementToPieces(query) + if err != nil { + return nil, err + } + results := make([]*sqltypes.Result, 0, len(queries)) + for _, query := range queries { + qr, err := dc.ExecuteFetch(query, maxrows) + if err != nil { + return nil, err + } + results = append(results, qr) + } + return results, nil +} diff --git a/go/vt/binlog/event_streamer.go b/go/vt/binlog/event_streamer.go deleted file mode 100644 index a872b089bff..00000000000 --- a/go/vt/binlog/event_streamer.go +++ /dev/null @@ -1,315 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binlog - -import ( - "context" - "encoding/base64" - "fmt" - "strconv" - "strings" - - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" -) - -var ( - binlogSetInsertID = "SET INSERT_ID=" - binlogSetInsertIDLen = len(binlogSetInsertID) - streamCommentStart = "/* _stream " - streamCommentStartLen = len(streamCommentStart) -) - -type sendEventFunc func(event *querypb.StreamEvent) error - -// EventStreamer is an adapter on top of a binlog Streamer that convert -// the events into StreamEvent objects. -type EventStreamer struct { - bls *Streamer - sendEvent sendEventFunc -} - -// NewEventStreamer returns a new EventStreamer on top of a Streamer -func NewEventStreamer(cp dbconfigs.Connector, se *schema.Engine, startPos replication.Position, timestamp int64, sendEvent sendEventFunc) *EventStreamer { - evs := &EventStreamer{ - sendEvent: sendEvent, - } - evs.bls = NewStreamer(cp, se, nil, startPos, timestamp, evs.transactionToEvent) - evs.bls.extractPK = true - return evs -} - -// Stream starts streaming updates -func (evs *EventStreamer) Stream(ctx context.Context) error { - return evs.bls.Stream(ctx) -} - -func (evs *EventStreamer) transactionToEvent(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { - event := &querypb.StreamEvent{ - EventToken: eventToken, - } - var err error - var insertid int64 - for _, stmt := range statements { - switch stmt.Statement.Category { - case binlogdatapb.BinlogTransaction_Statement_BL_SET: - sql := string(stmt.Statement.Sql) - if strings.HasPrefix(sql, binlogSetInsertID) { - insertid, err = strconv.ParseInt(sql[binlogSetInsertIDLen:], 10, 64) - if err != nil { - binlogStreamerErrors.Add("EventStreamer", 1) - log.Errorf("%v: %s", err, sql) - } - } - case binlogdatapb.BinlogTransaction_Statement_BL_INSERT, - binlogdatapb.BinlogTransaction_Statement_BL_UPDATE, - binlogdatapb.BinlogTransaction_Statement_BL_DELETE: - var dmlStatement *querypb.StreamEvent_Statement - dmlStatement, insertid, err = evs.buildDMLStatement(stmt, insertid) - if err != nil { - dmlStatement = &querypb.StreamEvent_Statement{ - Category: querypb.StreamEvent_Statement_Error, - Sql: stmt.Statement.Sql, - } - } - event.Statements = append(event.Statements, dmlStatement) - case binlogdatapb.BinlogTransaction_Statement_BL_DDL: - ddlStatement := &querypb.StreamEvent_Statement{ - Category: querypb.StreamEvent_Statement_DDL, - Sql: stmt.Statement.Sql, - } - event.Statements = append(event.Statements, ddlStatement) - case binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED: - unrecognized := &querypb.StreamEvent_Statement{ - Category: querypb.StreamEvent_Statement_Error, - Sql: stmt.Statement.Sql, - } - event.Statements = append(event.Statements, unrecognized) - default: - binlogStreamerErrors.Add("EventStreamer", 1) - log.Errorf("Unrecognized event: %v: %s", stmt.Statement.Category, stmt.Statement.Sql) - } - } - return evs.sendEvent(event) -} - -/* -buildDMLStatement recovers the PK from a FullBinlogStatement. -For RBR, the values are already in there, just need to be translated. -For SBR, parses the tuples of the full stream comment. -The _stream comment is extracted into a StreamEvent.Statement. -*/ -// Example query: insert into _table_(foo) values ('foo') /* _stream _table_ (eid id name ) (null 1 'bmFtZQ==' ); */ -// the "null" value is used for auto-increment columns. -func (evs *EventStreamer) buildDMLStatement(stmt FullBinlogStatement, insertid int64) (*querypb.StreamEvent_Statement, int64, error) { - // For RBR events, we know all this already, just extract it. - if stmt.PKNames != nil { - // We get an array of []sqltypes.Value, need to convert to querypb.Row. - dmlStatement := &querypb.StreamEvent_Statement{ - Category: querypb.StreamEvent_Statement_DML, - TableName: stmt.Table, - PrimaryKeyFields: stmt.PKNames, - PrimaryKeyValues: []*querypb.Row{sqltypes.RowToProto3(stmt.PKValues)}, - } - // InsertID is only needed to fill in the ID on next queries, - // but if we use RBR, it's already in the values, so just return 0. - return dmlStatement, 0, nil - } - - sql := string(stmt.Statement.Sql) - - // first extract the comment - commentIndex := strings.LastIndex(sql, streamCommentStart) - if commentIndex == -1 { - return nil, insertid, fmt.Errorf("missing stream comment") - } - dmlComment := sql[commentIndex+streamCommentStartLen:] - - // then start building the response - dmlStatement := &querypb.StreamEvent_Statement{ - Category: querypb.StreamEvent_Statement_DML, - } - tokenizer := sqlparser.NewStringTokenizer(dmlComment) - - // first parse the table name - typ, val := tokenizer.Scan() - if typ != sqlparser.ID { - return nil, insertid, fmt.Errorf("expecting table name in stream comment") - } - dmlStatement.TableName = string(val) - - // then parse the PK names - var err error - dmlStatement.PrimaryKeyFields, err = parsePkNames(tokenizer) - hasNegatives := make([]bool, len(dmlStatement.PrimaryKeyFields)) - if err != nil { - return nil, insertid, err - } - - // then parse the PK values, one at a time - for typ, _ = tokenizer.Scan(); typ != ';'; typ, _ = tokenizer.Scan() { - switch typ { - case '(': - // pkTuple is a list of pk values - var pkTuple *querypb.Row - pkTuple, insertid, err = parsePkTuple(tokenizer, insertid, dmlStatement.PrimaryKeyFields, hasNegatives) - if err != nil { - return nil, insertid, err - } - dmlStatement.PrimaryKeyValues = append(dmlStatement.PrimaryKeyValues, pkTuple) - default: - return nil, insertid, fmt.Errorf("expecting '('") - } - } - - return dmlStatement, insertid, nil -} - -// parsePkNames parses something like (eid id name ) -func parsePkNames(tokenizer *sqlparser.Tokenizer) ([]*querypb.Field, error) { - var columns []*querypb.Field - if typ, _ := tokenizer.Scan(); typ != '(' { - return nil, fmt.Errorf("expecting '('") - } - for typ, val := tokenizer.Scan(); typ != ')'; typ, val = tokenizer.Scan() { - switch typ { - case sqlparser.ID: - columns = append(columns, &querypb.Field{ - Name: string(val), - }) - default: - return nil, fmt.Errorf("syntax error at position: %d", tokenizer.Pos) - } - } - return columns, nil -} - -// parsePkTuple parses something like (null 1 'bmFtZQ==' ). For numbers, the default -// type is Int64. If an unsigned number that can't fit in an int64 is seen, then the -// type is set to Uint64. In such cases, if a negative number was previously seen, the -// function returns an error. -func parsePkTuple(tokenizer *sqlparser.Tokenizer, insertid int64, fields []*querypb.Field, hasNegatives []bool) (*querypb.Row, int64, error) { - result := &querypb.Row{} - - index := 0 - for typ, val := tokenizer.Scan(); typ != ')'; typ, val = tokenizer.Scan() { - if index >= len(fields) { - return nil, insertid, fmt.Errorf("length mismatch in values") - } - - switch typ { - case '-': - hasNegatives[index] = true - typ2, val2 := tokenizer.Scan() - if typ2 != sqlparser.INTEGRAL { - return nil, insertid, fmt.Errorf("expecting number after '-'") - } - fullVal := append([]byte{'-'}, val2...) - if _, err := strconv.ParseInt(string(fullVal), 0, 64); err != nil { - return nil, insertid, err - } - switch fields[index].Type { - case sqltypes.Null: - fields[index].Type = sqltypes.Int64 - case sqltypes.Int64: - // no-op - default: - return nil, insertid, fmt.Errorf("incompatible negative number field with type %v", fields[index].Type) - } - - result.Lengths = append(result.Lengths, int64(len(fullVal))) - result.Values = append(result.Values, fullVal...) - case sqlparser.INTEGRAL: - unsigned, err := strconv.ParseUint(string(val), 0, 64) - if err != nil { - return nil, insertid, err - } - if unsigned > uint64(9223372036854775807) { - // Number is a uint64 that can't fit in an int64. - if hasNegatives[index] { - return nil, insertid, fmt.Errorf("incompatible unsigned number field with type %v", fields[index].Type) - } - switch fields[index].Type { - case sqltypes.Null, sqltypes.Int64: - fields[index].Type = sqltypes.Uint64 - case sqltypes.Uint64: - // no-op - default: - return nil, insertid, fmt.Errorf("incompatible number field with type %v", fields[index].Type) - } - } else { - // Could be int64 or uint64. - switch fields[index].Type { - case sqltypes.Null: - fields[index].Type = sqltypes.Int64 - case sqltypes.Int64, sqltypes.Uint64: - // no-op - default: - return nil, insertid, fmt.Errorf("incompatible number field with type %v", fields[index].Type) - } - } - - result.Lengths = append(result.Lengths, int64(len(val))) - result.Values = append(result.Values, val...) - case sqlparser.NULL: - switch fields[index].Type { - case sqltypes.Null: - fields[index].Type = sqltypes.Int64 - case sqltypes.Int64, sqltypes.Uint64: - // no-op - default: - return nil, insertid, fmt.Errorf("incompatible auto-increment field with type %v", fields[index].Type) - } - - v := strconv.AppendInt(nil, insertid, 10) - result.Lengths = append(result.Lengths, int64(len(v))) - result.Values = append(result.Values, v...) - insertid++ - case sqlparser.STRING: - switch fields[index].Type { - case sqltypes.Null: - fields[index].Type = sqltypes.VarBinary - case sqltypes.VarBinary: - // no-op - default: - return nil, insertid, fmt.Errorf("incompatible string field with type %v", fields[index].Type) - } - - decoded, err := base64.StdEncoding.DecodeString(val) - if err != nil { - return nil, insertid, err - } - result.Lengths = append(result.Lengths, int64(len(decoded))) - result.Values = append(result.Values, decoded...) - default: - return nil, insertid, fmt.Errorf("syntax error at position: %d", tokenizer.Pos) - } - index++ - } - - if index != len(fields) { - return nil, insertid, fmt.Errorf("length mismatch in values") - } - return result, insertid, nil -} diff --git a/go/vt/binlog/event_streamer_test.go b/go/vt/binlog/event_streamer_test.go deleted file mode 100644 index 38e50240d1c..00000000000 --- a/go/vt/binlog/event_streamer_test.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binlog - -import ( - "testing" - - "vitess.io/vitess/go/test/utils" - - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" -) - -var dmlErrorCases = []string{ - "query", - "query /* _stream 10 (eid id `name` ) (null 1 'bmFtZQ==' ); */", - "query /* _stream _table_ eid id `name` ) (null 1 'bmFtZQ==' ); */", - "query /* _stream _table_ (10 id `name` ) (null 1 'bmFtZQ==' ); */", - "query /* _stream _table_ (eid id `name` (null 1 'bmFtZQ==' ); */", - "query /* _stream _table_ (eid id `name`) (null 'aaa' 'bmFtZQ==' ); */", - "query /* _stream _table_ (eid id `name`) (null 'bmFtZQ==' ); */", - "query /* _stream _table_ (eid id `name`) (null 1.1 'bmFtZQ==' ); */", - "query /* _stream _table_ (eid id `name`) (null a 'bmFtZQ==' ); */", -} - -func TestEventErrors(t *testing.T) { - var got *querypb.StreamEvent - evs := &EventStreamer{ - sendEvent: func(event *querypb.StreamEvent) error { - got = event - return nil - }, - } - for _, sql := range dmlErrorCases { - statements := []FullBinlogStatement{ - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, - Sql: []byte(sql), - }, - }, - } - err := evs.transactionToEvent(nil, statements) - if err != nil { - t.Errorf("%s: %v", sql, err) - continue - } - want := &querypb.StreamEvent{ - Statements: []*querypb.StreamEvent_Statement{ - { - Category: querypb.StreamEvent_Statement_Error, - Sql: []byte(sql), - }, - }, - } - if !proto.Equal(got, want) { - t.Errorf("error for SQL: '%v' got: %+v, want: %+v", sql, got, want) - } - } -} - -func TestSetErrors(t *testing.T) { - evs := &EventStreamer{ - sendEvent: func(event *querypb.StreamEvent) error { - return nil - }, - } - statements := []FullBinlogStatement{ - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, - Sql: []byte("SET INSERT_ID=abcd"), - }, - }, - } - before := binlogStreamerErrors.Counts()["EventStreamer"] - err := evs.transactionToEvent(nil, statements) - require.NoError(t, err) - got := binlogStreamerErrors.Counts()["EventStreamer"] - if got != before+1 { - t.Errorf("got: %v, want: %+v", got, before+1) - } -} - -func TestDMLEvent(t *testing.T) { - statements := []FullBinlogStatement{ - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, - Sql: []byte("SET TIMESTAMP=2"), - }, - }, - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, - Sql: []byte("SET INSERT_ID=10"), - }, - }, - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, - Sql: []byte("query /* _stream _table_ (eid id `name`) (null 1 'bmFtZQ==' ) (null 18446744073709551615 'bmFtZQ==' ); */"), - }, - }, - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, - Sql: []byte("query"), - }, - }, - } - eventToken := &querypb.EventToken{ - Timestamp: 1, - Position: "MariaDB/0-41983-20", - } - evs := &EventStreamer{ - sendEvent: func(event *querypb.StreamEvent) error { - for _, statement := range event.Statements { - switch statement.Category { - case querypb.StreamEvent_Statement_DML: - want := `category:DML table_name:"_table_" primary_key_fields:{name:"eid" type:INT64} primary_key_fields:{name:"id" type:UINT64} primary_key_fields:{name:"name" type:VARBINARY} primary_key_values:{lengths:2 lengths:1 lengths:4 values:"101name"} primary_key_values:{lengths:2 lengths:20 lengths:4 values:"1118446744073709551615name"}` - utils.MustMatchPB(t, want, statement) - case querypb.StreamEvent_Statement_Error: - want := `sql:"query"` - utils.MustMatchPB(t, want, statement) - default: - t.Errorf("unexpected: %#v", event) - } - } - // then test the position - want := `timestamp:1 position:"MariaDB/0-41983-20"` - utils.MustMatchPB(t, want, event.EventToken) - return nil - }, - } - err := evs.transactionToEvent(eventToken, statements) - require.NoError(t, err) -} - -func TestDDLEvent(t *testing.T) { - statements := []FullBinlogStatement{ - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, - Sql: []byte("SET TIMESTAMP=2"), - }, - }, - { - Statement: &binlogdatapb.BinlogTransaction_Statement{ - Category: binlogdatapb.BinlogTransaction_Statement_BL_DDL, - Sql: []byte("DDL"), - }, - }, - } - eventToken := &querypb.EventToken{ - Timestamp: 1, - Position: "MariaDB/0-41983-20", - } - evs := &EventStreamer{ - sendEvent: func(event *querypb.StreamEvent) error { - for _, statement := range event.Statements { - switch statement.Category { - case querypb.StreamEvent_Statement_DDL: - want := `category:DDL sql:"DDL"` - utils.MustMatchPB(t, want, statement) - default: - t.Errorf("unexpected: %#v", event) - } - } - // then test the position - want := `timestamp:1 position:"MariaDB/0-41983-20"` - utils.MustMatchPB(t, want, event.EventToken) - return nil - }, - } - err := evs.transactionToEvent(eventToken, statements) - require.NoError(t, err) -} diff --git a/go/vt/binlog/keyspace_id_resolver.go b/go/vt/binlog/keyspace_id_resolver.go index 6903ba53b71..1ca198760a3 100644 --- a/go/vt/binlog/keyspace_id_resolver.go +++ b/go/vt/binlog/keyspace_id_resolver.go @@ -17,13 +17,13 @@ limitations under the License. package binlog import ( + "context" "fmt" "strings" - "context" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -47,13 +47,13 @@ type keyspaceIDResolverFactory func(*schema.Table) (int, keyspaceIDResolver, err // newKeyspaceIDResolverFactory creates a new // keyspaceIDResolverFactory for the provided keyspace and cell. -func newKeyspaceIDResolverFactory(ctx context.Context, ts *topo.Server, keyspace string, cell string) (keyspaceIDResolverFactory, error) { - return newKeyspaceIDResolverFactoryV3(ctx, ts, keyspace, cell) +func newKeyspaceIDResolverFactory(ctx context.Context, ts *topo.Server, keyspace string, cell string, parser *sqlparser.Parser) (keyspaceIDResolverFactory, error) { + return newKeyspaceIDResolverFactoryV3(ctx, ts, keyspace, cell, parser) } // newKeyspaceIDResolverFactoryV3 finds the SrvVSchema in the cell, // gets the keyspace part, and uses it to find the column name. -func newKeyspaceIDResolverFactoryV3(ctx context.Context, ts *topo.Server, keyspace string, cell string) (keyspaceIDResolverFactory, error) { +func newKeyspaceIDResolverFactoryV3(ctx context.Context, ts *topo.Server, keyspace string, cell string, parser *sqlparser.Parser) (keyspaceIDResolverFactory, error) { srvVSchema, err := ts.GetSrvVSchema(ctx, cell) if err != nil { return nil, err @@ -62,7 +62,7 @@ func newKeyspaceIDResolverFactoryV3(ctx context.Context, ts *topo.Server, keyspa if !ok { return nil, fmt.Errorf("SrvVSchema has no entry for keyspace %v", keyspace) } - keyspaceSchema, err := vindexes.BuildKeyspaceSchema(kschema, keyspace) + keyspaceSchema, err := vindexes.BuildKeyspaceSchema(kschema, keyspace, parser) if err != nil { return nil, fmt.Errorf("cannot build vschema for keyspace %v: %v", keyspace, err) } diff --git a/go/vt/binlog/updatestreamctl.go b/go/vt/binlog/updatestreamctl.go index 78d61c0860c..4397eccd4da 100644 --- a/go/vt/binlog/updatestreamctl.go +++ b/go/vt/binlog/updatestreamctl.go @@ -27,6 +27,7 @@ import ( "vitess.io/vitess/go/tb" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -70,47 +71,6 @@ type UpdateStreamControl interface { IsEnabled() bool } -// UpdateStreamControlMock is an implementation of UpdateStreamControl -// to be used in tests -type UpdateStreamControlMock struct { - enabled bool - sync.Mutex -} - -// NewUpdateStreamControlMock creates a new UpdateStreamControlMock -func NewUpdateStreamControlMock() *UpdateStreamControlMock { - return &UpdateStreamControlMock{} -} - -// InitDBConfig is part of UpdateStreamControl -func (m *UpdateStreamControlMock) InitDBConfig(*dbconfigs.DBConfigs) { -} - -// RegisterService is part of UpdateStreamControl -func (m *UpdateStreamControlMock) RegisterService() { -} - -// Enable is part of UpdateStreamControl -func (m *UpdateStreamControlMock) Enable() { - m.Lock() - m.enabled = true - m.Unlock() -} - -// Disable is part of UpdateStreamControl -func (m *UpdateStreamControlMock) Disable() { - m.Lock() - m.enabled = false - m.Unlock() -} - -// IsEnabled is part of UpdateStreamControl -func (m *UpdateStreamControlMock) IsEnabled() bool { - m.Lock() - defer m.Unlock() - return m.enabled -} - // UpdateStreamImpl is the real implementation of UpdateStream // and UpdateStreamControl type UpdateStreamImpl struct { @@ -126,6 +86,7 @@ type UpdateStreamImpl struct { state atomic.Int64 stateWaitGroup sync.WaitGroup streams StreamList + parser *sqlparser.Parser } // StreamList is a map of context.CancelFunc to mass-interrupt ongoing @@ -179,12 +140,13 @@ type RegisterUpdateStreamServiceFunc func(UpdateStream) var RegisterUpdateStreamServices []RegisterUpdateStreamServiceFunc // NewUpdateStream returns a new UpdateStreamImpl object -func NewUpdateStream(ts *topo.Server, keyspace string, cell string, se *schema.Engine) *UpdateStreamImpl { +func NewUpdateStream(ts *topo.Server, keyspace string, cell string, se *schema.Engine, parser *sqlparser.Parser) *UpdateStreamImpl { return &UpdateStreamImpl{ ts: ts, keyspace: keyspace, cell: cell, se: se, + parser: parser, } } @@ -275,7 +237,7 @@ func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, positi return callback(trans) }) bls := NewStreamer(updateStream.cp, updateStream.se, charset, pos, 0, f) - bls.resolverFactory, err = newKeyspaceIDResolverFactory(ctx, updateStream.ts, updateStream.keyspace, updateStream.cell) + bls.resolverFactory, err = newKeyspaceIDResolverFactory(ctx, updateStream.ts, updateStream.keyspace, updateStream.cell, updateStream.parser) if err != nil { return fmt.Errorf("newKeyspaceIDResolverFactory failed: %v", err) } diff --git a/go/vt/callinfo/callinfo_test.go b/go/vt/callinfo/callinfo_test.go new file mode 100644 index 00000000000..c81b7792242 --- /dev/null +++ b/go/vt/callinfo/callinfo_test.go @@ -0,0 +1,117 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package callinfo + +import ( + "context" + "testing" + + "github.com/google/safehtml" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/callinfo/fakecallinfo" +) + +var fci fakecallinfo.FakeCallInfo = fakecallinfo.FakeCallInfo{ + User: "test", + Remote: "locahost", + Method: "", + Html: safehtml.HTML{}, +} + +func TestNewContext(t *testing.T) { + tests := []struct { + name string + ctx context.Context + ci CallInfo + expectedContext context.Context + }{ + { + name: "empty", + ctx: context.Background(), + ci: nil, + expectedContext: context.WithValue(context.Background(), callInfoKey, nil), + }, + { + name: "not empty", + ctx: context.Background(), + ci: &fci, + expectedContext: context.WithValue(context.Background(), callInfoKey, &fci), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expectedContext, NewContext(tt.ctx, tt.ci)) + }) + } +} + +func TestFromContext(t *testing.T) { + tests := []struct { + name string + ctx context.Context + expectedCi CallInfo + ok bool + }{ + { + name: "empty", + ctx: context.WithValue(context.Background(), callInfoKey, nil), + expectedCi: nil, + ok: false, + }, + { + name: "not empty", + expectedCi: &fci, + ctx: context.WithValue(context.Background(), callInfoKey, &fci), + ok: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ci, ok := FromContext(tt.ctx) + require.Equal(t, tt.expectedCi, ci) + require.Equal(t, tt.ok, ok) + }) + } +} + +func TestHTMLFromContext(t *testing.T) { + tests := []struct { + name string + ctx context.Context + expectedHTML safehtml.HTML + }{ + { + name: "empty", + ctx: context.WithValue(context.Background(), callInfoKey, nil), + expectedHTML: safehtml.HTML{}, + }, + { + name: "not empty", + ctx: context.WithValue(context.Background(), callInfoKey, &fci), + expectedHTML: safehtml.HTML{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expectedHTML, HTMLFromContext(tt.ctx)) + }) + } +} diff --git a/go/vt/callinfo/plugin_grpc_test.go b/go/vt/callinfo/plugin_grpc_test.go new file mode 100644 index 00000000000..2c5c8f9d888 --- /dev/null +++ b/go/vt/callinfo/plugin_grpc_test.go @@ -0,0 +1,37 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package callinfo + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGRPCCallInfo(t *testing.T) { + grpcCi := gRPCCallInfoImpl{ + method: "tcp", + remoteAddr: "localhost", + } + + require.Equal(t, context.Background(), GRPCCallInfo(context.Background())) + require.Equal(t, grpcCi.remoteAddr, grpcCi.RemoteAddr()) + require.Equal(t, "gRPC", grpcCi.Username()) + require.Equal(t, "localhost:tcp(gRPC)", grpcCi.Text()) + require.Equal(t, "Method: tcp Remote Addr: localhost", grpcCi.HTML().String()) +} diff --git a/go/vt/callinfo/plugin_mysql_test.go b/go/vt/callinfo/plugin_mysql_test.go new file mode 100644 index 00000000000..abe6aa371dd --- /dev/null +++ b/go/vt/callinfo/plugin_mysql_test.go @@ -0,0 +1,35 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package callinfo + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMysqlCallInfo(t *testing.T) { + mysqlCi := mysqlCallInfoImpl{ + remoteAddr: "localhost", + user: "test", + } + + require.Equal(t, mysqlCi.remoteAddr, mysqlCi.RemoteAddr()) + require.Equal(t, mysqlCi.user, mysqlCi.Username()) + require.Equal(t, "test@localhost(Mysql)", mysqlCi.Text()) + require.Equal(t, "MySQL User: test Remote Addr: localhost", mysqlCi.HTML().String()) +} diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index fe3a228835c..82c322e7ae9 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -26,13 +26,13 @@ import ( "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/vttls" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/log" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttls" "vitess.io/vitess/go/yaml2" ) @@ -123,7 +123,7 @@ func RegisterFlags(userKeys ...string) { servenv.OnParse(func(fs *pflag.FlagSet) { registerBaseFlags(fs) for _, userKey := range userKeys { - uc, cp := GlobalDBConfigs.getParams(userKey, &GlobalDBConfigs) + uc, cp := GlobalDBConfigs.getParams(userKey) registerPerUserFlags(fs, userKey, uc, cp) } }) @@ -318,9 +318,9 @@ func (dbcfgs *DBConfigs) Clone() *DBConfigs { // parameters. This is only for legacy support. // If no per-user parameters are supplied, then the defaultSocketFile // is used to initialize the per-user conn params. -func (dbcfgs *DBConfigs) InitWithSocket(defaultSocketFile string) { +func (dbcfgs *DBConfigs) InitWithSocket(defaultSocketFile string, collationEnv *collations.Environment) { for _, userKey := range All { - uc, cp := dbcfgs.getParams(userKey, dbcfgs) + uc, cp := dbcfgs.getParams(userKey) // TODO @rafael: For ExternalRepl we need to respect the provided host / port // At the moment this is an snowflake user connection type that it used by // vreplication to connect to external mysql hosts that are not part of a vitess @@ -338,8 +338,13 @@ func (dbcfgs *DBConfigs) InitWithSocket(defaultSocketFile string) { // If the connection params has a charset defined, it will not be overridden by the // global configuration. - if dbcfgs.Charset != "" && cp.Charset == "" { - cp.Charset = dbcfgs.Charset + if dbcfgs.Charset != "" && cp.Charset == collations.Unknown { + ch, err := collationEnv.ParseConnectionCharset(dbcfgs.Charset) + if err != nil { + log.Warningf("Error parsing charset %s: %v", dbcfgs.Charset, err) + ch = collationEnv.DefaultConnectionCharset() + } + cp.Charset = ch } if dbcfgs.Flags != 0 { @@ -367,7 +372,7 @@ func (dbcfgs *DBConfigs) InitWithSocket(defaultSocketFile string) { log.Infof("DBConfigs: %v\n", dbcfgs.String()) } -func (dbcfgs *DBConfigs) getParams(userKey string, dbc *DBConfigs) (*UserConfig, *mysql.ConnParams) { +func (dbcfgs *DBConfigs) getParams(userKey string) (*UserConfig, *mysql.ConnParams) { var uc *UserConfig var cp *mysql.ConnParams switch userKey { diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index a97f2526c17..029682d13b7 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/yaml2" ) @@ -36,10 +37,10 @@ func TestInit(t *testing.T) { dbaParams: mysql.ConnParams{Host: "host"}, Charset: "utf8", } - dbConfigs.InitWithSocket("default") - assert.Equal(t, mysql.ConnParams{UnixSocket: "socket", Charset: "utf8"}, dbConfigs.appParams) - assert.Equal(t, mysql.ConnParams{Host: "host", Charset: "utf8"}, dbConfigs.dbaParams) - assert.Equal(t, mysql.ConnParams{UnixSocket: "default", Charset: "utf8"}, dbConfigs.appdebugParams) + dbConfigs.InitWithSocket("default", collations.MySQL8()) + assert.Equal(t, mysql.ConnParams{UnixSocket: "socket", Charset: collations.CollationUtf8mb3ID}, dbConfigs.appParams) + assert.Equal(t, mysql.ConnParams{Host: "host", Charset: collations.CollationUtf8mb3ID}, dbConfigs.dbaParams) + assert.Equal(t, mysql.ConnParams{UnixSocket: "default", Charset: collations.CollationUtf8mb3ID}, dbConfigs.appdebugParams) dbConfigs = DBConfigs{ Host: "a", @@ -72,7 +73,7 @@ func TestInit(t *testing.T) { Host: "host", }, } - dbConfigs.InitWithSocket("default") + dbConfigs.InitWithSocket("default", collations.MySQL8()) want := mysql.ConnParams{ Host: "a", @@ -80,7 +81,7 @@ func TestInit(t *testing.T) { Uname: "app", Pass: "apppass", UnixSocket: "b", - Charset: "utf8mb4", + Charset: collations.CollationUtf8mb4ID, Flags: 2, Flavor: "flavor", ConnectTimeoutMs: 250, @@ -91,7 +92,7 @@ func TestInit(t *testing.T) { Host: "a", Port: 1, UnixSocket: "b", - Charset: "utf8mb4", + Charset: collations.CollationUtf8mb4ID, Flags: 2, Flavor: "flavor", SslCa: "d", @@ -107,7 +108,7 @@ func TestInit(t *testing.T) { Uname: "dba", Pass: "dbapass", UnixSocket: "b", - Charset: "utf8mb4", + Charset: collations.CollationUtf8mb4ID, Flags: 2, Flavor: "flavor", SslCa: "d", @@ -143,21 +144,21 @@ func TestInit(t *testing.T) { }, appParams: mysql.ConnParams{ UnixSocket: "socket", - Charset: "utf8mb4", + Charset: collations.CollationUtf8mb4ID, }, dbaParams: mysql.ConnParams{ Host: "host", Flags: 2, }, } - dbConfigs.InitWithSocket("default") + dbConfigs.InitWithSocket("default", collations.MySQL8()) want = mysql.ConnParams{ Host: "a", Port: 1, Uname: "app", Pass: "apppass", UnixSocket: "b", - Charset: "utf8mb4", + Charset: collations.CollationUtf8mb4ID, } assert.Equal(t, want, dbConfigs.appParams) want = mysql.ConnParams{ @@ -168,7 +169,7 @@ func TestInit(t *testing.T) { SslCaPath: "e", SslCert: "f", SslKey: "g", - Charset: "utf8", + Charset: collations.CollationUtf8mb3ID, } assert.Equal(t, want, dbConfigs.appdebugParams) want = mysql.ConnParams{ @@ -182,7 +183,7 @@ func TestInit(t *testing.T) { SslCaPath: "e", SslCert: "f", SslKey: "g", - Charset: "utf8", + Charset: collations.CollationUtf8mb3ID, } assert.Equal(t, want, dbConfigs.dbaParams) } @@ -201,13 +202,13 @@ func TestUseTCP(t *testing.T) { }, Charset: "utf8", } - dbConfigs.InitWithSocket("default") + dbConfigs.InitWithSocket("default", collations.MySQL8()) want := mysql.ConnParams{ Host: "a", Port: 1, Uname: "app", - Charset: "utf8", + Charset: collations.CollationUtf8mb3ID, } assert.Equal(t, want, dbConfigs.appParams) @@ -216,7 +217,7 @@ func TestUseTCP(t *testing.T) { Port: 1, Uname: "dba", UnixSocket: "b", - Charset: "utf8", + Charset: collations.CollationUtf8mb3ID, } assert.Equal(t, want, dbConfigs.dbaParams) } diff --git a/go/vt/discovery/fake_healthcheck.go b/go/vt/discovery/fake_healthcheck.go index cb959902c19..1c83de5b149 100644 --- a/go/vt/discovery/fake_healthcheck.go +++ b/go/vt/discovery/fake_healthcheck.go @@ -252,6 +252,17 @@ func (fhc *FakeHealthCheck) CacheStatus() TabletsCacheStatusList { return tcsl } +// HealthyStatus returns the status for each healthy tablet +func (fhc *FakeHealthCheck) HealthyStatus() TabletsCacheStatusList { + tcsMap := fhc.CacheStatusMap() + tcsl := make(TabletsCacheStatusList, 0, len(tcsMap)) + for _, tcs := range tcsMap { + tcsl = append(tcsl, tcs) + } + sort.Sort(tcsl) + return tcsl +} + // CacheStatusMap returns a map of the health check cache. func (fhc *FakeHealthCheck) CacheStatusMap() map[string]*TabletsCacheStatus { tcsMap := make(map[string]*TabletsCacheStatus) diff --git a/go/vt/discovery/healthcheck.go b/go/vt/discovery/healthcheck.go index 9d17005d0ad..5d6a5e32662 100644 --- a/go/vt/discovery/healthcheck.go +++ b/go/vt/discovery/healthcheck.go @@ -25,7 +25,7 @@ limitations under the License. // Alternatively, use a Watcher implementation which will constantly watch // a source (e.g. the topology) and add and remove tablets as they are // added or removed from the source. -// For a Watcher example have a look at NewCellTabletsWatcher(). +// For a Watcher example have a look at NewTopologyWatcher(). // // Internally, the HealthCheck module is connected to each tablet and has a // streaming RPC (StreamHealth) open to receive periodic health infos. @@ -87,11 +87,16 @@ var ( // refreshKnownTablets tells us whether to process all tablets or only new tablets. refreshKnownTablets = true - // topoReadConcurrency tells us how many topo reads are allowed in parallel. - topoReadConcurrency = 32 - // How much to sleep between each check. waitAvailableTabletInterval = 100 * time.Millisecond + + // HealthCheckCacheTemplate uses healthCheckTemplate with the `HealthCheck Tablet - Cache` title to create the + // HTML code required to render the cache of the HealthCheck. + HealthCheckCacheTemplate = fmt.Sprintf(healthCheckTemplate, "HealthCheck - Cache") + + // HealthCheckHealthyTemplate uses healthCheckTemplate with the `HealthCheck Tablet - Healthy Tablets` title to + // create the HTML code required to render the list of healthy tablets from the HealthCheck. + HealthCheckHealthyTemplate = fmt.Sprintf(healthCheckTemplate, "HealthCheck - Healthy Tablets") ) // See the documentation for NewHealthCheck below for an explanation of these parameters. @@ -99,13 +104,9 @@ const ( DefaultHealthCheckRetryDelay = 5 * time.Second DefaultHealthCheckTimeout = 1 * time.Minute - // DefaultTopoReadConcurrency is used as the default value for the topoReadConcurrency parameter of a TopologyWatcher. - DefaultTopoReadConcurrency int = 5 - // DefaultTopologyWatcherRefreshInterval is used as the default value for - // the refresh interval of a topology watcher. - DefaultTopologyWatcherRefreshInterval = 1 * time.Minute - // HealthCheckTemplate is the HTML code to display a TabletsCacheStatusList - HealthCheckTemplate = ` + // healthCheckTemplate is the HTML code to display a TabletsCacheStatusList, it takes a parameter for the title + // as the template can be used for both HealthCheck's cache and healthy tablets list. + healthCheckTemplate = ` - + @@ -167,7 +168,6 @@ func registerWebUIFlags(fs *pflag.FlagSet) { fs.StringVar(&TabletURLTemplateString, "tablet_url_template", "http://{{.GetTabletHostPort}}", "Format string describing debug tablet url formatting. See getTabletDebugURL() for how to customize this.") fs.DurationVar(&refreshInterval, "tablet_refresh_interval", 1*time.Minute, "Tablet refresh interval.") fs.BoolVar(&refreshKnownTablets, "tablet_refresh_known_tablets", true, "Whether to reload the tablet's address/port map from topo in case they change.") - fs.IntVar(&topoReadConcurrency, "topo_read_concurrency", 32, "Concurrency of topo reads.") ParseTabletURLTemplateFromFlag() } @@ -193,6 +193,9 @@ type HealthCheck interface { // CacheStatus returns a displayable version of the health check cache. CacheStatus() TabletsCacheStatusList + // HealthyStatus returns a displayable version of the health check healthy list. + HealthyStatus() TabletsCacheStatusList + // CacheStatusMap returns a map of the health check cache. CacheStatusMap() map[string]*TabletsCacheStatus @@ -350,7 +353,7 @@ func NewHealthCheck(ctx context.Context, retryDelay, healthCheckTimeout time.Dur } else if len(KeyspacesToWatch) > 0 { filter = NewFilterByKeyspace(KeyspacesToWatch) } - topoWatchers = append(topoWatchers, NewCellTabletsWatcher(ctx, topoServer, hc, filter, c, refreshInterval, refreshKnownTablets, topoReadConcurrency)) + topoWatchers = append(topoWatchers, NewTopologyWatcher(ctx, topoServer, hc, filter, c, refreshInterval, refreshKnownTablets, topo.DefaultConcurrency)) } hc.topoWatchers = topoWatchers @@ -622,28 +625,55 @@ func (hc *HealthCheckImpl) CacheStatus() TabletsCacheStatusList { return tcsl } +// HealthyStatus returns a displayable version of the cache. +func (hc *HealthCheckImpl) HealthyStatus() TabletsCacheStatusList { + tcsMap := hc.HealthyStatusMap() + tcsl := make(TabletsCacheStatusList, 0, len(tcsMap)) + for _, tcs := range tcsMap { + tcsl = append(tcsl, tcs) + } + sort.Sort(tcsl) + return tcsl +} + func (hc *HealthCheckImpl) CacheStatusMap() map[string]*TabletsCacheStatus { tcsMap := make(map[string]*TabletsCacheStatus) hc.mu.Lock() defer hc.mu.Unlock() for _, ths := range hc.healthData { for _, th := range ths { - key := fmt.Sprintf("%v.%v.%v.%v", th.Tablet.Alias.Cell, th.Target.Keyspace, th.Target.Shard, th.Target.TabletType.String()) - var tcs *TabletsCacheStatus - var ok bool - if tcs, ok = tcsMap[key]; !ok { - tcs = &TabletsCacheStatus{ - Cell: th.Tablet.Alias.Cell, - Target: th.Target, - } - tcsMap[key] = tcs - } - tcs.TabletsStats = append(tcs.TabletsStats, th) + tabletHealthToTabletCacheStatus(th, tcsMap) } } return tcsMap } +func (hc *HealthCheckImpl) HealthyStatusMap() map[string]*TabletsCacheStatus { + tcsMap := make(map[string]*TabletsCacheStatus) + hc.mu.Lock() + defer hc.mu.Unlock() + for _, ths := range hc.healthy { + for _, th := range ths { + tabletHealthToTabletCacheStatus(th, tcsMap) + } + } + return tcsMap +} + +func tabletHealthToTabletCacheStatus(th *TabletHealth, tcsMap map[string]*TabletsCacheStatus) { + key := fmt.Sprintf("%v.%v.%v.%v", th.Tablet.Alias.Cell, th.Target.Keyspace, th.Target.Shard, th.Target.TabletType.String()) + var tcs *TabletsCacheStatus + var ok bool + if tcs, ok = tcsMap[key]; !ok { + tcs = &TabletsCacheStatus{ + Cell: th.Tablet.Alias.Cell, + Target: th.Target, + } + tcsMap[key] = tcs + } + tcs.TabletsStats = append(tcs.TabletsStats, th) +} + // Close stops the healthcheck. func (hc *HealthCheckImpl) Close() error { hc.mu.Lock() diff --git a/go/vt/discovery/healthcheck_test.go b/go/vt/discovery/healthcheck_test.go index 5fadc57eb2e..9563d9bfdc5 100644 --- a/go/vt/discovery/healthcheck_test.go +++ b/go/vt/discovery/healthcheck_test.go @@ -1267,7 +1267,7 @@ func TestTemplate(t *testing.T) { TabletsStats: ts, } templ := template.New("") - templ, err := templ.Parse(HealthCheckTemplate) + templ, err := templ.Parse(healthCheckTemplate) require.Nil(t, err, "error parsing template: %v", err) wr := &bytes.Buffer{} err = templ.Execute(wr, []*TabletsCacheStatus{tcs}) @@ -1295,7 +1295,7 @@ func TestDebugURLFormatting(t *testing.T) { TabletsStats: ts, } templ := template.New("") - templ, err := templ.Parse(HealthCheckTemplate) + templ, err := templ.Parse(healthCheckTemplate) require.Nil(t, err, "error parsing template") wr := &bytes.Buffer{} err = templ.Execute(wr, []*TabletsCacheStatus{tcs}) diff --git a/go/vt/discovery/keyspace_events.go b/go/vt/discovery/keyspace_events.go index 163f240de8c..c2567da9a87 100644 --- a/go/vt/discovery/keyspace_events.go +++ b/go/vt/discovery/keyspace_events.go @@ -529,6 +529,11 @@ func (kss *keyspaceState) isServing() bool { // In addition, the traffic switcher updates SrvVSchema when the DeniedTables attributes in a Shard record is // modified. func (kss *keyspaceState) onSrvVSchema(vs *vschemapb.SrvVSchema, err error) bool { + // the vschema can be nil if the server is currently shutting down + if vs == nil { + return true + } + kss.mu.Lock() defer kss.mu.Unlock() kss.moveTablesState, _ = kss.getMoveTablesStatus(vs) diff --git a/go/vt/discovery/keyspace_events_test.go b/go/vt/discovery/keyspace_events_test.go index 43af4bf49de..af60479a42b 100644 --- a/go/vt/discovery/keyspace_events_test.go +++ b/go/vt/discovery/keyspace_events_test.go @@ -49,14 +49,7 @@ func TestSrvKeyspaceWithNilNewKeyspace(t *testing.T) { keyspace: keyspace, shards: make(map[string]*shardState), } - kss.lastKeyspace = &topodatapb.SrvKeyspace{ - ServedFrom: []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: keyspace, - }, - }, - } + kss.lastKeyspace = &topodatapb.SrvKeyspace{} require.True(t, kss.onSrvKeyspace(nil, nil)) } diff --git a/go/vt/discovery/tablet_picker.go b/go/vt/discovery/tablet_picker.go index a507528d3a2..7525ab82dfc 100644 --- a/go/vt/discovery/tablet_picker.go +++ b/go/vt/discovery/tablet_picker.go @@ -428,7 +428,7 @@ func (tp *TabletPicker) GetMatchingTablets(ctx context.Context) []*topo.TabletIn shortCtx, cancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer cancel() - tabletMap, err := tp.ts.GetTabletMap(shortCtx, aliases) + tabletMap, err := tp.ts.GetTabletMap(shortCtx, aliases, nil) if err != nil { log.Warningf("Error fetching tablets from topo: %v", err) // If we get a partial result we can still use it, otherwise return. diff --git a/go/vt/discovery/topology_watcher.go b/go/vt/discovery/topology_watcher.go index b3298f55700..3945268f62e 100644 --- a/go/vt/discovery/topology_watcher.go +++ b/go/vt/discovery/topology_watcher.go @@ -70,8 +70,7 @@ type TopologyWatcher struct { cell string refreshInterval time.Duration refreshKnownTablets bool - getTablets func(tw *TopologyWatcher) ([]*topodata.TabletAlias, error) - sem chan int + concurrency int ctx context.Context cancelFunc context.CancelFunc // wg keeps track of all launched Go routines. @@ -92,34 +91,28 @@ type TopologyWatcher struct { } // NewTopologyWatcher returns a TopologyWatcher that monitors all -// the tablets that it is configured to watch, and reloads them periodically if needed. -// As of now there is only one implementation: watch all tablets in a cell. -func NewTopologyWatcher(ctx context.Context, topoServer *topo.Server, hc HealthCheck, filter TabletFilter, cell string, refreshInterval time.Duration, refreshKnownTablets bool, topoReadConcurrency int, getTablets func(tw *TopologyWatcher) ([]*topodata.TabletAlias, error)) *TopologyWatcher { +// the tablets in a cell, and reloads them as needed. +func NewTopologyWatcher(ctx context.Context, topoServer *topo.Server, hc HealthCheck, f TabletFilter, cell string, refreshInterval time.Duration, refreshKnownTablets bool, topoReadConcurrency int) *TopologyWatcher { tw := &TopologyWatcher{ topoServer: topoServer, healthcheck: hc, - tabletFilter: filter, + tabletFilter: f, cell: cell, refreshInterval: refreshInterval, refreshKnownTablets: refreshKnownTablets, - getTablets: getTablets, - sem: make(chan int, topoReadConcurrency), + concurrency: topoReadConcurrency, tablets: make(map[string]*tabletInfo), } tw.firstLoadChan = make(chan struct{}) - // We want the span from the context, but not the cancelation that comes with it + // We want the span from the context, but not the cancellation that comes with it spanContext := trace.CopySpan(context.Background(), ctx) tw.ctx, tw.cancelFunc = context.WithCancel(spanContext) return tw } -// NewCellTabletsWatcher returns a TopologyWatcher that monitors all -// the tablets in a cell, and reloads them as needed. -func NewCellTabletsWatcher(ctx context.Context, topoServer *topo.Server, hc HealthCheck, f TabletFilter, cell string, refreshInterval time.Duration, refreshKnownTablets bool, topoReadConcurrency int) *TopologyWatcher { - return NewTopologyWatcher(ctx, topoServer, hc, f, cell, refreshInterval, refreshKnownTablets, topoReadConcurrency, func(tw *TopologyWatcher) ([]*topodata.TabletAlias, error) { - return tw.topoServer.GetTabletAliasesByCell(ctx, tw.cell) - }) +func (tw *TopologyWatcher) getTablets() ([]*topo.TabletInfo, error) { + return tw.topoServer.GetTabletsByCell(tw.ctx, tw.cell, &topo.GetTabletsByCellOptions{Concurrency: tw.concurrency}) } // Start starts the topology watcher. @@ -149,30 +142,31 @@ func (tw *TopologyWatcher) Stop() { } func (tw *TopologyWatcher) loadTablets() { - var wg sync.WaitGroup newTablets := make(map[string]*tabletInfo) - // First get the list of relevant tabletAliases. - tabletAliases, err := tw.getTablets(tw) + // First get the list of all tablets. + tabletInfos, err := tw.getTablets() topologyWatcherOperations.Add(topologyWatcherOpListTablets, 1) if err != nil { topologyWatcherErrors.Add(topologyWatcherOpListTablets, 1) - select { - case <-tw.ctx.Done(): + // If we get a partial result error, we just log it and process the tablets that we did manage to fetch. + if topo.IsErrType(err, topo.PartialResult) { + log.Errorf("received partial result from getTablets for cell %v: %v", tw.cell, err) + } else { // For all other errors, just return. + log.Errorf("error getting tablets for cell: %v: %v", tw.cell, err) return - default: } - log.Errorf("cannot get tablets for cell: %v: %v", tw.cell, err) - return } // Accumulate a list of all known alias strings to use later // when sorting. - tabletAliasStrs := make([]string, 0, len(tabletAliases)) + tabletAliasStrs := make([]string, 0, len(tabletInfos)) tw.mu.Lock() - for _, tAlias := range tabletAliases { - aliasStr := topoproto.TabletAliasString(tAlias) + defer tw.mu.Unlock() + + for _, tInfo := range tabletInfos { + aliasStr := topoproto.TabletAliasString(tInfo.Alias) tabletAliasStrs = append(tabletAliasStrs, aliasStr) if !tw.refreshKnownTablets { @@ -182,38 +176,13 @@ func (tw *TopologyWatcher) loadTablets() { continue } } - - wg.Add(1) - go func(alias *topodata.TabletAlias) { - defer wg.Done() - tw.sem <- 1 // Wait for active queue to drain. - tablet, err := tw.topoServer.GetTablet(tw.ctx, alias) - topologyWatcherOperations.Add(topologyWatcherOpGetTablet, 1) - <-tw.sem // Done; enable next request to run. - if err != nil { - topologyWatcherErrors.Add(topologyWatcherOpGetTablet, 1) - select { - case <-tw.ctx.Done(): - return - default: - } - log.Errorf("cannot get tablet for alias %v: %v", alias, err) - return - } - tw.mu.Lock() - aliasStr := topoproto.TabletAliasString(alias) - newTablets[aliasStr] = &tabletInfo{ - alias: aliasStr, - tablet: tablet.Tablet, - } - tw.mu.Unlock() - }(tAlias) + // There's no network call here, so we just do the tablets one at a time instead of in parallel goroutines. + newTablets[aliasStr] = &tabletInfo{ + alias: aliasStr, + tablet: tInfo.Tablet, + } } - tw.mu.Unlock() - wg.Wait() - tw.mu.Lock() - for alias, newVal := range newTablets { if tw.tabletFilter != nil && !tw.tabletFilter.IsIncluded(newVal.tablet) { continue @@ -266,8 +235,6 @@ func (tw *TopologyWatcher) loadTablets() { tw.topoChecksum = crc32.ChecksumIEEE(buf.Bytes()) tw.lastRefresh = time.Now() - tw.mu.Unlock() - } // RefreshLag returns the time since the last refresh. diff --git a/go/vt/discovery/topology_watcher_test.go b/go/vt/discovery/topology_watcher_test.go index 3ac567acef8..bdf2c2dd2da 100644 --- a/go/vt/discovery/topology_watcher_test.go +++ b/go/vt/discovery/topology_watcher_test.go @@ -65,7 +65,7 @@ func TestStartAndCloseTopoWatcher(t *testing.T) { fhc := NewFakeHealthCheck(nil) defer fhc.Close() topologyWatcherOperations.ZeroAll() - tw := NewCellTabletsWatcher(context.Background(), ts, fhc, nil, "aa", 100*time.Microsecond, true, 5) + tw := NewTopologyWatcher(context.Background(), ts, fhc, nil, "aa", 100*time.Microsecond, true, 5) done := make(chan bool, 3) result := make(chan bool, 1) @@ -102,9 +102,8 @@ func TestStartAndCloseTopoWatcher(t *testing.T) { done <- true _, ok := <-result - if !ok { - t.Fatal("timed out") - } + require.True(t, ok, "timed out") + } func TestCellTabletsWatcher(t *testing.T) { @@ -125,7 +124,7 @@ func checkWatcher(t *testing.T, refreshKnownTablets bool) { logger := logutil.NewMemoryLogger() topologyWatcherOperations.ZeroAll() counts := topologyWatcherOperations.Counts() - tw := NewCellTabletsWatcher(context.Background(), ts, fhc, nil, "aa", 10*time.Minute, refreshKnownTablets, 5) + tw := NewTopologyWatcher(context.Background(), ts, fhc, nil, "aa", 10*time.Minute, refreshKnownTablets, 5) counts = checkOpCounts(t, counts, map[string]int64{}) checkChecksum(t, tw, 0) @@ -143,19 +142,18 @@ func checkWatcher(t *testing.T, refreshKnownTablets bool) { Keyspace: "keyspace", Shard: "shard", } - if err := ts.CreateTablet(context.Background(), tablet); err != nil { - t.Fatalf("CreateTablet failed: %v", err) - } + require.NoError(t, ts.CreateTablet(context.Background(), tablet), "CreateTablet failed for %v", tablet.Alias) + tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 1, "AddTablet": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "AddTablet": 1}) checkChecksum(t, tw, 3238442862) // Check the tablet is returned by GetAllTablets(). allTablets := fhc.GetAllTablets() key := TabletToMapKey(tablet) - if _, ok := allTablets[key]; !ok || len(allTablets) != 1 || !proto.Equal(allTablets[key], tablet) { - t.Errorf("fhc.GetAllTablets() = %+v; want %+v", allTablets, tablet) - } + assert.Len(t, allTablets, 1) + assert.Contains(t, allTablets, key) + assert.True(t, proto.Equal(tablet, allTablets[key])) // Add a second tablet to the topology. tablet2 := &topodatapb.Tablet{ @@ -170,75 +168,51 @@ func checkWatcher(t *testing.T, refreshKnownTablets bool) { Keyspace: "keyspace", Shard: "shard", } - if err := ts.CreateTablet(context.Background(), tablet2); err != nil { - t.Fatalf("CreateTablet failed: %v", err) - } + require.NoError(t, ts.CreateTablet(context.Background(), tablet2), "CreateTablet failed for %v", tablet2.Alias) tw.loadTablets() - // If refreshKnownTablets is disabled, only the new tablet is read - // from the topo - if refreshKnownTablets { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 2, "AddTablet": 1}) - } else { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 1, "AddTablet": 1}) - } + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "AddTablet": 1}) checkChecksum(t, tw, 2762153755) // Check the new tablet is returned by GetAllTablets(). allTablets = fhc.GetAllTablets() key = TabletToMapKey(tablet2) - if _, ok := allTablets[key]; !ok || len(allTablets) != 2 || !proto.Equal(allTablets[key], tablet2) { - t.Errorf("fhc.GetAllTablets() = %+v; want %+v", allTablets, tablet2) - } - - // Load the tablets again to show that when refreshKnownTablets is disabled, - // only the list is read from the topo and the checksum doesn't change - tw.loadTablets() - if refreshKnownTablets { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 2}) - } else { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1}) - } - checkChecksum(t, tw, 2762153755) + assert.Len(t, allTablets, 2) + assert.Contains(t, allTablets, key) + assert.True(t, proto.Equal(tablet2, allTablets[key])) // same tablet, different port, should update (previous // one should go away, new one be added) // // if refreshKnownTablets is disabled, this case is *not* - // detected and the tablet remains in the topo using the + // detected and the tablet remains in the healthcheck using the // old key origTablet := tablet.CloneVT() origKey := TabletToMapKey(tablet) tablet.PortMap["vt"] = 456 - if _, err := ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { + _, err := ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { t.PortMap["vt"] = 456 return nil - }); err != nil { - t.Fatalf("UpdateTabletFields failed: %v", err) - } + }) + require.Nil(t, err, "UpdateTabletFields failed") + tw.loadTablets() allTablets = fhc.GetAllTablets() key = TabletToMapKey(tablet) if refreshKnownTablets { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 2, "ReplaceTablet": 1}) - - if _, ok := allTablets[key]; !ok || len(allTablets) != 2 || !proto.Equal(allTablets[key], tablet) { - t.Errorf("fhc.GetAllTablets() = %+v; want %+v", allTablets, tablet) - } - if _, ok := allTablets[origKey]; ok { - t.Errorf("fhc.GetAllTablets() = %+v; don't want %v", allTablets, origKey) - } + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "ReplaceTablet": 1}) + assert.Len(t, allTablets, 2) + assert.Contains(t, allTablets, key) + assert.True(t, proto.Equal(tablet, allTablets[key])) + assert.NotContains(t, allTablets, origKey) checkChecksum(t, tw, 2762153755) } else { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1}) - - if _, ok := allTablets[origKey]; !ok || len(allTablets) != 2 || !proto.Equal(allTablets[origKey], origTablet) { - t.Errorf("fhc.GetAllTablets() = %+v; want %+v", allTablets, origTablet) - } - if _, ok := allTablets[key]; ok { - t.Errorf("fhc.GetAllTablets() = %+v; don't want %v", allTablets, key) - } + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "ReplaceTablet": 0}) + assert.Len(t, allTablets, 2) + assert.Contains(t, allTablets, origKey) + assert.True(t, proto.Equal(origTablet, allTablets[origKey])) + assert.NotContains(t, allTablets, key) checkChecksum(t, tw, 2762153755) } @@ -248,94 +222,77 @@ func checkWatcher(t *testing.T, refreshKnownTablets bool) { if refreshKnownTablets { origTablet := tablet.CloneVT() origTablet2 := tablet2.CloneVT() - if _, err := ts.UpdateTabletFields(context.Background(), tablet2.Alias, func(t *topodatapb.Tablet) error { + _, err := ts.UpdateTabletFields(context.Background(), tablet2.Alias, func(t *topodatapb.Tablet) error { t.Hostname = tablet.Hostname t.PortMap = tablet.PortMap tablet2 = t return nil - }); err != nil { - t.Fatalf("UpdateTabletFields failed: %v", err) - } - if _, err := ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { + }) + require.Nil(t, err, "UpdateTabletFields failed") + _, err = ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { t.Hostname = "host3" tablet = t return nil - }); err != nil { - t.Fatalf("UpdateTabletFields failed: %v", err) - } + }) + require.Nil(t, err, "UpdateTabletFields failed") tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 2, "ReplaceTablet": 2}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "ReplaceTablet": 2}) allTablets = fhc.GetAllTablets() key2 := TabletToMapKey(tablet2) - if _, ok := allTablets[key2]; !ok { - t.Fatalf("tablet was lost because it's reusing an address recently used by another tablet: %v", key2) - } + assert.Contains(t, allTablets, key2, "tablet was lost because it's reusing an address recently used by another tablet: %v", key2) // Change tablets back to avoid altering later tests. - if _, err := ts.UpdateTabletFields(context.Background(), tablet2.Alias, func(t *topodatapb.Tablet) error { + _, err = ts.UpdateTabletFields(context.Background(), tablet2.Alias, func(t *topodatapb.Tablet) error { t.Hostname = origTablet2.Hostname t.PortMap = origTablet2.PortMap tablet2 = t return nil - }); err != nil { - t.Fatalf("UpdateTabletFields failed: %v", err) - } - if _, err := ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { + }) + require.Nil(t, err, "UpdateTabletFields failed") + + _, err = ts.UpdateTabletFields(context.Background(), tablet.Alias, func(t *topodatapb.Tablet) error { t.Hostname = origTablet.Hostname tablet = t return nil - }); err != nil { - t.Fatalf("UpdateTabletFields failed: %v", err) - } + }) + require.Nil(t, err, "UpdateTabletFields failed") + tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 2, "ReplaceTablet": 2}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "ReplaceTablet": 2}) } // Remove the tablet and check that it is detected as being gone. - if err := ts.DeleteTablet(context.Background(), tablet.Alias); err != nil { - t.Fatalf("DeleteTablet failed: %v", err) - } - if _, err := topo.FixShardReplication(context.Background(), ts, logger, "aa", "keyspace", "shard"); err != nil { - t.Fatalf("FixShardReplication failed: %v", err) - } + require.NoError(t, ts.DeleteTablet(context.Background(), tablet.Alias)) + + _, err = topo.FixShardReplication(context.Background(), ts, logger, "aa", "keyspace", "shard") + require.Nil(t, err, "FixShardReplication failed") tw.loadTablets() - if refreshKnownTablets { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 1, "RemoveTablet": 1}) - } else { - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "RemoveTablet": 1}) - } + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "RemoveTablet": 1}) checkChecksum(t, tw, 789108290) allTablets = fhc.GetAllTablets() + assert.Len(t, allTablets, 1) key = TabletToMapKey(tablet) - if _, ok := allTablets[key]; ok || len(allTablets) != 1 { - t.Errorf("fhc.GetAllTablets() = %+v; don't want %v", allTablets, key) - } + assert.NotContains(t, allTablets, key) + key = TabletToMapKey(tablet2) - if _, ok := allTablets[key]; !ok || len(allTablets) != 1 || !proto.Equal(allTablets[key], tablet2) { - t.Errorf("fhc.GetAllTablets() = %+v; want %+v", allTablets, tablet2) - } + assert.Contains(t, allTablets, key) + assert.True(t, proto.Equal(tablet2, allTablets[key])) // Remove the other and check that it is detected as being gone. - if err := ts.DeleteTablet(context.Background(), tablet2.Alias); err != nil { - t.Fatalf("DeleteTablet failed: %v", err) - } - if _, err := topo.FixShardReplication(context.Background(), ts, logger, "aa", "keyspace", "shard"); err != nil { - t.Fatalf("FixShardReplication failed: %v", err) - } + require.NoError(t, ts.DeleteTablet(context.Background(), tablet2.Alias)) + _, err = topo.FixShardReplication(context.Background(), ts, logger, "aa", "keyspace", "shard") + require.Nil(t, err, "FixShardReplication failed") tw.loadTablets() checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "RemoveTablet": 1}) checkChecksum(t, tw, 0) allTablets = fhc.GetAllTablets() + assert.Len(t, allTablets, 0) key = TabletToMapKey(tablet) - if _, ok := allTablets[key]; ok || len(allTablets) != 0 { - t.Errorf("fhc.GetAllTablets() = %+v; don't want %v", allTablets, key) - } + assert.NotContains(t, allTablets, key) key = TabletToMapKey(tablet2) - if _, ok := allTablets[key]; ok || len(allTablets) != 0 { - t.Errorf("fhc.GetAllTablets() = %+v; don't want %v", allTablets, key) - } + assert.NotContains(t, allTablets, key) tw.Stop() } @@ -402,19 +359,13 @@ func TestFilterByShard(t *testing.T) { for _, tc := range testcases { fbs, err := NewFilterByShard(tc.filters) - if err != nil { - t.Errorf("cannot create FilterByShard for filters %v: %v", tc.filters, err) - } + require.Nil(t, err, "cannot create FilterByShard for filters %v", tc.filters) tablet := &topodatapb.Tablet{ Keyspace: tc.keyspace, Shard: tc.shard, } - - got := fbs.IsIncluded(tablet) - if got != tc.included { - t.Errorf("isIncluded(%v,%v) for filters %v returned %v but expected %v", tc.keyspace, tc.shard, tc.filters, got, tc.included) - } + require.Equal(t, tc.included, fbs.IsIncluded(tablet)) } } @@ -444,7 +395,7 @@ func TestFilterByKeyspace(t *testing.T) { f := NewFilterByKeyspace(testKeyspacesToWatch) ts := memorytopo.NewServer(ctx, testCell) defer ts.Close() - tw := NewCellTabletsWatcher(context.Background(), ts, hc, f, testCell, 10*time.Minute, true, 5) + tw := NewTopologyWatcher(context.Background(), ts, hc, f, testCell, 10*time.Minute, true, 5) for _, test := range testFilterByKeyspace { // Add a new tablet to the topology. @@ -462,22 +413,21 @@ func TestFilterByKeyspace(t *testing.T) { Shard: testShard, } - got := f.IsIncluded(tablet) - if got != test.expected { - t.Errorf("isIncluded(%v) for keyspace %v returned %v but expected %v", test.keyspace, test.keyspace, got, test.expected) - } + assert.Equal(t, test.expected, f.IsIncluded(tablet)) - if err := ts.CreateTablet(context.Background(), tablet); err != nil { - t.Errorf("CreateTablet failed: %v", err) - } + // Make this fatal because there is no point continuing if CreateTablet fails + require.NoError(t, ts.CreateTablet(context.Background(), tablet)) tw.loadTablets() key := TabletToMapKey(tablet) allTablets := hc.GetAllTablets() - if _, ok := allTablets[key]; ok != test.expected && proto.Equal(allTablets[key], tablet) != test.expected { - t.Errorf("Error adding tablet - got %v; want %v", ok, test.expected) + if test.expected { + assert.Contains(t, allTablets, key) + } else { + assert.NotContains(t, allTablets, key) } + assert.Equal(t, test.expected, proto.Equal(tablet, allTablets[key])) // Replace the tablet we added above tabletReplacement := &topodatapb.Tablet{ @@ -492,35 +442,31 @@ func TestFilterByKeyspace(t *testing.T) { Keyspace: test.keyspace, Shard: testShard, } - got = f.IsIncluded(tabletReplacement) - if got != test.expected { - t.Errorf("isIncluded(%v) for keyspace %v returned %v but expected %v", test.keyspace, test.keyspace, got, test.expected) - } - if err := ts.CreateTablet(context.Background(), tabletReplacement); err != nil { - t.Errorf("CreateTablet failed: %v", err) - } + assert.Equal(t, test.expected, f.IsIncluded(tabletReplacement)) + require.NoError(t, ts.CreateTablet(context.Background(), tabletReplacement)) tw.loadTablets() key = TabletToMapKey(tabletReplacement) allTablets = hc.GetAllTablets() - if _, ok := allTablets[key]; ok != test.expected && proto.Equal(allTablets[key], tabletReplacement) != test.expected { - t.Errorf("Error replacing tablet - got %v; want %v", ok, test.expected) + if test.expected { + assert.Contains(t, allTablets, key) + } else { + assert.NotContains(t, allTablets, key) } + assert.Equal(t, test.expected, proto.Equal(tabletReplacement, allTablets[key])) // Delete the tablet - if err := ts.DeleteTablet(context.Background(), tabletReplacement.Alias); err != nil { - t.Fatalf("DeleteTablet failed: %v", err) - } + require.NoError(t, ts.DeleteTablet(context.Background(), tabletReplacement.Alias)) } } -// TestFilterByKeypsaceSkipsIgnoredTablets confirms a bug fix for the case when a TopologyWatcher +// TestFilterByKeyspaceSkipsIgnoredTablets confirms a bug fix for the case when a TopologyWatcher // has a FilterByKeyspace TabletFilter configured along with refreshKnownTablets turned off. We want // to ensure that the TopologyWatcher: -// - does not continuosly call GetTablets for tablets that do not satisfy the filter -// - does not add or remove these filtered out tablets from the its healtcheck -func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { +// - does not continuously call GetTablets for tablets that do not satisfy the filter +// - does not add or remove these filtered out tablets from its healthcheck +func TestFilterByKeyspaceSkipsIgnoredTablets(t *testing.T) { ctx := utils.LeakCheckContext(t) ts := memorytopo.NewServer(ctx, "aa") @@ -530,7 +476,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { topologyWatcherOperations.ZeroAll() counts := topologyWatcherOperations.Counts() f := NewFilterByKeyspace(testKeyspacesToWatch) - tw := NewCellTabletsWatcher(context.Background(), ts, fhc, f, "aa", 10*time.Minute, false /*refreshKnownTablets*/, 5) + tw := NewTopologyWatcher(context.Background(), ts, fhc, f, "aa", 10*time.Minute, false /*refreshKnownTablets*/, 5) counts = checkOpCounts(t, counts, map[string]int64{}) checkChecksum(t, tw, 0) @@ -551,7 +497,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { require.NoError(t, ts.CreateTablet(context.Background(), tablet)) tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 1, "AddTablet": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "AddTablet": 1}) checkChecksum(t, tw, 3238442862) // Check tablet is reported by HealthCheck @@ -576,7 +522,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { require.NoError(t, ts.CreateTablet(context.Background(), tablet2)) tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0}) checkChecksum(t, tw, 2762153755) // Check the new tablet is NOT reported by HealthCheck. @@ -588,7 +534,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { // Load the tablets again to show that when refreshKnownTablets is disabled, // only the list is read from the topo and the checksum doesn't change tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0}) checkChecksum(t, tw, 2762153755) // With refreshKnownTablets set to false, changes to the port map for the same tablet alias @@ -600,7 +546,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { require.NoError(t, err) tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0}) checkChecksum(t, tw, 2762153755) allTablets = fhc.GetAllTablets() @@ -616,7 +562,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { require.NoError(t, ts.DeleteTablet(context.Background(), tablet.Alias)) tw.loadTablets() - counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "RemoveTablet": 1}) + counts = checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0, "RemoveTablet": 1}) checkChecksum(t, tw, 789108290) assert.Empty(t, fhc.GetAllTablets()) @@ -624,7 +570,7 @@ func TestFilterByKeypsaceSkipsIgnoredTablets(t *testing.T) { require.NoError(t, ts.DeleteTablet(context.Background(), tablet2.Alias)) tw.loadTablets() - checkOpCounts(t, counts, map[string]int64{"ListTablets": 1}) + checkOpCounts(t, counts, map[string]int64{"ListTablets": 1, "GetTablet": 0}) checkChecksum(t, tw, 0) assert.Empty(t, fhc.GetAllTablets()) diff --git a/go/vt/discovery/utils_test.go b/go/vt/discovery/utils_test.go index 27416da44b0..edca8c17602 100644 --- a/go/vt/discovery/utils_test.go +++ b/go/vt/discovery/utils_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/vt/env/env.go b/go/vt/env/env.go index 70feb43186c..186f81cd585 100644 --- a/go/vt/env/env.go +++ b/go/vt/env/env.go @@ -18,7 +18,6 @@ package env import ( "errors" - "fmt" "os" "os/exec" "path" @@ -30,9 +29,12 @@ const ( // DefaultVtDataRoot is the default value for VTROOT environment variable DefaultVtDataRoot = "/vt" // DefaultVtRoot is only required for hooks - DefaultVtRoot = "/usr/local/vitess" + DefaultVtRoot = "/usr/local/vitess" + mysqldSbinPath = "/usr/sbin/mysqld" ) +var errMysqldNotFound = errors.New("VT_MYSQL_ROOT is not set and no mysqld could be found in your PATH") + // VtRoot returns $VTROOT or tries to guess its value if it's not set. // This is the root for the 'vt' distribution, which contains bin/vttablet // for instance. @@ -64,25 +66,30 @@ func VtDataRoot() string { } // VtMysqlRoot returns the root for the mysql distribution, -// which contains bin/mysql CLI for instance. -// If it is not set, look for mysqld in the path. +// which contains the bin/mysql CLI for instance. +// If $VT_MYSQL_ROOT is not set, look for mysqld in the $PATH. func VtMysqlRoot() (string, error) { - // if the environment variable is set, use that + // If the environment variable is set, use that. if root := os.Getenv("VT_MYSQL_ROOT"); root != "" { return root, nil } - // otherwise let's look for mysqld in the PATH. - // ensure that /usr/sbin is included, as it might not be by default - // This is the default location for mysqld from packages. - newPath := fmt.Sprintf("/usr/sbin:%s", os.Getenv("PATH")) - os.Setenv("PATH", newPath) - path, err := exec.LookPath("mysqld") + getRoot := func(path string) string { + return filepath.Dir(filepath.Dir(path)) // Strip mysqld and [s]bin parts + } + binpath, err := exec.LookPath("mysqld") if err != nil { - return "", errors.New("VT_MYSQL_ROOT is not set and no mysqld could be found in your PATH") + // First see if /usr/sbin/mysqld exists as it might not be in + // the PATH by default and this is often the default location + // used by mysqld OS system packages (apt, dnf, etc). + fi, err := os.Stat(mysqldSbinPath) + if err == nil /* file exists */ && fi.Mode().IsRegular() /* not a DIR or other special file */ && + fi.Mode()&0111 != 0 /* executable by anyone */ { + return getRoot(mysqldSbinPath), nil + } + return "", errMysqldNotFound } - path = filepath.Dir(filepath.Dir(path)) // strip mysqld, and the sbin - return path, nil + return getRoot(binpath), nil } // VtMysqlBaseDir returns the Mysql base directory, which diff --git a/go/vt/env/env_test.go b/go/vt/env/env_test.go index 4aa53a25bed..f91cdf94673 100644 --- a/go/vt/env/env_test.go +++ b/go/vt/env/env_test.go @@ -18,7 +18,10 @@ package env import ( "os" + "path/filepath" "testing" + + "github.com/stretchr/testify/require" ) func TestVtDataRoot(t *testing.T) { @@ -43,3 +46,82 @@ func TestVtDataRoot(t *testing.T) { t.Errorf("The value of VtDataRoot should be %v, not %v.", passed, root) } } + +func TestVtMysqlRoot(t *testing.T) { + envVar := "VT_MYSQL_ROOT" + originalMySQLRoot := os.Getenv(envVar) + defer os.Setenv(envVar, originalMySQLRoot) + originalPATH := os.Getenv("PATH") + defer os.Setenv("PATH", originalPATH) + + // The test directory is used to create our fake mysqld binary. + testDir := t.TempDir() // This is automatically cleaned up + createExecutable := func(path string) error { + fullPath := testDir + path + err := os.MkdirAll(filepath.Dir(fullPath), 0755) + require.NoError(t, err) + return os.WriteFile(fullPath, []byte("test"), 0755) + } + + type testcase struct { + name string + preFunc func() error + vtMysqlRootEnvVal string + pathEnvVal string + expect string // The return value we expect from VtMysqlRoot() + expectErr string + } + testcases := []testcase{ + { + name: "VT_MYSQL_ROOT set", + vtMysqlRootEnvVal: "/home/mysql/binaries", + }, + { + name: "VT_MYSQL_ROOT empty; PATH set without /usr/sbin", + pathEnvVal: testDir + filepath.Dir(mysqldSbinPath) + + ":/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin:/home/mysql/binaries", + preFunc: func() error { + return createExecutable(mysqldSbinPath) + }, + expect: testDir + "/usr", + }, + } + + // If /usr/sbin/mysqld exists, confirm that we find it even + // when /usr/sbin is not in the PATH. + _, err := os.Stat(mysqldSbinPath) + if err == nil { + t.Logf("Found %s, confirming auto detection behavior", mysqldSbinPath) + testcases = append(testcases, testcase{ + name: "VT_MYSQL_ROOT empty; PATH empty; mysqld in /usr/sbin", + expect: "/usr", + }) + } else { + testcases = append(testcases, testcase{ // Error expected + name: "VT_MYSQL_ROOT empty; PATH empty; mysqld not in /usr/sbin", + expectErr: errMysqldNotFound.Error(), + }) + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + if tc.preFunc != nil { + err := tc.preFunc() + require.NoError(t, err) + } + os.Setenv(envVar, tc.vtMysqlRootEnvVal) + os.Setenv("PATH", tc.pathEnvVal) + path, err := VtMysqlRoot() + if tc.expectErr != "" { + require.EqualError(t, err, tc.expectErr) + } else { + require.NoError(t, err) + } + if tc.vtMysqlRootEnvVal != "" { + // This should always be returned. + tc.expect = tc.vtMysqlRootEnvVal + } + require.Equal(t, tc.expect, path) + }) + } +} diff --git a/go/vt/external/golib/sqlutils/sqlite_dialect_test.go b/go/vt/external/golib/sqlutils/sqlite_dialect_test.go index 039e42eefff..1298c379adf 100644 --- a/go/vt/external/golib/sqlutils/sqlite_dialect_test.go +++ b/go/vt/external/golib/sqlutils/sqlite_dialect_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -244,3 +245,70 @@ func TestToSqlite3GeneralConversions(t *testing.T) { require.Equal(t, result, "select group_concat( 'abc' , 'def') as s") } } + +func TestIsCreateIndex(t *testing.T) { + tests := []struct { + input string + expected bool + }{ + {"create index my_index on my_table(column);", true}, + {"CREATE INDEX my_index ON my_table(column);", true}, + {"create unique index my_index on my_table(column);", true}, + {"CREATE UNIQUE INDEX my_index ON my_table(column);", true}, + {"create index my_index on my_table(column) where condition;", true}, + {"create unique index my_index on my_table(column) where condition;", true}, + {"create table my_table(column);", false}, + {"drop index my_index on my_table;", false}, + {"alter table my_table add index my_index (column);", false}, + {"", false}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + result := IsCreateIndex(test.input) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestIsDropIndex(t *testing.T) { + tests := []struct { + input string + expected bool + }{ + {"drop index my_index on my_table;", true}, + {"DROP INDEX my_index ON my_table;", true}, + {"drop index if exists my_index on my_table;", true}, + {"DROP INDEX IF EXISTS my_index ON my_table;", true}, + {"drop table my_table;", false}, + {"create index my_index on my_table(column);", false}, + {"alter table my_table add index my_index (column);", false}, + {"", false}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + result := IsDropIndex(test.input) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestToSqlite3Dialect(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"create table my_table(id int);", "create table my_table(id int);"}, + {"alter table my_table add column new_col int;", "alter table my_table add column new_col int;"}, + {"insert into my_table values (1);", "insert into my_table values (1);"}, + {"", ""}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + result := ToSqlite3Dialect(test.input) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/go/vt/external/golib/sqlutils/sqlutils_test.go b/go/vt/external/golib/sqlutils/sqlutils_test.go new file mode 100644 index 00000000000..a7ac8680072 --- /dev/null +++ b/go/vt/external/golib/sqlutils/sqlutils_test.go @@ -0,0 +1,255 @@ +/* + Copyright 2024 The Vitess Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package sqlutils + +import ( + "database/sql" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRowMap(t *testing.T) { + tt := []struct { + name string + rowMap RowMap + expected any + }{ + { + "GetString", + RowMap{"key": CellData{String: "value"}}, + "value", + }, + { + "GetInt64", + RowMap{"key": CellData{String: "123"}}, + int64(123), + }, + { + "GetInt32", + RowMap{"key": CellData{String: "42"}}, + int32(42), + }, + { + "GetNullInt64", + RowMap{"key": CellData{String: "789"}}, + sql.NullInt64{Int64: 789, Valid: true}, + }, + { + "GetNullInt64 Error", + RowMap{"key": CellData{String: "foo"}}, + sql.NullInt64{Valid: false}, + }, + { + "GetInt", + RowMap{"key": CellData{String: "456"}}, + 456, + }, + { + "GetUint", + RowMap{"key": CellData{String: "123"}}, + uint(123), + }, + { + "GetUint64", + RowMap{"key": CellData{String: "999"}}, + uint64(999), + }, + { + "GetUint32", + RowMap{"key": CellData{String: "888"}}, + uint32(888), + }, + { + "GetBool", + RowMap{"key": CellData{String: "1"}}, + true, + }, + { + "GetTime", + RowMap{"key": CellData{String: "2024-01-24 12:34:56.789"}}, + time.Date(2024, time.January, 24, 12, 34, 56, 789000000, time.UTC), + }, + { + "GetTime Error", + RowMap{"key": CellData{String: "invalid_time_format"}}, + time.Time{}, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + switch tc.name { + case "GetString": + assert.Equal(t, tc.expected, tc.rowMap.GetString("key")) + case "GetInt64": + assert.Equal(t, tc.expected, tc.rowMap.GetInt64("key")) + case "GetInt32": + assert.Equal(t, tc.expected, tc.rowMap.GetInt32("key")) + case "GetNullInt64": + assert.Equal(t, tc.expected, tc.rowMap.GetNullInt64("key")) + case "GetNullInt64 Error": + assert.Equal(t, tc.expected, tc.rowMap.GetNullInt64("key")) + case "GetInt": + assert.Equal(t, tc.expected, tc.rowMap.GetInt("key")) + case "GetUint": + assert.Equal(t, tc.expected, tc.rowMap.GetUint("key")) + case "GetUint64": + assert.Equal(t, tc.expected, tc.rowMap.GetUint64("key")) + case "GetUint32": + assert.Equal(t, tc.expected, tc.rowMap.GetUint32("key")) + case "GetBool": + assert.Equal(t, tc.expected, tc.rowMap.GetBool("key")) + case "GetTime": + assert.Equal(t, tc.expected, tc.rowMap.GetTime("key")) + case "GetTime Error": + assert.Equal(t, tc.expected, tc.rowMap.GetTime("key")) + } + }) + } +} + +func TestNullString(t *testing.T) { + cellData := CellData{String: "test_value", Valid: true} + + result := cellData.NullString() + + expected := &sql.NullString{String: "test_value", Valid: true} + assert.Equal(t, expected, result) +} + +func TestMarshalJSON(t *testing.T) { + tt := []struct { + name string + rowData RowData + expected string + }{ + {"Valid", RowData{{String: "value", Valid: true}}, `["value"]`}, + {"Invalid", RowData{{String: "", Valid: false}}, "[null]"}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result, err := tc.rowData.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, tc.expected, string(result)) + }) + } +} + +func TestUnmarshalJSON(t *testing.T) { + tt := []struct { + name string + input string + expected CellData + isError bool + }{ + {"Valid JSON", `"value"`, CellData{String: "value", Valid: true}, false}, + {"Invalid JSON", `"invalid_json`, CellData{}, true}, + {"Null JSON", `null`, CellData{String: "", Valid: true}, false}, //?? + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + var cellData CellData + err := cellData.UnmarshalJSON([]byte(tc.input)) + + if tc.isError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, cellData) + } + }) + } +} + +func TestQueryRowsMap(t *testing.T) { + tt := []struct { + name string + db *sql.DB + query string + onRowFunc func(RowMap) error + args []any + shouldErr bool + }{ + {"Error", nil, "", nil, nil, true}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + err := QueryRowsMap(tc.db, tc.query, tc.onRowFunc, tc.args...) + if tc.shouldErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestExecNoPrepare(t *testing.T) { + tt := []struct { + name string + db *sql.DB + query string + args []any + shouldErr bool + expect sql.Result + }{ + {"Error", nil, "", nil, true, nil}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + out, err := ExecNoPrepare(tc.db, tc.query, tc.args...) + if tc.shouldErr { + assert.Error(t, err) + assert.Nil(t, out) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expect, out) + } + }) + } +} + +func TestArgs(t *testing.T) { + args := []any{1, "abc", true} + expected := []any{1, "abc", true} + result := Args(args...) + assert.Equal(t, expected, result) +} + +func TestNilIfZero(t *testing.T) { + tt := []struct { + name string + i int64 + expected any + }{ + {"NonZero", int64(42), int64(42)}, + {"Zero", int64(0), nil}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result := NilIfZero(tc.i) + assert.Equal(t, tc.expected, result) + }) + } +} diff --git a/go/vt/grpcclient/client_auth_static.go b/go/vt/grpcclient/client_auth_static.go index 22f69569956..bbb91a9fa55 100644 --- a/go/vt/grpcclient/client_auth_static.go +++ b/go/vt/grpcclient/client_auth_static.go @@ -20,24 +20,35 @@ import ( "context" "encoding/json" "os" + "os/signal" + "sync" + "syscall" "google.golang.org/grpc" "google.golang.org/grpc/credentials" + + "vitess.io/vitess/go/vt/servenv" ) var ( credsFile string // registered as --grpc_auth_static_client_creds in RegisterFlags // StaticAuthClientCreds implements client interface to be able to WithPerRPCCredentials _ credentials.PerRPCCredentials = (*StaticAuthClientCreds)(nil) + + clientCreds *StaticAuthClientCreds + clientCredsCancel context.CancelFunc + clientCredsErr error + clientCredsMu sync.Mutex + clientCredsSigChan chan os.Signal ) -// StaticAuthClientCreds holder for client credentials +// StaticAuthClientCreds holder for client credentials. type StaticAuthClientCreds struct { Username string Password string } -// GetRequestMetadata gets the request metadata as a map from StaticAuthClientCreds +// GetRequestMetadata gets the request metadata as a map from StaticAuthClientCreds. func (c *StaticAuthClientCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { return map[string]string{ "username": c.Username, @@ -47,30 +58,82 @@ func (c *StaticAuthClientCreds) GetRequestMetadata(context.Context, ...string) ( // RequireTransportSecurity indicates whether the credentials requires transport security. // Given that people can use this with or without TLS, at the moment we are not enforcing -// transport security +// transport security. func (c *StaticAuthClientCreds) RequireTransportSecurity() bool { return false } // AppendStaticAuth optionally appends static auth credentials if provided. func AppendStaticAuth(opts []grpc.DialOption) ([]grpc.DialOption, error) { - if credsFile == "" { - return opts, nil - } - data, err := os.ReadFile(credsFile) + creds, err := getStaticAuthCreds() if err != nil { return nil, err } - clientCreds := &StaticAuthClientCreds{} - err = json.Unmarshal(data, clientCreds) + if creds != nil { + grpcCreds := grpc.WithPerRPCCredentials(creds) + opts = append(opts, grpcCreds) + } + return opts, nil +} + +// ResetStaticAuth resets the static auth credentials. +func ResetStaticAuth() { + clientCredsMu.Lock() + defer clientCredsMu.Unlock() + if clientCredsCancel != nil { + clientCredsCancel() + clientCredsCancel = nil + } + clientCreds = nil + clientCredsErr = nil +} + +// getStaticAuthCreds returns the static auth creds and error. +func getStaticAuthCreds() (*StaticAuthClientCreds, error) { + clientCredsMu.Lock() + defer clientCredsMu.Unlock() + if credsFile != "" && clientCreds == nil { + var ctx context.Context + ctx, clientCredsCancel = context.WithCancel(context.Background()) + go handleClientCredsSignals(ctx) + clientCreds, clientCredsErr = loadStaticAuthCredsFromFile(credsFile) + } + return clientCreds, clientCredsErr +} + +// handleClientCredsSignals handles signals to reload client creds. +func handleClientCredsSignals(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case <-clientCredsSigChan: + if newCreds, err := loadStaticAuthCredsFromFile(credsFile); err == nil { + clientCredsMu.Lock() + clientCreds = newCreds + clientCredsErr = err + clientCredsMu.Unlock() + } + } + } +} + +// loadStaticAuthCredsFromFile loads static auth credentials from a file. +func loadStaticAuthCredsFromFile(path string) (*StaticAuthClientCreds, error) { + data, err := os.ReadFile(path) if err != nil { return nil, err } - creds := grpc.WithPerRPCCredentials(clientCreds) - opts = append(opts, creds) - return opts, nil + creds := &StaticAuthClientCreds{} + err = json.Unmarshal(data, creds) + return creds, err } func init() { + servenv.OnInit(func() { + clientCredsSigChan = make(chan os.Signal, 1) + signal.Notify(clientCredsSigChan, syscall.SIGHUP) + _, _ = getStaticAuthCreds() // preload static auth credentials + }) RegisterGRPCDialOptions(AppendStaticAuth) } diff --git a/go/vt/grpcclient/client_auth_static_test.go b/go/vt/grpcclient/client_auth_static_test.go new file mode 100644 index 00000000000..e14ace527d1 --- /dev/null +++ b/go/vt/grpcclient/client_auth_static_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package grpcclient + +import ( + "errors" + "fmt" + "os" + "reflect" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +func TestAppendStaticAuth(t *testing.T) { + { + clientCreds = nil + clientCredsErr = nil + opts, err := AppendStaticAuth([]grpc.DialOption{}) + assert.Nil(t, err) + assert.Len(t, opts, 0) + } + { + clientCreds = nil + clientCredsErr = errors.New("test err") + opts, err := AppendStaticAuth([]grpc.DialOption{}) + assert.NotNil(t, err) + assert.Len(t, opts, 0) + } + { + clientCreds = &StaticAuthClientCreds{Username: "test", Password: "123456"} + clientCredsErr = nil + opts, err := AppendStaticAuth([]grpc.DialOption{}) + assert.Nil(t, err) + assert.Len(t, opts, 1) + } +} + +func TestGetStaticAuthCreds(t *testing.T) { + tmp, err := os.CreateTemp("", t.Name()) + assert.Nil(t, err) + defer os.Remove(tmp.Name()) + credsFile = tmp.Name() + clientCredsSigChan = make(chan os.Signal, 1) + + // load old creds + fmt.Fprint(tmp, `{"Username": "old", "Password": "123456"}`) + ResetStaticAuth() + creds, err := getStaticAuthCreds() + assert.Nil(t, err) + assert.Equal(t, &StaticAuthClientCreds{Username: "old", Password: "123456"}, creds) + + // write new creds to the same file + _ = tmp.Truncate(0) + _, _ = tmp.Seek(0, 0) + fmt.Fprint(tmp, `{"Username": "new", "Password": "123456789"}`) + + // test the creds did not change yet + creds, err = getStaticAuthCreds() + assert.Nil(t, err) + assert.Equal(t, &StaticAuthClientCreds{Username: "old", Password: "123456"}, creds) + + // test SIGHUP signal triggers reload + credsOld := creds + clientCredsSigChan <- syscall.SIGHUP + timeoutChan := time.After(time.Second * 10) + for { + select { + case <-timeoutChan: + assert.Fail(t, "timed out waiting for SIGHUP reload of static auth creds") + return + default: + // confirm new creds get loaded + creds, err = getStaticAuthCreds() + if reflect.DeepEqual(creds, credsOld) { + continue // not changed yet + } + assert.Nil(t, err) + assert.Equal(t, &StaticAuthClientCreds{Username: "new", Password: "123456789"}, creds) + return + } + } +} + +func TestLoadStaticAuthCredsFromFile(t *testing.T) { + { + f, err := os.CreateTemp("", t.Name()) + if !assert.Nil(t, err) { + assert.FailNowf(t, "cannot create temp file: %s", err.Error()) + } + defer os.Remove(f.Name()) + fmt.Fprint(f, `{ + "Username": "test", + "Password": "correct horse battery staple" + }`) + if !assert.Nil(t, err) { + assert.FailNowf(t, "cannot read auth file: %s", err.Error()) + } + + creds, err := loadStaticAuthCredsFromFile(f.Name()) + assert.Nil(t, err) + assert.Equal(t, "test", creds.Username) + assert.Equal(t, "correct horse battery staple", creds.Password) + } + { + _, err := loadStaticAuthCredsFromFile(`does-not-exist`) + assert.NotNil(t, err) + } +} diff --git a/go/vt/grpcclient/client_flaky_test.go b/go/vt/grpcclient/client_test.go similarity index 100% rename from go/vt/grpcclient/client_flaky_test.go rename to go/vt/grpcclient/client_test.go diff --git a/go/vt/hook/hook.go b/go/vt/hook/hook.go index 6cee35e4241..4f402cdcb44 100644 --- a/go/vt/hook/hook.go +++ b/go/vt/hook/hook.go @@ -17,7 +17,6 @@ limitations under the License. package hook import ( - "bytes" "context" "errors" "fmt" @@ -147,7 +146,7 @@ func (hook *Hook) ExecuteContext(ctx context.Context) (result *HookResult) { } // Run it. - var stdout, stderr bytes.Buffer + var stdout, stderr strings.Builder cmd.Stdout = &stdout cmd.Stderr = &stderr @@ -234,7 +233,7 @@ func (hook *Hook) ExecuteAsWritePipe(out io.Writer) (io.WriteCloser, WaitFunc, i return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdin: %v", err) } cmd.Stdout = out - var stderr bytes.Buffer + var stderr strings.Builder cmd.Stderr = &stderr // Start the process. @@ -273,7 +272,7 @@ func (hook *Hook) ExecuteAsReadPipe(in io.Reader) (io.Reader, WaitFunc, int, err return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdout: %v", err) } cmd.Stdin = in - var stderr bytes.Buffer + var stderr strings.Builder cmd.Stderr = &stderr // Start the process. diff --git a/go/vt/hook/hook_test.go b/go/vt/hook/hook_test.go index 041e568e5ff..f5064175768 100644 --- a/go/vt/hook/hook_test.go +++ b/go/vt/hook/hook_test.go @@ -1,10 +1,29 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package hook import ( "context" + "io" "os" "os/exec" "path" + "strings" + "sync" "testing" "time" @@ -22,6 +41,11 @@ func TestExecuteContext(t *testing.T) { require.NoError(t, err) sleepHookPath := path.Join(vtroot, "vthook", "sleep") + + if _, err := os.Lstat(sleepHookPath); err == nil { + require.NoError(t, os.Remove(sleepHookPath)) + } + require.NoError(t, os.Symlink(sleep, sleepHookPath)) defer func() { require.NoError(t, os.Remove(sleepHookPath)) @@ -38,3 +62,234 @@ func TestExecuteContext(t *testing.T) { hr = h.Execute() assert.Equal(t, HOOK_SUCCESS, hr.ExitStatus) } + +func TestExecuteOptional(t *testing.T) { + vtroot, err := vtenv.VtRoot() + require.NoError(t, err) + + echo, err := exec.LookPath("echo") + require.NoError(t, err) + + echoHookPath := path.Join(vtroot, "vthook", "echo") + + if _, err := os.Lstat(echoHookPath); err == nil { + require.NoError(t, os.Remove(echoHookPath)) + } + + require.NoError(t, os.Symlink(echo, echoHookPath)) + defer func() { + require.NoError(t, os.Remove(echoHookPath)) + }() + tt := []struct { + name string + hookName string + parameters []string + expectedError string + }{ + { + name: "HookSuccess", + hookName: "echo", + parameters: []string{"test"}, + }, + { + name: "HookDoesNotExist", + hookName: "nonexistent-hook", + parameters: []string{"test"}, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + h := NewHook(tc.hookName, tc.parameters) + err := h.ExecuteOptional() + if tc.expectedError == "" { + assert.NoError(t, err) + } else { + assert.Error(t, err) + assert.ErrorContains(t, err, tc.expectedError) + } + }) + } +} + +func TestNewHook(t *testing.T) { + h := NewHook("test-hook", []string{"arg1", "arg2"}) + assert.Equal(t, "test-hook", h.Name) + assert.Equal(t, []string{"arg1", "arg2"}, h.Parameters) +} + +func TestNewSimpleHook(t *testing.T) { + h := NewSimpleHook("simple-hook") + assert.Equal(t, "simple-hook", h.Name) + assert.Empty(t, h.Parameters) +} + +func TestNewHookWithEnv(t *testing.T) { + h := NewHookWithEnv("env-hook", []string{"arg1", "arg2"}, map[string]string{"KEY": "VALUE"}) + assert.Equal(t, "env-hook", h.Name) + assert.Equal(t, []string{"arg1", "arg2"}, h.Parameters) + assert.Equal(t, map[string]string{"KEY": "VALUE"}, h.ExtraEnv) +} + +func TestString(t *testing.T) { + tt := []struct { + name string + input HookResult + expected string + }{ + { + name: "HOOK_SUCCESS", + input: HookResult{ExitStatus: HOOK_SUCCESS, Stdout: "output"}, + expected: "result: HOOK_SUCCESS\nstdout:\noutput", + }, + { + name: "HOOK_DOES_NOT_EXIST", + input: HookResult{ExitStatus: HOOK_DOES_NOT_EXIST}, + expected: "result: HOOK_DOES_NOT_EXIST", + }, + { + name: "HOOK_STAT_FAILED", + input: HookResult{ExitStatus: HOOK_STAT_FAILED}, + expected: "result: HOOK_STAT_FAILED", + }, + { + name: "HOOK_CANNOT_GET_EXIT_STATUS", + input: HookResult{ExitStatus: HOOK_CANNOT_GET_EXIT_STATUS}, + expected: "result: HOOK_CANNOT_GET_EXIT_STATUS", + }, + { + name: "HOOK_INVALID_NAME", + input: HookResult{ExitStatus: HOOK_INVALID_NAME}, + expected: "result: HOOK_INVALID_NAME", + }, + { + name: "HOOK_VTROOT_ERROR", + input: HookResult{ExitStatus: HOOK_VTROOT_ERROR}, + expected: "result: HOOK_VTROOT_ERROR", + }, + { + name: "case default", + input: HookResult{ExitStatus: 42}, + expected: "result: exit(42)", + }, + { + name: "WithStderr", + input: HookResult{ExitStatus: HOOK_SUCCESS, Stderr: "error"}, + expected: "result: HOOK_SUCCESS\nstderr:\nerror", + }, + { + name: "WithStderr", + input: HookResult{ExitStatus: HOOK_SUCCESS, Stderr: "error"}, + expected: "result: HOOK_SUCCESS\nstderr:\nerror", + }, + { + name: "WithStdoutAndStderr", + input: HookResult{ExitStatus: HOOK_SUCCESS, Stdout: "output", Stderr: "error"}, + expected: "result: HOOK_SUCCESS\nstdout:\noutput\nstderr:\nerror", + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result := tc.input.String() + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestExecuteAsReadPipe(t *testing.T) { + vtroot, err := vtenv.VtRoot() + require.NoError(t, err) + + cat, err := exec.LookPath("cat") + require.NoError(t, err) + + catHookPath := path.Join(vtroot, "vthook", "cat") + + if _, err := os.Lstat(catHookPath); err == nil { + require.NoError(t, os.Remove(catHookPath)) + } + + require.NoError(t, os.Symlink(cat, catHookPath)) + defer func() { + require.NoError(t, os.Remove(catHookPath)) + }() + + h := NewHook("cat", nil) + reader, waitFunc, status, err := h.ExecuteAsReadPipe(strings.NewReader("Hello, World!\n")) + require.NoError(t, err) + defer reader.(io.Closer).Close() + + output, err := io.ReadAll(reader) + require.NoError(t, err) + assert.Equal(t, "Hello, World!\n", string(output)) + + stderr, waitErr := waitFunc() + assert.Empty(t, stderr) + assert.NoError(t, waitErr) + assert.Equal(t, HOOK_SUCCESS, status) +} + +func TestExecuteAsReadPipeErrorFindingHook(t *testing.T) { + h := NewHook("nonexistent-hook", nil) + reader, waitFunc, status, err := h.ExecuteAsReadPipe(strings.NewReader("Hello, World!\n")) + require.Error(t, err) + assert.Nil(t, reader) + assert.Nil(t, waitFunc) + assert.Equal(t, HOOK_DOES_NOT_EXIST, status) +} + +func TestExecuteAsWritePipe(t *testing.T) { + var writer strings.Builder + var writerMutex sync.Mutex + + vtroot, err := vtenv.VtRoot() + require.NoError(t, err) + + echo, err := exec.LookPath("echo") + require.NoError(t, err) + + echoHookPath := path.Join(vtroot, "vthook", "echo") + + if _, err := os.Lstat(echoHookPath); err == nil { + require.NoError(t, os.Remove(echoHookPath)) + } + + require.NoError(t, os.Symlink(echo, echoHookPath)) + defer func() { + require.NoError(t, os.Remove(echoHookPath)) + }() + + h := NewHook("echo", nil) + + writerMutex.Lock() + var writerTemp strings.Builder + _, waitFunc, status, err := h.ExecuteAsWritePipe(&writerTemp) + writerMutex.Unlock() + + require.NoError(t, err) + defer func() { + writerMutex.Lock() + writer.Reset() + writerMutex.Unlock() + }() + + writerMutex.Lock() + _, err = writer.Write([]byte("Hello, World!\n")) + writerMutex.Unlock() + require.NoError(t, err) + + stderr, waitErr := waitFunc() + assert.Empty(t, stderr) + assert.NoError(t, waitErr) + assert.Equal(t, HOOK_SUCCESS, status) +} + +func TestExecuteAsWritePipeErrorFindingHook(t *testing.T) { + h := NewHook("nonexistent-hook", nil) + var writer strings.Builder + writerPtr := &writer + _, _, status, err := h.ExecuteAsWritePipe(writerPtr) + assert.Error(t, err) + assert.Equal(t, HOOK_DOES_NOT_EXIST, status) +} diff --git a/go/vt/key/destination.go b/go/vt/key/destination.go index 437e980f480..6b8f145390b 100644 --- a/go/vt/key/destination.go +++ b/go/vt/key/destination.go @@ -17,7 +17,6 @@ limitations under the License. package key import ( - "bytes" "encoding/hex" "math/rand" "sort" @@ -48,7 +47,7 @@ type Destination interface { // DestinationsString returns a printed version of the destination array. func DestinationsString(destinations []Destination) string { - var buffer bytes.Buffer + var buffer strings.Builder buffer.WriteString("Destinations:") for i, d := range destinations { if i > 0 { @@ -155,40 +154,6 @@ func processExactKeyRange(allShards []*topodatapb.ShardReference, kr *topodatapb return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "keyrange %v does not exactly match shards", KeyRangeString(kr)) } -// -// DestinationExactKeyRanges -// - -// DestinationExactKeyRanges is the destination for multiple KeyRanges. -// The KeyRanges must map exactly to one or more shards, and cannot -// start or end in the middle of a shard. -// It implements the Destination interface. -type DestinationExactKeyRanges []*topodatapb.KeyRange - -// Resolve is part of the Destination interface. -func (d DestinationExactKeyRanges) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error { - for _, kr := range d { - if err := processExactKeyRange(allShards, kr, addShard); err != nil { - return err - } - } - return nil -} - -// String is part of the Destination interface. -func (d DestinationExactKeyRanges) String() string { - var buffer bytes.Buffer - buffer.WriteString("DestinationExactKeyRanges(") - for i, kr := range d { - if i > 0 { - buffer.WriteByte(',') - } - buffer.WriteString(KeyRangeString(kr)) - } - buffer.WriteByte(')') - return buffer.String() -} - // // DestinationKeyRange // @@ -226,38 +191,6 @@ func processKeyRange(allShards []*topodatapb.ShardReference, kr *topodatapb.KeyR return nil } -// -// DestinationKeyRanges -// - -// DestinationKeyRanges is the destination for multiple KeyRanges. -// It implements the Destination interface. -type DestinationKeyRanges []*topodatapb.KeyRange - -// Resolve is part of the Destination interface. -func (d DestinationKeyRanges) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error { - for _, kr := range d { - if err := processKeyRange(allShards, kr, addShard); err != nil { - return err - } - } - return nil -} - -// String is part of the Destination interface. -func (d DestinationKeyRanges) String() string { - var buffer bytes.Buffer - buffer.WriteString("DestinationKeyRanges(") - for i, kr := range d { - if i > 0 { - buffer.WriteByte(',') - } - buffer.WriteString(KeyRangeString(kr)) - } - buffer.WriteByte(')') - return buffer.String() -} - // // DestinationKeyspaceID // @@ -318,7 +251,7 @@ func (d DestinationKeyspaceIDs) Resolve(allShards []*topodatapb.ShardReference, // String is part of the Destination interface. func (d DestinationKeyspaceIDs) String() string { - var buffer bytes.Buffer + var buffer strings.Builder buffer.WriteString("DestinationKeyspaceIDs(") for i, ksid := range d { if i > 0 { diff --git a/go/vt/logutil/logger.go b/go/vt/logutil/logger.go index 524ca4db4d7..47c3f124238 100644 --- a/go/vt/logutil/logger.go +++ b/go/vt/logutil/logger.go @@ -17,7 +17,6 @@ limitations under the License. package logutil import ( - "bytes" "fmt" "io" "runtime" @@ -57,7 +56,7 @@ type Logger interface { // EventToBuffer formats an individual Event into a buffer, without the // final '\n' -func EventToBuffer(event *logutilpb.Event, buf *bytes.Buffer) { +func EventToBuffer(event *logutilpb.Event, buf *strings.Builder) { // Avoid Fprintf, for speed. The format is so simple that we // can do it quickly by hand. It's worth about 3X. Fprintf is hard. @@ -98,8 +97,8 @@ func EventToBuffer(event *logutilpb.Event, buf *bytes.Buffer) { // EventString returns the line in one string func EventString(event *logutilpb.Event) string { - buf := new(bytes.Buffer) - EventToBuffer(event, buf) + var buf strings.Builder + EventToBuffer(event, &buf) return buf.String() } @@ -207,27 +206,6 @@ func (cl *CallbackLogger) Printf(format string, v ...any) { }) } -// ChannelLogger is a Logger that sends the logging events through a channel for -// consumption. -type ChannelLogger struct { - CallbackLogger - C chan *logutilpb.Event -} - -// NewChannelLogger returns a CallbackLogger which will write the data -// on a channel -func NewChannelLogger(size int) *ChannelLogger { - c := make(chan *logutilpb.Event, size) - return &ChannelLogger{ - CallbackLogger: CallbackLogger{ - f: func(e *logutilpb.Event) { - c <- e - }, - }, - C: c, - } -} - // MemoryLogger keeps the logging events in memory. // All protected by a mutex. type MemoryLogger struct { @@ -251,11 +229,11 @@ func NewMemoryLogger() *MemoryLogger { // String returns all the lines in one String, separated by '\n' func (ml *MemoryLogger) String() string { - buf := new(bytes.Buffer) + var buf strings.Builder ml.mu.Lock() defer ml.mu.Unlock() for _, event := range ml.Events { - EventToBuffer(event, buf) + EventToBuffer(event, &buf) buf.WriteByte('\n') } return buf.String() @@ -355,7 +333,7 @@ func (tl *TeeLogger) Printf(format string, v ...any) { const digits = "0123456789" // twoDigits adds a zero-prefixed two-digit integer to buf -func twoDigits(buf *bytes.Buffer, value int) { +func twoDigits(buf *strings.Builder, value int) { buf.WriteByte(digits[value/10]) buf.WriteByte(digits[value%10]) } @@ -363,7 +341,7 @@ func twoDigits(buf *bytes.Buffer, value int) { // nDigits adds an n-digit integer d to buf // padding with pad on the left. // It assumes d >= 0. -func nDigits(buf *bytes.Buffer, n, d int, pad byte) { +func nDigits(buf *strings.Builder, n, d int, pad byte) { tmp := make([]byte, n) j := n - 1 for ; j >= 0 && d > 0; j-- { @@ -377,7 +355,7 @@ func nDigits(buf *bytes.Buffer, n, d int, pad byte) { } // someDigits adds a zero-prefixed variable-width integer to buf -func someDigits(buf *bytes.Buffer, d int64) { +func someDigits(buf *strings.Builder, d int64) { // Print into the top, then copy down. tmp := make([]byte, 10) j := 10 diff --git a/go/vt/logutil/logger_test.go b/go/vt/logutil/logger_test.go index 0eb4edb2b93..ce25543da5f 100644 --- a/go/vt/logutil/logger_test.go +++ b/go/vt/logutil/logger_test.go @@ -112,44 +112,15 @@ func TestMemoryLogger(t *testing.T) { } } -func TestChannelLogger(t *testing.T) { - cl := NewChannelLogger(10) - cl.Infof("test %v", 123) - cl.Warningf("test %v", 123) - cl.Errorf("test %v", 123) - cl.Printf("test %v", 123) - close(cl.C) - - count := 0 - for e := range cl.C { - if got, want := e.Value, "test 123"; got != want { - t.Errorf("e.Value = %q, want %q", got, want) - } - if e.File != "logger_test.go" { - t.Errorf("Invalid file name: %v", e.File) - } - count++ - } - if got, want := count, 4; got != want { - t.Errorf("count = %v, want %v", got, want) - } -} - func TestTeeLogger(t *testing.T) { - ml := NewMemoryLogger() - cl := NewChannelLogger(10) - tl := NewTeeLogger(ml, cl) + ml1 := NewMemoryLogger() + ml2 := NewMemoryLogger() + tl := NewTeeLogger(ml1, ml2) tl.Infof("test infof %v %v", 1, 2) tl.Warningf("test warningf %v %v", 2, 3) tl.Errorf("test errorf %v %v", 3, 4) tl.Printf("test printf %v %v", 4, 5) - close(cl.C) - - clEvents := []*logutilpb.Event{} - for e := range cl.C { - clEvents = append(clEvents, e) - } wantEvents := []*logutilpb.Event{ {Level: logutilpb.Level_INFO, Value: "test infof 1 2"}, @@ -159,7 +130,7 @@ func TestTeeLogger(t *testing.T) { } wantFile := "logger_test.go" - for i, events := range [][]*logutilpb.Event{ml.Events, clEvents} { + for i, events := range [][]*logutilpb.Event{ml1.Events, ml2.Events} { if got, want := len(events), len(wantEvents); got != want { t.Fatalf("[%v] len(events) = %v, want %v", i, got, want) } diff --git a/go/vt/logutil/logutil_flaky_test.go b/go/vt/logutil/logutil_test.go similarity index 100% rename from go/vt/logutil/logutil_flaky_test.go rename to go/vt/logutil/logutil_test.go diff --git a/go/vt/logz/logz_utils_test.go b/go/vt/logz/logz_utils_test.go new file mode 100644 index 00000000000..9a8b78917ff --- /dev/null +++ b/go/vt/logz/logz_utils_test.go @@ -0,0 +1,206 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logz + +import ( + "testing" + + "net/http" + "net/http/httptest" + + "github.com/stretchr/testify/require" +) + +func TestWrappable(t *testing.T) { + tests := []struct { + input string + output string + }{ + { + input: "s", + output: "s", + }, + { + input: "val,ue", + output: "val,\u200bue", + }, + { + input: ")al,ue", + output: ")\u200bal,\u200bue", + }, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + require.Equal(t, tt.output, Wrappable(tt.input)) + }) + } +} + +func TestStartAndEndHTMLTable(t *testing.T) { + // Create a mock HTTP response writer + w := httptest.NewRecorder() + + // Call the function to be tested + StartHTMLTable(w) + + // Check the response status code + require.Equal(t, http.StatusOK, w.Code) + + // Define the expected HTML content + expectedHTML := ` + + +
HealthCheck Tablet Cache%s
Cell
+` + + // Check if the response body matches the expected HTML content + require.Contains(t, w.Body.String(), expectedHTML) + + // Call the function to be tested + EndHTMLTable(w) + + // Check the response status code + require.Equal(t, http.StatusOK, w.Code) + + expectedHTML = ` +
+ +` + + // Check if the response body matches the expected HTML content + require.Contains(t, w.Body.String(), expectedHTML) +} diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 7058745d6c6..3ba6b187a2f 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -239,8 +239,9 @@ func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, file return nil, fmt.Errorf("AddFile cannot be called on read-only backup") } // Error out if the file size it too large ( ~4.75 TB) - if filesize > azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks { - return nil, fmt.Errorf("filesize (%v) is too large to upload to az blob (max size %v)", filesize, azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks) + maxSize := int64(azblob.BlockBlobMaxStageBlockBytes * azblob.BlockBlobMaxBlocks) + if filesize > maxSize { + return nil, fmt.Errorf("filesize (%v) is too large to upload to az blob (max size %v)", filesize, maxSize) } obj := objName(bh.dir, bh.name, filename) diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go index e9f0b19d54a..fb401966c50 100644 --- a/go/vt/mysqlctl/backup.go +++ b/go/vt/mysqlctl/backup.go @@ -86,6 +86,8 @@ var ( // backupCompressBlocks is the number of blocks that are processed // once before the writer blocks backupCompressBlocks = 2 + + EmptyBackupMessage = "no new data to backup, skipping it" ) func init() { @@ -168,14 +170,20 @@ func Backup(ctx context.Context, params BackupParams) error { } // Take the backup, and either AbortBackup or EndBackup. - usable, err := be.ExecuteBackup(ctx, beParams, bh) + backupResult, err := be.ExecuteBackup(ctx, beParams, bh) logger := params.Logger var finishErr error - if usable { - finishErr = bh.EndBackup(ctx) - } else { + switch backupResult { + case BackupUnusable: logger.Errorf2(err, "backup is not usable, aborting it") finishErr = bh.AbortBackup(ctx) + case BackupEmpty: + logger.Infof(EmptyBackupMessage) + // While an empty backup is considered "successful", it should leave no trace. + // We therefore ensire to clean up an backup files/directories/entries. + finishErr = bh.AbortBackup(ctx) + case BackupUsable: + finishErr = bh.EndBackup(ctx) } if err != nil { if finishErr != nil { @@ -453,7 +461,7 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) // The MySQL manual recommends restarting mysqld after running mysql_upgrade, // so that any changes made to system tables take effect. params.Logger.Infof("Restore: restarting mysqld after mysql_upgrade") - if err := params.Mysqld.Shutdown(context.Background(), params.Cnf, true); err != nil { + if err := params.Mysqld.Shutdown(context.Background(), params.Cnf, true, params.MysqlShutdownTimeout); err != nil { return nil, err } if err := params.Mysqld.Start(context.Background(), params.Cnf); err != nil { diff --git a/go/vt/mysqlctl/backup_blackbox_test.go b/go/vt/mysqlctl/backup_blackbox_test.go index 8de6a8679fa..4508c4e4306 100644 --- a/go/vt/mysqlctl/backup_blackbox_test.go +++ b/go/vt/mysqlctl/backup_blackbox_test.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" @@ -47,6 +48,8 @@ import ( "vitess.io/vitess/go/vt/topo/memorytopo" ) +const mysqlShutdownTimeout = 1 * time.Minute + func setBuiltinBackupMysqldDeadline(t time.Duration) time.Duration { old := mysqlctl.BuiltinBackupMysqldTimeout mysqlctl.BuiltinBackupMysqldTimeout = t @@ -146,7 +149,7 @@ func TestExecuteBackup(t *testing.T) { fakeStats := backupstats.NewFakeStats() - ok, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), Mysqld: mysqld, Cnf: &mysqlctl.Mycnf{ @@ -154,16 +157,17 @@ func TestExecuteBackup(t *testing.T) { InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), DataDir: path.Join(backupRoot, "datadir"), }, - Concurrency: 2, - HookExtraEnv: map[string]string{}, - TopoServer: ts, - Keyspace: keyspace, - Shard: shard, - Stats: fakeStats, + Concurrency: 2, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + Stats: fakeStats, + MysqlShutdownTimeout: mysqlShutdownTimeout, }, bh) require.NoError(t, err) - assert.True(t, ok) + assert.Equal(t, mysqlctl.BackupUsable, backupResult) var destinationCloseStats int var destinationOpenStats int @@ -205,7 +209,7 @@ func TestExecuteBackup(t *testing.T) { mysqld.ExpectedExecuteSuperQueryCurrent = 0 // resest the index of what queries we've run mysqld.ShutdownTime = time.Minute // reminder that shutdownDeadline is 1s - ok, err = be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + backupResult, err = be.ExecuteBackup(ctx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), Mysqld: mysqld, Cnf: &mysqlctl.Mycnf{ @@ -213,14 +217,15 @@ func TestExecuteBackup(t *testing.T) { InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), DataDir: path.Join(backupRoot, "datadir"), }, - HookExtraEnv: map[string]string{}, - TopoServer: ts, - Keyspace: keyspace, - Shard: shard, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: mysqlShutdownTimeout, }, bh) assert.Error(t, err) - assert.False(t, ok) + assert.Equal(t, mysqlctl.BackupUnusable, backupResult) } func TestExecuteBackupWithSafeUpgrade(t *testing.T) { @@ -291,7 +296,7 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { "SET GLOBAL innodb_fast_shutdown=0": {}, } - ok, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), Mysqld: mysqld, Cnf: &mysqlctl.Mycnf{ @@ -299,16 +304,17 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), DataDir: path.Join(backupRoot, "datadir"), }, - Concurrency: 2, - TopoServer: ts, - Keyspace: keyspace, - Shard: shard, - Stats: backupstats.NewFakeStats(), - UpgradeSafe: true, + Concurrency: 2, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + Stats: backupstats.NewFakeStats(), + UpgradeSafe: true, + MysqlShutdownTimeout: mysqlShutdownTimeout, }, bh) require.NoError(t, err) - assert.True(t, ok) + assert.Equal(t, mysqlctl.BackupUsable, backupResult) } // TestExecuteBackupWithCanceledContext tests the ability of the backup function to gracefully handle cases where errors @@ -377,7 +383,7 @@ func TestExecuteBackupWithCanceledContext(t *testing.T) { cancelledCtx, cancelCtx := context.WithCancel(context.Background()) cancelCtx() - ok, err := be.ExecuteBackup(cancelledCtx, mysqlctl.BackupParams{ + backupResult, err := be.ExecuteBackup(cancelledCtx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), Mysqld: mysqld, Cnf: &mysqlctl.Mycnf{ @@ -385,18 +391,19 @@ func TestExecuteBackupWithCanceledContext(t *testing.T) { InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), DataDir: path.Join(backupRoot, "datadir"), }, - Stats: backupstats.NewFakeStats(), - Concurrency: 2, - HookExtraEnv: map[string]string{}, - TopoServer: ts, - Keyspace: keyspace, - Shard: shard, + Stats: backupstats.NewFakeStats(), + Concurrency: 2, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: mysqlShutdownTimeout, }, bh) require.Error(t, err) // all four files will fail require.ErrorContains(t, err, "context canceled;context canceled;context canceled;context canceled") - assert.False(t, ok) + assert.Equal(t, mysqlctl.BackupUnusable, backupResult) } // TestExecuteRestoreWithCanceledContext tests the ability of the restore function to gracefully handle cases where errors @@ -461,7 +468,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { defer mysqld.Close() mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} - ok, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ + backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), Mysqld: mysqld, Cnf: &mysqlctl.Mycnf{ @@ -469,16 +476,17 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), DataDir: path.Join(backupRoot, "datadir"), }, - Stats: backupstats.NewFakeStats(), - Concurrency: 2, - HookExtraEnv: map[string]string{}, - TopoServer: ts, - Keyspace: keyspace, - Shard: shard, + Stats: backupstats.NewFakeStats(), + Concurrency: 2, + HookExtraEnv: map[string]string{}, + TopoServer: ts, + Keyspace: keyspace, + Shard: shard, + MysqlShutdownTimeout: mysqlShutdownTimeout, }, bh) require.NoError(t, err) - assert.True(t, ok) + assert.Equal(t, mysqlctl.BackupUsable, backupResult) // Now try to restore the above backup. bh = filebackupstorage.NewBackupHandle(nil, "", "", true) @@ -500,19 +508,20 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { RelayLogIndexPath: path.Join(backupRoot, "relaylogindex"), RelayLogInfoPath: path.Join(backupRoot, "relayloginfo"), }, - Logger: logutil.NewConsoleLogger(), - Mysqld: mysqld, - Concurrency: 2, - HookExtraEnv: map[string]string{}, - DeleteBeforeRestore: false, - DbName: "test", - Keyspace: "test", - Shard: "-", - StartTime: time.Now(), - RestoreToPos: replication.Position{}, - RestoreToTimestamp: time.Time{}, - DryRun: false, - Stats: fakeStats, + Logger: logutil.NewConsoleLogger(), + Mysqld: mysqld, + Concurrency: 2, + HookExtraEnv: map[string]string{}, + DeleteBeforeRestore: false, + DbName: "test", + Keyspace: "test", + Shard: "-", + StartTime: time.Now(), + RestoreToPos: replication.Position{}, + RestoreToTimestamp: time.Time{}, + DryRun: false, + Stats: fakeStats, + MysqlShutdownTimeout: mysqlShutdownTimeout, } // Successful restore. @@ -593,9 +602,9 @@ func needInnoDBRedoLogSubdir() (needIt bool, err error) { return needIt, err } versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch) - _, capableOf, _ := mysql.GetFlavor(versionStr, nil) + capableOf := mysql.ServerVersionCapableOf(versionStr) if capableOf == nil { return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr) } - return capableOf(mysql.DynamicRedoLogCapacityFlavorCapability) + return capableOf(capabilities.DynamicRedoLogCapacityFlavorCapability) } diff --git a/go/vt/mysqlctl/backup_test.go b/go/vt/mysqlctl/backup_test.go index 5b97f709c2f..ad7e0faab98 100644 --- a/go/vt/mysqlctl/backup_test.go +++ b/go/vt/mysqlctl/backup_test.go @@ -42,6 +42,8 @@ import ( "vitess.io/vitess/go/vt/mysqlctl/backupstorage" ) +const mysqlShutdownTimeout = 1 * time.Minute + // TestBackupExecutesBackupWithScopedParams tests that Backup passes // a Scope()-ed stats to backupengine ExecuteBackup. func TestBackupExecutesBackupWithScopedParams(t *testing.T) { @@ -563,7 +565,7 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv { sqldb := fakesqldb.New(t) sqldb.SetNeverFail(true) mysqld := NewFakeMysqlDaemon(sqldb) - require.Nil(t, mysqld.Shutdown(ctx, nil, false)) + require.Nil(t, mysqld.Shutdown(ctx, nil, false, mysqlShutdownTimeout)) dirName, err := os.MkdirTemp("", "vt_backup_test") require.Nil(t, err) @@ -575,33 +577,35 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv { stats := backupstats.NewFakeStats() backupParams := BackupParams{ - Cnf: cnf, - Logger: logger, - Mysqld: mysqld, - Concurrency: 1, - HookExtraEnv: map[string]string{}, - TopoServer: nil, - Keyspace: "test", - Shard: "-", - BackupTime: time.Now(), - IncrementalFromPos: "", - Stats: stats, + Cnf: cnf, + Logger: logger, + Mysqld: mysqld, + Concurrency: 1, + HookExtraEnv: map[string]string{}, + TopoServer: nil, + Keyspace: "test", + Shard: "-", + BackupTime: time.Now(), + IncrementalFromPos: "", + Stats: stats, + MysqlShutdownTimeout: mysqlShutdownTimeout, } restoreParams := RestoreParams{ - Cnf: cnf, - Logger: logger, - Mysqld: mysqld, - Concurrency: 1, - HookExtraEnv: map[string]string{}, - DeleteBeforeRestore: false, - DbName: "test", - Keyspace: "test", - Shard: "-", - StartTime: time.Now(), - RestoreToPos: replication.Position{}, - DryRun: false, - Stats: stats, + Cnf: cnf, + Logger: logger, + Mysqld: mysqld, + Concurrency: 1, + HookExtraEnv: map[string]string{}, + DeleteBeforeRestore: false, + DbName: "test", + Keyspace: "test", + Shard: "-", + StartTime: time.Now(), + RestoreToPos: replication.Position{}, + DryRun: false, + Stats: stats, + MysqlShutdownTimeout: mysqlShutdownTimeout, } manifest := BackupManifest{ diff --git a/go/vt/mysqlctl/backupengine.go b/go/vt/mysqlctl/backupengine.go index 5a79edbdde0..915b5e6894f 100644 --- a/go/vt/mysqlctl/backupengine.go +++ b/go/vt/mysqlctl/backupengine.go @@ -45,9 +45,17 @@ var ( backupEngineImplementation = builtinBackupEngineName ) +type BackupResult int + +const ( + BackupUnusable BackupResult = iota + BackupEmpty + BackupUsable +) + // BackupEngine is the interface to take a backup with a given engine. type BackupEngine interface { - ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (bool, error) + ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (BackupResult, error) ShouldDrainForBackup(req *tabletmanagerdatapb.BackupRequest) bool } @@ -77,23 +85,26 @@ type BackupParams struct { Stats backupstats.Stats // UpgradeSafe indicates whether the backup is safe for upgrade and created with innodb_fast_shutdown=0 UpgradeSafe bool + // MysqlShutdownTimeout defines how long we wait during MySQL shutdown if that is part of the backup process. + MysqlShutdownTimeout time.Duration } func (b *BackupParams) Copy() BackupParams { return BackupParams{ - Cnf: b.Cnf, - Mysqld: b.Mysqld, - Logger: b.Logger, - Concurrency: b.Concurrency, - HookExtraEnv: b.HookExtraEnv, - TopoServer: b.TopoServer, - Keyspace: b.Keyspace, - Shard: b.Shard, - TabletAlias: b.TabletAlias, - BackupTime: b.BackupTime, - IncrementalFromPos: b.IncrementalFromPos, - Stats: b.Stats, - UpgradeSafe: b.UpgradeSafe, + Cnf: b.Cnf, + Mysqld: b.Mysqld, + Logger: b.Logger, + Concurrency: b.Concurrency, + HookExtraEnv: b.HookExtraEnv, + TopoServer: b.TopoServer, + Keyspace: b.Keyspace, + Shard: b.Shard, + TabletAlias: b.TabletAlias, + BackupTime: b.BackupTime, + IncrementalFromPos: b.IncrementalFromPos, + Stats: b.Stats, + UpgradeSafe: b.UpgradeSafe, + MysqlShutdownTimeout: b.MysqlShutdownTimeout, } } @@ -130,24 +141,27 @@ type RestoreParams struct { DryRun bool // Stats let's restore engines report detailed restore timings. Stats backupstats.Stats + // MysqlShutdownTimeout defines how long we wait during MySQL shutdown if that is part of the backup process. + MysqlShutdownTimeout time.Duration } func (p *RestoreParams) Copy() RestoreParams { return RestoreParams{ - Cnf: p.Cnf, - Mysqld: p.Mysqld, - Logger: p.Logger, - Concurrency: p.Concurrency, - HookExtraEnv: p.HookExtraEnv, - DeleteBeforeRestore: p.DeleteBeforeRestore, - DbName: p.DbName, - Keyspace: p.Keyspace, - Shard: p.Shard, - StartTime: p.StartTime, - RestoreToPos: p.RestoreToPos, - RestoreToTimestamp: p.RestoreToTimestamp, - DryRun: p.DryRun, - Stats: p.Stats, + Cnf: p.Cnf, + Mysqld: p.Mysqld, + Logger: p.Logger, + Concurrency: p.Concurrency, + HookExtraEnv: p.HookExtraEnv, + DeleteBeforeRestore: p.DeleteBeforeRestore, + DbName: p.DbName, + Keyspace: p.Keyspace, + Shard: p.Shard, + StartTime: p.StartTime, + RestoreToPos: p.RestoreToPos, + RestoreToTimestamp: p.RestoreToTimestamp, + DryRun: p.DryRun, + Stats: p.Stats, + MysqlShutdownTimeout: p.MysqlShutdownTimeout, } } @@ -263,6 +277,9 @@ type IncrementalBackupDetails struct { // their own custom fields by embedding this struct anonymously into their own // custom struct, as long as their custom fields don't have conflicting names. type BackupManifest struct { + // BackupName is the name of the backup, which is also the name of the directory + BackupName string + // BackupMethod is the name of the backup engine that created this backup. // If this is empty, the backup engine is assumed to be "builtin" since that // was the only engine that ever left this field empty. All new backup @@ -402,9 +419,9 @@ func (p *RestorePath) String() string { return sb.String() } -// FindLatestSuccessfulBackup returns the handle and manifest for the last good backup, +// findLatestSuccessfulBackup returns the handle and manifest for the last good backup, // which can be either full or increment -func FindLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs []backupstorage.BackupHandle, excludeBackupName string) (backupstorage.BackupHandle, *BackupManifest, error) { +func findLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs []backupstorage.BackupHandle, excludeBackupName string) (backupstorage.BackupHandle, *BackupManifest, error) { for index := len(bhs) - 1; index >= 0; index-- { bh := bhs[index] if bh.Name() == excludeBackupName { @@ -425,8 +442,8 @@ func FindLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs return nil, nil, ErrNoCompleteBackup } -// FindLatestSuccessfulBackupPosition returns the position of the last known successful backup -func FindLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams, excludeBackupName string) (backupName string, pos replication.Position, err error) { +// findLatestSuccessfulBackupPosition returns the position of the last known successful backup +func findLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams, excludeBackupName string) (backupName string, pos replication.Position, err error) { bs, err := backupstorage.GetBackupStorage() if err != nil { return "", pos, err @@ -440,7 +457,7 @@ func FindLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams if err != nil { return "", pos, vterrors.Wrap(err, "ListBackups failed") } - bh, manifest, err := FindLatestSuccessfulBackup(ctx, params.Logger, bhs, excludeBackupName) + bh, manifest, err := findLatestSuccessfulBackup(ctx, params.Logger, bhs, excludeBackupName) if err != nil { return "", pos, vterrors.Wrap(err, "FindLatestSuccessfulBackup failed") } @@ -448,6 +465,32 @@ func FindLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams return bh.Name(), pos, nil } +// findBackupPosition returns the position of a given backup, assuming the backup exists. +func findBackupPosition(ctx context.Context, params BackupParams, backupName string) (pos replication.Position, err error) { + bs, err := backupstorage.GetBackupStorage() + if err != nil { + return pos, err + } + defer bs.Close() + + backupDir := GetBackupDir(params.Keyspace, params.Shard) + bhs, err := bs.ListBackups(ctx, backupDir) + if err != nil { + return pos, vterrors.Wrap(err, "ListBackups failed") + } + for _, bh := range bhs { + if bh.Name() != backupName { + continue + } + manifest, err := GetBackupManifest(ctx, bh) + if err != nil { + return pos, vterrors.Wrapf(err, "GetBackupManifest failed for backup: %v", backupName) + } + return manifest.Position, nil + } + return pos, vterrors.Errorf(vtrpc.Code_NOT_FOUND, "could not find backup %q for %s/%s", backupName, params.Keyspace, params.Shard) +} + // FindBackupToRestore returns a path, a sequence of backup handles, to be restored. // The returned handles stand for valid backups with complete manifests. func FindBackupToRestore(ctx context.Context, params RestoreParams, bhs []backupstorage.BackupHandle) (restorePath *RestorePath, err error) { @@ -584,10 +627,10 @@ func validateMySQLVersionUpgradeCompatible(to string, from string, upgradeSafe b return fmt.Errorf("running MySQL version %q is newer than backup MySQL version %q which is not safe to upgrade", to, from) } -func prepareToRestore(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger) error { +func prepareToRestore(ctx context.Context, cnf *Mycnf, mysqld MysqlDaemon, logger logutil.Logger, mysqlShutdownTimeout time.Duration) error { // shutdown mysqld if it is running logger.Infof("Restore: shutdown mysqld") - if err := mysqld.Shutdown(ctx, cnf, true); err != nil { + if err := mysqld.Shutdown(ctx, cnf, true, mysqlShutdownTimeout); err != nil { return err } diff --git a/go/vt/mysqlctl/binlogs_gtid.go b/go/vt/mysqlctl/binlogs_gtid.go index 3ea48663578..61ca0a87f70 100644 --- a/go/vt/mysqlctl/binlogs_gtid.go +++ b/go/vt/mysqlctl/binlogs_gtid.go @@ -104,8 +104,7 @@ func ChooseBinlogsForIncrementalBackup( // The other thing to validate, is that we can't allow a situation where the backup-GTIDs have entries not covered // by our binary log's Previous-GTIDs (padded with purged GTIDs). Because that means we can't possibly restore to // such position. - prevGTIDsUnionPurged := prevGTIDsUnion.Union(purgedGTIDSet) - if !prevGTIDsUnionPurged.Contains(backupFromGTIDSet) { + if prevGTIDsUnionPurged := prevGTIDsUnion.Union(purgedGTIDSet); !prevGTIDsUnionPurged.Contains(backupFromGTIDSet) { return nil, "", "", vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "Mismatching GTID entries. Requested backup pos has entries not found in the binary logs, and binary logs have entries not found in the requested backup pos. Neither fully contains the other.\n- Requested pos=%v\n- binlog pos=%v\n- purgedGTIDSet=%v\n- union=%v\n- union purged=%v", backupFromGTIDSet, previousGTIDsPos.GTIDSet, purgedGTIDSet, prevGTIDsUnion, prevGTIDsUnionPurged) @@ -133,7 +132,16 @@ func ChooseBinlogsForIncrementalBackup( } return binaryLogsToBackup, incrementalBackupFromGTID, incrementalBackupToGTID, nil } - return nil, "", "", vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "no binary logs to backup (increment is empty)") + if prevGTIDsUnion.Union(purgedGTIDSet).Equal(backupFromGTIDSet) { + // This means we've iterated over all binary logs, and as it turns out, the backup pos is + // identical to the Previous-GTIDs of the last binary log. But, we also know that we ourselves + // have flushed the binary logs so as to generate the new (now last) binary log. + // Which means, from the Pos of the backup till the time we issued FLUSH BINARY LOGS, there + // were no new GTID entries. The database was performed no writes during that period, + // so we have no entries to backup and the backup is therefore empty. + return nil, "", "", nil + } + return nil, "", "", vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot find binary logs that cover requested GTID range. backupFromGTIDSet=%v, prevGTIDsUnion=%v", backupFromGTIDSet.String(), prevGTIDsUnion.String()) } // IsValidIncrementalBakcup determines whether the given manifest can be used to extend a backup diff --git a/go/vt/mysqlctl/binlogs_gtid_test.go b/go/vt/mysqlctl/binlogs_gtid_test.go index 655208e908e..ec1c220fd39 100644 --- a/go/vt/mysqlctl/binlogs_gtid_test.go +++ b/go/vt/mysqlctl/binlogs_gtid_test.go @@ -85,13 +85,13 @@ func TestChooseBinlogsForIncrementalBackup(t *testing.T) { name: "last binlog excluded, no binlogs found", previousGTIDs: basePreviousGTIDs, backupPos: "16b1039f-22b6-11ed-b765-0a43f95f28a3:1-331", - expectError: "no binary logs to backup", + expectBinlogs: nil, }, { name: "backup pos beyond all binlogs", previousGTIDs: basePreviousGTIDs, backupPos: "16b1039f-22b6-11ed-b765-0a43f95f28a3:1-630000", - expectError: "no binary logs to backup", + expectError: "cannot find binary logs that cover requested GTID range", }, { name: "missing GTID entries", @@ -294,13 +294,14 @@ func TestChooseBinlogsForIncrementalBackup(t *testing.T) { return } require.NoError(t, err) - require.NotEmpty(t, binlogsToBackup) assert.Equal(t, tc.expectBinlogs, binlogsToBackup) - if tc.previousGTIDs[binlogsToBackup[0]] != "" { - assert.Equal(t, tc.previousGTIDs[binlogsToBackup[0]], fromGTID) + if len(binlogsToBackup) > 0 { + if tc.previousGTIDs[binlogsToBackup[0]] != "" { + assert.Equal(t, tc.previousGTIDs[binlogsToBackup[0]], fromGTID) + } + assert.Equal(t, tc.previousGTIDs[binlogs[len(binlogs)-1]], toGTID) + assert.NotEqual(t, fromGTID, toGTID) } - assert.Equal(t, tc.previousGTIDs[binlogs[len(binlogs)-1]], toGTID) - assert.NotEqual(t, fromGTID, toGTID) }) } } diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go index e46932bcd51..023439d1fff 100644 --- a/go/vt/mysqlctl/builtinbackupengine.go +++ b/go/vt/mysqlctl/builtinbackupengine.go @@ -58,7 +58,7 @@ import ( const ( builtinBackupEngineName = "builtin" - autoIncrementalFromPos = "auto" + AutoIncrementalFromPos = "auto" dataDictionaryFile = "mysql.ibd" ) @@ -203,8 +203,8 @@ func (fe *FileEntry) open(cnf *Mycnf, readOnly bool) (*os.File, error) { } // ExecuteBackup runs a backup based on given params. This could be a full or incremental backup. -// The function returns a boolean that indicates if the backup is usable, and an overall error. -func (be *BuiltinBackupEngine) ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (bool, error) { +// The function returns a BackupResult that indicates the usability of the backup, and an overall error. +func (be *BuiltinBackupEngine) ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (BackupResult, error) { params.Logger.Infof("Executing Backup at %v for keyspace/shard %v/%v on tablet %v, concurrency: %v, compress: %v, incrementalFromPos: %v", params.BackupTime, params.Keyspace, params.Shard, params.TabletAlias, params.Concurrency, backupStorageCompress, params.IncrementalFromPos) @@ -233,49 +233,53 @@ func getIncrementalFromPosGTIDSet(incrementalFromPos string) (replication.Mysql5 // executeIncrementalBackup runs an incremental backup, based on given 'incremental_from_pos', which can be: // - A valid position // - "auto", indicating the incremental backup should begin with last successful backup end position. -func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (bool, error) { +// The function returns a BackupResult that indicates the usability of the backup, and an overall error. +func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (BackupResult, error) { // Collect MySQL status: // UUID serverUUID, err := params.Mysqld.GetServerUUID(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get server uuid") + return BackupUnusable, vterrors.Wrap(err, "can't get server uuid") } mysqlVersion, err := params.Mysqld.GetVersionString(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get MySQL version") + return BackupUnusable, vterrors.Wrap(err, "can't get MySQL version") } + // We now need to figure out the GTIDSet from which we want to take the incremental backup. The user may have + // specified a position, or they may have specified "auto", or they may have specified a backup name, in which + // case we need to find the position of that backup. var fromBackupName string - if params.IncrementalFromPos == autoIncrementalFromPos { + if params.IncrementalFromPos == AutoIncrementalFromPos { + // User has supplied "auto". params.Logger.Infof("auto evaluating incremental_from_pos") - backupName, pos, err := FindLatestSuccessfulBackupPosition(ctx, params, bh.Name()) + backupName, pos, err := findLatestSuccessfulBackupPosition(ctx, params, bh.Name()) if err != nil { - return false, err + return BackupUnusable, err } fromBackupName = backupName params.IncrementalFromPos = replication.EncodePosition(pos) params.Logger.Infof("auto evaluated incremental_from_pos: %s", params.IncrementalFromPos) } - // @@gtid_purged - getPurgedGTIDSet := func() (replication.Position, replication.Mysql56GTIDSet, error) { - gtidPurged, err := params.Mysqld.GetGTIDPurged(ctx) + if _, err := replication.DecodePositionDefaultFlavor(params.IncrementalFromPos, replication.Mysql56FlavorID); err != nil { + // This does not seem to be a valid position. Maybe it's a backup name? + backupName := params.IncrementalFromPos + pos, err := findBackupPosition(ctx, params, backupName) if err != nil { - return gtidPurged, nil, vterrors.Wrap(err, "can't get @@gtid_purged") - } - purgedGTIDSet, ok := gtidPurged.GTIDSet.(replication.Mysql56GTIDSet) - if !ok { - return gtidPurged, nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot get MySQL GTID purged value: %v", gtidPurged) + return BackupUnusable, err } - return gtidPurged, purgedGTIDSet, nil + fromBackupName = backupName + params.IncrementalFromPos = replication.EncodePosition(pos) + params.Logger.Infof("evaluated incremental_from_pos using backup name %q: %s", backupName, params.IncrementalFromPos) } // params.IncrementalFromPos is a string. We want to turn that into a MySQL GTID backupFromGTIDSet, err := getIncrementalFromPosGTIDSet(params.IncrementalFromPos) if err != nil { - return false, err + return BackupUnusable, err } - // OK, we now have the formal MySQL GTID from which we want to take the incremental backip. + // OK, we now have the formal MySQL GTID from which we want to take the incremental backup. // binlogs may not contain information about purged GTIDs. e.g. some binlog.000003 may have // previous GTIDs like 00021324-1111-1111-1111-111111111111:30-60, ie 1-29 range is missing. This can happen @@ -285,18 +289,30 @@ func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, par // ignore the purged GTIDs: if err := params.Mysqld.FlushBinaryLogs(ctx); err != nil { - return false, vterrors.Wrapf(err, "cannot flush binary logs in incremental backup") + return BackupUnusable, vterrors.Wrapf(err, "cannot flush binary logs in incremental backup") } binaryLogs, err := params.Mysqld.GetBinaryLogs(ctx) if err != nil { - return false, vterrors.Wrapf(err, "cannot get binary logs in incremental backup") + return BackupUnusable, vterrors.Wrapf(err, "cannot get binary logs in incremental backup") + } + + getPurgedGTIDSet := func() (replication.Position, replication.Mysql56GTIDSet, error) { + gtidPurged, err := params.Mysqld.GetGTIDPurged(ctx) + if err != nil { + return gtidPurged, nil, vterrors.Wrap(err, "can't get @@gtid_purged") + } + purgedGTIDSet, ok := gtidPurged.GTIDSet.(replication.Mysql56GTIDSet) + if !ok { + return gtidPurged, nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "failed to parse a valid MySQL GTID set from value: %v", gtidPurged) + } + return gtidPurged, purgedGTIDSet, nil } // gtid_purged is important information. The restore flow uses this info to to complement binary logs' Previous-GTIDs. // It is important to only get gtid_purged _after_ we've rotated into the new binary log, because the `FLUSH BINARY LOGS` // command may also purge old logs, hence affecting the value of gtid_purged. gtidPurged, purgedGTIDSet, err := getPurgedGTIDSet() if err != nil { - return false, err + return BackupUnusable, err } previousGTIDs := map[string]string{} getBinlogPreviousGTIDs := func(ctx context.Context, binlog string) (gtids string, err error) { @@ -314,15 +330,19 @@ func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, par } binaryLogsToBackup, incrementalBackupFromGTID, incrementalBackupToGTID, err := ChooseBinlogsForIncrementalBackup(ctx, backupFromGTIDSet, purgedGTIDSet, binaryLogs, getBinlogPreviousGTIDs) if err != nil { - return false, vterrors.Wrapf(err, "cannot get binary logs to backup in incremental backup") + return BackupUnusable, vterrors.Wrapf(err, "cannot get binary logs to backup in incremental backup") + } + if len(binaryLogsToBackup) == 0 { + // Empty backup. + return BackupEmpty, nil } incrementalBackupFromPosition, err := replication.ParsePosition(replication.Mysql56FlavorID, incrementalBackupFromGTID) if err != nil { - return false, vterrors.Wrapf(err, "cannot parse position %v", incrementalBackupFromGTID) + return BackupUnusable, vterrors.Wrapf(err, "cannot parse position %v", incrementalBackupFromGTID) } incrementalBackupToPosition, err := replication.ParsePosition(replication.Mysql56FlavorID, incrementalBackupToGTID) if err != nil { - return false, vterrors.Wrapf(err, "cannot parse position %v", incrementalBackupToGTID) + return BackupUnusable, vterrors.Wrapf(err, "cannot parse position %v", incrementalBackupToGTID) } // The backup position is the GTISset of the last binary log (taken from Previous-GTIDs of the one-next binary log), and we // also include gtid_purged ; this complies with the "standard" way MySQL "thinks" about GTIDs: there's gtid_executed, which includes @@ -337,16 +357,16 @@ func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, par fe := FileEntry{Base: backupBinlogDir, Name: binlogFile} fullPath, err := fe.fullPath(params.Cnf) if err != nil { - return false, err + return BackupUnusable, err } req.BinlogFileNames = append(req.BinlogFileNames, fullPath) } resp, err := params.Mysqld.ReadBinlogFilesTimestamps(ctx, req) if err != nil { - return false, vterrors.Wrapf(err, "reading timestamps from binlog files %v", binaryLogsToBackup) + return BackupUnusable, vterrors.Wrapf(err, "reading timestamps from binlog files %v", binaryLogsToBackup) } if resp.FirstTimestampBinlog == "" || resp.LastTimestampBinlog == "" { - return false, vterrors.Errorf(vtrpc.Code_ABORTED, "empty binlog name in response. Request=%v, Response=%v", req, resp) + return BackupUnusable, vterrors.Errorf(vtrpc.Code_ABORTED, "empty binlog name in response. Request=%v, Response=%v", req, resp) } log.Infof("ReadBinlogFilesTimestampsResponse: %+v", resp) incrDetails := &IncrementalBackupDetails{ @@ -365,14 +385,14 @@ func (be *BuiltinBackupEngine) executeIncrementalBackup(ctx context.Context, par // It is a fact that incrementalBackupFromGTID is earlier or equal to params.IncrementalFromPos. // In the backup manifest file, we document incrementalBackupFromGTID, not the user's requested position. if err := be.backupFiles(ctx, params, bh, incrementalBackupToPosition, gtidPurged, incrementalBackupFromPosition, fromBackupName, binaryLogsToBackup, serverUUID, mysqlVersion, incrDetails); err != nil { - return false, err + return BackupUnusable, err } - return true, nil + return BackupUsable, nil } -// executeFullBackup returns a boolean that indicates if the backup is usable, +// executeFullBackup returns a BackupResult that indicates the usability of the backup, // and an overall error. -func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (bool, error) { +func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (BackupResult, error) { if params.IncrementalFromPos != "" { return be.executeIncrementalBackup(ctx, params, bh) @@ -396,17 +416,17 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac // keep going if we're the primary, might be a degenerate case sourceIsPrimary = true default: - return false, vterrors.Wrap(err, "can't get replica status") + return BackupUnusable, vterrors.Wrap(err, "can't get replica status") } // get the read-only flag readOnly, err = params.Mysqld.IsReadOnly() if err != nil { - return false, vterrors.Wrap(err, "failed to get read_only status") + return BackupUnusable, vterrors.Wrap(err, "failed to get read_only status") } superReadOnly, err = params.Mysqld.IsSuperReadOnly() if err != nil { - return false, vterrors.Wrap(err, "can't get super_read_only status") + return BackupUnusable, vterrors.Wrap(err, "can't get super_read_only status") } log.Infof("Flag values during full backup, read_only: %v, super_read_only:%t", readOnly, superReadOnly) @@ -416,7 +436,7 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac if !superReadOnly { params.Logger.Infof("Enabling super_read_only on primary prior to backup") if _, err = params.Mysqld.SetSuperReadOnly(true); err != nil { - return false, vterrors.Wrap(err, "failed to enable super_read_only") + return BackupUnusable, vterrors.Wrap(err, "failed to enable super_read_only") } defer func() { // Resetting super_read_only back to its original value @@ -429,16 +449,16 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac } replicationPosition, err = params.Mysqld.PrimaryPosition() if err != nil { - return false, vterrors.Wrap(err, "can't get position on primary") + return BackupUnusable, vterrors.Wrap(err, "can't get position on primary") } } else { // This is a replica if err := params.Mysqld.StopReplication(params.HookExtraEnv); err != nil { - return false, vterrors.Wrapf(err, "can't stop replica") + return BackupUnusable, vterrors.Wrapf(err, "can't stop replica") } replicaStatus, err := params.Mysqld.ReplicationStatus() if err != nil { - return false, vterrors.Wrap(err, "can't get replica status") + return BackupUnusable, vterrors.Wrap(err, "can't get replica status") } replicationPosition = replicaStatus.Position } @@ -446,48 +466,51 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac gtidPurgedPosition, err := params.Mysqld.GetGTIDPurged(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get gtid_purged") + return BackupUnusable, vterrors.Wrap(err, "can't get gtid_purged") } serverUUID, err := params.Mysqld.GetServerUUID(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get server uuid") + return BackupUnusable, vterrors.Wrap(err, "can't get server uuid") } mysqlVersion, err := params.Mysqld.GetVersionString(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get MySQL version") + return BackupUnusable, vterrors.Wrap(err, "can't get MySQL version") } // check if we need to set innodb_fast_shutdown=0 for a backup safe for upgrades if params.UpgradeSafe { if _, err := params.Mysqld.FetchSuperQuery(ctx, "SET GLOBAL innodb_fast_shutdown=0"); err != nil { - return false, vterrors.Wrapf(err, "failed to disable fast shutdown") + return BackupUnusable, vterrors.Wrapf(err, "failed to disable fast shutdown") } } // shutdown mysqld shutdownCtx, cancel := context.WithTimeout(ctx, BuiltinBackupMysqldTimeout) - err = params.Mysqld.Shutdown(shutdownCtx, params.Cnf, true) + err = params.Mysqld.Shutdown(shutdownCtx, params.Cnf, true, params.MysqlShutdownTimeout) defer cancel() if err != nil { - return false, vterrors.Wrap(err, "can't shutdown mysqld") + return BackupUnusable, vterrors.Wrap(err, "can't shutdown mysqld") } // Backup everything, capture the error. backupErr := be.backupFiles(ctx, params, bh, replicationPosition, gtidPurgedPosition, replication.Position{}, "", nil, serverUUID, mysqlVersion, nil) - usable := backupErr == nil + backupResult := BackupUnusable + if backupErr == nil { + backupResult = BackupUsable + } // Try to restart mysqld, use background context in case we timed out the original context err = params.Mysqld.Start(context.Background(), params.Cnf) if err != nil { - return usable, vterrors.Wrap(err, "can't restart mysqld") + return backupResult, vterrors.Wrap(err, "can't restart mysqld") } // Resetting super_read_only back to its original value params.Logger.Infof("resetting mysqld super_read_only to %v", superReadOnly) if _, err := params.Mysqld.SetSuperReadOnly(superReadOnly); err != nil { - return usable, err + return backupResult, err } // Restore original mysqld state that we saved above. @@ -498,18 +521,18 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac semiSyncSource, semiSyncReplica) err := params.Mysqld.SetSemiSyncEnabled(semiSyncSource, semiSyncReplica) if err != nil { - return usable, err + return backupResult, err } } if replicaStartRequired { params.Logger.Infof("restarting mysql replication") if err := params.Mysqld.StartReplication(params.HookExtraEnv); err != nil { - return usable, vterrors.Wrap(err, "cannot restart replica") + return backupResult, vterrors.Wrap(err, "cannot restart replica") } // this should be quick, but we might as well just wait if err := WaitForReplicationStart(params.Mysqld, replicationStartDeadline); err != nil { - return usable, vterrors.Wrap(err, "replica is not restarting") + return backupResult, vterrors.Wrap(err, "replica is not restarting") } // Wait for a reliable value for ReplicationLagSeconds from ReplicationStatus() @@ -527,16 +550,16 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac pos, err := getPrimaryPosition(remoteCtx, tmc, params.TopoServer, params.Keyspace, params.Shard) // If we are unable to get the primary's position, return error. if err != nil { - return usable, err + return backupResult, err } if !replicationPosition.Equal(pos) { for { if err := ctx.Err(); err != nil { - return usable, err + return backupResult, err } status, err := params.Mysqld.ReplicationStatus() if err != nil { - return usable, err + return backupResult, err } newPos := status.Position if !newPos.Equal(replicationPosition) { @@ -547,7 +570,7 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac } } - return usable, backupErr + return backupResult, backupErr } // backupFiles finds the list of files to backup, and creates the backup. @@ -647,6 +670,7 @@ func (be *BuiltinBackupEngine) backupFiles( bm := &builtinBackupManifest{ // Common base fields BackupManifest: BackupManifest{ + BackupName: bh.Name(), BackupMethod: builtinBackupEngineName, Position: backupPosition, PurgedPosition: purgedPosition, @@ -886,7 +910,7 @@ func (be *BuiltinBackupEngine) backupFile(ctx context.Context, params BackupPara // executeRestoreFullBackup restores the files from a full backup. The underlying mysql database service is expected to be stopped. func (be *BuiltinBackupEngine) executeRestoreFullBackup(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle, bm builtinBackupManifest) error { - if err := prepareToRestore(ctx, params.Cnf, params.Mysqld, params.Logger); err != nil { + if err := prepareToRestore(ctx, params.Cnf, params.Mysqld, params.Logger, params.MysqlShutdownTimeout); err != nil { return err } diff --git a/go/vt/mysqlctl/cmd.go b/go/vt/mysqlctl/cmd.go index 5c3bda11437..cd4fd42f181 100644 --- a/go/vt/mysqlctl/cmd.go +++ b/go/vt/mysqlctl/cmd.go @@ -23,12 +23,13 @@ package mysqlctl import ( "fmt" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/dbconfigs" ) // CreateMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL // installation that hasn't been set up yet. -func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int) (*Mysqld, *Mycnf, error) { +func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int, collationEnv *collations.Environment) (*Mysqld, *Mycnf, error) { mycnf := NewMycnf(tabletUID, mysqlPort) // Choose a random MySQL server-id, since this is a fresh data dir. // We don't want to use the tablet UID as the MySQL server-id, @@ -46,20 +47,20 @@ func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int) ( mycnf.SocketFile = mysqlSocket } - dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile, collationEnv) return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } // OpenMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL // installation that already exists. The Mycnf will be built based on the my.cnf file // of the MySQL instance. -func OpenMysqldAndMycnf(tabletUID uint32) (*Mysqld, *Mycnf, error) { +func OpenMysqldAndMycnf(tabletUID uint32, collationEnv *collations.Environment) (*Mysqld, *Mycnf, error) { // We pass a port of 0, this will be read and overwritten from the path on disk - mycnf, err := ReadMycnf(NewMycnf(tabletUID, 0)) + mycnf, err := ReadMycnf(NewMycnf(tabletUID, 0), 0) if err != nil { return nil, nil, fmt.Errorf("couldn't read my.cnf file: %v", err) } - dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile, collationEnv) return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } diff --git a/go/vt/mysqlctl/fakebackupengine.go b/go/vt/mysqlctl/fakebackupengine.go index 2b8c3208ac5..d78282e6aff 100644 --- a/go/vt/mysqlctl/fakebackupengine.go +++ b/go/vt/mysqlctl/fakebackupengine.go @@ -41,7 +41,7 @@ type FakeBackupEngineExecuteBackupCall struct { } type FakeBackupEngineExecuteBackupReturn struct { - Ok bool + Res BackupResult Err error } @@ -59,14 +59,14 @@ func (be *FakeBackupEngine) ExecuteBackup( ctx context.Context, params BackupParams, bh backupstorage.BackupHandle, -) (bool, error) { +) (BackupResult, error) { be.ExecuteBackupCalls = append(be.ExecuteBackupCalls, FakeBackupEngineExecuteBackupCall{params, bh}) if be.ExecuteBackupDuration > 0 { time.Sleep(be.ExecuteBackupDuration) } - return be.ExecuteBackupReturn.Ok, be.ExecuteBackupReturn.Err + return be.ExecuteBackupReturn.Res, be.ExecuteBackupReturn.Err } func (be *FakeBackupEngine) ExecuteRestore( diff --git a/go/vt/mysqlctl/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon.go index 791b43da583..33a553a25e9 100644 --- a/go/vt/mysqlctl/fakemysqldaemon.go +++ b/go/vt/mysqlctl/fakemysqldaemon.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/dbconnpool" "vitess.io/vitess/go/vt/mysqlctl/tmutils" @@ -196,7 +197,7 @@ func NewFakeMysqlDaemon(db *fakesqldb.DB) *FakeMysqlDaemon { } if db != nil { result.appPool = dbconnpool.NewConnectionPool("AppConnPool", nil, 5, time.Minute, 0, 0) - result.appPool.Open(db.ConnParams()) + result.appPool.Open(dbconfigs.New(db.ConnParams())) } return result } @@ -220,7 +221,7 @@ func (fmd *FakeMysqlDaemon) Start(ctx context.Context, cnf *Mycnf, mysqldArgs .. } // Shutdown is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool) error { +func (fmd *FakeMysqlDaemon) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool, mysqlShutdownTimeout time.Duration) error { if !fmd.Running { return fmt.Errorf("fake mysql daemon not running") } @@ -653,12 +654,12 @@ func (fmd *FakeMysqlDaemon) GetAppConnection(ctx context.Context) (*dbconnpool.P // GetDbaConnection is part of the MysqlDaemon interface. func (fmd *FakeMysqlDaemon) GetDbaConnection(ctx context.Context) (*dbconnpool.DBConnection, error) { - return dbconnpool.NewDBConnection(ctx, fmd.db.ConnParams()) + return dbconnpool.NewDBConnection(ctx, dbconfigs.New(fmd.db.ConnParams())) } // GetAllPrivsConnection is part of the MysqlDaemon interface. func (fmd *FakeMysqlDaemon) GetAllPrivsConnection(ctx context.Context) (*dbconnpool.DBConnection, error) { - return dbconnpool.NewDBConnection(ctx, fmd.db.ConnParams()) + return dbconnpool.NewDBConnection(ctx, dbconfigs.New(fmd.db.ConnParams())) } // SetSemiSyncEnabled is part of the MysqlDaemon interface. diff --git a/go/vt/mysqlctl/grpcmysqlctlserver/server.go b/go/vt/mysqlctl/grpcmysqlctlserver/server.go index 84953020534..38ce4b2b2df 100644 --- a/go/vt/mysqlctl/grpcmysqlctlserver/server.go +++ b/go/vt/mysqlctl/grpcmysqlctlserver/server.go @@ -22,9 +22,11 @@ package grpcmysqlctlserver import ( "context" + "time" "google.golang.org/grpc" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/vt/mysqlctl" mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" ) @@ -41,9 +43,18 @@ func (s *server) Start(ctx context.Context, request *mysqlctlpb.StartRequest) (* return &mysqlctlpb.StartResponse{}, s.mysqld.Start(ctx, s.cnf, request.MysqldArgs...) } +const mysqlShutdownTimeout = 5 * time.Minute + // Shutdown implements the server side of the MysqlctlClient interface. func (s *server) Shutdown(ctx context.Context, request *mysqlctlpb.ShutdownRequest) (*mysqlctlpb.ShutdownResponse, error) { - return &mysqlctlpb.ShutdownResponse{}, s.mysqld.Shutdown(ctx, s.cnf, request.WaitForMysqld) + timeout, ok, err := protoutil.DurationFromProto(request.MysqlShutdownTimeout) + if err != nil { + return nil, err + } + if !ok { + timeout = mysqlShutdownTimeout + } + return &mysqlctlpb.ShutdownResponse{}, s.mysqld.Shutdown(ctx, s.cnf, request.WaitForMysqld, timeout) } // RunMysqlUpgrade implements the server side of the MysqlctlClient interface. @@ -56,6 +67,11 @@ func (s *server) ApplyBinlogFile(ctx context.Context, request *mysqlctlpb.ApplyB return &mysqlctlpb.ApplyBinlogFileResponse{}, s.mysqld.ApplyBinlogFile(ctx, request) } +// ReadBinlogFilesTimestamps implements the server side of the MysqlctlClient interface. +func (s *server) ReadBinlogFilesTimestamps(ctx context.Context, request *mysqlctlpb.ReadBinlogFilesTimestampsRequest) (*mysqlctlpb.ReadBinlogFilesTimestampsResponse, error) { + return s.mysqld.ReadBinlogFilesTimestamps(ctx, request) +} + // ReinitConfig implements the server side of the MysqlctlClient interface. func (s *server) ReinitConfig(ctx context.Context, request *mysqlctlpb.ReinitConfigRequest) (*mysqlctlpb.ReinitConfigResponse, error) { return &mysqlctlpb.ReinitConfigResponse{}, s.mysqld.ReinitConfig(ctx, s.cnf) diff --git a/go/vt/mysqlctl/mycnf.go b/go/vt/mysqlctl/mycnf.go index 3af6b8e8607..7ae2d5d0aa9 100644 --- a/go/vt/mysqlctl/mycnf.go +++ b/go/vt/mysqlctl/mycnf.go @@ -28,6 +28,7 @@ import ( "os" "path" "strconv" + "time" ) // Mycnf is a memory structure that contains a bunch of interesting @@ -112,6 +113,10 @@ type Mycnf struct { Path string // the actual path that represents this mycnf } +const ( + myCnfWaitRetryTime = 100 * time.Millisecond +) + // TabletDir returns the tablet directory. func (cnf *Mycnf) TabletDir() string { return path.Dir(cnf.DataDir) @@ -153,17 +158,27 @@ func normKey(bkey []byte) string { // ReadMycnf will read an existing my.cnf from disk, and update the passed in Mycnf object // with values from the my.cnf on disk. -func ReadMycnf(mycnf *Mycnf) (*Mycnf, error) { +func ReadMycnf(mycnf *Mycnf, waitTime time.Duration) (*Mycnf, error) { f, err := os.Open(mycnf.Path) + if waitTime != 0 { + timer := time.NewTimer(waitTime) + for err != nil { + select { + case <-timer.C: + return nil, err + default: + time.Sleep(myCnfWaitRetryTime) + f, err = os.Open(mycnf.Path) + } + } + } if err != nil { return nil, err } defer f.Close() buf := bufio.NewReader(f) - if err != nil { - return nil, err - } + mycnf.mycnfMap = make(map[string]string) var lval, rval string var parts [][]byte diff --git a/go/vt/mysqlctl/mycnf_flag.go b/go/vt/mysqlctl/mycnf_flag.go index 33a18d69940..8559e5c1431 100644 --- a/go/vt/mysqlctl/mycnf_flag.go +++ b/go/vt/mysqlctl/mycnf_flag.go @@ -17,6 +17,8 @@ limitations under the License. package mysqlctl import ( + "time" + "github.com/spf13/pflag" "vitess.io/vitess/go/vt/log" @@ -51,6 +53,10 @@ var ( flagMycnfFile string ) +const ( + waitForMyCnf = 10 * time.Second +) + // RegisterFlags registers the command line flags for // specifying the values of a mycnf config file. See NewMycnfFromFlags // to get the supported modes. @@ -129,5 +135,5 @@ func NewMycnfFromFlags(uid uint32) (mycnf *Mycnf, err error) { } mycnf = NewMycnf(uid, 0) mycnf.Path = flagMycnfFile - return ReadMycnf(mycnf) + return ReadMycnf(mycnf, waitForMyCnf) } diff --git a/go/vt/mysqlctl/mycnf_gen.go b/go/vt/mysqlctl/mycnf_gen.go index b29d152707f..dd0d6c81c81 100644 --- a/go/vt/mysqlctl/mycnf_gen.go +++ b/go/vt/mysqlctl/mycnf_gen.go @@ -19,11 +19,11 @@ limitations under the License. package mysqlctl import ( - "bytes" "crypto/rand" "fmt" "math/big" "path" + "strings" "text/template" "github.com/spf13/pflag" @@ -54,9 +54,7 @@ const ( innodbLogSubdir = "innodb/logs" ) -var ( - tabletDir string -) +var tabletDir string func init() { for _, cmd := range []string{"mysqlctl", "mysqlctld", "vtcombo", "vttablet", "vttestserver", "vtctld", "vtctldclient"} { @@ -149,8 +147,8 @@ func (cnf *Mycnf) fillMycnfTemplate(tmplSrc string) (string, error) { if err != nil { return "", err } - mycnfData := new(bytes.Buffer) - err = myTemplate.Execute(mycnfData, cnf) + var mycnfData strings.Builder + err = myTemplate.Execute(&mycnfData, cnf) if err != nil { return "", err } diff --git a/go/vt/mysqlctl/mycnf_test.go b/go/vt/mysqlctl/mycnf_test.go index d422ed899c4..7b8c2b1ddf0 100644 --- a/go/vt/mysqlctl/mycnf_test.go +++ b/go/vt/mysqlctl/mycnf_test.go @@ -20,7 +20,13 @@ import ( "bytes" "os" "strings" + "sync" "testing" + "time" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/servenv" @@ -29,6 +35,9 @@ import ( var MycnfPath = "/tmp/my.cnf" func TestMycnf(t *testing.T) { + // Remove any my.cnf file if it already exists. + os.Remove(MycnfPath) + uid := uint32(11111) cnf := NewMycnf(uid, 6802) myTemplateSource := new(bytes.Buffer) @@ -39,36 +48,45 @@ func TestMycnf(t *testing.T) { f, _ := os.ReadFile("../../../config/mycnf/default.cnf") myTemplateSource.Write(f) data, err := cnf.makeMycnf(myTemplateSource.String()) - if err != nil { - t.Errorf("err: %v", err) - } else { - t.Logf("data: %v", data) - } - err = os.WriteFile(MycnfPath, []byte(data), 0666) - if err != nil { - t.Errorf("failed creating my.cnf %v", err) - } - _, err = os.ReadFile(MycnfPath) - if err != nil { - t.Errorf("failed reading, err %v", err) - return - } + require.NoError(t, err) + t.Logf("data: %v", data) + + // Since there is no my.cnf file, reading it should fail with a no such file error. mycnf := NewMycnf(uid, 0) mycnf.Path = MycnfPath - mycnf, err = ReadMycnf(mycnf) - if err != nil { - t.Errorf("failed reading, err %v", err) - } else { + _, err = ReadMycnf(mycnf, 0) + require.ErrorContains(t, err, "no such file or directory") + + // Next up we will spawn a go-routine to try and read the cnf file with a timeout. + // We will create the cnf file after some delay and verify that ReadMycnf does wait that long + // and ends up succeeding in reading the my.cnf file. + waitTime := 1 * time.Second + wg := sync.WaitGroup{} + wg.Add(1) + + go func() { + defer wg.Done() + startTime := time.Now() + var readErr error + mycnf, readErr = ReadMycnf(mycnf, 1*time.Minute) + require.NoError(t, readErr, "failed reading") t.Logf("socket file %v", mycnf.SocketFile) - } + totalTimeSpent := time.Since(startTime) + require.GreaterOrEqual(t, totalTimeSpent, waitTime) + }() + + time.Sleep(waitTime) + err = os.WriteFile(MycnfPath, []byte(data), 0666) + require.NoError(t, err, "failed creating my.cnf") + _, err = os.ReadFile(MycnfPath) + require.NoError(t, err, "failed reading") + + // Wait for ReadMycnf to finish and then verify that the data read is correct. + wg.Wait() // Tablet UID should be 11111, which determines tablet/data dir. - if got, want := mycnf.DataDir, "/vt_0000011111/"; !strings.Contains(got, want) { - t.Errorf("mycnf.DataDir = %v, want *%v*", got, want) - } + require.Contains(t, mycnf.DataDir, "/vt_0000011111/") // MySQL server-id should be 22222, different from Tablet UID. - if got, want := mycnf.ServerID, uint32(22222); got != want { - t.Errorf("mycnf.ServerID = %v, want %v", got, want) - } + require.EqualValues(t, uint32(22222), mycnf.ServerID) } // Run this test if any changes are made to hook handling / make_mycnf hook @@ -97,7 +115,7 @@ func NoTestMycnfHook(t *testing.T) { // this is not being passed, so it should be nil os.Setenv("MY_VAR", "myvalue") - dbconfigs.GlobalDBConfigs.InitWithSocket(cnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(cnf.SocketFile, collations.MySQL8()) mysqld := NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) @@ -112,7 +130,7 @@ func NoTestMycnfHook(t *testing.T) { } mycnf := NewMycnf(uid, 0) mycnf.Path = cnf.Path - mycnf, err = ReadMycnf(mycnf) + mycnf, err = ReadMycnf(mycnf, 0) if err != nil { t.Errorf("failed reading, err %v", err) } else { diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go index f50d368ed7d..9e8baebefd6 100644 --- a/go/vt/mysqlctl/mysql_daemon.go +++ b/go/vt/mysqlctl/mysql_daemon.go @@ -18,6 +18,7 @@ package mysqlctl import ( "context" + "time" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" @@ -33,7 +34,7 @@ import ( type MysqlDaemon interface { // methods related to mysql running or not Start(ctx context.Context, cnf *Mycnf, mysqldArgs ...string) error - Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool) error + Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool, mysqlShutdownTimeout time.Duration) error RunMysqlUpgrade(ctx context.Context) error ApplyBinlogFile(ctx context.Context, req *mysqlctlpb.ApplyBinlogFileRequest) error ReadBinlogFilesTimestamps(ctx context.Context, req *mysqlctlpb.ReadBinlogFilesTimestampsRequest) (*mysqlctlpb.ReadBinlogFilesTimestampsResponse, error) @@ -93,7 +94,6 @@ type MysqlDaemon interface { GetSchema(ctx context.Context, dbName string, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) GetColumns(ctx context.Context, dbName, table string) ([]*querypb.Field, []string, error) GetPrimaryKeyColumns(ctx context.Context, dbName, table string) ([]string, error) - GetPrimaryKeyEquivalentColumns(ctx context.Context, dbName, table string) ([]string, string, error) PreflightSchemaChange(ctx context.Context, dbName string, changes []string) ([]*tabletmanagerdatapb.SchemaChangeResult, error) ApplySchemaChange(ctx context.Context, dbName string, change *tmutils.SchemaChange) (*tabletmanagerdatapb.SchemaChangeResult, error) diff --git a/go/vt/mysqlctl/mysqlctlproto/backup_test.go b/go/vt/mysqlctl/mysqlctlproto/backup_test.go index a5e31295a0e..cccbb349aa2 100644 --- a/go/vt/mysqlctl/mysqlctlproto/backup_test.go +++ b/go/vt/mysqlctl/mysqlctlproto/backup_test.go @@ -102,8 +102,6 @@ func TestBackupHandleToProto(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.bh.testname(), func(t *testing.T) { t.Parallel() diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index ee872c214f4..5b2d4fc19b7 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -38,31 +38,37 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "github.com/spf13/pflag" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/protoutil" - "vitess.io/vitess/config" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/dbconnpool" + vtenv "vitess.io/vitess/go/vt/env" "vitess.io/vitess/go/vt/hook" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/mysqlctlclient" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" - vtenv "vitess.io/vitess/go/vt/env" mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" - "vitess.io/vitess/go/vt/proto/vtrpc" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -var ( +// The string we expect before the MySQL version number +// in strings containing MySQL version information. +const versionStringPrefix = "Ver " + +// How many bytes from MySQL error log to sample for error messages +const maxLogFileSampleSize = 4096 +var ( // DisableActiveReparents is a flag to disable active // reparents for safety reasons. It is used in three places: // 1. in this file to skip registering the commands. @@ -86,15 +92,18 @@ var ( replicationConnectRetry = 10 * time.Second - versionRegex = regexp.MustCompile(`Ver ([0-9]+)\.([0-9]+)\.([0-9]+)`) + versionRegex = regexp.MustCompile(fmt.Sprintf(`%s([0-9]+)\.([0-9]+)\.([0-9]+)`, versionStringPrefix)) + // versionSQLQuery will return a version string directly from + // a MySQL server that is compatible with what we expect from + // mysqld --version and matches the versionRegex. Example + // result: Ver 8.0.35 MySQL Community Server - GPL + versionSQLQuery = fmt.Sprintf("select concat('%s', @@global.version, ' ', @@global.version_comment) as version", + versionStringPrefix) binlogEntryCommittedTimestampRegex = regexp.MustCompile("original_committed_timestamp=([0-9]+)") binlogEntryTimestampGTIDRegexp = regexp.MustCompile(`^#(.+) server id.*\bGTID\b`) ) -// How many bytes from MySQL error log to sample for error messages -const maxLogFileSampleSize = 4096 - // Mysqld is the object that represents a mysqld daemon running on this server. type Mysqld struct { dbcfgs *dbconfigs.DBConfigs @@ -321,7 +330,7 @@ func (mysqld *Mysqld) Start(ctx context.Context, cnf *Mycnf, mysqldArgs ...strin return client.Start(ctx, mysqldArgs...) } - if err := mysqld.startNoWait(ctx, cnf, mysqldArgs...); err != nil { + if err := mysqld.startNoWait(cnf, mysqldArgs...); err != nil { return err } @@ -329,7 +338,7 @@ func (mysqld *Mysqld) Start(ctx context.Context, cnf *Mycnf, mysqldArgs ...strin } // startNoWait is the internal version of Start, and it doesn't wait. -func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs ...string) error { +func (mysqld *Mysqld) startNoWait(cnf *Mycnf, mysqldArgs ...string) error { var name string ts := fmt.Sprintf("Mysqld.Start(%v)", time.Now().Unix()) @@ -355,6 +364,13 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs .. if err != nil { return err } + // If we're here, and the lockfile still exists for the socket, we have + // to clean that up since we know at this point we need to start MySQL. + // Having this stray lock file present means MySQL fails to start. This + // only happens when running without mysqld_safe. + if err := cleanupLockfile(cnf.SocketFile, ts); err != nil { + return err + } } mysqlBaseDir, err := vtenv.VtMysqlBaseDir() if err != nil { @@ -396,7 +412,7 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs .. }() err = cmd.Start() if err != nil { - return err + return vterrors.Wrapf(err, "failed to start mysqld") } mysqld.mutex.Lock() @@ -426,6 +442,48 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs .. return nil } +func cleanupLockfile(socket string, ts string) error { + lockPath := fmt.Sprintf("%s.lock", socket) + pid, err := os.ReadFile(lockPath) + if errors.Is(err, os.ErrNotExist) { + log.Infof("%v: no stale lock file at %s", ts, lockPath) + // If there's no lock file, we can early return here, nothing + // to clean up then. + return nil + } else if err != nil { + log.Errorf("%v: error checking if lock file exists: %v", ts, err) + // Any other errors here are unexpected. + return err + } + p, err := strconv.Atoi(string(bytes.TrimSpace(pid))) + if err != nil { + log.Errorf("%v: error parsing pid from lock file: %v", ts, err) + return err + } + proc, err := os.FindProcess(p) + if err != nil { + log.Errorf("%v: error finding process: %v", ts, err) + return err + } + err = proc.Signal(syscall.Signal(0)) + if err == nil { + // If the process still exists, it's not safe to + // remove the lock file, so we have to keep it around. + log.Errorf("%v: not removing socket lock file: %v with pid %v", ts, lockPath, p) + return fmt.Errorf("process %v is still running", p) + } + if !errors.Is(err, os.ErrProcessDone) { + // Any errors except for the process being done + // is unexpected here. + log.Errorf("%v: error checking process %v: %v", ts, p, err) + return err + } + + // All good, process is gone and we can safely clean up the lock file. + log.Infof("%v: removing stale socket lock file: %v", ts, lockPath) + return os.Remove(lockPath) +} + // Wait returns nil when mysqld is up and accepting connections. It // will use the dba credentials to try to connect. Use wait() with // different credentials if needed. @@ -472,7 +530,7 @@ func (mysqld *Mysqld) wait(ctx context.Context, cnf *Mycnf, params *mysql.ConnPa // flushed - on the order of 20-30 minutes. // // If a mysqlctld address is provided in a flag, Shutdown will run remotely. -func (mysqld *Mysqld) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool) error { +func (mysqld *Mysqld) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bool, shutdownTimeout time.Duration) error { log.Infof("Mysqld.Shutdown") // Execute as remote action on mysqlctld if requested. @@ -531,7 +589,7 @@ func (mysqld *Mysqld) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bo defer os.Remove(cnf) args := []string{ "--defaults-extra-file=" + cnf, - "--shutdown-timeout=300", + fmt.Sprintf("--shutdown-timeout=%d", int(shutdownTimeout.Seconds())), "--connect-timeout=30", "--wait=10", "shutdown", @@ -642,7 +700,7 @@ func (mysqld *Mysqld) Init(ctx context.Context, cnf *Mycnf, initDBSQLFile string // Start mysqld. We do not use Start, as we have to wait using // the root user. - if err = mysqld.startNoWait(ctx, cnf); err != nil { + if err = mysqld.startNoWait(cnf); err != nil { log.Errorf("failed starting mysqld: %v\n%v", err, readTailOfMysqldErrorLog(cnf.ErrorLogPath)) return err } @@ -780,7 +838,7 @@ func (mysqld *Mysqld) initConfig(cnf *Mycnf, outFile string) error { return err } - return os.WriteFile(outFile, []byte(configData), 0664) + return os.WriteFile(outFile, []byte(configData), 0o664) } func (mysqld *Mysqld) getMycnfTemplate() string { @@ -791,7 +849,7 @@ func (mysqld *Mysqld) getMycnfTemplate() string { } return string(data) // use only specified template } - myTemplateSource := new(bytes.Buffer) + var myTemplateSource strings.Builder myTemplateSource.WriteString("[mysqld]\n") myTemplateSource.WriteString(config.MycnfDefault) @@ -974,9 +1032,9 @@ func (mysqld *Mysqld) createTopDir(cnf *Mycnf, dir string) error { } // Teardown will shutdown the running daemon, and delete the root directory. -func (mysqld *Mysqld) Teardown(ctx context.Context, cnf *Mycnf, force bool) error { +func (mysqld *Mysqld) Teardown(ctx context.Context, cnf *Mycnf, force bool, shutdownTimeout time.Duration) error { log.Infof("mysqlctl.Teardown") - if err := mysqld.Shutdown(ctx, cnf, true); err != nil { + if err := mysqld.Shutdown(ctx, cnf, true, shutdownTimeout); err != nil { log.Warningf("failed mysqld shutdown: %v", err.Error()) if !force { return err @@ -1136,7 +1194,13 @@ func buildLdPaths() ([]string, error) { // GetVersionString is part of the MysqlExecutor interface. func (mysqld *Mysqld) GetVersionString(ctx context.Context) (string, error) { - // Execute as remote action on mysqlctld to ensure we get the actual running MySQL version. + // Try to query the mysqld instance directly. + qr, err := mysqld.FetchSuperQuery(ctx, versionSQLQuery) + if err == nil && len(qr.Rows) == 1 { + return qr.Rows[0][0].ToString(), nil + } + // Execute as remote action on mysqlctld to use the actual running MySQL + // version. if socketFile != "" { client, err := mysqlctlclient.New("unix", socketFile) if err != nil { @@ -1145,6 +1209,7 @@ func (mysqld *Mysqld) GetVersionString(ctx context.Context) (string, error) { defer client.Close() return client.VersionString(ctx) } + // Fall back to the sys exec method using mysqld --version. return GetVersionString() } @@ -1185,11 +1250,18 @@ func (mysqld *Mysqld) ApplyBinlogFile(ctx context.Context, req *mysqlctlpb.Apply if err != nil { return err } + var mysqlbinlogErrFile *os.File { name, err := binaryPath(dir, "mysqlbinlog") if err != nil { return err } + mysqlbinlogErrFile, err = os.CreateTemp("", "err-mysqlbinlog-") + if err != nil { + return err + } + defer os.Remove(mysqlbinlogErrFile.Name()) + args := []string{} if gtids := req.BinlogRestorePosition; gtids != "" { args = append(args, @@ -1209,7 +1281,8 @@ func (mysqld *Mysqld) ApplyBinlogFile(ctx context.Context, req *mysqlctlpb.Apply mysqlbinlogCmd = exec.Command(name, args...) mysqlbinlogCmd.Dir = dir mysqlbinlogCmd.Env = env - log.Infof("ApplyBinlogFile: running mysqlbinlog command: %#v", mysqlbinlogCmd) + mysqlbinlogCmd.Stderr = mysqlbinlogErrFile + log.Infof("ApplyBinlogFile: running mysqlbinlog command: %#v with errfile=%v", mysqlbinlogCmd, mysqlbinlogErrFile.Name()) pipe, err = mysqlbinlogCmd.StdoutPipe() // to be piped into mysql if err != nil { return err @@ -1279,6 +1352,12 @@ func (mysqld *Mysqld) ApplyBinlogFile(ctx context.Context, req *mysqlctlpb.Apply } // Wait for both to complete: if err := mysqlbinlogCmd.Wait(); err != nil { + if mysqlbinlogErrFile != nil { + errFileContent, _ := os.ReadFile(mysqlbinlogErrFile.Name()) + if len(errFileContent) > 0 { + err = vterrors.Wrapf(err, "with error output: %s", string(errFileContent)) + } + } return vterrors.Wrapf(err, "mysqlbinlog command failed") } if err := mysqlCmd.Wait(); err != nil { @@ -1379,7 +1458,7 @@ func (mysqld *Mysqld) scanBinlogTimestamp( // ReadBinlogFilesTimestamps reads all given binlog files via `mysqlbinlog` command and returns the first and last found transaction timestamps func (mysqld *Mysqld) ReadBinlogFilesTimestamps(ctx context.Context, req *mysqlctlpb.ReadBinlogFilesTimestampsRequest) (*mysqlctlpb.ReadBinlogFilesTimestampsResponse, error) { if len(req.BinlogFileNames) == 0 { - return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "empty binlog list in ReadBinlogFilesTimestampsRequest") + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "empty binlog list in ReadBinlogFilesTimestampsRequest") } if socketFile != "" { log.Infof("executing Mysqld.ReadBinlogFilesTimestamps() remotely via mysqlctld server: %v", socketFile) diff --git a/go/vt/mysqlctl/mysqld_test.go b/go/vt/mysqlctl/mysqld_test.go index 435090008f2..0caba8c904d 100644 --- a/go/vt/mysqlctl/mysqld_test.go +++ b/go/vt/mysqlctl/mysqld_test.go @@ -17,6 +17,8 @@ limitations under the License. package mysqlctl import ( + "os" + "strconv" "testing" "time" @@ -172,3 +174,27 @@ func TestParseBinlogEntryTimestamp(t *testing.T) { }) } } + +func TestCleanupLockfile(t *testing.T) { + t.Cleanup(func() { + os.Remove("mysql.sock.lock") + }) + ts := "prefix" + // All good if no lockfile exists + assert.NoError(t, cleanupLockfile("mysql.sock", ts)) + + // If lockfile exists, but the process is not found, we clean it up. + os.WriteFile("mysql.sock.lock", []byte("123456789"), 0o600) + assert.NoError(t, cleanupLockfile("mysql.sock", ts)) + assert.NoFileExists(t, "mysql.sock.lock") + + // If lockfile exists, but the process is not found, we clean it up. + os.WriteFile("mysql.sock.lock", []byte("123456789\n"), 0o600) + assert.NoError(t, cleanupLockfile("mysql.sock", ts)) + assert.NoFileExists(t, "mysql.sock.lock") + + // If the lockfile exists, and the process is found, we don't clean it up. + os.WriteFile("mysql.sock.lock", []byte(strconv.Itoa(os.Getpid())), 0o600) + assert.Error(t, cleanupLockfile("mysql.sock", ts)) + assert.FileExists(t, "mysql.sock.lock") +} diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go index 23b19669f16..af20bbef85f 100644 --- a/go/vt/mysqlctl/replication.go +++ b/go/vt/mysqlctl/replication.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/vt/hook" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vterrors" ) type ResetSuperReadOnlyFunc func() error @@ -236,7 +237,8 @@ func (mysqld *Mysqld) IsSuperReadOnly() (bool, error) { if err != nil { return false, err } - if err == nil && len(qr.Rows) == 1 { + + if len(qr.Rows) == 1 { sro := qr.Rows[0][0].ToString() if sro == "1" || sro == "ON" { return true, nil @@ -315,7 +317,8 @@ func (mysqld *Mysqld) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) return resetFunc, nil } -// WaitSourcePos lets replicas wait to given replication position +// WaitSourcePos lets replicas wait for the given replication position to +// be reached. func (mysqld *Mysqld) WaitSourcePos(ctx context.Context, targetPos replication.Position) error { // Get a connection. conn, err := getPoolReconnect(ctx, mysqld.dbaPool) @@ -324,61 +327,33 @@ func (mysqld *Mysqld) WaitSourcePos(ctx context.Context, targetPos replication.P } defer conn.Recycle() - // First check if filePos flavored Position was passed in. If so, we can't defer to the flavor in the connection, - // unless that flavor is also filePos. - waitCommandName := "WaitUntilPositionCommand" - var query string + // First check if filePos flavored Position was passed in. If so, we + // can't defer to the flavor in the connection, unless that flavor is + // also filePos. if targetPos.MatchesFlavor(replication.FilePosFlavorID) { - // If we are the primary, WaitUntilFilePositionCommand will fail. - // But position is most likely reached. So, check the position - // first. + // If we are the primary, WaitUntilFilePosition will fail. But + // position is most likely reached. So, check the position first. mpos, err := conn.Conn.PrimaryFilePosition() if err != nil { - return fmt.Errorf("WaitSourcePos: PrimaryFilePosition failed: %v", err) + return vterrors.Wrapf(err, "WaitSourcePos: PrimaryFilePosition failed") } if mpos.AtLeast(targetPos) { return nil } - - // Find the query to run, run it. - query, err = conn.Conn.WaitUntilFilePositionCommand(ctx, targetPos) - if err != nil { - return err - } - waitCommandName = "WaitUntilFilePositionCommand" } else { - // If we are the primary, WaitUntilPositionCommand will fail. - // But position is most likely reached. So, check the position - // first. + // If we are the primary, WaitUntilPosition will fail. But + // position is most likely reached. So, check the position first. mpos, err := conn.Conn.PrimaryPosition() if err != nil { - return fmt.Errorf("WaitSourcePos: PrimaryPosition failed: %v", err) + return vterrors.Wrapf(err, "WaitSourcePos: PrimaryPosition failed") } if mpos.AtLeast(targetPos) { return nil } - - // Find the query to run, run it. - query, err = conn.Conn.WaitUntilPositionCommand(ctx, targetPos) - if err != nil { - return err - } } - qr, err := mysqld.FetchSuperQuery(ctx, query) - if err != nil { - return fmt.Errorf("%v(%v) failed: %v", waitCommandName, query, err) - } - - if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 { - return fmt.Errorf("unexpected result format from %v(%v): %#v", waitCommandName, query, qr) - } - result := qr.Rows[0][0] - if result.IsNull() { - return fmt.Errorf("%v(%v) failed: replication is probably stopped", waitCommandName, query) - } - if result.ToString() == "-1" { - return fmt.Errorf("timed out waiting for position %v", targetPos) + if err := conn.Conn.WaitUntilPosition(ctx, targetPos); err != nil { + return vterrors.Wrapf(err, "WaitSourcePos failed") } return nil } diff --git a/go/vt/mysqlctl/schema.go b/go/vt/mysqlctl/schema.go index 6f1c7c19570..a5ac38670e7 100644 --- a/go/vt/mysqlctl/schema.go +++ b/go/vt/mysqlctl/schema.go @@ -66,12 +66,6 @@ func (mysqld *Mysqld) executeSchemaCommands(ctx context.Context, sql string) err return mysqld.executeMysqlScript(ctx, params, sql) } -func encodeEntityName(name string) string { - var buf strings.Builder - sqltypes.NewVarChar(name).EncodeSQL(&buf) - return buf.String() -} - // tableListSQL returns an IN clause "('t1', 't2'...) for a list of tables." func tableListSQL(tables []string) (string, error) { if len(tables) == 0 { @@ -80,7 +74,7 @@ func tableListSQL(tables []string) (string, error) { encodedTables := make([]string, len(tables)) for i, tableName := range tables { - encodedTables[i] = encodeEntityName(tableName) + encodedTables[i] = sqltypes.EncodeStringSQL(tableName) } return "(" + strings.Join(encodedTables, ", ") + ")", nil @@ -307,9 +301,13 @@ func GetColumnsList(dbName, tableName string, exec func(string, int, bool) (*sql if dbName == "" { dbName2 = "database()" } else { - dbName2 = encodeEntityName(dbName) + dbName2 = sqltypes.EncodeStringSQL(dbName) + } + sanitizedTableName, err := sqlescape.UnescapeID(tableName) + if err != nil { + return "", err } - query := fmt.Sprintf(GetColumnNamesQuery, dbName2, encodeEntityName(sqlescape.UnescapeID(tableName))) + query := fmt.Sprintf(GetColumnNamesQuery, dbName2, sqltypes.EncodeStringSQL(sanitizedTableName)) qr, err := exec(query, -1, true) if err != nil { return "", err @@ -342,9 +340,16 @@ func GetColumns(dbName, table string, exec func(string, int, bool) (*sqltypes.Re if selectColumns == "" { selectColumns = "*" } - tableSpec := sqlescape.EscapeID(sqlescape.UnescapeID(table)) + tableSpec, err := sqlescape.EnsureEscaped(table) + if err != nil { + return nil, nil, err + } if dbName != "" { - tableSpec = fmt.Sprintf("%s.%s", sqlescape.EscapeID(sqlescape.UnescapeID(dbName)), tableSpec) + dbName, err := sqlescape.EnsureEscaped(dbName) + if err != nil { + return nil, nil, err + } + tableSpec = fmt.Sprintf("%s.%s", dbName, tableSpec) } query := fmt.Sprintf(GetFieldsQuery, selectColumns, tableSpec) qr, err := exec(query, 0, true) @@ -396,7 +401,7 @@ func (mysqld *Mysqld) getPrimaryKeyColumns(ctx context.Context, dbName string, t FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME IN %s AND LOWER(INDEX_NAME) = 'primary' ORDER BY table_name, SEQ_IN_INDEX` - sql = fmt.Sprintf(sql, encodeEntityName(dbName), tableList) + sql = fmt.Sprintf(sql, sqltypes.EncodeStringSQL(dbName), tableList) qr, err := conn.Conn.ExecuteFetch(sql, len(tables)*100, true) if err != nil { return nil, err @@ -579,13 +584,7 @@ func (mysqld *Mysqld) ApplySchemaChange(ctx context.Context, dbName string, chan // defined PRIMARY KEY then it may return the columns for // that index if it is likely the most efficient one amongst // the available PKE indexes on the table. -func (mysqld *Mysqld) GetPrimaryKeyEquivalentColumns(ctx context.Context, dbName, table string) ([]string, string, error) { - conn, err := getPoolReconnect(ctx, mysqld.dbaPool) - if err != nil { - return nil, "", err - } - defer conn.Recycle() - +func GetPrimaryKeyEquivalentColumns(ctx context.Context, exec func(string, int, bool) (*sqltypes.Result, error), dbName, table string) ([]string, string, error) { // We use column name aliases to guarantee lower case for our named results. sql := ` SELECT index_cols.COLUMN_NAME AS column_name, index_cols.INDEX_NAME as index_name FROM information_schema.STATISTICS AS index_cols INNER JOIN @@ -626,10 +625,10 @@ func (mysqld *Mysqld) GetPrimaryKeyEquivalentColumns(ctx context.Context, dbName ) AS pke ON index_cols.INDEX_NAME = pke.INDEX_NAME WHERE index_cols.TABLE_SCHEMA = %s AND index_cols.TABLE_NAME = %s AND NON_UNIQUE = 0 AND NULLABLE != 'YES' ORDER BY SEQ_IN_INDEX ASC` - encodedDbName := encodeEntityName(dbName) - encodedTable := encodeEntityName(table) + encodedDbName := sqltypes.EncodeStringSQL(dbName) + encodedTable := sqltypes.EncodeStringSQL(table) sql = fmt.Sprintf(sql, encodedDbName, encodedTable, encodedDbName, encodedTable, encodedDbName, encodedTable) - qr, err := conn.Conn.ExecuteFetch(sql, 1000, true) + qr, err := exec(sql, 1000, true) if err != nil { return nil, "", err } diff --git a/go/vt/mysqlctl/tmutils/schema.go b/go/vt/mysqlctl/tmutils/schema.go index aae529f89b0..781b943a4b2 100644 --- a/go/vt/mysqlctl/tmutils/schema.go +++ b/go/vt/mysqlctl/tmutils/schema.go @@ -40,31 +40,6 @@ const ( TableView = "VIEW" ) -// TableDefinitionGetColumn returns the index of a column inside a -// TableDefinition. -func TableDefinitionGetColumn(td *tabletmanagerdatapb.TableDefinition, name string) (index int, ok bool) { - lowered := strings.ToLower(name) - for i, n := range td.Columns { - if lowered == strings.ToLower(n) { - return i, true - } - } - return -1, false -} - -// TableDefinitions is a list of TableDefinition, for sorting -type TableDefinitions []*tabletmanagerdatapb.TableDefinition - -// Len returns TableDefinitions length. -func (tds TableDefinitions) Len() int { - return len(tds) -} - -// Swap used for sorting TableDefinitions. -func (tds TableDefinitions) Swap(i, j int) { - tds[i], tds[j] = tds[j], tds[i] -} - // TableFilter is a filter for table names and types. type TableFilter struct { includeViews bool diff --git a/go/vt/mysqlctl/utils.go b/go/vt/mysqlctl/utils.go deleted file mode 100644 index cc34be6abfe..00000000000 --- a/go/vt/mysqlctl/utils.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mysqlctl - -import ( - "vitess.io/vitess/go/vt/log" -) - -type MapFunc func(index int) error - -// ConcurrentMap applies fun in a concurrent manner on integers from 0 -// to n-1 (they are assumed to be indexes of some slice containing -// items to be processed). The first error returned by a fun -// application will returned (subsequent errors will only be -// logged). It will use concurrency goroutines. -func ConcurrentMap(concurrency, n int, fun MapFunc) error { - errors := make(chan error) - work := make(chan int, n) - - for i := 0; i < n; i++ { - work <- i - } - close(work) - - for j := 0; j < concurrency; j++ { - go func() { - for i := range work { - errors <- fun(i) - } - }() - } - var err error - - for i := 0; i < n; i++ { - if e := <-errors; e != nil { - if err != nil { - log.Errorf("multiple errors, this one happened but it won't be returned: %v", err) - } - err = e - } - } - return err -} diff --git a/go/vt/mysqlctl/utils_test.go b/go/vt/mysqlctl/utils_test.go deleted file mode 100644 index 0fdcae92bfa..00000000000 --- a/go/vt/mysqlctl/utils_test.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mysqlctl - -import ( - "errors" - "testing" -) - -func TestConcurrentMap(t *testing.T) { - work := make([]int, 10) - result := make([]int, 10) - for i := 0; i < 10; i++ { - work[i] = i - } - mapFunc := func(i int) error { - result[i] = work[i] - return nil - } - if err := ConcurrentMap(2, 10, mapFunc); err != nil { - t.Errorf("Unexpected error: %v", err) - } - - for i := 0; i < 10; i++ { - if got, expected := result[i], work[i]; got != expected { - t.Errorf("Wrong values in result: got %v, expected %v", got, expected) - } - } - fooErr := errors.New("foo") - if err := ConcurrentMap(2, 10, func(i int) error { return fooErr }); err != fooErr { - t.Errorf("Didn't get expected error: %v", err) - } -} diff --git a/go/vt/mysqlctl/xtrabackupengine.go b/go/vt/mysqlctl/xtrabackupengine.go index d11699167d9..3f8491fdfb6 100644 --- a/go/vt/mysqlctl/xtrabackupengine.go +++ b/go/vt/mysqlctl/xtrabackupengine.go @@ -167,27 +167,27 @@ func closeFile(wc io.WriteCloser, fileName string, logger logutil.Logger, finalE } // ExecuteBackup runs a backup based on given params. This could be a full or incremental backup. -// The function returns a boolean that indicates if the backup is usable, and an overall error. -func (be *XtrabackupEngine) ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (bool, error) { +// The function returns a BackupResult that indicates the usability of the backup, and an overall error. +func (be *XtrabackupEngine) ExecuteBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (BackupResult, error) { params.Logger.Infof("Executing Backup at %v for keyspace/shard %v/%v on tablet %v, concurrency: %v, compress: %v, incrementalFromPos: %v", params.BackupTime, params.Keyspace, params.Shard, params.TabletAlias, params.Concurrency, backupStorageCompress, params.IncrementalFromPos) return be.executeFullBackup(ctx, params, bh) } -// executeFullBackup returns a boolean that indicates if the backup is usable, +// executeFullBackup returns a BackupResult that indicates the usability of the backup, // and an overall error. -func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (complete bool, finalErr error) { +func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params BackupParams, bh backupstorage.BackupHandle) (backupResult BackupResult, finalErr error) { if params.IncrementalFromPos != "" { - return false, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "incremental backups not supported in xtrabackup engine.") + return BackupUnusable, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "incremental backups not supported in xtrabackup engine.") } if xtrabackupUser == "" { - return false, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "xtrabackupUser must be specified.") + return BackupUnusable, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "xtrabackupUser must be specified.") } // an extension is required when using an external compressor if backupStorageCompress && ExternalCompressorCmd != "" && ExternalCompressorExt == "" { - return false, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, + return BackupUnusable, vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "flag --external-compressor-extension not provided when using an external compressor") } @@ -198,20 +198,20 @@ func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params Backup } if err != nil { - return false, vterrors.Wrap(err, "unable to obtain a connection to the database") + return BackupUnusable, vterrors.Wrap(err, "unable to obtain a connection to the database") } pos, err := conn.PrimaryPosition() if err != nil { - return false, vterrors.Wrap(err, "unable to obtain primary position") + return BackupUnusable, vterrors.Wrap(err, "unable to obtain primary position") } serverUUID, err := conn.GetServerUUID() if err != nil { - return false, vterrors.Wrap(err, "can't get server uuid") + return BackupUnusable, vterrors.Wrap(err, "can't get server uuid") } mysqlVersion, err := params.Mysqld.GetVersionString(ctx) if err != nil { - return false, vterrors.Wrap(err, "can't get MySQL version") + return BackupUnusable, vterrors.Wrap(err, "can't get MySQL version") } flavor := pos.GTIDSet.Flavor() @@ -229,14 +229,14 @@ func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params Backup params.Logger.Infof("Starting backup with %v stripe(s)", numStripes) replicationPosition, err := be.backupFiles(ctx, params, bh, backupFileName, numStripes, flavor) if err != nil { - return false, err + return BackupUnusable, err } // open the MANIFEST params.Logger.Infof("Writing backup MANIFEST") mwc, err := bh.AddFile(ctx, backupManifestFileName, backupstorage.FileSizeUnknown) if err != nil { - return false, vterrors.Wrapf(err, "cannot add %v to backup", backupManifestFileName) + return BackupUnusable, vterrors.Wrapf(err, "cannot add %v to backup", backupManifestFileName) } defer closeFile(mwc, backupManifestFileName, params.Logger, &finalErr) @@ -244,6 +244,7 @@ func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params Backup bm := &xtraBackupManifest{ // Common base fields BackupManifest: BackupManifest{ + BackupName: bh.Name(), BackupMethod: xtrabackupEngineName, Position: replicationPosition, PurgedPosition: replicationPosition, @@ -273,14 +274,14 @@ func (be *XtrabackupEngine) executeFullBackup(ctx context.Context, params Backup data, err := json.MarshalIndent(bm, "", " ") if err != nil { - return false, vterrors.Wrapf(err, "cannot JSON encode %v", backupManifestFileName) + return BackupUnusable, vterrors.Wrapf(err, "cannot JSON encode %v", backupManifestFileName) } if _, err := mwc.Write([]byte(data)); err != nil { - return false, vterrors.Wrapf(err, "cannot write %v", backupManifestFileName) + return BackupUnusable, vterrors.Wrapf(err, "cannot write %v", backupManifestFileName) } params.Logger.Infof("Backup completed") - return true, nil + return BackupUsable, nil } func (be *XtrabackupEngine) backupFiles( @@ -484,7 +485,7 @@ func (be *XtrabackupEngine) ExecuteRestore(ctx context.Context, params RestorePa return nil, err } - if err := prepareToRestore(ctx, params.Cnf, params.Mysqld, params.Logger); err != nil { + if err := prepareToRestore(ctx, params.Cnf, params.Mysqld, params.Logger, params.MysqlShutdownTimeout); err != nil { return nil, err } diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index c0a4bd08860..03c40a3c59e 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -19,7 +19,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: binlogdata.proto @@ -1007,6 +1007,8 @@ type Rule struct { // such columns need to have special transofrmation of the data, from an integral format into a // string format. e.g. the value 0 needs to be converted to '0'. ConvertIntToEnum map[string]bool `protobuf:"bytes,8,rep,name=convert_int_to_enum,json=convertIntToEnum,proto3" json:"convert_int_to_enum,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + // ForceUniqueKey gives vtreamer a hint for `FORCE INDEX (...)` usage. + ForceUniqueKey string `protobuf:"bytes,9,opt,name=force_unique_key,json=forceUniqueKey,proto3" json:"force_unique_key,omitempty"` } func (x *Rule) Reset() { @@ -1097,6 +1099,13 @@ func (x *Rule) GetConvertIntToEnum() map[string]bool { return nil } +func (x *Rule) GetForceUniqueKey() string { + if x != nil { + return x.ForceUniqueKey + } + return "" +} + // Filter represents a list of ordered rules. The first // match wins. type Filter struct { @@ -3056,7 +3065,7 @@ var file_binlogdata_proto_rawDesc = []byte{ 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x43, 0x68, 0x61, - 0x72, 0x73, 0x65, 0x74, 0x22, 0xdf, 0x05, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x14, 0x0a, + 0x72, 0x73, 0x65, 0x74, 0x22, 0x89, 0x06, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x14, 0x63, @@ -3087,209 +3096,244 @@ var file_binlogdata_proto_rawDesc = []byte{ 0x26, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x54, 0x6f, 0x45, 0x6e, 0x75, 0x6d, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, - 0x49, 0x6e, 0x74, 0x54, 0x6f, 0x45, 0x6e, 0x75, 0x6d, 0x1a, 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x75, 0x6d, 0x54, 0x6f, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x60, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, - 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x54, - 0x6f, 0x45, 0x6e, 0x75, 0x6d, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x12, 0x26, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x75, - 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x10, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x36, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x6f, - 0x64, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x52, 0x52, 0x5f, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x53, - 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x42, 0x45, 0x53, 0x54, 0x5f, - 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x10, 0x01, 0x22, 0xea, 0x03, 0x0a, 0x0c, 0x42, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, - 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, - 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, - 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4f, 0x6e, 0x44, 0x44, 0x4c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x12, 0x26, - 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, - 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, - 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xc6, 0x01, 0x0a, 0x09, 0x52, 0x6f, 0x77, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, - 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x20, 0x0a, 0x05, 0x61, 0x66, 0x74, 0x65, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, - 0x6f, 0x77, 0x52, 0x05, 0x61, 0x66, 0x74, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x61, 0x74, - 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x77, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x52, 0x0b, 0x64, - 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x1a, 0x32, 0x0a, 0x06, 0x42, 0x69, - 0x74, 0x6d, 0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, - 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x6f, 0x6c, 0x73, 0x22, 0xa9, - 0x01, 0x0a, 0x08, 0x52, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x72, 0x6f, - 0x77, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x77, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x72, 0x6f, 0x77, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x0a, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x22, 0x88, 0x01, 0x0a, 0x09, 0x53, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x67, 0x74, 0x69, 0x64, 0x12, 0x35, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x5f, - 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, - 0x4b, 0x52, 0x08, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x4b, 0x73, 0x22, 0x3f, 0x0a, 0x05, 0x56, - 0x47, 0x74, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x67, 0x74, - 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x69, 0x6e, 0x6c, - 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, - 0x52, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x73, 0x22, 0x41, 0x0a, 0x0d, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, - 0xbc, 0x02, 0x0a, 0x07, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x6d, - 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, - 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, - 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x0b, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x52, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x47, - 0x74, 0x69, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, - 0x61, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, - 0x6e, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x77, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x8b, - 0x04, 0x0a, 0x06, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x5f, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, - 0x72, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x67, 0x74, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x47, - 0x74, 0x69, 0x64, 0x52, 0x05, 0x76, 0x67, 0x74, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x07, 0x6a, 0x6f, - 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x69, - 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, - 0x52, 0x07, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6d, 0x6c, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6d, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3c, - 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x5f, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, - 0x0c, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, - 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x70, 0x5f, 0x6b, 0x5f, 0x63, - 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x70, 0x4b, - 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0e, 0x70, 0x5f, 0x6b, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x4b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x41, 0x0a, 0x0d, - 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x30, 0x0a, - 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, - 0x61, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, - 0xc7, 0x02, 0x0a, 0x0e, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, - 0x52, 0x11, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x13, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, - 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x43, - 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, - 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x49, 0x6e, 0x74, 0x54, 0x6f, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x6f, 0x72, + 0x63, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, + 0x4b, 0x65, 0x79, 0x1a, 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x6e, + 0x75, 0x6d, 0x54, 0x6f, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x60, 0x0a, 0x13, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x54, 0x6f, 0x45, 0x6e, 0x75, 0x6d, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xff, 0x01, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x05, 0x72, + 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x69, 0x6e, + 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, 0x75, + 0x6c, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x10, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x0f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x5f, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x72, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, + 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x36, 0x0a, 0x0e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x13, 0x0a, 0x0f, + 0x45, 0x52, 0x52, 0x5f, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, + 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x42, 0x45, 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, + 0x10, 0x01, 0x22, 0xea, 0x03, 0x0a, 0x0c, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x09, + 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x12, 0x2e, 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4f, + 0x6e, 0x44, 0x44, 0x4c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, + 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x79, + 0x73, 0x71, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, + 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, + 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, + 0xc6, 0x01, 0x0a, 0x09, 0x52, 0x6f, 0x77, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, + 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x12, 0x20, 0x0a, 0x05, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x05, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x69, 0x6e, 0x6c, + 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x77, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x2e, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6c, + 0x75, 0x6d, 0x6e, 0x73, 0x1a, 0x32, 0x0a, 0x06, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x63, 0x6f, 0x6c, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x08, 0x52, 0x6f, 0x77, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x72, 0x6f, 0x77, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x69, 0x6e, 0x6c, + 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x77, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x0a, 0x72, 0x6f, 0x77, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x09, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x35, + 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x5f, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x52, 0x0c, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x73, 0x22, 0x3d, 0x0a, 0x0f, 0x56, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, - 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x85, 0x02, 0x0a, 0x12, 0x56, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x52, 0x08, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x50, 0x4b, 0x73, 0x22, 0x3f, 0x0a, 0x05, 0x56, 0x47, 0x74, 0x69, 0x64, 0x12, 0x36, + 0x0a, 0x0b, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x52, 0x0a, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x47, 0x74, 0x69, 0x64, 0x73, 0x22, 0x41, 0x0a, 0x0d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0xbc, 0x02, 0x0a, 0x07, 0x4a, 0x6f, + 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, + 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, + 0x25, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, + 0x67, 0x74, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x69, + 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, + 0x69, 0x64, 0x52, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x47, 0x74, 0x69, 0x64, 0x73, 0x12, 0x3d, + 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x8b, 0x04, 0x0a, 0x06, 0x56, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x16, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, + 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x31, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x52, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x72, 0x6f, 0x77, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x05, 0x76, + 0x67, 0x74, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x69, 0x6e, + 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x47, 0x74, 0x69, 0x64, 0x52, 0x05, 0x76, + 0x67, 0x74, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x07, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x07, 0x6a, 0x6f, 0x75, 0x72, + 0x6e, 0x61, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6d, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x64, 0x6d, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x70, 0x5f, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x61, + 0x73, 0x74, 0x50, 0x4b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x50, + 0x4b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x6f, + 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x68, 0x72, + 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x0c, 0x4d, 0x69, 0x6e, 0x69, 0x6d, + 0x61, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x70, 0x5f, 0x6b, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x70, 0x4b, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x73, 0x12, 0x23, 0x0a, 0x0e, 0x70, 0x5f, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x4b, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x41, 0x0a, 0x0d, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, + 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x30, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0xc7, 0x02, 0x0a, 0x0e, 0x56, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, + 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x65, 0x66, 0x66, 0x65, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, + 0x13, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, + 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x70, 0x5f, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, + 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, + 0x61, 0x73, 0x74, 0x50, 0x4b, 0x52, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, + 0x50, 0x4b, 0x73, 0x22, 0x3d, 0x0a, 0x0f, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x85, 0x02, 0x0a, 0x12, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x6f, + 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, 0x65, 0x66, 0x66, + 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x13, 0x69, 0x6d, + 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, + 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, + 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x2a, + 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x22, 0xf9, 0x01, 0x0a, 0x13, 0x56, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x70, 0x6b, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, + 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x22, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, + 0x6f, 0x77, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, + 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, + 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x14, 0x56, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x65, @@ -3300,133 +3344,101 @@ var file_binlogdata_proto_rawDesc = []byte{ 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, - 0x22, 0xf9, 0x01, 0x0a, 0x13, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x6f, 0x77, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x28, - 0x0a, 0x08, 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, - 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, - 0x72, 0x6f, 0x77, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x22, 0x0a, 0x06, - 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x22, 0xc5, 0x01, 0x0a, - 0x14, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, - 0x76, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, - 0x72, 0x49, 0x44, 0x52, 0x11, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, - 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x13, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, - 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x56, 0x54, 0x47, 0x61, - 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, - 0x64, 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, - 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x22, 0xde, 0x01, 0x0a, 0x15, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, - 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xde, + 0x01, 0x0a, 0x15, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x28, 0x0a, + 0x08, 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x70, + 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x72, + 0x6f, 0x77, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x22, 0x0a, 0x06, 0x6c, + 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x22, + 0x69, 0x0a, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, + 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x5f, 0x6b, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x52, + 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x12, 0x1c, 0x0a, 0x09, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x58, 0x0a, 0x0b, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, + 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x6c, 0x61, + 0x73, 0x74, 0x70, 0x6b, 0x22, 0xdc, 0x01, 0x0a, 0x15, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, + 0x0a, 0x13, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x65, 0x66, + 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x45, 0x0a, 0x13, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, + 0x72, 0x49, 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, + 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x22, 0x72, 0x0a, 0x16, 0x56, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x52, 0x08, 0x70, 0x6b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, - 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, - 0x73, 0x12, 0x22, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x06, 0x6c, - 0x61, 0x73, 0x74, 0x70, 0x6b, 0x22, 0x69, 0x0a, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x70, 0x5f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, - 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, - 0x61, 0x73, 0x74, 0x50, 0x4b, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, - 0x50, 0x4b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x22, 0x58, 0x0a, 0x0b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x4b, 0x12, - 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, - 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x70, 0x6b, 0x22, 0xdc, 0x01, 0x0a, 0x15, 0x56, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x13, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, - 0x49, 0x44, 0x52, 0x11, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x61, 0x6c, - 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x13, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, - 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x11, 0x69, 0x6d, 0x6d, 0x65, 0x64, - 0x69, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x72, 0x0a, 0x16, 0x56, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1e, 0x0a, - 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x2a, 0x3e, 0x0a, - 0x0b, 0x4f, 0x6e, 0x44, 0x44, 0x4c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, - 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, - 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x58, 0x45, 0x43, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, - 0x45, 0x58, 0x45, 0x43, 0x5f, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x03, 0x2a, 0x7b, 0x0a, - 0x18, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x61, 0x74, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4d, 0x6f, - 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x10, - 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x10, 0x03, 0x12, 0x0b, - 0x0a, 0x07, 0x52, 0x65, 0x73, 0x68, 0x61, 0x72, 0x64, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x44, 0x4c, 0x10, 0x05, 0x2a, 0x44, 0x0a, 0x1b, 0x56, 0x52, + 0x6c, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x67, 0x74, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x52, 0x6f, + 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x2a, 0x3e, 0x0a, 0x0b, 0x4f, 0x6e, 0x44, 0x44, 0x4c, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, + 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x45, 0x58, 0x45, 0x43, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x58, 0x45, 0x43, 0x5f, 0x49, + 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x03, 0x2a, 0x7b, 0x0a, 0x18, 0x56, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x73, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x44, + 0x44, 0x4c, 0x10, 0x05, 0x2a, 0x44, 0x0a, 0x1b, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x43, 0x6f, 0x70, 0x79, 0x10, 0x02, 0x2a, 0x71, 0x0a, 0x19, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, - 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x10, 0x01, - 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x43, 0x6f, 0x70, 0x79, 0x10, 0x02, - 0x2a, 0x71, 0x0a, 0x19, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x6e, - 0x69, 0x74, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, - 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x0b, - 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x61, 0x67, 0x67, 0x69, 0x6e, - 0x67, 0x10, 0x06, 0x2a, 0x8d, 0x02, 0x0a, 0x0a, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x47, 0x54, 0x49, 0x44, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x45, 0x47, - 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, - 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x4f, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, 0x12, 0x07, - 0x0a, 0x03, 0x44, 0x44, 0x4c, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x4e, 0x53, 0x45, 0x52, - 0x54, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x10, 0x07, - 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, - 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x09, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, - 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x0b, 0x12, 0x07, 0x0a, 0x03, - 0x52, 0x4f, 0x57, 0x10, 0x0c, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x10, 0x0d, - 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41, 0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x0e, 0x12, - 0x09, 0x0a, 0x05, 0x56, 0x47, 0x54, 0x49, 0x44, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4a, 0x4f, - 0x55, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x45, 0x52, 0x53, 0x49, - 0x4f, 0x4e, 0x10, 0x11, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x53, 0x54, 0x50, 0x4b, 0x10, 0x12, - 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x41, 0x56, 0x45, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x13, 0x12, - 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x50, 0x59, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, - 0x44, 0x10, 0x14, 0x2a, 0x27, 0x0a, 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x52, 0x44, 0x53, 0x10, 0x01, 0x42, 0x29, 0x5a, 0x27, - 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, - 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x10, 0x01, 0x12, 0x0b, + 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x43, + 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x6e, + 0x69, 0x6e, 0x67, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x05, + 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x61, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x10, 0x06, 0x2a, 0x8d, 0x02, + 0x0a, 0x0a, 0x56, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x54, 0x49, + 0x44, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0a, + 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x4f, + 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x44, 0x4c, 0x10, + 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x4e, 0x53, 0x45, 0x52, 0x54, 0x10, 0x06, 0x12, 0x0b, 0x0a, + 0x07, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, + 0x10, 0x09, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x4f, + 0x54, 0x48, 0x45, 0x52, 0x10, 0x0b, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x4f, 0x57, 0x10, 0x0c, 0x12, + 0x09, 0x0a, 0x05, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x10, 0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, + 0x41, 0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x0e, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x47, 0x54, + 0x49, 0x44, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4a, 0x4f, 0x55, 0x52, 0x4e, 0x41, 0x4c, 0x10, + 0x10, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x11, 0x12, 0x0a, + 0x0a, 0x06, 0x4c, 0x41, 0x53, 0x54, 0x50, 0x4b, 0x10, 0x12, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x41, + 0x56, 0x45, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x13, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x50, + 0x59, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x14, 0x2a, 0x27, 0x0a, + 0x0d, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, + 0x0a, 0x06, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, + 0x41, 0x52, 0x44, 0x53, 0x10, 0x01, 0x42, 0x29, 0x5a, 0x27, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, + 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/go/vt/proto/binlogdata/binlogdata_vtproto.pb.go b/go/vt/proto/binlogdata/binlogdata_vtproto.pb.go index fd1fe5e459d..f5a77cd964c 100644 --- a/go/vt/proto/binlogdata/binlogdata_vtproto.pb.go +++ b/go/vt/proto/binlogdata/binlogdata_vtproto.pb.go @@ -201,6 +201,7 @@ func (m *Rule) CloneVT() *Rule { SourceUniqueKeyColumns: m.SourceUniqueKeyColumns, TargetUniqueKeyColumns: m.TargetUniqueKeyColumns, SourceUniqueKeyTargetColumns: m.SourceUniqueKeyTargetColumns, + ForceUniqueKey: m.ForceUniqueKey, } if rhs := m.ConvertEnumToText; rhs != nil { tmpContainer := make(map[string]string, len(rhs)) @@ -1298,6 +1299,13 @@ func (m *Rule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.ForceUniqueKey) > 0 { + i -= len(m.ForceUniqueKey) + copy(dAtA[i:], m.ForceUniqueKey) + i = encodeVarint(dAtA, i, uint64(len(m.ForceUniqueKey))) + i-- + dAtA[i] = 0x4a + } if len(m.ConvertIntToEnum) > 0 { for k := range m.ConvertIntToEnum { v := m.ConvertIntToEnum[k] @@ -3362,6 +3370,10 @@ func (m *Rule) SizeVT() (n int) { n += mapEntrySize + 1 + sov(uint64(mapEntrySize)) } } + l = len(m.ForceUniqueKey) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -5565,6 +5577,38 @@ func (m *Rule) UnmarshalVT(dAtA []byte) error { } m.ConvertIntToEnum[mapkey] = mapvalue iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ForceUniqueKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ForceUniqueKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/binlogservice/binlogservice.pb.go b/go/vt/proto/binlogservice/binlogservice.pb.go index 4eac50296c1..ea491cca54f 100644 --- a/go/vt/proto/binlogservice/binlogservice.pb.go +++ b/go/vt/proto/binlogservice/binlogservice.pb.go @@ -19,7 +19,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: binlogservice.proto diff --git a/go/vt/proto/logutil/logutil.pb.go b/go/vt/proto/logutil/logutil.pb.go index b2675716168..68c2fa79dec 100644 --- a/go/vt/proto/logutil/logutil.pb.go +++ b/go/vt/proto/logutil/logutil.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: logutil.proto diff --git a/go/vt/proto/mysqlctl/mysqlctl.pb.go b/go/vt/proto/mysqlctl/mysqlctl.pb.go index 19f70887681..ee8d41273c2 100644 --- a/go/vt/proto/mysqlctl/mysqlctl.pb.go +++ b/go/vt/proto/mysqlctl/mysqlctl.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: mysqlctl.proto @@ -190,7 +190,8 @@ type ShutdownRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WaitForMysqld bool `protobuf:"varint,1,opt,name=wait_for_mysqld,json=waitForMysqld,proto3" json:"wait_for_mysqld,omitempty"` + WaitForMysqld bool `protobuf:"varint,1,opt,name=wait_for_mysqld,json=waitForMysqld,proto3" json:"wait_for_mysqld,omitempty"` + MysqlShutdownTimeout *vttime.Duration `protobuf:"bytes,2,opt,name=mysql_shutdown_timeout,json=mysqlShutdownTimeout,proto3" json:"mysql_shutdown_timeout,omitempty"` } func (x *ShutdownRequest) Reset() { @@ -232,6 +233,13 @@ func (x *ShutdownRequest) GetWaitForMysqld() bool { return false } +func (x *ShutdownRequest) GetMysqlShutdownTimeout() *vttime.Duration { + if x != nil { + return x.MysqlShutdownTimeout + } + return nil +} + type ShutdownResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -922,129 +930,134 @@ var file_mysqlctl_proto_rawDesc = []byte{ 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x64, 0x41, 0x72, 0x67, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x0a, 0x0f, 0x53, 0x68, - 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, - 0x0f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x4d, - 0x79, 0x73, 0x71, 0x6c, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x75, 0x6e, - 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, - 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, - 0x01, 0x0a, 0x16, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, - 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x17, 0x62, - 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x64, 0x61, - 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, - 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x15, 0x62, 0x69, 0x6e, 0x6c, - 0x6f, 0x67, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, - 0x65, 0x22, 0x19, 0x0a, 0x17, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x0a, 0x20, + 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x53, + 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, + 0x0a, 0x0f, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x6d, 0x79, 0x73, 0x71, 0x6c, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, + 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x64, 0x12, 0x46, 0x0a, 0x16, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, + 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x14, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x53, + 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x12, + 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, + 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, + 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x16, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x62, 0x69, + 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, + 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x62, + 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x17, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x72, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x52, 0x15, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x44, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4e, 0x0a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, + 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x69, 0x6e, + 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x21, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, + 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0f, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x52, 0x0e, 0x66, 0x69, 0x72, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x34, 0x0a, 0x16, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x66, 0x69, 0x72, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x12, 0x33, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, + 0x6c, 0x61, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x32, 0x0a, + 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, + 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x61, + 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x69, 0x6e, 0x6c, 0x6f, + 0x67, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x69, 0x6e, + 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x16, 0x0a, 0x14, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31, 0x0a, 0x15, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe6, 0x02, 0x0a, + 0x0a, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, + 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x0e, 0x0a, 0x0a, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x01, 0x12, + 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, + 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x10, 0x04, 0x32, 0xb0, 0x05, 0x0a, 0x08, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x43, + 0x74, 0x6c, 0x12, 0x3a, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x2e, 0x6d, 0x79, + 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, + 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x19, 0x2e, 0x6d, 0x79, 0x73, + 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, + 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, + 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, + 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, + 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, + 0x0f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, + 0x12, 0x20, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x19, 0x52, 0x65, 0x61, 0x64, 0x42, + 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x73, 0x12, 0x2a, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xf9, 0x01, 0x0a, - 0x21, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0e, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x34, 0x0a, 0x16, 0x66, 0x69, 0x72, - 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x12, - 0x33, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x69, 0x6e, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x16, 0x0a, 0x14, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x17, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x31, 0x0a, 0x15, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0xe6, 0x02, 0x0a, 0x0a, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x12, 0x20, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x79, 0x73, - 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0x4b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x50, - 0x4c, 0x45, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, - 0x54, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, - 0x03, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x04, 0x32, 0xb0, 0x05, 0x0a, - 0x08, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x43, 0x74, 0x6c, 0x12, 0x3a, 0x0a, 0x05, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x16, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x79, 0x73, - 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, - 0x6e, 0x12, 0x19, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x68, 0x75, - 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, - 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x52, 0x75, - 0x6e, 0x4d, 0x79, 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x12, 0x20, 0x2e, - 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x4d, 0x79, 0x73, 0x71, - 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x21, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x4d, 0x79, - 0x73, 0x71, 0x6c, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, - 0x74, 0x6c, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, - 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x79, 0x73, 0x71, - 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x76, - 0x0a, 0x19, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x2a, 0x2e, 0x6d, 0x79, - 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, - 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, - 0x6c, 0x2e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, - 0x2e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, - 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, - 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x2e, 0x6d, - 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, - 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x27, 0x5a, 0x25, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, - 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1a, 0x2b, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x4f, 0x0a, 0x0c, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1d, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x69, 0x6e, 0x69, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x69, 0x6e, 0x69, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x1e, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, + 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, + 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x27, 0x5a, 0x25, 0x76, 0x69, 0x74, 0x65, + 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, + 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, + 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1080,37 +1093,39 @@ var file_mysqlctl_proto_goTypes = []interface{}{ (*VersionStringRequest)(nil), // 15: mysqlctl.VersionStringRequest (*VersionStringResponse)(nil), // 16: mysqlctl.VersionStringResponse (*BackupInfo)(nil), // 17: mysqlctl.BackupInfo - (*vttime.Time)(nil), // 18: vttime.Time - (*topodata.TabletAlias)(nil), // 19: topodata.TabletAlias + (*vttime.Duration)(nil), // 18: vttime.Duration + (*vttime.Time)(nil), // 19: vttime.Time + (*topodata.TabletAlias)(nil), // 20: topodata.TabletAlias } var file_mysqlctl_proto_depIdxs = []int32{ - 18, // 0: mysqlctl.ApplyBinlogFileRequest.binlog_restore_datetime:type_name -> vttime.Time - 18, // 1: mysqlctl.ReadBinlogFilesTimestampsResponse.first_timestamp:type_name -> vttime.Time - 18, // 2: mysqlctl.ReadBinlogFilesTimestampsResponse.last_timestamp:type_name -> vttime.Time - 19, // 3: mysqlctl.BackupInfo.tablet_alias:type_name -> topodata.TabletAlias - 18, // 4: mysqlctl.BackupInfo.time:type_name -> vttime.Time - 0, // 5: mysqlctl.BackupInfo.status:type_name -> mysqlctl.BackupInfo.Status - 1, // 6: mysqlctl.MysqlCtl.Start:input_type -> mysqlctl.StartRequest - 3, // 7: mysqlctl.MysqlCtl.Shutdown:input_type -> mysqlctl.ShutdownRequest - 5, // 8: mysqlctl.MysqlCtl.RunMysqlUpgrade:input_type -> mysqlctl.RunMysqlUpgradeRequest - 7, // 9: mysqlctl.MysqlCtl.ApplyBinlogFile:input_type -> mysqlctl.ApplyBinlogFileRequest - 9, // 10: mysqlctl.MysqlCtl.ReadBinlogFilesTimestamps:input_type -> mysqlctl.ReadBinlogFilesTimestampsRequest - 11, // 11: mysqlctl.MysqlCtl.ReinitConfig:input_type -> mysqlctl.ReinitConfigRequest - 13, // 12: mysqlctl.MysqlCtl.RefreshConfig:input_type -> mysqlctl.RefreshConfigRequest - 15, // 13: mysqlctl.MysqlCtl.VersionString:input_type -> mysqlctl.VersionStringRequest - 2, // 14: mysqlctl.MysqlCtl.Start:output_type -> mysqlctl.StartResponse - 4, // 15: mysqlctl.MysqlCtl.Shutdown:output_type -> mysqlctl.ShutdownResponse - 6, // 16: mysqlctl.MysqlCtl.RunMysqlUpgrade:output_type -> mysqlctl.RunMysqlUpgradeResponse - 8, // 17: mysqlctl.MysqlCtl.ApplyBinlogFile:output_type -> mysqlctl.ApplyBinlogFileResponse - 10, // 18: mysqlctl.MysqlCtl.ReadBinlogFilesTimestamps:output_type -> mysqlctl.ReadBinlogFilesTimestampsResponse - 12, // 19: mysqlctl.MysqlCtl.ReinitConfig:output_type -> mysqlctl.ReinitConfigResponse - 14, // 20: mysqlctl.MysqlCtl.RefreshConfig:output_type -> mysqlctl.RefreshConfigResponse - 16, // 21: mysqlctl.MysqlCtl.VersionString:output_type -> mysqlctl.VersionStringResponse - 14, // [14:22] is the sub-list for method output_type - 6, // [6:14] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 18, // 0: mysqlctl.ShutdownRequest.mysql_shutdown_timeout:type_name -> vttime.Duration + 19, // 1: mysqlctl.ApplyBinlogFileRequest.binlog_restore_datetime:type_name -> vttime.Time + 19, // 2: mysqlctl.ReadBinlogFilesTimestampsResponse.first_timestamp:type_name -> vttime.Time + 19, // 3: mysqlctl.ReadBinlogFilesTimestampsResponse.last_timestamp:type_name -> vttime.Time + 20, // 4: mysqlctl.BackupInfo.tablet_alias:type_name -> topodata.TabletAlias + 19, // 5: mysqlctl.BackupInfo.time:type_name -> vttime.Time + 0, // 6: mysqlctl.BackupInfo.status:type_name -> mysqlctl.BackupInfo.Status + 1, // 7: mysqlctl.MysqlCtl.Start:input_type -> mysqlctl.StartRequest + 3, // 8: mysqlctl.MysqlCtl.Shutdown:input_type -> mysqlctl.ShutdownRequest + 5, // 9: mysqlctl.MysqlCtl.RunMysqlUpgrade:input_type -> mysqlctl.RunMysqlUpgradeRequest + 7, // 10: mysqlctl.MysqlCtl.ApplyBinlogFile:input_type -> mysqlctl.ApplyBinlogFileRequest + 9, // 11: mysqlctl.MysqlCtl.ReadBinlogFilesTimestamps:input_type -> mysqlctl.ReadBinlogFilesTimestampsRequest + 11, // 12: mysqlctl.MysqlCtl.ReinitConfig:input_type -> mysqlctl.ReinitConfigRequest + 13, // 13: mysqlctl.MysqlCtl.RefreshConfig:input_type -> mysqlctl.RefreshConfigRequest + 15, // 14: mysqlctl.MysqlCtl.VersionString:input_type -> mysqlctl.VersionStringRequest + 2, // 15: mysqlctl.MysqlCtl.Start:output_type -> mysqlctl.StartResponse + 4, // 16: mysqlctl.MysqlCtl.Shutdown:output_type -> mysqlctl.ShutdownResponse + 6, // 17: mysqlctl.MysqlCtl.RunMysqlUpgrade:output_type -> mysqlctl.RunMysqlUpgradeResponse + 8, // 18: mysqlctl.MysqlCtl.ApplyBinlogFile:output_type -> mysqlctl.ApplyBinlogFileResponse + 10, // 19: mysqlctl.MysqlCtl.ReadBinlogFilesTimestamps:output_type -> mysqlctl.ReadBinlogFilesTimestampsResponse + 12, // 20: mysqlctl.MysqlCtl.ReinitConfig:output_type -> mysqlctl.ReinitConfigResponse + 14, // 21: mysqlctl.MysqlCtl.RefreshConfig:output_type -> mysqlctl.RefreshConfigResponse + 16, // 22: mysqlctl.MysqlCtl.VersionString:output_type -> mysqlctl.VersionStringResponse + 15, // [15:23] is the sub-list for method output_type + 7, // [7:15] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_mysqlctl_proto_init() } diff --git a/go/vt/proto/mysqlctl/mysqlctl_vtproto.pb.go b/go/vt/proto/mysqlctl/mysqlctl_vtproto.pb.go index bb2ec78e03a..fab1af2f471 100644 --- a/go/vt/proto/mysqlctl/mysqlctl_vtproto.pb.go +++ b/go/vt/proto/mysqlctl/mysqlctl_vtproto.pb.go @@ -63,7 +63,8 @@ func (m *ShutdownRequest) CloneVT() *ShutdownRequest { return (*ShutdownRequest)(nil) } r := &ShutdownRequest{ - WaitForMysqld: m.WaitForMysqld, + WaitForMysqld: m.WaitForMysqld, + MysqlShutdownTimeout: m.MysqlShutdownTimeout.CloneVT(), } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -430,6 +431,16 @@ func (m *ShutdownRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.MysqlShutdownTimeout != nil { + size, err := m.MysqlShutdownTimeout.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } if m.WaitForMysqld { i-- if m.WaitForMysqld { @@ -1085,6 +1096,10 @@ func (m *ShutdownRequest) SizeVT() (n int) { if m.WaitForMysqld { n += 2 } + if m.MysqlShutdownTimeout != nil { + l = m.MysqlShutdownTimeout.SizeVT() + n += 1 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -1487,6 +1502,42 @@ func (m *ShutdownRequest) UnmarshalVT(dAtA []byte) error { } } m.WaitForMysqld = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MysqlShutdownTimeout", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MysqlShutdownTimeout == nil { + m.MysqlShutdownTimeout = &vttime.Duration{} + } + if err := m.MysqlShutdownTimeout.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go index b8e7e4fe05e..a64f11ab273 100644 --- a/go/vt/proto/query/query.pb.go +++ b/go/vt/proto/query/query.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: query.proto diff --git a/go/vt/proto/queryservice/queryservice.pb.go b/go/vt/proto/queryservice/queryservice.pb.go index babedcde966..eba1e78b80b 100644 --- a/go/vt/proto/queryservice/queryservice.pb.go +++ b/go/vt/proto/queryservice/queryservice.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: queryservice.proto diff --git a/go/vt/proto/replicationdata/replicationdata.pb.go b/go/vt/proto/replicationdata/replicationdata.pb.go index ec90d6943ac..bc0709bc016 100644 --- a/go/vt/proto/replicationdata/replicationdata.pb.go +++ b/go/vt/proto/replicationdata/replicationdata.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: replicationdata.proto diff --git a/go/vt/proto/tableacl/tableacl.pb.go b/go/vt/proto/tableacl/tableacl.pb.go index 3b26ace8157..1e4c5c6a991 100644 --- a/go/vt/proto/tableacl/tableacl.pb.go +++ b/go/vt/proto/tableacl/tableacl.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: tableacl.proto diff --git a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go index c9039a3cfd9..446b44cdddc 100644 --- a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go +++ b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: tabletmanagerdata.proto @@ -2044,11 +2044,12 @@ type ExecuteFetchAsDbaRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Query []byte `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` - DbName string `protobuf:"bytes,2,opt,name=db_name,json=dbName,proto3" json:"db_name,omitempty"` - MaxRows uint64 `protobuf:"varint,3,opt,name=max_rows,json=maxRows,proto3" json:"max_rows,omitempty"` - DisableBinlogs bool `protobuf:"varint,4,opt,name=disable_binlogs,json=disableBinlogs,proto3" json:"disable_binlogs,omitempty"` - ReloadSchema bool `protobuf:"varint,5,opt,name=reload_schema,json=reloadSchema,proto3" json:"reload_schema,omitempty"` + Query []byte `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` + DbName string `protobuf:"bytes,2,opt,name=db_name,json=dbName,proto3" json:"db_name,omitempty"` + MaxRows uint64 `protobuf:"varint,3,opt,name=max_rows,json=maxRows,proto3" json:"max_rows,omitempty"` + DisableBinlogs bool `protobuf:"varint,4,opt,name=disable_binlogs,json=disableBinlogs,proto3" json:"disable_binlogs,omitempty"` + ReloadSchema bool `protobuf:"varint,5,opt,name=reload_schema,json=reloadSchema,proto3" json:"reload_schema,omitempty"` + DisableForeignKeyChecks bool `protobuf:"varint,6,opt,name=disable_foreign_key_checks,json=disableForeignKeyChecks,proto3" json:"disable_foreign_key_checks,omitempty"` } func (x *ExecuteFetchAsDbaRequest) Reset() { @@ -2118,6 +2119,13 @@ func (x *ExecuteFetchAsDbaRequest) GetReloadSchema() bool { return false } +func (x *ExecuteFetchAsDbaRequest) GetDisableForeignKeyChecks() bool { + if x != nil { + return x.DisableForeignKeyChecks + } + return false +} + type ExecuteFetchAsDbaResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4546,7 +4554,7 @@ type BackupRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Concurrency int64 `protobuf:"varint,1,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + Concurrency int32 `protobuf:"varint,1,opt,name=concurrency,proto3" json:"concurrency,omitempty"` AllowPrimary bool `protobuf:"varint,2,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` // IncrementalFromPos indicates a position of a previous backup. When this value is non-empty // then the backup becomes incremental and applies as of given position. @@ -4588,7 +4596,7 @@ func (*BackupRequest) Descriptor() ([]byte, []int) { return file_tabletmanagerdata_proto_rawDescGZIP(), []int{92} } -func (x *BackupRequest) GetConcurrency() int64 { +func (x *BackupRequest) GetConcurrency() int32 { if x != nil { return x.Concurrency } @@ -5439,9 +5447,10 @@ type VDiffReportOptions struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OnlyPks bool `protobuf:"varint,1,opt,name=only_pks,json=onlyPks,proto3" json:"only_pks,omitempty"` - DebugQuery bool `protobuf:"varint,2,opt,name=debug_query,json=debugQuery,proto3" json:"debug_query,omitempty"` - Format string `protobuf:"bytes,3,opt,name=format,proto3" json:"format,omitempty"` + OnlyPks bool `protobuf:"varint,1,opt,name=only_pks,json=onlyPks,proto3" json:"only_pks,omitempty"` + DebugQuery bool `protobuf:"varint,2,opt,name=debug_query,json=debugQuery,proto3" json:"debug_query,omitempty"` + Format string `protobuf:"bytes,3,opt,name=format,proto3" json:"format,omitempty"` + MaxSampleRows int64 `protobuf:"varint,4,opt,name=max_sample_rows,json=maxSampleRows,proto3" json:"max_sample_rows,omitempty"` } func (x *VDiffReportOptions) Reset() { @@ -5497,6 +5506,13 @@ func (x *VDiffReportOptions) GetFormat() string { return "" } +func (x *VDiffReportOptions) GetMaxSampleRows() int64 { + if x != nil { + return x.MaxSampleRows + } + return 0 +} + type VDiffCoreOptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5510,6 +5526,7 @@ type VDiffCoreOptions struct { TimeoutSeconds int64 `protobuf:"varint,6,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` MaxExtraRowsToCompare int64 `protobuf:"varint,7,opt,name=max_extra_rows_to_compare,json=maxExtraRowsToCompare,proto3" json:"max_extra_rows_to_compare,omitempty"` UpdateTableStats bool `protobuf:"varint,8,opt,name=update_table_stats,json=updateTableStats,proto3" json:"update_table_stats,omitempty"` + MaxDiffSeconds int64 `protobuf:"varint,9,opt,name=max_diff_seconds,json=maxDiffSeconds,proto3" json:"max_diff_seconds,omitempty"` } func (x *VDiffCoreOptions) Reset() { @@ -5600,6 +5617,13 @@ func (x *VDiffCoreOptions) GetUpdateTableStats() bool { return false } +func (x *VDiffCoreOptions) GetMaxDiffSeconds() int64 { + if x != nil { + return x.MaxDiffSeconds + } + return 0 +} + type VDiffOptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5674,6 +5698,7 @@ type UpdateVReplicationWorkflowRequest struct { TabletSelectionPreference TabletSelectionPreference `protobuf:"varint,4,opt,name=tablet_selection_preference,json=tabletSelectionPreference,proto3,enum=tabletmanagerdata.TabletSelectionPreference" json:"tablet_selection_preference,omitempty"` OnDdl binlogdata.OnDDLAction `protobuf:"varint,5,opt,name=on_ddl,json=onDdl,proto3,enum=binlogdata.OnDDLAction" json:"on_ddl,omitempty"` State binlogdata.VReplicationWorkflowState `protobuf:"varint,6,opt,name=state,proto3,enum=binlogdata.VReplicationWorkflowState" json:"state,omitempty"` + Shards []string `protobuf:"bytes,7,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *UpdateVReplicationWorkflowRequest) Reset() { @@ -5750,6 +5775,13 @@ func (x *UpdateVReplicationWorkflowRequest) GetState() binlogdata.VReplicationWo return binlogdata.VReplicationWorkflowState(0) } +func (x *UpdateVReplicationWorkflowRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type UpdateVReplicationWorkflowResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6399,7 +6431,7 @@ var file_tabletmanagerdata_proto_rawDesc = []byte{ 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xb2, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xef, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x62, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, @@ -6410,430 +6442,439 @@ var file_tabletmanagerdata_proto_rawDesc = []byte{ 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x47, 0x0a, 0x19, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x62, 0x61, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x1d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x64, - 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, - 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x22, 0x4c, 0x0a, 0x1e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x22, 0x4b, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, - 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x22, - 0x47, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, - 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x19, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4f, 0x0a, 0x15, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x50, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x35, 0x0a, 0x17, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x16, - 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x19, 0x0a, 0x17, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x0a, - 0x16, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x74, 0x6f, 0x70, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x5e, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x21, 0x0a, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x22, 0x3c, 0x0a, 0x1e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x35, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, - 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, - 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x62, 0x0a, 0x21, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x41, 0x66, 0x74, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x24, 0x0a, 0x22, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, - 0x41, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, - 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, - 0x22, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x52, - 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x0a, 0x17, 0x56, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x46, 0x0a, 0x18, 0x56, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x4b, 0x65, + 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x22, 0x47, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x62, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x22, 0x4b, 0x0a, 0x1d, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, - 0x1e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x61, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x30, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, - 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, - 0x63, 0x22, 0x31, 0x0a, 0x13, 0x49, 0x6e, 0x69, 0x74, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd8, 0x01, 0x0a, 0x1e, 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x3a, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0c, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x21, 0x0a, 0x1f, 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, + 0x22, 0x8e, 0x01, 0x0a, 0x1d, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, + 0x68, 0x41, 0x73, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x22, 0x4c, 0x0a, 0x1e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, + 0x68, 0x41, 0x73, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, + 0x4b, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, + 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x22, 0x47, 0x0a, 0x19, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x4c, 0x0a, 0x19, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x16, 0x0a, 0x14, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4f, 0x0a, 0x15, 0x50, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x36, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x35, 0x0a, 0x17, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x16, 0x57, 0x61, 0x69, + 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x19, 0x0a, 0x17, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x74, + 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x5e, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, + 0x77, 0x61, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, + 0x3c, 0x0a, 0x1e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x35, 0x0a, + 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, + 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, + 0x53, 0x79, 0x6e, 0x63, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x62, 0x0a, 0x21, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x41, 0x66, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x22, 0x24, 0x0a, 0x22, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x41, 0x66, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x2b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x22, 0x19, 0x0a, + 0x17, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x0a, 0x17, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x46, 0x0a, 0x18, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x4b, 0x0a, + 0x1d, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x61, 0x69, + 0x74, 0x46, 0x6f, 0x72, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, + 0x72, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x12, + 0x49, 0x6e, 0x69, 0x74, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x31, + 0x0a, 0x13, 0x49, 0x6e, 0x69, 0x74, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0xd8, 0x01, 0x0a, 0x1e, 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, + 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, + 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0c, 0x70, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x21, 0x0a, 0x1f, + 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xba, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, + 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x15, 0x0a, 0x13, + 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x64, 0x0a, 0x15, 0x44, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0d, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, + 0x02, 0x22, 0x36, 0x0a, 0x18, 0x55, 0x6e, 0x64, 0x6f, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x1b, 0x0a, 0x19, 0x55, 0x6e, 0x64, + 0x6f, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x57, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x57, 0x61, + 0x73, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x23, 0x0a, 0x21, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x24, 0x0a, 0x22, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x0a, 0x11, + 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x49, 0x0a, 0x12, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xed, 0x01, 0x0a, + 0x1b, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x4e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, - 0x15, 0x0a, 0x13, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x64, - 0x0a, 0x15, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, - 0x08, 0x01, 0x10, 0x02, 0x22, 0x36, 0x0a, 0x18, 0x55, 0x6e, 0x64, 0x6f, 0x44, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x1b, 0x0a, 0x19, - 0x55, 0x6e, 0x64, 0x6f, 0x44, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x57, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x57, 0x61, 0x73, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x0a, 0x21, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x24, 0x0a, 0x22, 0x52, 0x65, 0x73, - 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x13, 0x0a, 0x11, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x12, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x75, 0x6c, - 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0xed, 0x01, 0x0a, 0x1b, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x26, - 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, - 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, - 0x1e, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x4b, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x57, 0x61, 0x73, 0x52, 0x65, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, - 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x1d, 0x0a, 0x1b, + 0x64, 0x4e, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x77, + 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x1e, 0x0a, 0x1c, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4b, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x57, 0x61, 0x73, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x0a, 0x22, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, - 0x64, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x58, 0x0a, 0x15, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x24, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x13, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x6b, 0x0a, 0x23, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, - 0x64, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x33, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x6d, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x34, 0x0a, - 0x16, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x30, 0x0a, 0x14, - 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, - 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x73, 0x12, 0x21, - 0x0a, 0x0c, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x53, 0x61, 0x66, - 0x65, 0x22, 0x36, 0x0a, 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xc8, 0x01, 0x0a, 0x18, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, - 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, - 0x79, 0x52, 0x75, 0x6e, 0x12, 0x3e, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x74, 0x6f, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x52, 0x12, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x22, 0x41, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, - 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xd4, 0x04, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x69, 0x6e, - 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x42, 0x69, - 0x6e, 0x6c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, 0x62, 0x69, 0x6e, 0x6c, - 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, - 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x62, - 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x53, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x75, 0x62, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x62, 0x69, - 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, - 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, - 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, - 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x22, 0x50, - 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x57, 0x61, 0x73, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x0a, 0x22, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, + 0x0a, 0x15, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, + 0x6f, 0x64, 0x65, 0x52, 0x13, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x6b, 0x0a, 0x23, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x33, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x22, 0x34, 0x0a, 0x16, 0x50, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x6f, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, + 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x53, 0x61, 0x66, 0x65, 0x22, 0x36, + 0x0a, 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xc8, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, + 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, + 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, + 0x6e, 0x12, 0x3e, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x12, 0x72, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x22, 0x41, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x22, 0xd4, 0x04, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x42, 0x69, 0x6e, 0x6c, 0x6f, + 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0c, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x62, 0x69, 0x6e, 0x6c, + 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x53, 0x0a, + 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, + 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, + 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, + 0x6f, 0x70, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x22, 0x50, 0x0a, 0x22, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3f, 0x0a, + 0x21, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x50, + 0x0a, 0x22, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x22, 0x3f, 0x0a, 0x21, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x22, 0x50, 0x0a, 0x22, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0x3d, 0x0a, 0x1f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x22, 0x94, 0x09, 0x0a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, - 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x49, 0x0a, - 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x53, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x77, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, - 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, - 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, - 0x54, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3a, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x07, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0xc1, 0x04, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x2a, 0x0a, 0x03, 0x62, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x42, 0x69, 0x6e, 0x6c, 0x6f, - 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x62, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x70, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x12, 0x19, - 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x74, 0x6f, 0x70, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, - 0x5f, 0x74, 0x70, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x54, - 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x11, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x61, 0x67, 0x12, 0x2f, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x15, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x52, 0x14, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0a, 0x72, 0x6f, 0x77, 0x73, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x12, 0x33, - 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, - 0x65, 0x61, 0x74, 0x12, 0x33, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x68, 0x72, 0x6f, - 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x54, - 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x22, 0xd7, 0x01, 0x0a, 0x0c, 0x56, 0x44, - 0x69, 0x66, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x64, 0x69, - 0x66, 0x66, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, - 0x64, 0x69, 0x66, 0x66, 0x55, 0x75, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x44, - 0x69, 0x66, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x22, 0x6a, 0x0a, 0x0d, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x64, 0x69, 0x66, 0x66, 0x55, 0x75, 0x69, 0x64, 0x22, - 0x79, 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, 0x66, 0x50, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x22, 0x68, 0x0a, 0x12, 0x56, 0x44, - 0x69, 0x66, 0x66, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x70, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x6f, 0x6e, 0x6c, 0x79, 0x50, 0x6b, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, - 0x65, 0x62, 0x75, 0x67, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x22, 0xb0, 0x02, 0x0a, 0x10, 0x56, 0x44, 0x69, 0x66, 0x66, 0x43, 0x6f, - 0x72, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x74, 0x72, 0x79, - 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x5f, 0x70, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x50, 0x63, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, - 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x72, 0x6f, 0x77, - 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x77, 0x73, - 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0xf2, 0x01, 0x0a, 0x0c, 0x56, 0x44, 0x69, 0x66, + 0x22, 0x3d, 0x0a, 0x1f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, + 0x94, 0x09, 0x0a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, + 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x17, 0x0a, + 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x0d, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x24, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x54, 0x79, 0x70, 0x65, 0x12, 0x53, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x27, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x53, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, + 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, + 0x79, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x54, 0x0a, 0x07, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x73, 0x1a, 0xc1, 0x04, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, + 0x03, 0x62, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x69, 0x6e, + 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x62, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x6f, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x73, + 0x74, 0x6f, 0x70, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x74, 0x6f, 0x70, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x70, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x54, 0x70, 0x73, 0x12, + 0x2e, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x61, + 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x67, 0x12, + 0x2f, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x12, 0x41, 0x0a, 0x15, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x14, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x6f, + 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x72, 0x6f, 0x77, 0x73, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x0e, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x12, 0x33, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, + 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x68, 0x72, 0x6f, + 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, + 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x22, 0xd7, 0x01, 0x0a, 0x0c, 0x56, 0x44, 0x69, 0x66, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x64, 0x69, 0x66, 0x66, 0x5f, + 0x75, 0x75, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, 0x64, 0x69, 0x66, + 0x66, 0x55, 0x75, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x44, 0x69, 0x66, 0x66, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x6a, 0x0a, 0x0d, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x76, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x76, 0x64, 0x69, 0x66, 0x66, 0x55, 0x75, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x12, + 0x56, 0x44, 0x69, 0x66, 0x66, 0x50, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x22, 0x90, 0x01, 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, + 0x66, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x19, + 0x0a, 0x08, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x70, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x6f, 0x6e, 0x6c, 0x79, 0x50, 0x6b, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, + 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x22, 0xda, 0x02, 0x0a, 0x10, 0x56, + 0x44, 0x69, 0x66, 0x66, 0x43, 0x6f, 0x72, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, + 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, + 0x6f, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, + 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x70, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x50, 0x63, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x74, + 0x72, 0x61, 0x52, 0x6f, 0x77, 0x73, 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x12, + 0x2c, 0x0a, 0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x28, 0x0a, + 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x44, 0x69, 0x66, 0x66, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xf2, 0x01, 0x0a, 0x0c, 0x56, 0x44, 0x69, 0x66, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0e, 0x70, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, @@ -6848,7 +6889,7 @@ var file_tabletmanagerdata_proto_rawDesc = []byte{ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x72, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe9, 0x02, 0x0a, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x81, 0x03, 0x0a, 0x21, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, @@ -6871,40 +6912,42 @@ var file_tabletmanagerdata_proto_rawDesc = []byte{ 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x50, 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, - 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, - 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x2f, 0x0a, 0x15, 0x52, 0x65, - 0x73, 0x65, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x52, - 0x65, 0x73, 0x65, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x68, - 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x61, 0x70, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x70, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x16, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, - 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, - 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, - 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, - 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x65, 0x64, 0x2a, 0x3e, 0x0a, 0x19, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, - 0x4f, 0x52, 0x44, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, - 0x57, 0x4e, 0x10, 0x03, 0x42, 0x30, 0x5a, 0x2e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, - 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x22, 0x50, 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0x2f, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x65, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x65, 0x74, 0x53, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, + 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x70, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x68, 0x72, 0x6f, 0x74, + 0x74, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x5f, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x63, + 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x2a, 0x3e, 0x0a, 0x19, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x10, 0x01, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x42, 0x30, 0x5a, 0x2e, + 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, + 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/go/vt/proto/tabletmanagerdata/tabletmanagerdata_vtproto.pb.go b/go/vt/proto/tabletmanagerdata/tabletmanagerdata_vtproto.pb.go index 502a4c17ff9..4d5b4188e5e 100644 --- a/go/vt/proto/tabletmanagerdata/tabletmanagerdata_vtproto.pb.go +++ b/go/vt/proto/tabletmanagerdata/tabletmanagerdata_vtproto.pb.go @@ -792,10 +792,11 @@ func (m *ExecuteFetchAsDbaRequest) CloneVT() *ExecuteFetchAsDbaRequest { return (*ExecuteFetchAsDbaRequest)(nil) } r := &ExecuteFetchAsDbaRequest{ - DbName: m.DbName, - MaxRows: m.MaxRows, - DisableBinlogs: m.DisableBinlogs, - ReloadSchema: m.ReloadSchema, + DbName: m.DbName, + MaxRows: m.MaxRows, + DisableBinlogs: m.DisableBinlogs, + ReloadSchema: m.ReloadSchema, + DisableForeignKeyChecks: m.DisableForeignKeyChecks, } if rhs := m.Query; rhs != nil { tmpBytes := make([]byte, len(rhs)) @@ -2076,9 +2077,10 @@ func (m *VDiffReportOptions) CloneVT() *VDiffReportOptions { return (*VDiffReportOptions)(nil) } r := &VDiffReportOptions{ - OnlyPks: m.OnlyPks, - DebugQuery: m.DebugQuery, - Format: m.Format, + OnlyPks: m.OnlyPks, + DebugQuery: m.DebugQuery, + Format: m.Format, + MaxSampleRows: m.MaxSampleRows, } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -2104,6 +2106,7 @@ func (m *VDiffCoreOptions) CloneVT() *VDiffCoreOptions { TimeoutSeconds: m.TimeoutSeconds, MaxExtraRowsToCompare: m.MaxExtraRowsToCompare, UpdateTableStats: m.UpdateTableStats, + MaxDiffSeconds: m.MaxDiffSeconds, } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -2156,6 +2159,11 @@ func (m *UpdateVReplicationWorkflowRequest) CloneVT() *UpdateVReplicationWorkflo copy(tmpContainer, rhs) r.TabletTypes = tmpContainer } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -4041,6 +4049,16 @@ func (m *ExecuteFetchAsDbaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, err i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.DisableForeignKeyChecks { + i-- + if m.DisableForeignKeyChecks { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } if m.ReloadSchema { i-- if m.ReloadSchema { @@ -7194,6 +7212,11 @@ func (m *VDiffReportOptions) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.MaxSampleRows != 0 { + i = encodeVarint(dAtA, i, uint64(m.MaxSampleRows)) + i-- + dAtA[i] = 0x20 + } if len(m.Format) > 0 { i -= len(m.Format) copy(dAtA[i:], m.Format) @@ -7254,6 +7277,11 @@ func (m *VDiffCoreOptions) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.MaxDiffSeconds != 0 { + i = encodeVarint(dAtA, i, uint64(m.MaxDiffSeconds)) + i-- + dAtA[i] = 0x48 + } if m.UpdateTableStats { i-- if m.UpdateTableStats { @@ -7407,6 +7435,15 @@ func (m *UpdateVReplicationWorkflowRequest) MarshalToSizedBufferVT(dAtA []byte) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x3a + } + } if m.State != 0 { i = encodeVarint(dAtA, i, uint64(m.State)) i-- @@ -8353,6 +8390,9 @@ func (m *ExecuteFetchAsDbaRequest) SizeVT() (n int) { if m.ReloadSchema { n += 2 } + if m.DisableForeignKeyChecks { + n += 2 + } n += len(m.unknownFields) return n } @@ -9448,6 +9488,9 @@ func (m *VDiffReportOptions) SizeVT() (n int) { if l > 0 { n += 1 + l + sov(uint64(l)) } + if m.MaxSampleRows != 0 { + n += 1 + sov(uint64(m.MaxSampleRows)) + } n += len(m.unknownFields) return n } @@ -9483,6 +9526,9 @@ func (m *VDiffCoreOptions) SizeVT() (n int) { if m.UpdateTableStats { n += 2 } + if m.MaxDiffSeconds != 0 { + n += 1 + sov(uint64(m.MaxDiffSeconds)) + } n += len(m.unknownFields) return n } @@ -9541,6 +9587,12 @@ func (m *UpdateVReplicationWorkflowRequest) SizeVT() (n int) { if m.State != 0 { n += 1 + sov(uint64(m.State)) } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -13692,6 +13744,26 @@ func (m *ExecuteFetchAsDbaRequest) UnmarshalVT(dAtA []byte) error { } } m.ReloadSchema = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DisableForeignKeyChecks", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DisableForeignKeyChecks = bool(v != 0) default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -17807,7 +17879,7 @@ func (m *BackupRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= int64(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -20376,6 +20448,25 @@ func (m *VDiffReportOptions) UnmarshalVT(dAtA []byte) error { } m.Format = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxSampleRows", wireType) + } + m.MaxSampleRows = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxSampleRows |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -20595,6 +20686,25 @@ func (m *VDiffCoreOptions) UnmarshalVT(dAtA []byte) error { } } m.UpdateTableStats = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxDiffSeconds", wireType) + } + m.MaxDiffSeconds = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxDiffSeconds |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -20995,6 +21105,38 @@ func (m *UpdateVReplicationWorkflowRequest) UnmarshalVT(dAtA []byte) error { break } } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go b/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go index 608282049ba..679b3822885 100644 --- a/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go +++ b/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: tabletmanagerservice.proto diff --git a/go/vt/proto/throttlerdata/throttlerdata.pb.go b/go/vt/proto/throttlerdata/throttlerdata.pb.go index fb12bc09ce8..7ad1d380f4f 100644 --- a/go/vt/proto/throttlerdata/throttlerdata.pb.go +++ b/go/vt/proto/throttlerdata/throttlerdata.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: throttlerdata.proto diff --git a/go/vt/proto/throttlerservice/throttlerservice.pb.go b/go/vt/proto/throttlerservice/throttlerservice.pb.go index 9bca73e067c..5c855feedd1 100644 --- a/go/vt/proto/throttlerservice/throttlerservice.pb.go +++ b/go/vt/proto/throttlerservice/throttlerservice.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: throttlerservice.proto diff --git a/go/vt/proto/topodata/topodata.pb.go b/go/vt/proto/topodata/topodata.pb.go index 43ecdbce963..0f20a6470c8 100644 --- a/go/vt/proto/topodata/topodata.pb.go +++ b/go/vt/proto/topodata/topodata.pb.go @@ -20,7 +20,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: topodata.proto @@ -656,9 +656,6 @@ type Keyspace struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ServedFrom will redirect the appropriate traffic to - // another keyspace. - ServedFroms []*Keyspace_ServedFrom `protobuf:"bytes,4,rep,name=served_froms,json=servedFroms,proto3" json:"served_froms,omitempty"` // keyspace_type will determine how this keyspace is treated by // vtgate / vschema. Normal keyspaces are routable by // any query. Snapshot keyspaces are only accessible @@ -716,13 +713,6 @@ func (*Keyspace) Descriptor() ([]byte, []int) { return file_topodata_proto_rawDescGZIP(), []int{4} } -func (x *Keyspace) GetServedFroms() []*Keyspace_ServedFrom { - if x != nil { - return x.ServedFroms - } - return nil -} - func (x *Keyspace) GetKeyspaceType() KeyspaceType { if x != nil { return x.KeyspaceType @@ -1172,7 +1162,6 @@ type SrvKeyspace struct { // The partitions this keyspace is serving, per tablet type. Partitions []*SrvKeyspace_KeyspacePartition `protobuf:"bytes,1,rep,name=partitions,proto3" json:"partitions,omitempty"` - ServedFrom []*SrvKeyspace_ServedFrom `protobuf:"bytes,4,rep,name=served_from,json=servedFrom,proto3" json:"served_from,omitempty"` // ThrottlerConfig has the configuration for the tablet server's // lag throttler, and applies to the entire keyspace, across all // shards and tablets. This is copied from the global keyspace @@ -1219,13 +1208,6 @@ func (x *SrvKeyspace) GetPartitions() []*SrvKeyspace_KeyspacePartition { return nil } -func (x *SrvKeyspace) GetServedFrom() []*SrvKeyspace_ServedFrom { - if x != nil { - return x.ServedFrom - } - return nil -} - func (x *SrvKeyspace) GetThrottlerConfig() *ThrottlerConfig { if x != nil { return x.ThrottlerConfig @@ -1666,74 +1648,6 @@ func (x *Shard_TabletControl) GetFrozen() bool { return false } -// ServedFrom indicates a relationship between a TabletType and the -// keyspace name that's serving it. -type Keyspace_ServedFrom struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // the tablet type (key for the map) - TabletType TabletType `protobuf:"varint,1,opt,name=tablet_type,json=tabletType,proto3,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // the cells to limit this to - Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` - // the keyspace name that's serving it - Keyspace string `protobuf:"bytes,3,opt,name=keyspace,proto3" json:"keyspace,omitempty"` -} - -func (x *Keyspace_ServedFrom) Reset() { - *x = Keyspace_ServedFrom{} - if protoimpl.UnsafeEnabled { - mi := &file_topodata_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Keyspace_ServedFrom) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Keyspace_ServedFrom) ProtoMessage() {} - -func (x *Keyspace_ServedFrom) ProtoReflect() protoreflect.Message { - mi := &file_topodata_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Keyspace_ServedFrom.ProtoReflect.Descriptor instead. -func (*Keyspace_ServedFrom) Descriptor() ([]byte, []int) { - return file_topodata_proto_rawDescGZIP(), []int{4, 0} -} - -func (x *Keyspace_ServedFrom) GetTabletType() TabletType { - if x != nil { - return x.TabletType - } - return TabletType_UNKNOWN -} - -func (x *Keyspace_ServedFrom) GetCells() []string { - if x != nil { - return x.Cells - } - return nil -} - -func (x *Keyspace_ServedFrom) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - // Node describes a tablet instance within the cell type ShardReplication_Node struct { state protoimpl.MessageState @@ -1746,7 +1660,7 @@ type ShardReplication_Node struct { func (x *ShardReplication_Node) Reset() { *x = ShardReplication_Node{} if protoimpl.UnsafeEnabled { - mi := &file_topodata_proto_msgTypes[22] + mi := &file_topodata_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1759,7 +1673,7 @@ func (x *ShardReplication_Node) String() string { func (*ShardReplication_Node) ProtoMessage() {} func (x *ShardReplication_Node) ProtoReflect() protoreflect.Message { - mi := &file_topodata_proto_msgTypes[22] + mi := &file_topodata_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1798,7 +1712,7 @@ type SrvKeyspace_KeyspacePartition struct { func (x *SrvKeyspace_KeyspacePartition) Reset() { *x = SrvKeyspace_KeyspacePartition{} if protoimpl.UnsafeEnabled { - mi := &file_topodata_proto_msgTypes[24] + mi := &file_topodata_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1811,7 +1725,7 @@ func (x *SrvKeyspace_KeyspacePartition) String() string { func (*SrvKeyspace_KeyspacePartition) ProtoMessage() {} func (x *SrvKeyspace_KeyspacePartition) ProtoReflect() protoreflect.Message { - mi := &file_topodata_proto_msgTypes[24] + mi := &file_topodata_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1848,65 +1762,6 @@ func (x *SrvKeyspace_KeyspacePartition) GetShardTabletControls() []*ShardTabletC return nil } -// ServedFrom indicates a relationship between a TabletType and the -// keyspace name that's serving it. -type SrvKeyspace_ServedFrom struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // the tablet type - TabletType TabletType `protobuf:"varint,1,opt,name=tablet_type,json=tabletType,proto3,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // the keyspace name that's serving it - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` -} - -func (x *SrvKeyspace_ServedFrom) Reset() { - *x = SrvKeyspace_ServedFrom{} - if protoimpl.UnsafeEnabled { - mi := &file_topodata_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SrvKeyspace_ServedFrom) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SrvKeyspace_ServedFrom) ProtoMessage() {} - -func (x *SrvKeyspace_ServedFrom) ProtoReflect() protoreflect.Message { - mi := &file_topodata_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SrvKeyspace_ServedFrom.ProtoReflect.Descriptor instead. -func (*SrvKeyspace_ServedFrom) Descriptor() ([]byte, []int) { - return file_topodata_proto_rawDescGZIP(), []int{11, 1} -} - -func (x *SrvKeyspace_ServedFrom) GetTabletType() TabletType { - if x != nil { - return x.TabletType - } - return TabletType_UNKNOWN -} - -func (x *SrvKeyspace_ServedFrom) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - var File_topodata_proto protoreflect.FileDescriptor var file_topodata_proto_rawDesc = []byte{ @@ -2006,181 +1861,160 @@ var file_topodata_proto_rawDesc = []byte{ 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10, - 0x04, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x85, 0x04, - 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x73, 0x12, 0x3b, 0x0a, 0x0d, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, - 0x0a, 0x0d, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x52, 0x0c, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, - 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x44, - 0x0a, 0x10, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x0f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x5f, - 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, - 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x44, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x75, 0x0a, 0x0a, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, - 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x05, 0x6e, 0x6f, - 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, - 0x73, 0x1a, 0x40, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x15, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x38, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x6f, - 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x22, 0x39, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, - 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x4f, 0x50, 0x4f, 0x4c, 0x4f, 0x47, - 0x59, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x22, 0x55, 0x0a, 0x0e, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, - 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x34, 0x0a, 0x16, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x14, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, - 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x12, 0x2b, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, - 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x22, 0xce, 0x02, 0x0a, 0x0f, 0x54, 0x68, - 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, - 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x2d, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x53, 0x0a, 0x0e, 0x74, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2c, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x74, - 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x5c, 0x0a, 0x12, - 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb6, 0x04, 0x0a, 0x0b, 0x53, - 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x44, 0x0a, 0x10, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, - 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, - 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x74, 0x68, 0x72, - 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0xe1, 0x01, 0x0a, - 0x11, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x50, - 0x0a, 0x15, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x13, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, - 0x1a, 0x5f, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x35, - 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, - 0x05, 0x10, 0x06, 0x22, 0x4b, 0x0a, 0x08, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, - 0x22, 0x22, 0x0a, 0x0a, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, - 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x55, 0x0a, 0x0a, 0x54, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x22, 0x4e, 0x0a, 0x15, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x56, 0x69, 0x74, 0x65, 0x73, 0x73, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x0a, 0x74, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5a, 0x0a, 0x10, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x46, 0x0a, 0x0e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x56, 0x69, 0x74, 0x65, 0x73, - 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x0d, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2a, 0x28, 0x0a, 0x0c, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, - 0x4c, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, - 0x01, 0x2a, 0x9d, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, - 0x53, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, - 0x41, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x03, 0x12, - 0x09, 0x0a, 0x05, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x50, - 0x41, 0x52, 0x45, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x50, 0x45, 0x52, 0x49, 0x4d, - 0x45, 0x4e, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x41, 0x43, 0x4b, 0x55, - 0x50, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x07, - 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x08, 0x1a, 0x02, 0x10, - 0x01, 0x42, 0x38, 0x0a, 0x0f, 0x69, 0x6f, 0x2e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x25, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, - 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x04, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0xd2, 0x02, + 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x0d, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x52, 0x0c, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x2b, 0x0a, 0x11, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, 0x72, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x44, 0x0a, 0x10, + 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x0f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x5f, 0x64, 0x62, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x69, 0x64, + 0x65, 0x63, 0x61, 0x72, 0x44, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, + 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, + 0x10, 0x05, 0x22, 0x8b, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x1a, 0x40, + 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x22, 0xc6, 0x01, 0x0a, 0x15, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, + 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x39, + 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, + 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x4f, 0x50, 0x4f, 0x4c, 0x4f, 0x47, 0x59, 0x5f, 0x4d, + 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x22, 0x55, 0x0a, 0x0e, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, + 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x22, 0x8f, 0x01, 0x0a, 0x12, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6b, + 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x16, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, + 0x41, 0x70, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x12, 0x2b, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x65, 0x78, 0x65, 0x6d, 0x70, 0x74, 0x22, 0xce, 0x02, 0x0a, 0x0f, 0x54, 0x68, 0x72, 0x6f, 0x74, + 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, + 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x2d, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, + 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x53, 0x65, 0x6c, 0x66, 0x12, 0x53, 0x0a, 0x0e, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, + 0x64, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, + 0x64, 0x41, 0x70, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x74, 0x68, 0x72, 0x6f, + 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x5c, 0x0a, 0x12, 0x54, 0x68, 0x72, + 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, + 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x98, 0x03, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x72, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x44, 0x0a, 0x10, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0xe1, 0x01, 0x0a, 0x11, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, + 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, + 0x10, 0x06, 0x22, 0x4b, 0x0a, 0x08, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, + 0x22, 0x0a, 0x0a, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, + 0x6c, 0x6c, 0x73, 0x22, 0x55, 0x0a, 0x0a, 0x54, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x22, 0x4e, 0x0a, 0x15, 0x45, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x56, 0x69, 0x74, 0x65, 0x73, 0x73, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, + 0x74, 0x6f, 0x70, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5a, 0x0a, 0x10, 0x45, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x46, + 0x0a, 0x0e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x56, 0x69, 0x74, 0x65, 0x73, 0x73, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x0d, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2a, 0x28, 0x0a, 0x0c, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, + 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x10, 0x01, + 0x2a, 0x9d, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x53, + 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, + 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x03, 0x12, 0x09, + 0x0a, 0x05, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x50, 0x41, + 0x52, 0x45, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x50, 0x45, 0x52, 0x49, 0x4d, 0x45, + 0x4e, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x41, 0x43, 0x4b, 0x55, 0x50, + 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x07, 0x12, + 0x0b, 0x0a, 0x07, 0x44, 0x52, 0x41, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x08, 0x1a, 0x02, 0x10, 0x01, + 0x42, 0x38, 0x0a, 0x0f, 0x69, 0x6f, 0x2e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x5a, 0x25, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, + 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2196,7 +2030,7 @@ func file_topodata_proto_rawDescGZIP() []byte { } var file_topodata_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_topodata_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_topodata_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_topodata_proto_goTypes = []interface{}{ (KeyspaceType)(0), // 0: topodata.KeyspaceType (TabletType)(0), // 1: topodata.TabletType @@ -2222,12 +2056,10 @@ var file_topodata_proto_goTypes = []interface{}{ nil, // 21: topodata.Tablet.TagsEntry (*Shard_SourceShard)(nil), // 22: topodata.Shard.SourceShard (*Shard_TabletControl)(nil), // 23: topodata.Shard.TabletControl - (*Keyspace_ServedFrom)(nil), // 24: topodata.Keyspace.ServedFrom - (*ShardReplication_Node)(nil), // 25: topodata.ShardReplication.Node - nil, // 26: topodata.ThrottlerConfig.ThrottledAppsEntry - (*SrvKeyspace_KeyspacePartition)(nil), // 27: topodata.SrvKeyspace.KeyspacePartition - (*SrvKeyspace_ServedFrom)(nil), // 28: topodata.SrvKeyspace.ServedFrom - (*vttime.Time)(nil), // 29: vttime.Time + (*ShardReplication_Node)(nil), // 24: topodata.ShardReplication.Node + nil, // 25: topodata.ThrottlerConfig.ThrottledAppsEntry + (*SrvKeyspace_KeyspacePartition)(nil), // 26: topodata.SrvKeyspace.KeyspacePartition + (*vttime.Time)(nil), // 27: vttime.Time } var file_topodata_proto_depIdxs = []int32{ 4, // 0: topodata.Tablet.alias:type_name -> topodata.TabletAlias @@ -2235,42 +2067,38 @@ var file_topodata_proto_depIdxs = []int32{ 3, // 2: topodata.Tablet.key_range:type_name -> topodata.KeyRange 1, // 3: topodata.Tablet.type:type_name -> topodata.TabletType 21, // 4: topodata.Tablet.tags:type_name -> topodata.Tablet.TagsEntry - 29, // 5: topodata.Tablet.primary_term_start_time:type_name -> vttime.Time + 27, // 5: topodata.Tablet.primary_term_start_time:type_name -> vttime.Time 4, // 6: topodata.Shard.primary_alias:type_name -> topodata.TabletAlias - 29, // 7: topodata.Shard.primary_term_start_time:type_name -> vttime.Time + 27, // 7: topodata.Shard.primary_term_start_time:type_name -> vttime.Time 3, // 8: topodata.Shard.key_range:type_name -> topodata.KeyRange 22, // 9: topodata.Shard.source_shards:type_name -> topodata.Shard.SourceShard 23, // 10: topodata.Shard.tablet_controls:type_name -> topodata.Shard.TabletControl - 24, // 11: topodata.Keyspace.served_froms:type_name -> topodata.Keyspace.ServedFrom - 0, // 12: topodata.Keyspace.keyspace_type:type_name -> topodata.KeyspaceType - 29, // 13: topodata.Keyspace.snapshot_time:type_name -> vttime.Time - 13, // 14: topodata.Keyspace.throttler_config:type_name -> topodata.ThrottlerConfig - 25, // 15: topodata.ShardReplication.nodes:type_name -> topodata.ShardReplication.Node - 2, // 16: topodata.ShardReplicationError.type:type_name -> topodata.ShardReplicationError.Type - 4, // 17: topodata.ShardReplicationError.tablet_alias:type_name -> topodata.TabletAlias - 3, // 18: topodata.ShardReference.key_range:type_name -> topodata.KeyRange - 3, // 19: topodata.ShardTabletControl.key_range:type_name -> topodata.KeyRange - 29, // 20: topodata.ThrottledAppRule.expires_at:type_name -> vttime.Time - 26, // 21: topodata.ThrottlerConfig.throttled_apps:type_name -> topodata.ThrottlerConfig.ThrottledAppsEntry - 27, // 22: topodata.SrvKeyspace.partitions:type_name -> topodata.SrvKeyspace.KeyspacePartition - 28, // 23: topodata.SrvKeyspace.served_from:type_name -> topodata.SrvKeyspace.ServedFrom - 13, // 24: topodata.SrvKeyspace.throttler_config:type_name -> topodata.ThrottlerConfig - 17, // 25: topodata.ExternalVitessCluster.topo_config:type_name -> topodata.TopoConfig - 18, // 26: topodata.ExternalClusters.vitess_cluster:type_name -> topodata.ExternalVitessCluster - 3, // 27: topodata.Shard.SourceShard.key_range:type_name -> topodata.KeyRange - 1, // 28: topodata.Shard.TabletControl.tablet_type:type_name -> topodata.TabletType - 1, // 29: topodata.Keyspace.ServedFrom.tablet_type:type_name -> topodata.TabletType - 4, // 30: topodata.ShardReplication.Node.tablet_alias:type_name -> topodata.TabletAlias - 12, // 31: topodata.ThrottlerConfig.ThrottledAppsEntry.value:type_name -> topodata.ThrottledAppRule - 1, // 32: topodata.SrvKeyspace.KeyspacePartition.served_type:type_name -> topodata.TabletType - 10, // 33: topodata.SrvKeyspace.KeyspacePartition.shard_references:type_name -> topodata.ShardReference - 11, // 34: topodata.SrvKeyspace.KeyspacePartition.shard_tablet_controls:type_name -> topodata.ShardTabletControl - 1, // 35: topodata.SrvKeyspace.ServedFrom.tablet_type:type_name -> topodata.TabletType - 36, // [36:36] is the sub-list for method output_type - 36, // [36:36] is the sub-list for method input_type - 36, // [36:36] is the sub-list for extension type_name - 36, // [36:36] is the sub-list for extension extendee - 0, // [0:36] is the sub-list for field type_name + 0, // 11: topodata.Keyspace.keyspace_type:type_name -> topodata.KeyspaceType + 27, // 12: topodata.Keyspace.snapshot_time:type_name -> vttime.Time + 13, // 13: topodata.Keyspace.throttler_config:type_name -> topodata.ThrottlerConfig + 24, // 14: topodata.ShardReplication.nodes:type_name -> topodata.ShardReplication.Node + 2, // 15: topodata.ShardReplicationError.type:type_name -> topodata.ShardReplicationError.Type + 4, // 16: topodata.ShardReplicationError.tablet_alias:type_name -> topodata.TabletAlias + 3, // 17: topodata.ShardReference.key_range:type_name -> topodata.KeyRange + 3, // 18: topodata.ShardTabletControl.key_range:type_name -> topodata.KeyRange + 27, // 19: topodata.ThrottledAppRule.expires_at:type_name -> vttime.Time + 25, // 20: topodata.ThrottlerConfig.throttled_apps:type_name -> topodata.ThrottlerConfig.ThrottledAppsEntry + 26, // 21: topodata.SrvKeyspace.partitions:type_name -> topodata.SrvKeyspace.KeyspacePartition + 13, // 22: topodata.SrvKeyspace.throttler_config:type_name -> topodata.ThrottlerConfig + 17, // 23: topodata.ExternalVitessCluster.topo_config:type_name -> topodata.TopoConfig + 18, // 24: topodata.ExternalClusters.vitess_cluster:type_name -> topodata.ExternalVitessCluster + 3, // 25: topodata.Shard.SourceShard.key_range:type_name -> topodata.KeyRange + 1, // 26: topodata.Shard.TabletControl.tablet_type:type_name -> topodata.TabletType + 4, // 27: topodata.ShardReplication.Node.tablet_alias:type_name -> topodata.TabletAlias + 12, // 28: topodata.ThrottlerConfig.ThrottledAppsEntry.value:type_name -> topodata.ThrottledAppRule + 1, // 29: topodata.SrvKeyspace.KeyspacePartition.served_type:type_name -> topodata.TabletType + 10, // 30: topodata.SrvKeyspace.KeyspacePartition.shard_references:type_name -> topodata.ShardReference + 11, // 31: topodata.SrvKeyspace.KeyspacePartition.shard_tablet_controls:type_name -> topodata.ShardTabletControl + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_topodata_proto_init() } @@ -2508,18 +2336,6 @@ func file_topodata_proto_init() { } } file_topodata_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Keyspace_ServedFrom); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_topodata_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShardReplication_Node); i { case 0: return &v.state @@ -2531,7 +2347,7 @@ func file_topodata_proto_init() { return nil } } - file_topodata_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_topodata_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SrvKeyspace_KeyspacePartition); i { case 0: return &v.state @@ -2543,18 +2359,6 @@ func file_topodata_proto_init() { return nil } } - file_topodata_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SrvKeyspace_ServedFrom); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -2562,7 +2366,7 @@ func file_topodata_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_topodata_proto_rawDesc, NumEnums: 3, - NumMessages: 26, + NumMessages: 24, NumExtensions: 0, NumServices: 0, }, diff --git a/go/vt/proto/topodata/topodata_vtproto.pb.go b/go/vt/proto/topodata/topodata_vtproto.pb.go index 5e675bb4ea0..78971f9db9a 100644 --- a/go/vt/proto/topodata/topodata_vtproto.pb.go +++ b/go/vt/proto/topodata/topodata_vtproto.pb.go @@ -199,30 +199,6 @@ func (m *Shard) CloneMessageVT() proto.Message { return m.CloneVT() } -func (m *Keyspace_ServedFrom) CloneVT() *Keyspace_ServedFrom { - if m == nil { - return (*Keyspace_ServedFrom)(nil) - } - r := &Keyspace_ServedFrom{ - TabletType: m.TabletType, - Keyspace: m.Keyspace, - } - if rhs := m.Cells; rhs != nil { - tmpContainer := make([]string, len(rhs)) - copy(tmpContainer, rhs) - r.Cells = tmpContainer - } - if len(m.unknownFields) > 0 { - r.unknownFields = make([]byte, len(m.unknownFields)) - copy(r.unknownFields, m.unknownFields) - } - return r -} - -func (m *Keyspace_ServedFrom) CloneMessageVT() proto.Message { - return m.CloneVT() -} - func (m *Keyspace) CloneVT() *Keyspace { if m == nil { return (*Keyspace)(nil) @@ -235,13 +211,6 @@ func (m *Keyspace) CloneVT() *Keyspace { ThrottlerConfig: m.ThrottlerConfig.CloneVT(), SidecarDbName: m.SidecarDbName, } - if rhs := m.ServedFroms; rhs != nil { - tmpContainer := make([]*Keyspace_ServedFrom, len(rhs)) - for k, v := range rhs { - tmpContainer[k] = v.CloneVT() - } - r.ServedFroms = tmpContainer - } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -433,25 +402,6 @@ func (m *SrvKeyspace_KeyspacePartition) CloneMessageVT() proto.Message { return m.CloneVT() } -func (m *SrvKeyspace_ServedFrom) CloneVT() *SrvKeyspace_ServedFrom { - if m == nil { - return (*SrvKeyspace_ServedFrom)(nil) - } - r := &SrvKeyspace_ServedFrom{ - TabletType: m.TabletType, - Keyspace: m.Keyspace, - } - if len(m.unknownFields) > 0 { - r.unknownFields = make([]byte, len(m.unknownFields)) - copy(r.unknownFields, m.unknownFields) - } - return r -} - -func (m *SrvKeyspace_ServedFrom) CloneMessageVT() proto.Message { - return m.CloneVT() -} - func (m *SrvKeyspace) CloneVT() *SrvKeyspace { if m == nil { return (*SrvKeyspace)(nil) @@ -466,13 +416,6 @@ func (m *SrvKeyspace) CloneVT() *SrvKeyspace { } r.Partitions = tmpContainer } - if rhs := m.ServedFrom; rhs != nil { - tmpContainer := make([]*SrvKeyspace_ServedFrom, len(rhs)) - for k, v := range rhs { - tmpContainer[k] = v.CloneVT() - } - r.ServedFrom = tmpContainer - } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -1062,60 +1005,6 @@ func (m *Shard) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *Keyspace_ServedFrom) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Keyspace_ServedFrom) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *Keyspace_ServedFrom) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if len(m.Keyspace) > 0 { - i -= len(m.Keyspace) - copy(dAtA[i:], m.Keyspace) - i = encodeVarint(dAtA, i, uint64(len(m.Keyspace))) - i-- - dAtA[i] = 0x1a - } - if len(m.Cells) > 0 { - for iNdEx := len(m.Cells) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Cells[iNdEx]) - copy(dAtA[i:], m.Cells[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.Cells[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - if m.TabletType != 0 { - i = encodeVarint(dAtA, i, uint64(m.TabletType)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func (m *Keyspace) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -1192,18 +1081,6 @@ func (m *Keyspace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - if len(m.ServedFroms) > 0 { - for iNdEx := len(m.ServedFroms) - 1; iNdEx >= 0; iNdEx-- { - size, err := m.ServedFroms[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x22 - } - } return len(dAtA) - i, nil } @@ -1669,51 +1546,6 @@ func (m *SrvKeyspace_KeyspacePartition) MarshalToSizedBufferVT(dAtA []byte) (int return len(dAtA) - i, nil } -func (m *SrvKeyspace_ServedFrom) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SrvKeyspace_ServedFrom) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *SrvKeyspace_ServedFrom) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if len(m.Keyspace) > 0 { - i -= len(m.Keyspace) - copy(dAtA[i:], m.Keyspace) - i = encodeVarint(dAtA, i, uint64(len(m.Keyspace))) - i-- - dAtA[i] = 0x12 - } - if m.TabletType != 0 { - i = encodeVarint(dAtA, i, uint64(m.TabletType)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func (m *SrvKeyspace) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -1754,18 +1586,6 @@ func (m *SrvKeyspace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - if len(m.ServedFrom) > 0 { - for iNdEx := len(m.ServedFrom) - 1; iNdEx >= 0; iNdEx-- { - size, err := m.ServedFrom[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x22 - } - } if len(m.Partitions) > 0 { for iNdEx := len(m.Partitions) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Partitions[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) @@ -2221,41 +2041,12 @@ func (m *Shard) SizeVT() (n int) { return n } -func (m *Keyspace_ServedFrom) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.TabletType != 0 { - n += 1 + sov(uint64(m.TabletType)) - } - if len(m.Cells) > 0 { - for _, s := range m.Cells { - l = len(s) - n += 1 + l + sov(uint64(l)) - } - } - l = len(m.Keyspace) - if l > 0 { - n += 1 + l + sov(uint64(l)) - } - n += len(m.unknownFields) - return n -} - func (m *Keyspace) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l - if len(m.ServedFroms) > 0 { - for _, e := range m.ServedFroms { - l = e.SizeVT() - n += 1 + l + sov(uint64(l)) - } - } if m.KeyspaceType != 0 { n += 1 + sov(uint64(m.KeyspaceType)) } @@ -2454,23 +2245,6 @@ func (m *SrvKeyspace_KeyspacePartition) SizeVT() (n int) { return n } -func (m *SrvKeyspace_ServedFrom) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.TabletType != 0 { - n += 1 + sov(uint64(m.TabletType)) - } - l = len(m.Keyspace) - if l > 0 { - n += 1 + l + sov(uint64(l)) - } - n += len(m.unknownFields) - return n -} - func (m *SrvKeyspace) SizeVT() (n int) { if m == nil { return 0 @@ -2483,12 +2257,6 @@ func (m *SrvKeyspace) SizeVT() (n int) { n += 1 + l + sov(uint64(l)) } } - if len(m.ServedFrom) > 0 { - for _, e := range m.ServedFrom { - l = e.SizeVT() - n += 1 + l + sov(uint64(l)) - } - } if m.ThrottlerConfig != nil { l = m.ThrottlerConfig.SizeVT() n += 1 + l + sov(uint64(l)) @@ -4029,140 +3797,6 @@ func (m *Shard) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *Keyspace_ServedFrom) UnmarshalVT(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 ErrIntOverflow - } - 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: Keyspace_ServedFrom: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Keyspace_ServedFrom: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TabletType", wireType) - } - m.TabletType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TabletType |= TabletType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Keyspace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *Keyspace) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -4192,40 +3826,6 @@ func (m *Keyspace) UnmarshalVT(dAtA []byte) error { return fmt.Errorf("proto: Keyspace: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServedFroms", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServedFroms = append(m.ServedFroms, &Keyspace_ServedFrom{}) - if err := m.ServedFroms[len(m.ServedFroms)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field KeyspaceType", wireType) @@ -5522,108 +5122,6 @@ func (m *SrvKeyspace_KeyspacePartition) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *SrvKeyspace_ServedFrom) UnmarshalVT(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 ErrIntOverflow - } - 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: SrvKeyspace_ServedFrom: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SrvKeyspace_ServedFrom: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TabletType", wireType) - } - m.TabletType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TabletType |= TabletType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Keyspace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *SrvKeyspace) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -5687,40 +5185,6 @@ func (m *SrvKeyspace) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServedFrom", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServedFrom = append(m.ServedFrom, &SrvKeyspace_ServedFrom{}) - if err := m.ServedFrom[len(m.ServedFrom)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ThrottlerConfig", wireType) diff --git a/go/vt/proto/vschema/vschema.pb.go b/go/vt/proto/vschema/vschema.pb.go index 8726fb35745..ec70e8798d4 100644 --- a/go/vt/proto/vschema/vschema.pb.go +++ b/go/vt/proto/vschema/vschema.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vschema.proto @@ -600,9 +600,16 @@ type Column struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type query.Type `protobuf:"varint,2,opt,name=type,proto3,enum=query.Type" json:"type,omitempty"` - Invisible bool `protobuf:"varint,3,opt,name=invisible,proto3" json:"invisible,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type query.Type `protobuf:"varint,2,opt,name=type,proto3,enum=query.Type" json:"type,omitempty"` + Invisible bool `protobuf:"varint,3,opt,name=invisible,proto3" json:"invisible,omitempty"` + Default string `protobuf:"bytes,4,opt,name=default,proto3" json:"default,omitempty"` + CollationName string `protobuf:"bytes,5,opt,name=collation_name,json=collationName,proto3" json:"collation_name,omitempty"` + Size int32 `protobuf:"varint,6,opt,name=size,proto3" json:"size,omitempty"` + Scale int32 `protobuf:"varint,7,opt,name=scale,proto3" json:"scale,omitempty"` + Nullable *bool `protobuf:"varint,8,opt,name=nullable,proto3,oneof" json:"nullable,omitempty"` + // values contains the list of values for an enum or set column. + Values []string `protobuf:"bytes,9,rep,name=values,proto3" json:"values,omitempty"` } func (x *Column) Reset() { @@ -658,6 +665,48 @@ func (x *Column) GetInvisible() bool { return false } +func (x *Column) GetDefault() string { + if x != nil { + return x.Default + } + return "" +} + +func (x *Column) GetCollationName() string { + if x != nil { + return x.CollationName + } + return "" +} + +func (x *Column) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Column) GetScale() int32 { + if x != nil { + return x.Scale + } + return 0 +} + +func (x *Column) GetNullable() bool { + if x != nil && x.Nullable != nil { + return *x.Nullable + } + return false +} + +func (x *Column) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + // SrvVSchema is the roll-up of all the Keyspace schema for a cell. type SrvVSchema struct { state protoimpl.MessageState @@ -920,46 +969,57 @@ var file_vschema_proto_rawDesc = []byte{ 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, - 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x5b, 0x0a, 0x06, 0x43, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x79, 0x70, 0x65, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x69, 0x73, 0x69, - 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x69, 0x73, - 0x69, 0x62, 0x6c, 0x65, 0x22, 0xa7, 0x02, 0x0a, 0x0a, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x12, 0x40, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, - 0x6c, 0x65, 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x4f, 0x0a, - 0x0e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x44, - 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, - 0x6c, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x05, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x22, 0x6e, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, - 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x74, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x42, 0x26, 0x5a, 0x24, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, - 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x8c, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x69, 0x73, + 0x69, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x69, + 0x73, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, + 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x12, 0x1f, 0x0a, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, + 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xa7, 0x02, 0x0a, 0x0a, 0x53, 0x72, 0x76, 0x56, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x40, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x11, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, + 0x4f, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x44, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, + 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x6e, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x72, + 0x6f, 0x6d, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x42, 0x26, 0x5a, 0x24, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, + 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1158,6 +1218,7 @@ func file_vschema_proto_init() { } } } + file_vschema_proto_msgTypes[7].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/go/vt/proto/vschema/vschema_vtproto.pb.go b/go/vt/proto/vschema/vschema_vtproto.pb.go index 94527ef14ae..ba55bcfa97c 100644 --- a/go/vt/proto/vschema/vschema_vtproto.pb.go +++ b/go/vt/proto/vschema/vschema_vtproto.pb.go @@ -210,9 +210,22 @@ func (m *Column) CloneVT() *Column { return (*Column)(nil) } r := &Column{ - Name: m.Name, - Type: m.Type, - Invisible: m.Invisible, + Name: m.Name, + Type: m.Type, + Invisible: m.Invisible, + Default: m.Default, + CollationName: m.CollationName, + Size: m.Size, + Scale: m.Scale, + } + if rhs := m.Nullable; rhs != nil { + tmpVal := *rhs + r.Nullable = &tmpVal + } + if rhs := m.Values; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Values = tmpContainer } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -787,6 +800,49 @@ func (m *Column) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Values) > 0 { + for iNdEx := len(m.Values) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Values[iNdEx]) + copy(dAtA[i:], m.Values[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Values[iNdEx]))) + i-- + dAtA[i] = 0x4a + } + } + if m.Nullable != nil { + i-- + if *m.Nullable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Scale != 0 { + i = encodeVarint(dAtA, i, uint64(m.Scale)) + i-- + dAtA[i] = 0x38 + } + if m.Size != 0 { + i = encodeVarint(dAtA, i, uint64(m.Size)) + i-- + dAtA[i] = 0x30 + } + if len(m.CollationName) > 0 { + i -= len(m.CollationName) + copy(dAtA[i:], m.CollationName) + i = encodeVarint(dAtA, i, uint64(len(m.CollationName))) + i-- + dAtA[i] = 0x2a + } + if len(m.Default) > 0 { + i -= len(m.Default) + copy(dAtA[i:], m.Default) + i = encodeVarint(dAtA, i, uint64(len(m.Default))) + i-- + dAtA[i] = 0x22 + } if m.Invisible { i-- if m.Invisible { @@ -1203,6 +1259,29 @@ func (m *Column) SizeVT() (n int) { if m.Invisible { n += 2 } + l = len(m.Default) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.CollationName) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Size != 0 { + n += 1 + sov(uint64(m.Size)) + } + if m.Scale != 0 { + n += 1 + sov(uint64(m.Scale)) + } + if m.Nullable != nil { + n += 2 + } + if len(m.Values) > 0 { + for _, s := range m.Values { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -2725,6 +2804,161 @@ func (m *Column) UnmarshalVT(dAtA []byte) error { } } m.Invisible = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Default", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Default = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CollationName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CollationName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) + } + m.Size = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Scale", wireType) + } + m.Scale = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Scale |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nullable", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.Nullable = &b + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Values = append(m.Values, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/vtadmin/vtadmin.pb.go b/go/vt/proto/vtadmin/vtadmin.pb.go index 3e41edd5f7e..3810842bdf7 100644 --- a/go/vt/proto/vtadmin/vtadmin.pb.go +++ b/go/vt/proto/vtadmin/vtadmin.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtadmin.proto @@ -89,7 +89,7 @@ func (x Tablet_ServingState) Number() protoreflect.EnumNumber { // Deprecated: Use Tablet_ServingState.Descriptor instead. func (Tablet_ServingState) EnumDescriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{10, 0} + return file_vtadmin_proto_rawDescGZIP(), []int{11, 0} } // Cluster represents information about a Vitess cluster. @@ -589,6 +589,61 @@ func (x *Schema) GetTableSizes() map[string]*Schema_TableSize { return nil } +type SchemaMigration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + SchemaMigration *vtctldata.SchemaMigration `protobuf:"bytes,2,opt,name=schema_migration,json=schemaMigration,proto3" json:"schema_migration,omitempty"` +} + +func (x *SchemaMigration) Reset() { + *x = SchemaMigration{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SchemaMigration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SchemaMigration) ProtoMessage() {} + +func (x *SchemaMigration) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SchemaMigration.ProtoReflect.Descriptor instead. +func (*SchemaMigration) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{8} +} + +func (x *SchemaMigration) GetCluster() *Cluster { + if x != nil { + return x.Cluster + } + return nil +} + +func (x *SchemaMigration) GetSchemaMigration() *vtctldata.SchemaMigration { + if x != nil { + return x.SchemaMigration + } + return nil +} + // Shard groups the vtctldata information about a shard record together with // the Vitess cluster it belongs to. type Shard struct { @@ -603,7 +658,7 @@ type Shard struct { func (x *Shard) Reset() { *x = Shard{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[8] + mi := &file_vtadmin_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -616,7 +671,7 @@ func (x *Shard) String() string { func (*Shard) ProtoMessage() {} func (x *Shard) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[8] + mi := &file_vtadmin_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -629,7 +684,7 @@ func (x *Shard) ProtoReflect() protoreflect.Message { // Deprecated: Use Shard.ProtoReflect.Descriptor instead. func (*Shard) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{8} + return file_vtadmin_proto_rawDescGZIP(), []int{9} } func (x *Shard) GetCluster() *Cluster { @@ -659,7 +714,7 @@ type SrvVSchema struct { func (x *SrvVSchema) Reset() { *x = SrvVSchema{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[9] + mi := &file_vtadmin_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -672,7 +727,7 @@ func (x *SrvVSchema) String() string { func (*SrvVSchema) ProtoMessage() {} func (x *SrvVSchema) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[9] + mi := &file_vtadmin_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -685,7 +740,7 @@ func (x *SrvVSchema) ProtoReflect() protoreflect.Message { // Deprecated: Use SrvVSchema.ProtoReflect.Descriptor instead. func (*SrvVSchema) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{9} + return file_vtadmin_proto_rawDescGZIP(), []int{10} } func (x *SrvVSchema) GetCell() string { @@ -725,7 +780,7 @@ type Tablet struct { func (x *Tablet) Reset() { *x = Tablet{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[10] + mi := &file_vtadmin_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -738,7 +793,7 @@ func (x *Tablet) String() string { func (*Tablet) ProtoMessage() {} func (x *Tablet) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[10] + mi := &file_vtadmin_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -751,7 +806,7 @@ func (x *Tablet) ProtoReflect() protoreflect.Message { // Deprecated: Use Tablet.ProtoReflect.Descriptor instead. func (*Tablet) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{10} + return file_vtadmin_proto_rawDescGZIP(), []int{11} } func (x *Tablet) GetCluster() *Cluster { @@ -797,7 +852,7 @@ type VSchema struct { func (x *VSchema) Reset() { *x = VSchema{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[11] + mi := &file_vtadmin_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -810,7 +865,7 @@ func (x *VSchema) String() string { func (*VSchema) ProtoMessage() {} func (x *VSchema) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[11] + mi := &file_vtadmin_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -823,7 +878,7 @@ func (x *VSchema) ProtoReflect() protoreflect.Message { // Deprecated: Use VSchema.ProtoReflect.Descriptor instead. func (*VSchema) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{11} + return file_vtadmin_proto_rawDescGZIP(), []int{12} } func (x *VSchema) GetCluster() *Cluster { @@ -861,7 +916,7 @@ type Vtctld struct { func (x *Vtctld) Reset() { *x = Vtctld{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[12] + mi := &file_vtadmin_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -874,7 +929,7 @@ func (x *Vtctld) String() string { func (*Vtctld) ProtoMessage() {} func (x *Vtctld) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[12] + mi := &file_vtadmin_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -887,7 +942,7 @@ func (x *Vtctld) ProtoReflect() protoreflect.Message { // Deprecated: Use Vtctld.ProtoReflect.Descriptor instead. func (*Vtctld) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{12} + return file_vtadmin_proto_rawDescGZIP(), []int{13} } func (x *Vtctld) GetHostname() string { @@ -935,7 +990,7 @@ type VTGate struct { func (x *VTGate) Reset() { *x = VTGate{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[13] + mi := &file_vtadmin_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -948,7 +1003,7 @@ func (x *VTGate) String() string { func (*VTGate) ProtoMessage() {} func (x *VTGate) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[13] + mi := &file_vtadmin_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -961,7 +1016,7 @@ func (x *VTGate) ProtoReflect() protoreflect.Message { // Deprecated: Use VTGate.ProtoReflect.Descriptor instead. func (*VTGate) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{13} + return file_vtadmin_proto_rawDescGZIP(), []int{14} } func (x *VTGate) GetHostname() string { @@ -1019,7 +1074,7 @@ type Workflow struct { func (x *Workflow) Reset() { *x = Workflow{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[14] + mi := &file_vtadmin_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1032,7 +1087,7 @@ func (x *Workflow) String() string { func (*Workflow) ProtoMessage() {} func (x *Workflow) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[14] + mi := &file_vtadmin_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1045,7 +1100,7 @@ func (x *Workflow) ProtoReflect() protoreflect.Message { // Deprecated: Use Workflow.ProtoReflect.Descriptor instead. func (*Workflow) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{14} + return file_vtadmin_proto_rawDescGZIP(), []int{15} } func (x *Workflow) GetCluster() *Cluster { @@ -1069,32 +1124,32 @@ func (x *Workflow) GetWorkflow() *vtctldata.Workflow { return nil } -type CreateKeyspaceRequest struct { +type ApplySchemaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.CreateKeyspaceRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.ApplySchemaRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *CreateKeyspaceRequest) Reset() { - *x = CreateKeyspaceRequest{} +func (x *ApplySchemaRequest) Reset() { + *x = ApplySchemaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[15] + mi := &file_vtadmin_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CreateKeyspaceRequest) String() string { +func (x *ApplySchemaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateKeyspaceRequest) ProtoMessage() {} +func (*ApplySchemaRequest) ProtoMessage() {} -func (x *CreateKeyspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[15] +func (x *ApplySchemaRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1105,83 +1160,36 @@ func (x *CreateKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*CreateKeyspaceRequest) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{15} +// Deprecated: Use ApplySchemaRequest.ProtoReflect.Descriptor instead. +func (*ApplySchemaRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{16} } -func (x *CreateKeyspaceRequest) GetClusterId() string { +func (x *ApplySchemaRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *CreateKeyspaceRequest) GetOptions() *vtctldata.CreateKeyspaceRequest { - if x != nil { - return x.Options - } - return nil -} - -type CreateKeyspaceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` -} - -func (x *CreateKeyspaceResponse) Reset() { - *x = CreateKeyspaceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateKeyspaceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateKeyspaceResponse) ProtoMessage() {} - -func (x *CreateKeyspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateKeyspaceResponse.ProtoReflect.Descriptor instead. -func (*CreateKeyspaceResponse) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{16} -} - -func (x *CreateKeyspaceResponse) GetKeyspace() *Keyspace { +func (x *ApplySchemaRequest) GetRequest() *vtctldata.ApplySchemaRequest { if x != nil { - return x.Keyspace + return x.Request } return nil } -type CreateShardRequest struct { +type CancelSchemaMigrationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.CreateShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.CancelSchemaMigrationRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *CreateShardRequest) Reset() { - *x = CreateShardRequest{} +func (x *CancelSchemaMigrationRequest) Reset() { + *x = CancelSchemaMigrationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1189,13 +1197,13 @@ func (x *CreateShardRequest) Reset() { } } -func (x *CreateShardRequest) String() string { +func (x *CancelSchemaMigrationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateShardRequest) ProtoMessage() {} +func (*CancelSchemaMigrationRequest) ProtoMessage() {} -func (x *CreateShardRequest) ProtoReflect() protoreflect.Message { +func (x *CancelSchemaMigrationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1207,36 +1215,36 @@ func (x *CreateShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateShardRequest.ProtoReflect.Descriptor instead. -func (*CreateShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CancelSchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*CancelSchemaMigrationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{17} } -func (x *CreateShardRequest) GetClusterId() string { +func (x *CancelSchemaMigrationRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *CreateShardRequest) GetOptions() *vtctldata.CreateShardRequest { +func (x *CancelSchemaMigrationRequest) GetRequest() *vtctldata.CancelSchemaMigrationRequest { if x != nil { - return x.Options + return x.Request } return nil } -type DeleteKeyspaceRequest struct { +type CleanupSchemaMigrationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.DeleteKeyspaceRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.CleanupSchemaMigrationRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *DeleteKeyspaceRequest) Reset() { - *x = DeleteKeyspaceRequest{} +func (x *CleanupSchemaMigrationRequest) Reset() { + *x = CleanupSchemaMigrationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1244,13 +1252,13 @@ func (x *DeleteKeyspaceRequest) Reset() { } } -func (x *DeleteKeyspaceRequest) String() string { +func (x *CleanupSchemaMigrationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteKeyspaceRequest) ProtoMessage() {} +func (*CleanupSchemaMigrationRequest) ProtoMessage() {} -func (x *DeleteKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *CleanupSchemaMigrationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1262,36 +1270,36 @@ func (x *DeleteKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*DeleteKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CleanupSchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*CleanupSchemaMigrationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{18} } -func (x *DeleteKeyspaceRequest) GetClusterId() string { +func (x *CleanupSchemaMigrationRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *DeleteKeyspaceRequest) GetOptions() *vtctldata.DeleteKeyspaceRequest { +func (x *CleanupSchemaMigrationRequest) GetRequest() *vtctldata.CleanupSchemaMigrationRequest { if x != nil { - return x.Options + return x.Request } return nil } -type DeleteShardsRequest struct { +type CompleteSchemaMigrationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.DeleteShardsRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.CompleteSchemaMigrationRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *DeleteShardsRequest) Reset() { - *x = DeleteShardsRequest{} +func (x *CompleteSchemaMigrationRequest) Reset() { + *x = CompleteSchemaMigrationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1299,13 +1307,13 @@ func (x *DeleteShardsRequest) Reset() { } } -func (x *DeleteShardsRequest) String() string { +func (x *CompleteSchemaMigrationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteShardsRequest) ProtoMessage() {} +func (*CompleteSchemaMigrationRequest) ProtoMessage() {} -func (x *DeleteShardsRequest) ProtoReflect() protoreflect.Message { +func (x *CompleteSchemaMigrationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1317,37 +1325,36 @@ func (x *DeleteShardsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteShardsRequest.ProtoReflect.Descriptor instead. -func (*DeleteShardsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CompleteSchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*CompleteSchemaMigrationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{19} } -func (x *DeleteShardsRequest) GetClusterId() string { +func (x *CompleteSchemaMigrationRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *DeleteShardsRequest) GetOptions() *vtctldata.DeleteShardsRequest { +func (x *CompleteSchemaMigrationRequest) GetRequest() *vtctldata.CompleteSchemaMigrationRequest { if x != nil { - return x.Options + return x.Request } return nil } -type DeleteTabletRequest struct { +type CreateKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - AllowPrimary bool `protobuf:"varint,3,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.CreateKeyspaceRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *DeleteTabletRequest) Reset() { - *x = DeleteTabletRequest{} +func (x *CreateKeyspaceRequest) Reset() { + *x = CreateKeyspaceRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1355,13 +1362,13 @@ func (x *DeleteTabletRequest) Reset() { } } -func (x *DeleteTabletRequest) String() string { +func (x *CreateKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteTabletRequest) ProtoMessage() {} +func (*CreateKeyspaceRequest) ProtoMessage() {} -func (x *DeleteTabletRequest) ProtoReflect() protoreflect.Message { +func (x *CreateKeyspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1373,43 +1380,35 @@ func (x *DeleteTabletRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteTabletRequest.ProtoReflect.Descriptor instead. -func (*DeleteTabletRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*CreateKeyspaceRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{20} } -func (x *DeleteTabletRequest) GetAlias() *topodata.TabletAlias { +func (x *CreateKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *DeleteTabletRequest) GetClusterIds() []string { +func (x *CreateKeyspaceRequest) GetOptions() *vtctldata.CreateKeyspaceRequest { if x != nil { - return x.ClusterIds + return x.Options } return nil } -func (x *DeleteTabletRequest) GetAllowPrimary() bool { - if x != nil { - return x.AllowPrimary - } - return false -} - -type DeleteTabletResponse struct { +type CreateKeyspaceResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *DeleteTabletResponse) Reset() { - *x = DeleteTabletResponse{} +func (x *CreateKeyspaceResponse) Reset() { + *x = CreateKeyspaceResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1417,13 +1416,13 @@ func (x *DeleteTabletResponse) Reset() { } } -func (x *DeleteTabletResponse) String() string { +func (x *CreateKeyspaceResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteTabletResponse) ProtoMessage() {} +func (*CreateKeyspaceResponse) ProtoMessage() {} -func (x *DeleteTabletResponse) ProtoReflect() protoreflect.Message { +func (x *CreateKeyspaceResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1435,36 +1434,29 @@ func (x *DeleteTabletResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteTabletResponse.ProtoReflect.Descriptor instead. -func (*DeleteTabletResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateKeyspaceResponse.ProtoReflect.Descriptor instead. +func (*CreateKeyspaceResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{21} } -func (x *DeleteTabletResponse) GetStatus() string { +func (x *CreateKeyspaceResponse) GetKeyspace() *Keyspace { if x != nil { - return x.Status + return x.Keyspace } - return "" + return nil } -func (x *DeleteTabletResponse) GetCluster() *Cluster { - if x != nil { - return x.Cluster - } - return nil -} - -type EmergencyFailoverShardRequest struct { +type CreateShardRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.EmergencyReparentShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.CreateShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *EmergencyFailoverShardRequest) Reset() { - *x = EmergencyFailoverShardRequest{} +func (x *CreateShardRequest) Reset() { + *x = CreateShardRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1472,13 +1464,13 @@ func (x *EmergencyFailoverShardRequest) Reset() { } } -func (x *EmergencyFailoverShardRequest) String() string { +func (x *CreateShardRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*EmergencyFailoverShardRequest) ProtoMessage() {} +func (*CreateShardRequest) ProtoMessage() {} -func (x *EmergencyFailoverShardRequest) ProtoReflect() protoreflect.Message { +func (x *CreateShardRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1490,43 +1482,36 @@ func (x *EmergencyFailoverShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use EmergencyFailoverShardRequest.ProtoReflect.Descriptor instead. -func (*EmergencyFailoverShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateShardRequest.ProtoReflect.Descriptor instead. +func (*CreateShardRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{22} } -func (x *EmergencyFailoverShardRequest) GetClusterId() string { +func (x *CreateShardRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *EmergencyFailoverShardRequest) GetOptions() *vtctldata.EmergencyReparentShardRequest { +func (x *CreateShardRequest) GetOptions() *vtctldata.CreateShardRequest { if x != nil { return x.Options } return nil } -type EmergencyFailoverShardResponse struct { +type DeleteKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - // PromotedPrimary is the tablet alias that was promoted to shard primary. - // If NewPrimary was set in the request options, then this will be the - // same tablet alias. Otherwise, it will be the alias of the tablet found - // to be most up-to-date in the shard. - PromotedPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=promoted_primary,json=promotedPrimary,proto3" json:"promoted_primary,omitempty"` - Events []*logutil.Event `protobuf:"bytes,5,rep,name=events,proto3" json:"events,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.DeleteKeyspaceRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *EmergencyFailoverShardResponse) Reset() { - *x = EmergencyFailoverShardResponse{} +func (x *DeleteKeyspaceRequest) Reset() { + *x = DeleteKeyspaceRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1534,13 +1519,13 @@ func (x *EmergencyFailoverShardResponse) Reset() { } } -func (x *EmergencyFailoverShardResponse) String() string { +func (x *DeleteKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*EmergencyFailoverShardResponse) ProtoMessage() {} +func (*DeleteKeyspaceRequest) ProtoMessage() {} -func (x *EmergencyFailoverShardResponse) ProtoReflect() protoreflect.Message { +func (x *DeleteKeyspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1552,58 +1537,36 @@ func (x *EmergencyFailoverShardResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use EmergencyFailoverShardResponse.ProtoReflect.Descriptor instead. -func (*EmergencyFailoverShardResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*DeleteKeyspaceRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{23} } -func (x *EmergencyFailoverShardResponse) GetCluster() *Cluster { - if x != nil { - return x.Cluster - } - return nil -} - -func (x *EmergencyFailoverShardResponse) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *EmergencyFailoverShardResponse) GetShard() string { +func (x *DeleteKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Shard + return x.ClusterId } return "" } -func (x *EmergencyFailoverShardResponse) GetPromotedPrimary() *topodata.TabletAlias { - if x != nil { - return x.PromotedPrimary - } - return nil -} - -func (x *EmergencyFailoverShardResponse) GetEvents() []*logutil.Event { +func (x *DeleteKeyspaceRequest) GetOptions() *vtctldata.DeleteKeyspaceRequest { if x != nil { - return x.Events + return x.Options } return nil } -type FindSchemaRequest struct { +type DeleteShardsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,3,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.DeleteShardsRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *FindSchemaRequest) Reset() { - *x = FindSchemaRequest{} +func (x *DeleteShardsRequest) Reset() { + *x = DeleteShardsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1611,13 +1574,13 @@ func (x *FindSchemaRequest) Reset() { } } -func (x *FindSchemaRequest) String() string { +func (x *DeleteShardsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*FindSchemaRequest) ProtoMessage() {} +func (*DeleteShardsRequest) ProtoMessage() {} -func (x *FindSchemaRequest) ProtoReflect() protoreflect.Message { +func (x *DeleteShardsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1629,56 +1592,37 @@ func (x *FindSchemaRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use FindSchemaRequest.ProtoReflect.Descriptor instead. -func (*FindSchemaRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteShardsRequest.ProtoReflect.Descriptor instead. +func (*DeleteShardsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{24} } -func (x *FindSchemaRequest) GetTable() string { +func (x *DeleteShardsRequest) GetClusterId() string { if x != nil { - return x.Table + return x.ClusterId } return "" } -func (x *FindSchemaRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *FindSchemaRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { +func (x *DeleteShardsRequest) GetOptions() *vtctldata.DeleteShardsRequest { if x != nil { - return x.TableSizeOptions + return x.Options } return nil } -type GetBackupsRequest struct { +type DeleteTabletRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // Keyspaces, if set, limits backups to just the specified keyspaces. - // Applies to all clusters in the request. - Keyspaces []string `protobuf:"bytes,2,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` - // KeyspaceShards, if set, limits backups to just the specified - // keyspace/shards. Applies to all clusters in the request. - // - // This field takes precedence over Keyspaces. If KeyspaceShards is set, - // Keyspaces is ignored. - KeyspaceShards []string `protobuf:"bytes,3,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` - // RequestOptions controls the per-shard request options when making - // GetBackups requests to vtctlds. Note that the Keyspace and Shard fields - // of this field are ignored; it is used only to specify Limit and Detailed - // fields. - RequestOptions *vtctldata.GetBackupsRequest `protobuf:"bytes,4,opt,name=request_options,json=requestOptions,proto3" json:"request_options,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + AllowPrimary bool `protobuf:"varint,3,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` } -func (x *GetBackupsRequest) Reset() { - *x = GetBackupsRequest{} +func (x *DeleteTabletRequest) Reset() { + *x = DeleteTabletRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1686,13 +1630,13 @@ func (x *GetBackupsRequest) Reset() { } } -func (x *GetBackupsRequest) String() string { +func (x *DeleteTabletRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetBackupsRequest) ProtoMessage() {} +func (*DeleteTabletRequest) ProtoMessage() {} -func (x *GetBackupsRequest) ProtoReflect() protoreflect.Message { +func (x *DeleteTabletRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1704,49 +1648,43 @@ func (x *GetBackupsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetBackupsRequest.ProtoReflect.Descriptor instead. -func (*GetBackupsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteTabletRequest.ProtoReflect.Descriptor instead. +func (*DeleteTabletRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{25} } -func (x *GetBackupsRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GetBackupsRequest) GetKeyspaces() []string { +func (x *DeleteTabletRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.Keyspaces + return x.Alias } return nil } -func (x *GetBackupsRequest) GetKeyspaceShards() []string { +func (x *DeleteTabletRequest) GetClusterIds() []string { if x != nil { - return x.KeyspaceShards + return x.ClusterIds } return nil } -func (x *GetBackupsRequest) GetRequestOptions() *vtctldata.GetBackupsRequest { +func (x *DeleteTabletRequest) GetAllowPrimary() bool { if x != nil { - return x.RequestOptions + return x.AllowPrimary } - return nil + return false } -type GetBackupsResponse struct { +type DeleteTabletResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Backups []*ClusterBackup `protobuf:"bytes,1,rep,name=backups,proto3" json:"backups,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *GetBackupsResponse) Reset() { - *x = GetBackupsResponse{} +func (x *DeleteTabletResponse) Reset() { + *x = DeleteTabletResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1754,13 +1692,13 @@ func (x *GetBackupsResponse) Reset() { } } -func (x *GetBackupsResponse) String() string { +func (x *DeleteTabletResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetBackupsResponse) ProtoMessage() {} +func (*DeleteTabletResponse) ProtoMessage() {} -func (x *GetBackupsResponse) ProtoReflect() protoreflect.Message { +func (x *DeleteTabletResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1772,38 +1710,36 @@ func (x *GetBackupsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetBackupsResponse.ProtoReflect.Descriptor instead. -func (*GetBackupsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use DeleteTabletResponse.ProtoReflect.Descriptor instead. +func (*DeleteTabletResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{26} } -func (x *GetBackupsResponse) GetBackups() []*ClusterBackup { +func (x *DeleteTabletResponse) GetStatus() string { if x != nil { - return x.Backups + return x.Status + } + return "" +} + +func (x *DeleteTabletResponse) GetCluster() *Cluster { + if x != nil { + return x.Cluster } return nil } -type GetCellInfosRequest struct { +type EmergencyFailoverShardRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // Cells, if specified, limits the response to include only CellInfo objects - // with those names. If omitted, all CellInfo objects in each cluster are - // returned. - // - // Mutually-exclusive with NamesOnly. If both are set, this field takes - // precedence. - Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` - // Return only the cell names in each cluster; the actual CellInfo objects - // will be empty. - NamesOnly bool `protobuf:"varint,3,opt,name=names_only,json=namesOnly,proto3" json:"names_only,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.EmergencyReparentShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *GetCellInfosRequest) Reset() { - *x = GetCellInfosRequest{} +func (x *EmergencyFailoverShardRequest) Reset() { + *x = EmergencyFailoverShardRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1811,13 +1747,13 @@ func (x *GetCellInfosRequest) Reset() { } } -func (x *GetCellInfosRequest) String() string { +func (x *EmergencyFailoverShardRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetCellInfosRequest) ProtoMessage() {} +func (*EmergencyFailoverShardRequest) ProtoMessage() {} -func (x *GetCellInfosRequest) ProtoReflect() protoreflect.Message { +func (x *EmergencyFailoverShardRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1829,42 +1765,43 @@ func (x *GetCellInfosRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetCellInfosRequest.ProtoReflect.Descriptor instead. -func (*GetCellInfosRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use EmergencyFailoverShardRequest.ProtoReflect.Descriptor instead. +func (*EmergencyFailoverShardRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{27} } -func (x *GetCellInfosRequest) GetClusterIds() []string { +func (x *EmergencyFailoverShardRequest) GetClusterId() string { if x != nil { - return x.ClusterIds + return x.ClusterId } - return nil + return "" } -func (x *GetCellInfosRequest) GetCells() []string { +func (x *EmergencyFailoverShardRequest) GetOptions() *vtctldata.EmergencyReparentShardRequest { if x != nil { - return x.Cells + return x.Options } return nil } -func (x *GetCellInfosRequest) GetNamesOnly() bool { - if x != nil { - return x.NamesOnly - } - return false -} - -type GetCellInfosResponse struct { +type EmergencyFailoverShardResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CellInfos []*ClusterCellInfo `protobuf:"bytes,1,rep,name=cell_infos,json=cellInfos,proto3" json:"cell_infos,omitempty"` -} + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + // PromotedPrimary is the tablet alias that was promoted to shard primary. + // If NewPrimary was set in the request options, then this will be the + // same tablet alias. Otherwise, it will be the alias of the tablet found + // to be most up-to-date in the shard. + PromotedPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=promoted_primary,json=promotedPrimary,proto3" json:"promoted_primary,omitempty"` + Events []*logutil.Event `protobuf:"bytes,5,rep,name=events,proto3" json:"events,omitempty"` +} -func (x *GetCellInfosResponse) Reset() { - *x = GetCellInfosResponse{} +func (x *EmergencyFailoverShardResponse) Reset() { + *x = EmergencyFailoverShardResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1872,13 +1809,13 @@ func (x *GetCellInfosResponse) Reset() { } } -func (x *GetCellInfosResponse) String() string { +func (x *EmergencyFailoverShardResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetCellInfosResponse) ProtoMessage() {} +func (*EmergencyFailoverShardResponse) ProtoMessage() {} -func (x *GetCellInfosResponse) ProtoReflect() protoreflect.Message { +func (x *EmergencyFailoverShardResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1890,28 +1827,58 @@ func (x *GetCellInfosResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetCellInfosResponse.ProtoReflect.Descriptor instead. -func (*GetCellInfosResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use EmergencyFailoverShardResponse.ProtoReflect.Descriptor instead. +func (*EmergencyFailoverShardResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{28} } -func (x *GetCellInfosResponse) GetCellInfos() []*ClusterCellInfo { +func (x *EmergencyFailoverShardResponse) GetCluster() *Cluster { if x != nil { - return x.CellInfos + return x.Cluster } return nil } -type GetCellsAliasesRequest struct { +func (x *EmergencyFailoverShardResponse) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *EmergencyFailoverShardResponse) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +func (x *EmergencyFailoverShardResponse) GetPromotedPrimary() *topodata.TabletAlias { + if x != nil { + return x.PromotedPrimary + } + return nil +} + +func (x *EmergencyFailoverShardResponse) GetEvents() []*logutil.Event { + if x != nil { + return x.Events + } + return nil +} + +type FindSchemaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,3,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` } -func (x *GetCellsAliasesRequest) Reset() { - *x = GetCellsAliasesRequest{} +func (x *FindSchemaRequest) Reset() { + *x = FindSchemaRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1919,13 +1886,13 @@ func (x *GetCellsAliasesRequest) Reset() { } } -func (x *GetCellsAliasesRequest) String() string { +func (x *FindSchemaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetCellsAliasesRequest) ProtoMessage() {} +func (*FindSchemaRequest) ProtoMessage() {} -func (x *GetCellsAliasesRequest) ProtoReflect() protoreflect.Message { +func (x *FindSchemaRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1937,28 +1904,56 @@ func (x *GetCellsAliasesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetCellsAliasesRequest.ProtoReflect.Descriptor instead. -func (*GetCellsAliasesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use FindSchemaRequest.ProtoReflect.Descriptor instead. +func (*FindSchemaRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{29} } -func (x *GetCellsAliasesRequest) GetClusterIds() []string { +func (x *FindSchemaRequest) GetTable() string { + if x != nil { + return x.Table + } + return "" +} + +func (x *FindSchemaRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type GetCellsAliasesResponse struct { +func (x *FindSchemaRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { + if x != nil { + return x.TableSizeOptions + } + return nil +} + +type GetBackupsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Aliases []*ClusterCellsAliases `protobuf:"bytes,1,rep,name=aliases,proto3" json:"aliases,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Keyspaces, if set, limits backups to just the specified keyspaces. + // Applies to all clusters in the request. + Keyspaces []string `protobuf:"bytes,2,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + // KeyspaceShards, if set, limits backups to just the specified + // keyspace/shards. Applies to all clusters in the request. + // + // This field takes precedence over Keyspaces. If KeyspaceShards is set, + // Keyspaces is ignored. + KeyspaceShards []string `protobuf:"bytes,3,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` + // RequestOptions controls the per-shard request options when making + // GetBackups requests to vtctlds. Note that the Keyspace and Shard fields + // of this field are ignored; it is used only to specify Limit and Detailed + // fields. + RequestOptions *vtctldata.GetBackupsRequest `protobuf:"bytes,4,opt,name=request_options,json=requestOptions,proto3" json:"request_options,omitempty"` } -func (x *GetCellsAliasesResponse) Reset() { - *x = GetCellsAliasesResponse{} +func (x *GetBackupsRequest) Reset() { + *x = GetBackupsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1966,13 +1961,13 @@ func (x *GetCellsAliasesResponse) Reset() { } } -func (x *GetCellsAliasesResponse) String() string { +func (x *GetBackupsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetCellsAliasesResponse) ProtoMessage() {} +func (*GetBackupsRequest) ProtoMessage() {} -func (x *GetCellsAliasesResponse) ProtoReflect() protoreflect.Message { +func (x *GetBackupsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1984,26 +1979,49 @@ func (x *GetCellsAliasesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetCellsAliasesResponse.ProtoReflect.Descriptor instead. -func (*GetCellsAliasesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetBackupsRequest.ProtoReflect.Descriptor instead. +func (*GetBackupsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{30} } -func (x *GetCellsAliasesResponse) GetAliases() []*ClusterCellsAliases { +func (x *GetBackupsRequest) GetClusterIds() []string { if x != nil { - return x.Aliases + return x.ClusterIds } return nil } -type GetClustersRequest struct { +func (x *GetBackupsRequest) GetKeyspaces() []string { + if x != nil { + return x.Keyspaces + } + return nil +} + +func (x *GetBackupsRequest) GetKeyspaceShards() []string { + if x != nil { + return x.KeyspaceShards + } + return nil +} + +func (x *GetBackupsRequest) GetRequestOptions() *vtctldata.GetBackupsRequest { + if x != nil { + return x.RequestOptions + } + return nil +} + +type GetBackupsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Backups []*ClusterBackup `protobuf:"bytes,1,rep,name=backups,proto3" json:"backups,omitempty"` } -func (x *GetClustersRequest) Reset() { - *x = GetClustersRequest{} +func (x *GetBackupsResponse) Reset() { + *x = GetBackupsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2011,13 +2029,13 @@ func (x *GetClustersRequest) Reset() { } } -func (x *GetClustersRequest) String() string { +func (x *GetBackupsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersRequest) ProtoMessage() {} +func (*GetBackupsResponse) ProtoMessage() {} -func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { +func (x *GetBackupsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2029,21 +2047,38 @@ func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersRequest.ProtoReflect.Descriptor instead. -func (*GetClustersRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetBackupsResponse.ProtoReflect.Descriptor instead. +func (*GetBackupsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{31} } -type GetClustersResponse struct { +func (x *GetBackupsResponse) GetBackups() []*ClusterBackup { + if x != nil { + return x.Backups + } + return nil +} + +type GetCellInfosRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Cells, if specified, limits the response to include only CellInfo objects + // with those names. If omitted, all CellInfo objects in each cluster are + // returned. + // + // Mutually-exclusive with NamesOnly. If both are set, this field takes + // precedence. + Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` + // Return only the cell names in each cluster; the actual CellInfo objects + // will be empty. + NamesOnly bool `protobuf:"varint,3,opt,name=names_only,json=namesOnly,proto3" json:"names_only,omitempty"` } -func (x *GetClustersResponse) Reset() { - *x = GetClustersResponse{} +func (x *GetCellInfosRequest) Reset() { + *x = GetCellInfosRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2051,13 +2086,13 @@ func (x *GetClustersResponse) Reset() { } } -func (x *GetClustersResponse) String() string { +func (x *GetCellInfosRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetClustersResponse) ProtoMessage() {} +func (*GetCellInfosRequest) ProtoMessage() {} -func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { +func (x *GetCellInfosRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2069,29 +2104,42 @@ func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetClustersResponse.ProtoReflect.Descriptor instead. -func (*GetClustersResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetCellInfosRequest.ProtoReflect.Descriptor instead. +func (*GetCellInfosRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{32} } -func (x *GetClustersResponse) GetClusters() []*Cluster { +func (x *GetCellInfosRequest) GetClusterIds() []string { if x != nil { - return x.Clusters + return x.ClusterIds } return nil } -type GetFullStatusRequest struct { +func (x *GetCellInfosRequest) GetCells() []string { + if x != nil { + return x.Cells + } + return nil +} + +func (x *GetCellInfosRequest) GetNamesOnly() bool { + if x != nil { + return x.NamesOnly + } + return false +} + +type GetCellInfosResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Alias *topodata.TabletAlias `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` + CellInfos []*ClusterCellInfo `protobuf:"bytes,1,rep,name=cell_infos,json=cellInfos,proto3" json:"cell_infos,omitempty"` } -func (x *GetFullStatusRequest) Reset() { - *x = GetFullStatusRequest{} +func (x *GetCellInfosResponse) Reset() { + *x = GetCellInfosResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2099,13 +2147,13 @@ func (x *GetFullStatusRequest) Reset() { } } -func (x *GetFullStatusRequest) String() string { +func (x *GetCellInfosResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetFullStatusRequest) ProtoMessage() {} +func (*GetCellInfosResponse) ProtoMessage() {} -func (x *GetFullStatusRequest) ProtoReflect() protoreflect.Message { +func (x *GetCellInfosResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2117,26 +2165,19 @@ func (x *GetFullStatusRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetFullStatusRequest.ProtoReflect.Descriptor instead. -func (*GetFullStatusRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetCellInfosResponse.ProtoReflect.Descriptor instead. +func (*GetCellInfosResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{33} } -func (x *GetFullStatusRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetFullStatusRequest) GetAlias() *topodata.TabletAlias { +func (x *GetCellInfosResponse) GetCellInfos() []*ClusterCellInfo { if x != nil { - return x.Alias + return x.CellInfos } return nil } -type GetGatesRequest struct { +type GetCellsAliasesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -2144,8 +2185,8 @@ type GetGatesRequest struct { ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *GetGatesRequest) Reset() { - *x = GetGatesRequest{} +func (x *GetCellsAliasesRequest) Reset() { + *x = GetCellsAliasesRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2153,13 +2194,13 @@ func (x *GetGatesRequest) Reset() { } } -func (x *GetGatesRequest) String() string { +func (x *GetCellsAliasesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGatesRequest) ProtoMessage() {} +func (*GetCellsAliasesRequest) ProtoMessage() {} -func (x *GetGatesRequest) ProtoReflect() protoreflect.Message { +func (x *GetCellsAliasesRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2171,28 +2212,28 @@ func (x *GetGatesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetGatesRequest.ProtoReflect.Descriptor instead. -func (*GetGatesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetCellsAliasesRequest.ProtoReflect.Descriptor instead. +func (*GetCellsAliasesRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{34} } -func (x *GetGatesRequest) GetClusterIds() []string { +func (x *GetCellsAliasesRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type GetGatesResponse struct { +type GetCellsAliasesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Gates []*VTGate `protobuf:"bytes,1,rep,name=gates,proto3" json:"gates,omitempty"` + Aliases []*ClusterCellsAliases `protobuf:"bytes,1,rep,name=aliases,proto3" json:"aliases,omitempty"` } -func (x *GetGatesResponse) Reset() { - *x = GetGatesResponse{} +func (x *GetCellsAliasesResponse) Reset() { + *x = GetCellsAliasesResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2200,13 +2241,13 @@ func (x *GetGatesResponse) Reset() { } } -func (x *GetGatesResponse) String() string { +func (x *GetCellsAliasesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetGatesResponse) ProtoMessage() {} +func (*GetCellsAliasesResponse) ProtoMessage() {} -func (x *GetGatesResponse) ProtoReflect() protoreflect.Message { +func (x *GetCellsAliasesResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2218,29 +2259,26 @@ func (x *GetGatesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetGatesResponse.ProtoReflect.Descriptor instead. -func (*GetGatesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetCellsAliasesResponse.ProtoReflect.Descriptor instead. +func (*GetCellsAliasesResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{35} } -func (x *GetGatesResponse) GetGates() []*VTGate { +func (x *GetCellsAliasesResponse) GetAliases() []*ClusterCellsAliases { if x != nil { - return x.Gates + return x.Aliases } return nil } -type GetKeyspaceRequest struct { +type GetClustersRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *GetKeyspaceRequest) Reset() { - *x = GetKeyspaceRequest{} +func (x *GetClustersRequest) Reset() { + *x = GetClustersRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2248,13 +2286,13 @@ func (x *GetKeyspaceRequest) Reset() { } } -func (x *GetKeyspaceRequest) String() string { +func (x *GetClustersRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetKeyspaceRequest) ProtoMessage() {} +func (*GetClustersRequest) ProtoMessage() {} -func (x *GetKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *GetClustersRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2266,35 +2304,21 @@ func (x *GetKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*GetKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetClustersRequest.ProtoReflect.Descriptor instead. +func (*GetClustersRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{36} } -func (x *GetKeyspaceRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetKeyspaceRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -type GetKeyspacesRequest struct { +type GetClustersResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` } -func (x *GetKeyspacesRequest) Reset() { - *x = GetKeyspacesRequest{} +func (x *GetClustersResponse) Reset() { + *x = GetClustersResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2302,13 +2326,13 @@ func (x *GetKeyspacesRequest) Reset() { } } -func (x *GetKeyspacesRequest) String() string { +func (x *GetClustersResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetKeyspacesRequest) ProtoMessage() {} +func (*GetClustersResponse) ProtoMessage() {} -func (x *GetKeyspacesRequest) ProtoReflect() protoreflect.Message { +func (x *GetClustersResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2320,28 +2344,29 @@ func (x *GetKeyspacesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetKeyspacesRequest.ProtoReflect.Descriptor instead. -func (*GetKeyspacesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetClustersResponse.ProtoReflect.Descriptor instead. +func (*GetClustersResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{37} } -func (x *GetKeyspacesRequest) GetClusterIds() []string { +func (x *GetClustersResponse) GetClusters() []*Cluster { if x != nil { - return x.ClusterIds + return x.Clusters } return nil } -type GetKeyspacesResponse struct { +type GetFullStatusRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Keyspaces []*Keyspace `protobuf:"bytes,1,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` } -func (x *GetKeyspacesResponse) Reset() { - *x = GetKeyspacesResponse{} +func (x *GetFullStatusRequest) Reset() { + *x = GetFullStatusRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2349,13 +2374,13 @@ func (x *GetKeyspacesResponse) Reset() { } } -func (x *GetKeyspacesResponse) String() string { +func (x *GetFullStatusRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetKeyspacesResponse) ProtoMessage() {} +func (*GetFullStatusRequest) ProtoMessage() {} -func (x *GetKeyspacesResponse) ProtoReflect() protoreflect.Message { +func (x *GetFullStatusRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2367,31 +2392,35 @@ func (x *GetKeyspacesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetKeyspacesResponse.ProtoReflect.Descriptor instead. -func (*GetKeyspacesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetFullStatusRequest.ProtoReflect.Descriptor instead. +func (*GetFullStatusRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{38} } -func (x *GetKeyspacesResponse) GetKeyspaces() []*Keyspace { +func (x *GetFullStatusRequest) GetClusterId() string { if x != nil { - return x.Keyspaces + return x.ClusterId + } + return "" +} + +func (x *GetFullStatusRequest) GetAlias() *topodata.TabletAlias { + if x != nil { + return x.Alias } return nil } -type GetSchemaRequest struct { +type GetGatesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Table string `protobuf:"bytes,3,opt,name=table,proto3" json:"table,omitempty"` - TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,4,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *GetSchemaRequest) Reset() { - *x = GetSchemaRequest{} +func (x *GetGatesRequest) Reset() { + *x = GetGatesRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2399,13 +2428,13 @@ func (x *GetSchemaRequest) Reset() { } } -func (x *GetSchemaRequest) String() string { +func (x *GetGatesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSchemaRequest) ProtoMessage() {} +func (*GetGatesRequest) ProtoMessage() {} -func (x *GetSchemaRequest) ProtoReflect() protoreflect.Message { +func (x *GetGatesRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2417,50 +2446,28 @@ func (x *GetSchemaRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSchemaRequest.ProtoReflect.Descriptor instead. -func (*GetSchemaRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetGatesRequest.ProtoReflect.Descriptor instead. +func (*GetGatesRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{39} } -func (x *GetSchemaRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetSchemaRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *GetSchemaRequest) GetTable() string { - if x != nil { - return x.Table - } - return "" -} - -func (x *GetSchemaRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { +func (x *GetGatesRequest) GetClusterIds() []string { if x != nil { - return x.TableSizeOptions + return x.ClusterIds } return nil } -type GetSchemasRequest struct { +type GetGatesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,2,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` + Gates []*VTGate `protobuf:"bytes,1,rep,name=gates,proto3" json:"gates,omitempty"` } -func (x *GetSchemasRequest) Reset() { - *x = GetSchemasRequest{} +func (x *GetGatesResponse) Reset() { + *x = GetGatesResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2468,13 +2475,13 @@ func (x *GetSchemasRequest) Reset() { } } -func (x *GetSchemasRequest) String() string { +func (x *GetGatesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSchemasRequest) ProtoMessage() {} +func (*GetGatesResponse) ProtoMessage() {} -func (x *GetSchemasRequest) ProtoReflect() protoreflect.Message { +func (x *GetGatesResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2486,35 +2493,29 @@ func (x *GetSchemasRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSchemasRequest.ProtoReflect.Descriptor instead. -func (*GetSchemasRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetGatesResponse.ProtoReflect.Descriptor instead. +func (*GetGatesResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{40} } -func (x *GetSchemasRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GetSchemasRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { +func (x *GetGatesResponse) GetGates() []*VTGate { if x != nil { - return x.TableSizeOptions + return x.Gates } return nil } -type GetSchemasResponse struct { +type GetKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Schemas []*Schema `protobuf:"bytes,1,rep,name=schemas,proto3" json:"schemas,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *GetSchemasResponse) Reset() { - *x = GetSchemasResponse{} +func (x *GetKeyspaceRequest) Reset() { + *x = GetKeyspaceRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2522,13 +2523,13 @@ func (x *GetSchemasResponse) Reset() { } } -func (x *GetSchemasResponse) String() string { +func (x *GetKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSchemasResponse) ProtoMessage() {} +func (*GetKeyspaceRequest) ProtoMessage() {} -func (x *GetSchemasResponse) ProtoReflect() protoreflect.Message { +func (x *GetKeyspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2540,37 +2541,35 @@ func (x *GetSchemasResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSchemasResponse.ProtoReflect.Descriptor instead. -func (*GetSchemasResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*GetKeyspaceRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{41} } -func (x *GetSchemasResponse) GetSchemas() []*Schema { +func (x *GetKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Schemas + return x.ClusterId } - return nil + return "" } -type GetShardReplicationPositionsRequest struct { +func (x *GetKeyspaceRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +type GetKeyspacesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // Keyspaces, if set, limits replication positions to just the specified - // keyspaces. Applies to all clusters in the request. - Keyspaces []string `protobuf:"bytes,2,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` - // KeyspaceShards, if set, limits replicatin positions to just the specified - // keyspace/shards. Applies to all clusters in the request. - // - // This field takes precedence over Keyspaces. If KeyspaceShards is set, - // Keyspaces is ignored. - KeyspaceShards []string `protobuf:"bytes,3,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` } -func (x *GetShardReplicationPositionsRequest) Reset() { - *x = GetShardReplicationPositionsRequest{} +func (x *GetKeyspacesRequest) Reset() { + *x = GetKeyspacesRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2578,13 +2577,13 @@ func (x *GetShardReplicationPositionsRequest) Reset() { } } -func (x *GetShardReplicationPositionsRequest) String() string { +func (x *GetKeyspacesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetShardReplicationPositionsRequest) ProtoMessage() {} +func (*GetKeyspacesRequest) ProtoMessage() {} -func (x *GetShardReplicationPositionsRequest) ProtoReflect() protoreflect.Message { +func (x *GetKeyspacesRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2596,42 +2595,28 @@ func (x *GetShardReplicationPositionsRequest) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use GetShardReplicationPositionsRequest.ProtoReflect.Descriptor instead. -func (*GetShardReplicationPositionsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetKeyspacesRequest.ProtoReflect.Descriptor instead. +func (*GetKeyspacesRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{42} } -func (x *GetShardReplicationPositionsRequest) GetClusterIds() []string { +func (x *GetKeyspacesRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -func (x *GetShardReplicationPositionsRequest) GetKeyspaces() []string { - if x != nil { - return x.Keyspaces - } - return nil -} - -func (x *GetShardReplicationPositionsRequest) GetKeyspaceShards() []string { - if x != nil { - return x.KeyspaceShards - } - return nil -} - -type GetShardReplicationPositionsResponse struct { +type GetKeyspacesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ReplicationPositions []*ClusterShardReplicationPosition `protobuf:"bytes,1,rep,name=replication_positions,json=replicationPositions,proto3" json:"replication_positions,omitempty"` + Keyspaces []*Keyspace `protobuf:"bytes,1,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` } -func (x *GetShardReplicationPositionsResponse) Reset() { - *x = GetShardReplicationPositionsResponse{} +func (x *GetKeyspacesResponse) Reset() { + *x = GetKeyspacesResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2639,13 +2624,13 @@ func (x *GetShardReplicationPositionsResponse) Reset() { } } -func (x *GetShardReplicationPositionsResponse) String() string { +func (x *GetKeyspacesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetShardReplicationPositionsResponse) ProtoMessage() {} +func (*GetKeyspacesResponse) ProtoMessage() {} -func (x *GetShardReplicationPositionsResponse) ProtoReflect() protoreflect.Message { +func (x *GetKeyspacesResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2657,32 +2642,31 @@ func (x *GetShardReplicationPositionsResponse) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use GetShardReplicationPositionsResponse.ProtoReflect.Descriptor instead. -func (*GetShardReplicationPositionsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetKeyspacesResponse.ProtoReflect.Descriptor instead. +func (*GetKeyspacesResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{43} } -func (x *GetShardReplicationPositionsResponse) GetReplicationPositions() []*ClusterShardReplicationPosition { +func (x *GetKeyspacesResponse) GetKeyspaces() []*Keyspace { if x != nil { - return x.ReplicationPositions + return x.Keyspaces } return nil } -type GetSrvKeyspaceRequest struct { +type GetSchemaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - // Cells is a list of cells to lookup a SrvKeyspace for. Leaving this empty is - // equivalent to specifying all cells in the topo. - Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Table string `protobuf:"bytes,3,opt,name=table,proto3" json:"table,omitempty"` + TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,4,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` } -func (x *GetSrvKeyspaceRequest) Reset() { - *x = GetSrvKeyspaceRequest{} +func (x *GetSchemaRequest) Reset() { + *x = GetSchemaRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2690,13 +2674,13 @@ func (x *GetSrvKeyspaceRequest) Reset() { } } -func (x *GetSrvKeyspaceRequest) String() string { +func (x *GetSchemaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvKeyspaceRequest) ProtoMessage() {} +func (*GetSchemaRequest) ProtoMessage() {} -func (x *GetSrvKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *GetSchemaRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2708,46 +2692,50 @@ func (x *GetSrvKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*GetSrvKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemaRequest.ProtoReflect.Descriptor instead. +func (*GetSchemaRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{44} } -func (x *GetSrvKeyspaceRequest) GetClusterId() string { +func (x *GetSchemaRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *GetSrvKeyspaceRequest) GetKeyspace() string { +func (x *GetSchemaRequest) GetKeyspace() string { if x != nil { return x.Keyspace } return "" } -func (x *GetSrvKeyspaceRequest) GetCells() []string { +func (x *GetSchemaRequest) GetTable() string { if x != nil { - return x.Cells + return x.Table + } + return "" +} + +func (x *GetSchemaRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { + if x != nil { + return x.TableSizeOptions } return nil } -type GetSrvKeyspacesRequest struct { +type GetSchemasRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // An optional list of cluster IDs to filter specific clusters - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // Cells is a list of cells to lookup a SrvKeyspace for. Leaving this empty is - // equivalent to specifying all cells in the topo. - Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + TableSizeOptions *GetSchemaTableSizeOptions `protobuf:"bytes,2,opt,name=table_size_options,json=tableSizeOptions,proto3" json:"table_size_options,omitempty"` } -func (x *GetSrvKeyspacesRequest) Reset() { - *x = GetSrvKeyspacesRequest{} +func (x *GetSchemasRequest) Reset() { + *x = GetSchemasRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2755,13 +2743,13 @@ func (x *GetSrvKeyspacesRequest) Reset() { } } -func (x *GetSrvKeyspacesRequest) String() string { +func (x *GetSchemasRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvKeyspacesRequest) ProtoMessage() {} +func (*GetSchemasRequest) ProtoMessage() {} -func (x *GetSrvKeyspacesRequest) ProtoReflect() protoreflect.Message { +func (x *GetSchemasRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2773,36 +2761,35 @@ func (x *GetSrvKeyspacesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvKeyspacesRequest.ProtoReflect.Descriptor instead. -func (*GetSrvKeyspacesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemasRequest.ProtoReflect.Descriptor instead. +func (*GetSchemasRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{45} } -func (x *GetSrvKeyspacesRequest) GetClusterIds() []string { +func (x *GetSchemasRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -func (x *GetSrvKeyspacesRequest) GetCells() []string { +func (x *GetSchemasRequest) GetTableSizeOptions() *GetSchemaTableSizeOptions { if x != nil { - return x.Cells + return x.TableSizeOptions } return nil } -type GetSrvKeyspacesResponse struct { +type GetSchemasResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // GetSrvKeyspaces responses for each keyspace - SrvKeyspaces map[string]*vtctldata.GetSrvKeyspacesResponse `protobuf:"bytes,1,rep,name=srv_keyspaces,json=srvKeyspaces,proto3" json:"srv_keyspaces,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Schemas []*Schema `protobuf:"bytes,1,rep,name=schemas,proto3" json:"schemas,omitempty"` } -func (x *GetSrvKeyspacesResponse) Reset() { - *x = GetSrvKeyspacesResponse{} +func (x *GetSchemasResponse) Reset() { + *x = GetSchemasResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2810,13 +2797,13 @@ func (x *GetSrvKeyspacesResponse) Reset() { } } -func (x *GetSrvKeyspacesResponse) String() string { +func (x *GetSchemasResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvKeyspacesResponse) ProtoMessage() {} +func (*GetSchemasResponse) ProtoMessage() {} -func (x *GetSrvKeyspacesResponse) ProtoReflect() protoreflect.Message { +func (x *GetSchemasResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2828,29 +2815,28 @@ func (x *GetSrvKeyspacesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvKeyspacesResponse.ProtoReflect.Descriptor instead. -func (*GetSrvKeyspacesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemasResponse.ProtoReflect.Descriptor instead. +func (*GetSchemasResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{46} } -func (x *GetSrvKeyspacesResponse) GetSrvKeyspaces() map[string]*vtctldata.GetSrvKeyspacesResponse { +func (x *GetSchemasResponse) GetSchemas() []*Schema { if x != nil { - return x.SrvKeyspaces + return x.Schemas } return nil } -type GetSrvVSchemaRequest struct { +type GetSchemaMigrationsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Cell string `protobuf:"bytes,2,opt,name=cell,proto3" json:"cell,omitempty"` + ClusterRequests []*GetSchemaMigrationsRequest_ClusterRequest `protobuf:"bytes,1,rep,name=cluster_requests,json=clusterRequests,proto3" json:"cluster_requests,omitempty"` } -func (x *GetSrvVSchemaRequest) Reset() { - *x = GetSrvVSchemaRequest{} +func (x *GetSchemaMigrationsRequest) Reset() { + *x = GetSchemaMigrationsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2858,13 +2844,13 @@ func (x *GetSrvVSchemaRequest) Reset() { } } -func (x *GetSrvVSchemaRequest) String() string { +func (x *GetSchemaMigrationsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvVSchemaRequest) ProtoMessage() {} +func (*GetSchemaMigrationsRequest) ProtoMessage() {} -func (x *GetSrvVSchemaRequest) ProtoReflect() protoreflect.Message { +func (x *GetSchemaMigrationsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2876,36 +2862,28 @@ func (x *GetSrvVSchemaRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvVSchemaRequest.ProtoReflect.Descriptor instead. -func (*GetSrvVSchemaRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemaMigrationsRequest.ProtoReflect.Descriptor instead. +func (*GetSchemaMigrationsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{47} } -func (x *GetSrvVSchemaRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetSrvVSchemaRequest) GetCell() string { +func (x *GetSchemaMigrationsRequest) GetClusterRequests() []*GetSchemaMigrationsRequest_ClusterRequest { if x != nil { - return x.Cell + return x.ClusterRequests } - return "" + return nil } -type GetSrvVSchemasRequest struct { +type GetSchemaMigrationsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` + SchemaMigrations []*SchemaMigration `protobuf:"bytes,1,rep,name=schema_migrations,json=schemaMigrations,proto3" json:"schema_migrations,omitempty"` } -func (x *GetSrvVSchemasRequest) Reset() { - *x = GetSrvVSchemasRequest{} +func (x *GetSchemaMigrationsResponse) Reset() { + *x = GetSchemaMigrationsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2913,13 +2891,13 @@ func (x *GetSrvVSchemasRequest) Reset() { } } -func (x *GetSrvVSchemasRequest) String() string { +func (x *GetSchemaMigrationsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvVSchemasRequest) ProtoMessage() {} +func (*GetSchemaMigrationsResponse) ProtoMessage() {} -func (x *GetSrvVSchemasRequest) ProtoReflect() protoreflect.Message { +func (x *GetSchemaMigrationsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2931,35 +2909,37 @@ func (x *GetSrvVSchemasRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvVSchemasRequest.ProtoReflect.Descriptor instead. -func (*GetSrvVSchemasRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemaMigrationsResponse.ProtoReflect.Descriptor instead. +func (*GetSchemaMigrationsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{48} } -func (x *GetSrvVSchemasRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GetSrvVSchemasRequest) GetCells() []string { +func (x *GetSchemaMigrationsResponse) GetSchemaMigrations() []*SchemaMigration { if x != nil { - return x.Cells + return x.SchemaMigrations } return nil } -type GetSrvVSchemasResponse struct { +type GetShardReplicationPositionsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SrvVSchemas []*SrvVSchema `protobuf:"bytes,1,rep,name=srv_v_schemas,json=srvVSchemas,proto3" json:"srv_v_schemas,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Keyspaces, if set, limits replication positions to just the specified + // keyspaces. Applies to all clusters in the request. + Keyspaces []string `protobuf:"bytes,2,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + // KeyspaceShards, if set, limits replicatin positions to just the specified + // keyspace/shards. Applies to all clusters in the request. + // + // This field takes precedence over Keyspaces. If KeyspaceShards is set, + // Keyspaces is ignored. + KeyspaceShards []string `protobuf:"bytes,3,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` } -func (x *GetSrvVSchemasResponse) Reset() { - *x = GetSrvVSchemasResponse{} +func (x *GetShardReplicationPositionsRequest) Reset() { + *x = GetShardReplicationPositionsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2967,13 +2947,13 @@ func (x *GetSrvVSchemasResponse) Reset() { } } -func (x *GetSrvVSchemasResponse) String() string { +func (x *GetShardReplicationPositionsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSrvVSchemasResponse) ProtoMessage() {} +func (*GetShardReplicationPositionsRequest) ProtoMessage() {} -func (x *GetSrvVSchemasResponse) ProtoReflect() protoreflect.Message { +func (x *GetShardReplicationPositionsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2985,29 +2965,42 @@ func (x *GetSrvVSchemasResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSrvVSchemasResponse.ProtoReflect.Descriptor instead. -func (*GetSrvVSchemasResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetShardReplicationPositionsRequest.ProtoReflect.Descriptor instead. +func (*GetShardReplicationPositionsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{49} } -func (x *GetSrvVSchemasResponse) GetSrvVSchemas() []*SrvVSchema { +func (x *GetShardReplicationPositionsRequest) GetClusterIds() []string { if x != nil { - return x.SrvVSchemas + return x.ClusterIds } return nil } -type GetSchemaTableSizeOptions struct { +func (x *GetShardReplicationPositionsRequest) GetKeyspaces() []string { + if x != nil { + return x.Keyspaces + } + return nil +} + +func (x *GetShardReplicationPositionsRequest) GetKeyspaceShards() []string { + if x != nil { + return x.KeyspaceShards + } + return nil +} + +type GetShardReplicationPositionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - AggregateSizes bool `protobuf:"varint,1,opt,name=aggregate_sizes,json=aggregateSizes,proto3" json:"aggregate_sizes,omitempty"` - IncludeNonServingShards bool `protobuf:"varint,2,opt,name=include_non_serving_shards,json=includeNonServingShards,proto3" json:"include_non_serving_shards,omitempty"` + ReplicationPositions []*ClusterShardReplicationPosition `protobuf:"bytes,1,rep,name=replication_positions,json=replicationPositions,proto3" json:"replication_positions,omitempty"` } -func (x *GetSchemaTableSizeOptions) Reset() { - *x = GetSchemaTableSizeOptions{} +func (x *GetShardReplicationPositionsResponse) Reset() { + *x = GetShardReplicationPositionsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3015,13 +3008,13 @@ func (x *GetSchemaTableSizeOptions) Reset() { } } -func (x *GetSchemaTableSizeOptions) String() string { +func (x *GetShardReplicationPositionsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetSchemaTableSizeOptions) ProtoMessage() {} +func (*GetShardReplicationPositionsResponse) ProtoMessage() {} -func (x *GetSchemaTableSizeOptions) ProtoReflect() protoreflect.Message { +func (x *GetShardReplicationPositionsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3033,40 +3026,32 @@ func (x *GetSchemaTableSizeOptions) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetSchemaTableSizeOptions.ProtoReflect.Descriptor instead. -func (*GetSchemaTableSizeOptions) Descriptor() ([]byte, []int) { +// Deprecated: Use GetShardReplicationPositionsResponse.ProtoReflect.Descriptor instead. +func (*GetShardReplicationPositionsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{50} } -func (x *GetSchemaTableSizeOptions) GetAggregateSizes() bool { - if x != nil { - return x.AggregateSizes - } - return false -} - -func (x *GetSchemaTableSizeOptions) GetIncludeNonServingShards() bool { +func (x *GetShardReplicationPositionsResponse) GetReplicationPositions() []*ClusterShardReplicationPosition { if x != nil { - return x.IncludeNonServingShards + return x.ReplicationPositions } - return false + return nil } -type GetTabletRequest struct { +type GetSrvKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Unique (per cluster) tablet alias. - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - // ClusterIDs is an optional parameter to narrow the scope of the search, if - // the caller knows which cluster the tablet may be in, or, to disambiguate - // if multiple clusters have a tablet with the same hostname. - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // Cells is a list of cells to lookup a SrvKeyspace for. Leaving this empty is + // equivalent to specifying all cells in the topo. + Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` } -func (x *GetTabletRequest) Reset() { - *x = GetTabletRequest{} +func (x *GetSrvKeyspaceRequest) Reset() { + *x = GetSrvKeyspaceRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3074,13 +3059,13 @@ func (x *GetTabletRequest) Reset() { } } -func (x *GetTabletRequest) String() string { +func (x *GetSrvKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetTabletRequest) ProtoMessage() {} +func (*GetSrvKeyspaceRequest) ProtoMessage() {} -func (x *GetTabletRequest) ProtoReflect() protoreflect.Message { +func (x *GetSrvKeyspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3092,35 +3077,46 @@ func (x *GetTabletRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetTabletRequest.ProtoReflect.Descriptor instead. -func (*GetTabletRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*GetSrvKeyspaceRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{51} } -func (x *GetTabletRequest) GetAlias() *topodata.TabletAlias { +func (x *GetSrvKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *GetTabletRequest) GetClusterIds() []string { +func (x *GetSrvKeyspaceRequest) GetKeyspace() string { if x != nil { - return x.ClusterIds + return x.Keyspace + } + return "" +} + +func (x *GetSrvKeyspaceRequest) GetCells() []string { + if x != nil { + return x.Cells } return nil } -type GetTabletsRequest struct { +type GetSrvKeyspacesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // An optional list of cluster IDs to filter specific clusters ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Cells is a list of cells to lookup a SrvKeyspace for. Leaving this empty is + // equivalent to specifying all cells in the topo. + Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` } -func (x *GetTabletsRequest) Reset() { - *x = GetTabletsRequest{} +func (x *GetSrvKeyspacesRequest) Reset() { + *x = GetSrvKeyspacesRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3128,13 +3124,13 @@ func (x *GetTabletsRequest) Reset() { } } -func (x *GetTabletsRequest) String() string { +func (x *GetSrvKeyspacesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetTabletsRequest) ProtoMessage() {} +func (*GetSrvKeyspacesRequest) ProtoMessage() {} -func (x *GetTabletsRequest) ProtoReflect() protoreflect.Message { +func (x *GetSrvKeyspacesRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3146,28 +3142,36 @@ func (x *GetTabletsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetTabletsRequest.ProtoReflect.Descriptor instead. -func (*GetTabletsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvKeyspacesRequest.ProtoReflect.Descriptor instead. +func (*GetSrvKeyspacesRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{52} } -func (x *GetTabletsRequest) GetClusterIds() []string { +func (x *GetSrvKeyspacesRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type GetTabletsResponse struct { +func (x *GetSrvKeyspacesRequest) GetCells() []string { + if x != nil { + return x.Cells + } + return nil +} + +type GetSrvKeyspacesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Tablets []*Tablet `protobuf:"bytes,1,rep,name=tablets,proto3" json:"tablets,omitempty"` + // GetSrvKeyspaces responses for each keyspace + SrvKeyspaces map[string]*vtctldata.GetSrvKeyspacesResponse `protobuf:"bytes,1,rep,name=srv_keyspaces,json=srvKeyspaces,proto3" json:"srv_keyspaces,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *GetTabletsResponse) Reset() { - *x = GetTabletsResponse{} +func (x *GetSrvKeyspacesResponse) Reset() { + *x = GetSrvKeyspacesResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3175,13 +3179,13 @@ func (x *GetTabletsResponse) Reset() { } } -func (x *GetTabletsResponse) String() string { +func (x *GetSrvKeyspacesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetTabletsResponse) ProtoMessage() {} +func (*GetSrvKeyspacesResponse) ProtoMessage() {} -func (x *GetTabletsResponse) ProtoReflect() protoreflect.Message { +func (x *GetSrvKeyspacesResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3193,29 +3197,29 @@ func (x *GetTabletsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetTabletsResponse.ProtoReflect.Descriptor instead. -func (*GetTabletsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvKeyspacesResponse.ProtoReflect.Descriptor instead. +func (*GetSrvKeyspacesResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{53} } -func (x *GetTabletsResponse) GetTablets() []*Tablet { +func (x *GetSrvKeyspacesResponse) GetSrvKeyspaces() map[string]*vtctldata.GetSrvKeyspacesResponse { if x != nil { - return x.Tablets + return x.SrvKeyspaces } return nil } -type GetTopologyPathRequest struct { +type GetSrvVSchemaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Cell string `protobuf:"bytes,2,opt,name=cell,proto3" json:"cell,omitempty"` } -func (x *GetTopologyPathRequest) Reset() { - *x = GetTopologyPathRequest{} +func (x *GetSrvVSchemaRequest) Reset() { + *x = GetSrvVSchemaRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3223,13 +3227,13 @@ func (x *GetTopologyPathRequest) Reset() { } } -func (x *GetTopologyPathRequest) String() string { +func (x *GetSrvVSchemaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetTopologyPathRequest) ProtoMessage() {} +func (*GetSrvVSchemaRequest) ProtoMessage() {} -func (x *GetTopologyPathRequest) ProtoReflect() protoreflect.Message { +func (x *GetSrvVSchemaRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3241,36 +3245,36 @@ func (x *GetTopologyPathRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetTopologyPathRequest.ProtoReflect.Descriptor instead. -func (*GetTopologyPathRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvVSchemaRequest.ProtoReflect.Descriptor instead. +func (*GetSrvVSchemaRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{54} } -func (x *GetTopologyPathRequest) GetClusterId() string { +func (x *GetSrvVSchemaRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *GetTopologyPathRequest) GetPath() string { +func (x *GetSrvVSchemaRequest) GetCell() string { if x != nil { - return x.Path + return x.Cell } return "" } -type GetVSchemaRequest struct { +type GetSrvVSchemasRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Cells []string `protobuf:"bytes,2,rep,name=cells,proto3" json:"cells,omitempty"` } -func (x *GetVSchemaRequest) Reset() { - *x = GetVSchemaRequest{} +func (x *GetSrvVSchemasRequest) Reset() { + *x = GetSrvVSchemasRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3278,13 +3282,13 @@ func (x *GetVSchemaRequest) Reset() { } } -func (x *GetVSchemaRequest) String() string { +func (x *GetSrvVSchemasRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetVSchemaRequest) ProtoMessage() {} +func (*GetSrvVSchemasRequest) ProtoMessage() {} -func (x *GetVSchemaRequest) ProtoReflect() protoreflect.Message { +func (x *GetSrvVSchemasRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3296,35 +3300,35 @@ func (x *GetVSchemaRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetVSchemaRequest.ProtoReflect.Descriptor instead. -func (*GetVSchemaRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvVSchemasRequest.ProtoReflect.Descriptor instead. +func (*GetSrvVSchemasRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{55} } -func (x *GetVSchemaRequest) GetClusterId() string { +func (x *GetSrvVSchemasRequest) GetClusterIds() []string { if x != nil { - return x.ClusterId + return x.ClusterIds } - return "" + return nil } -func (x *GetVSchemaRequest) GetKeyspace() string { +func (x *GetSrvVSchemasRequest) GetCells() []string { if x != nil { - return x.Keyspace + return x.Cells } - return "" + return nil } -type GetVSchemasRequest struct { +type GetSrvVSchemasResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + SrvVSchemas []*SrvVSchema `protobuf:"bytes,1,rep,name=srv_v_schemas,json=srvVSchemas,proto3" json:"srv_v_schemas,omitempty"` } -func (x *GetVSchemasRequest) Reset() { - *x = GetVSchemasRequest{} +func (x *GetSrvVSchemasResponse) Reset() { + *x = GetSrvVSchemasResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3332,13 +3336,13 @@ func (x *GetVSchemasRequest) Reset() { } } -func (x *GetVSchemasRequest) String() string { +func (x *GetSrvVSchemasResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetVSchemasRequest) ProtoMessage() {} +func (*GetSrvVSchemasResponse) ProtoMessage() {} -func (x *GetVSchemasRequest) ProtoReflect() protoreflect.Message { +func (x *GetSrvVSchemasResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3350,28 +3354,29 @@ func (x *GetVSchemasRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetVSchemasRequest.ProtoReflect.Descriptor instead. -func (*GetVSchemasRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSrvVSchemasResponse.ProtoReflect.Descriptor instead. +func (*GetSrvVSchemasResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{56} } -func (x *GetVSchemasRequest) GetClusterIds() []string { +func (x *GetSrvVSchemasResponse) GetSrvVSchemas() []*SrvVSchema { if x != nil { - return x.ClusterIds + return x.SrvVSchemas } return nil } -type GetVSchemasResponse struct { +type GetSchemaTableSizeOptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - VSchemas []*VSchema `protobuf:"bytes,1,rep,name=v_schemas,json=vSchemas,proto3" json:"v_schemas,omitempty"` + AggregateSizes bool `protobuf:"varint,1,opt,name=aggregate_sizes,json=aggregateSizes,proto3" json:"aggregate_sizes,omitempty"` + IncludeNonServingShards bool `protobuf:"varint,2,opt,name=include_non_serving_shards,json=includeNonServingShards,proto3" json:"include_non_serving_shards,omitempty"` } -func (x *GetVSchemasResponse) Reset() { - *x = GetVSchemasResponse{} +func (x *GetSchemaTableSizeOptions) Reset() { + *x = GetSchemaTableSizeOptions{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3379,13 +3384,13 @@ func (x *GetVSchemasResponse) Reset() { } } -func (x *GetVSchemasResponse) String() string { +func (x *GetSchemaTableSizeOptions) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetVSchemasResponse) ProtoMessage() {} +func (*GetSchemaTableSizeOptions) ProtoMessage() {} -func (x *GetVSchemasResponse) ProtoReflect() protoreflect.Message { +func (x *GetSchemaTableSizeOptions) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3397,28 +3402,40 @@ func (x *GetVSchemasResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetVSchemasResponse.ProtoReflect.Descriptor instead. -func (*GetVSchemasResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetSchemaTableSizeOptions.ProtoReflect.Descriptor instead. +func (*GetSchemaTableSizeOptions) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{57} } -func (x *GetVSchemasResponse) GetVSchemas() []*VSchema { +func (x *GetSchemaTableSizeOptions) GetAggregateSizes() bool { if x != nil { - return x.VSchemas + return x.AggregateSizes } - return nil + return false } -type GetVtctldsRequest struct { +func (x *GetSchemaTableSizeOptions) GetIncludeNonServingShards() bool { + if x != nil { + return x.IncludeNonServingShards + } + return false +} + +type GetTabletRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Unique (per cluster) tablet alias. + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + // ClusterIDs is an optional parameter to narrow the scope of the search, if + // the caller knows which cluster the tablet may be in, or, to disambiguate + // if multiple clusters have a tablet with the same hostname. + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *GetVtctldsRequest) Reset() { - *x = GetVtctldsRequest{} +func (x *GetTabletRequest) Reset() { + *x = GetTabletRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3426,13 +3443,13 @@ func (x *GetVtctldsRequest) Reset() { } } -func (x *GetVtctldsRequest) String() string { +func (x *GetTabletRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetVtctldsRequest) ProtoMessage() {} +func (*GetTabletRequest) ProtoMessage() {} -func (x *GetVtctldsRequest) ProtoReflect() protoreflect.Message { +func (x *GetTabletRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3444,28 +3461,35 @@ func (x *GetVtctldsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetVtctldsRequest.ProtoReflect.Descriptor instead. -func (*GetVtctldsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetTabletRequest.ProtoReflect.Descriptor instead. +func (*GetTabletRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{58} } -func (x *GetVtctldsRequest) GetClusterIds() []string { +func (x *GetTabletRequest) GetAlias() *topodata.TabletAlias { + if x != nil { + return x.Alias + } + return nil +} + +func (x *GetTabletRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type GetVtctldsResponse struct { +type GetTabletsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Vtctlds []*Vtctld `protobuf:"bytes,1,rep,name=vtctlds,proto3" json:"vtctlds,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *GetVtctldsResponse) Reset() { - *x = GetVtctldsResponse{} +func (x *GetTabletsRequest) Reset() { + *x = GetTabletsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3473,13 +3497,13 @@ func (x *GetVtctldsResponse) Reset() { } } -func (x *GetVtctldsResponse) String() string { +func (x *GetTabletsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetVtctldsResponse) ProtoMessage() {} +func (*GetTabletsRequest) ProtoMessage() {} -func (x *GetVtctldsResponse) ProtoReflect() protoreflect.Message { +func (x *GetTabletsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3491,31 +3515,28 @@ func (x *GetVtctldsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetVtctldsResponse.ProtoReflect.Descriptor instead. -func (*GetVtctldsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetTabletsRequest.ProtoReflect.Descriptor instead. +func (*GetTabletsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{59} } -func (x *GetVtctldsResponse) GetVtctlds() []*Vtctld { +func (x *GetTabletsRequest) GetClusterIds() []string { if x != nil { - return x.Vtctlds + return x.ClusterIds } return nil } -type GetWorkflowRequest struct { +type GetTabletsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` - ActiveOnly bool `protobuf:"varint,4,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` + Tablets []*Tablet `protobuf:"bytes,1,rep,name=tablets,proto3" json:"tablets,omitempty"` } -func (x *GetWorkflowRequest) Reset() { - *x = GetWorkflowRequest{} +func (x *GetTabletsResponse) Reset() { + *x = GetTabletsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3523,13 +3544,13 @@ func (x *GetWorkflowRequest) Reset() { } } -func (x *GetWorkflowRequest) String() string { +func (x *GetTabletsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkflowRequest) ProtoMessage() {} +func (*GetTabletsResponse) ProtoMessage() {} -func (x *GetWorkflowRequest) ProtoReflect() protoreflect.Message { +func (x *GetTabletsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3541,67 +3562,29 @@ func (x *GetWorkflowRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkflowRequest.ProtoReflect.Descriptor instead. -func (*GetWorkflowRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetTabletsResponse.ProtoReflect.Descriptor instead. +func (*GetTabletsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{60} } -func (x *GetWorkflowRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *GetWorkflowRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *GetWorkflowRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetWorkflowRequest) GetActiveOnly() bool { +func (x *GetTabletsResponse) GetTablets() []*Tablet { if x != nil { - return x.ActiveOnly + return x.Tablets } - return false + return nil } -type GetWorkflowsRequest struct { +type GetTopologyPathRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // ActiveOnly specifies whether to return workflows that are currently - // active (running or paused) instead of all workflows. - ActiveOnly bool `protobuf:"varint,2,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` - // Keyspaces is a list of keyspaces to restrict the workflow search to. Note - // that the keyspaces list applies across all cluster IDs in the request. - // - // If, for example, you have two clusters, each with a keyspace called "foo" - // and want the workflows from "foo" in cluster1 but not from cluster2, you - // must make two requests. - // - // Keyspaces and IgnoreKeyspaces are mutually-exclusive, and Keyspaces takes - // precedence; if Keyspaces is a non-empty list, then IgnoreKeyspaces is - // ignored completely. - Keyspaces []string `protobuf:"bytes,3,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` - // IgnoreKeyspaces is a list of keyspaces to skip during the workflow - // search. It has the same semantics as the Keyspaces parameter, so refer to - // that documentation for more details. - IgnoreKeyspaces []string `protobuf:"bytes,4,rep,name=ignore_keyspaces,json=ignoreKeyspaces,proto3" json:"ignore_keyspaces,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` } -func (x *GetWorkflowsRequest) Reset() { - *x = GetWorkflowsRequest{} +func (x *GetTopologyPathRequest) Reset() { + *x = GetTopologyPathRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3609,13 +3592,13 @@ func (x *GetWorkflowsRequest) Reset() { } } -func (x *GetWorkflowsRequest) String() string { +func (x *GetTopologyPathRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkflowsRequest) ProtoMessage() {} +func (*GetTopologyPathRequest) ProtoMessage() {} -func (x *GetWorkflowsRequest) ProtoReflect() protoreflect.Message { +func (x *GetTopologyPathRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3627,49 +3610,36 @@ func (x *GetWorkflowsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkflowsRequest.ProtoReflect.Descriptor instead. -func (*GetWorkflowsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetTopologyPathRequest.ProtoReflect.Descriptor instead. +func (*GetTopologyPathRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{61} } -func (x *GetWorkflowsRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *GetWorkflowsRequest) GetActiveOnly() bool { - if x != nil { - return x.ActiveOnly - } - return false -} - -func (x *GetWorkflowsRequest) GetKeyspaces() []string { +func (x *GetTopologyPathRequest) GetClusterId() string { if x != nil { - return x.Keyspaces + return x.ClusterId } - return nil + return "" } -func (x *GetWorkflowsRequest) GetIgnoreKeyspaces() []string { +func (x *GetTopologyPathRequest) GetPath() string { if x != nil { - return x.IgnoreKeyspaces + return x.Path } - return nil + return "" } -type GetWorkflowsResponse struct { +type GetVSchemaRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - WorkflowsByCluster map[string]*ClusterWorkflows `protobuf:"bytes,1,rep,name=workflows_by_cluster,json=workflowsByCluster,proto3" json:"workflows_by_cluster,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *GetWorkflowsResponse) Reset() { - *x = GetWorkflowsResponse{} +func (x *GetVSchemaRequest) Reset() { + *x = GetVSchemaRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3677,13 +3647,13 @@ func (x *GetWorkflowsResponse) Reset() { } } -func (x *GetWorkflowsResponse) String() string { +func (x *GetVSchemaRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkflowsResponse) ProtoMessage() {} +func (*GetVSchemaRequest) ProtoMessage() {} -func (x *GetWorkflowsResponse) ProtoReflect() protoreflect.Message { +func (x *GetVSchemaRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3695,33 +3665,35 @@ func (x *GetWorkflowsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkflowsResponse.ProtoReflect.Descriptor instead. -func (*GetWorkflowsResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetVSchemaRequest.ProtoReflect.Descriptor instead. +func (*GetVSchemaRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{62} } -func (x *GetWorkflowsResponse) GetWorkflowsByCluster() map[string]*ClusterWorkflows { +func (x *GetVSchemaRequest) GetClusterId() string { if x != nil { - return x.WorkflowsByCluster + return x.ClusterId } - return nil + return "" } -type PingTabletRequest struct { +func (x *GetVSchemaRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +type GetVSchemasRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Unique (per cluster) tablet alias of the standard form: "$cell-$uid" - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - // ClusterIDs is an optional parameter to narrow the scope of the search, if - // the caller knows which cluster the tablet may be in, or, to disambiguate - // if multiple clusters have a tablet with the same hostname. - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *PingTabletRequest) Reset() { - *x = PingTabletRequest{} +func (x *GetVSchemasRequest) Reset() { + *x = GetVSchemasRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3729,13 +3701,13 @@ func (x *PingTabletRequest) Reset() { } } -func (x *PingTabletRequest) String() string { +func (x *GetVSchemasRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PingTabletRequest) ProtoMessage() {} +func (*GetVSchemasRequest) ProtoMessage() {} -func (x *PingTabletRequest) ProtoReflect() protoreflect.Message { +func (x *GetVSchemasRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3747,36 +3719,28 @@ func (x *PingTabletRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PingTabletRequest.ProtoReflect.Descriptor instead. -func (*PingTabletRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetVSchemasRequest.ProtoReflect.Descriptor instead. +func (*GetVSchemasRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{63} } -func (x *PingTabletRequest) GetAlias() *topodata.TabletAlias { - if x != nil { - return x.Alias - } - return nil -} - -func (x *PingTabletRequest) GetClusterIds() []string { +func (x *GetVSchemasRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type PingTabletResponse struct { +type GetVSchemasResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + VSchemas []*VSchema `protobuf:"bytes,1,rep,name=v_schemas,json=vSchemas,proto3" json:"v_schemas,omitempty"` } -func (x *PingTabletResponse) Reset() { - *x = PingTabletResponse{} +func (x *GetVSchemasResponse) Reset() { + *x = GetVSchemasResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3784,13 +3748,13 @@ func (x *PingTabletResponse) Reset() { } } -func (x *PingTabletResponse) String() string { +func (x *GetVSchemasResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PingTabletResponse) ProtoMessage() {} +func (*GetVSchemasResponse) ProtoMessage() {} -func (x *PingTabletResponse) ProtoReflect() protoreflect.Message { +func (x *GetVSchemasResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3802,36 +3766,28 @@ func (x *PingTabletResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PingTabletResponse.ProtoReflect.Descriptor instead. -func (*PingTabletResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetVSchemasResponse.ProtoReflect.Descriptor instead. +func (*GetVSchemasResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{64} } -func (x *PingTabletResponse) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *PingTabletResponse) GetCluster() *Cluster { +func (x *GetVSchemasResponse) GetVSchemas() []*VSchema { if x != nil { - return x.Cluster + return x.VSchemas } return nil } -type PlannedFailoverShardRequest struct { +type GetVtctldsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Options *vtctldata.PlannedReparentShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *PlannedFailoverShardRequest) Reset() { - *x = PlannedFailoverShardRequest{} +func (x *GetVtctldsRequest) Reset() { + *x = GetVtctldsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3839,13 +3795,13 @@ func (x *PlannedFailoverShardRequest) Reset() { } } -func (x *PlannedFailoverShardRequest) String() string { +func (x *GetVtctldsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PlannedFailoverShardRequest) ProtoMessage() {} +func (*GetVtctldsRequest) ProtoMessage() {} -func (x *PlannedFailoverShardRequest) ProtoReflect() protoreflect.Message { +func (x *GetVtctldsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3857,43 +3813,28 @@ func (x *PlannedFailoverShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PlannedFailoverShardRequest.ProtoReflect.Descriptor instead. -func (*PlannedFailoverShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetVtctldsRequest.ProtoReflect.Descriptor instead. +func (*GetVtctldsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{65} } -func (x *PlannedFailoverShardRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *PlannedFailoverShardRequest) GetOptions() *vtctldata.PlannedReparentShardRequest { +func (x *GetVtctldsRequest) GetClusterIds() []string { if x != nil { - return x.Options + return x.ClusterIds } return nil } -type PlannedFailoverShardResponse struct { +type GetVtctldsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - // PromotedPrimary is the tablet alias that was promoted to shard primary. - // If NewPrimary was set in the request options, then this will be the - // same tablet alias. Otherwise, it will be the alias of the tablet found - // to be most up-to-date in the shard. - PromotedPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=promoted_primary,json=promotedPrimary,proto3" json:"promoted_primary,omitempty"` - Events []*logutil.Event `protobuf:"bytes,5,rep,name=events,proto3" json:"events,omitempty"` + Vtctlds []*Vtctld `protobuf:"bytes,1,rep,name=vtctlds,proto3" json:"vtctlds,omitempty"` } -func (x *PlannedFailoverShardResponse) Reset() { - *x = PlannedFailoverShardResponse{} +func (x *GetVtctldsResponse) Reset() { + *x = GetVtctldsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3901,13 +3842,13 @@ func (x *PlannedFailoverShardResponse) Reset() { } } -func (x *PlannedFailoverShardResponse) String() string { +func (x *GetVtctldsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PlannedFailoverShardResponse) ProtoMessage() {} +func (*GetVtctldsResponse) ProtoMessage() {} -func (x *PlannedFailoverShardResponse) ProtoReflect() protoreflect.Message { +func (x *GetVtctldsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3919,59 +3860,31 @@ func (x *PlannedFailoverShardResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PlannedFailoverShardResponse.ProtoReflect.Descriptor instead. -func (*PlannedFailoverShardResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetVtctldsResponse.ProtoReflect.Descriptor instead. +func (*GetVtctldsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{66} } -func (x *PlannedFailoverShardResponse) GetCluster() *Cluster { +func (x *GetVtctldsResponse) GetVtctlds() []*Vtctld { if x != nil { - return x.Cluster + return x.Vtctlds } return nil } -func (x *PlannedFailoverShardResponse) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *PlannedFailoverShardResponse) GetShard() string { - if x != nil { - return x.Shard - } - return "" -} - -func (x *PlannedFailoverShardResponse) GetPromotedPrimary() *topodata.TabletAlias { - if x != nil { - return x.PromotedPrimary - } - return nil -} - -func (x *PlannedFailoverShardResponse) GetEvents() []*logutil.Event { - if x != nil { - return x.Events - } - return nil -} - -type RebuildKeyspaceGraphRequest struct { +type GetWorkflowRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` - AllowPartial bool `protobuf:"varint,4,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + ActiveOnly bool `protobuf:"varint,4,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` } -func (x *RebuildKeyspaceGraphRequest) Reset() { - *x = RebuildKeyspaceGraphRequest{} +func (x *GetWorkflowRequest) Reset() { + *x = GetWorkflowRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3979,13 +3892,13 @@ func (x *RebuildKeyspaceGraphRequest) Reset() { } } -func (x *RebuildKeyspaceGraphRequest) String() string { +func (x *GetWorkflowRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RebuildKeyspaceGraphRequest) ProtoMessage() {} +func (*GetWorkflowRequest) ProtoMessage() {} -func (x *RebuildKeyspaceGraphRequest) ProtoReflect() protoreflect.Message { +func (x *GetWorkflowRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -3997,49 +3910,67 @@ func (x *RebuildKeyspaceGraphRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RebuildKeyspaceGraphRequest.ProtoReflect.Descriptor instead. -func (*RebuildKeyspaceGraphRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkflowRequest.ProtoReflect.Descriptor instead. +func (*GetWorkflowRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{67} } -func (x *RebuildKeyspaceGraphRequest) GetClusterId() string { +func (x *GetWorkflowRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *RebuildKeyspaceGraphRequest) GetKeyspace() string { +func (x *GetWorkflowRequest) GetKeyspace() string { if x != nil { return x.Keyspace } return "" } -func (x *RebuildKeyspaceGraphRequest) GetCells() []string { +func (x *GetWorkflowRequest) GetName() string { if x != nil { - return x.Cells + return x.Name } - return nil + return "" } -func (x *RebuildKeyspaceGraphRequest) GetAllowPartial() bool { +func (x *GetWorkflowRequest) GetActiveOnly() bool { if x != nil { - return x.AllowPartial + return x.ActiveOnly } return false } -type RebuildKeyspaceGraphResponse struct { +type GetWorkflowsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + ClusterIds []string `protobuf:"bytes,1,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // ActiveOnly specifies whether to return workflows that are currently + // active (running or paused) instead of all workflows. + ActiveOnly bool `protobuf:"varint,2,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` + // Keyspaces is a list of keyspaces to restrict the workflow search to. Note + // that the keyspaces list applies across all cluster IDs in the request. + // + // If, for example, you have two clusters, each with a keyspace called "foo" + // and want the workflows from "foo" in cluster1 but not from cluster2, you + // must make two requests. + // + // Keyspaces and IgnoreKeyspaces are mutually-exclusive, and Keyspaces takes + // precedence; if Keyspaces is a non-empty list, then IgnoreKeyspaces is + // ignored completely. + Keyspaces []string `protobuf:"bytes,3,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + // IgnoreKeyspaces is a list of keyspaces to skip during the workflow + // search. It has the same semantics as the Keyspaces parameter, so refer to + // that documentation for more details. + IgnoreKeyspaces []string `protobuf:"bytes,4,rep,name=ignore_keyspaces,json=ignoreKeyspaces,proto3" json:"ignore_keyspaces,omitempty"` } -func (x *RebuildKeyspaceGraphResponse) Reset() { - *x = RebuildKeyspaceGraphResponse{} +func (x *GetWorkflowsRequest) Reset() { + *x = GetWorkflowsRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4047,13 +3978,13 @@ func (x *RebuildKeyspaceGraphResponse) Reset() { } } -func (x *RebuildKeyspaceGraphResponse) String() string { +func (x *GetWorkflowsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RebuildKeyspaceGraphResponse) ProtoMessage() {} +func (*GetWorkflowsRequest) ProtoMessage() {} -func (x *RebuildKeyspaceGraphResponse) ProtoReflect() protoreflect.Message { +func (x *GetWorkflowsRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4065,29 +3996,49 @@ func (x *RebuildKeyspaceGraphResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RebuildKeyspaceGraphResponse.ProtoReflect.Descriptor instead. -func (*RebuildKeyspaceGraphResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkflowsRequest.ProtoReflect.Descriptor instead. +func (*GetWorkflowsRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{68} } -func (x *RebuildKeyspaceGraphResponse) GetStatus() string { +func (x *GetWorkflowsRequest) GetClusterIds() []string { if x != nil { - return x.Status + return x.ClusterIds } - return "" + return nil } -type RefreshStateRequest struct { +func (x *GetWorkflowsRequest) GetActiveOnly() bool { + if x != nil { + return x.ActiveOnly + } + return false +} + +func (x *GetWorkflowsRequest) GetKeyspaces() []string { + if x != nil { + return x.Keyspaces + } + return nil +} + +func (x *GetWorkflowsRequest) GetIgnoreKeyspaces() []string { + if x != nil { + return x.IgnoreKeyspaces + } + return nil +} + +type GetWorkflowsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + WorkflowsByCluster map[string]*ClusterWorkflows `protobuf:"bytes,1,rep,name=workflows_by_cluster,json=workflowsByCluster,proto3" json:"workflows_by_cluster,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *RefreshStateRequest) Reset() { - *x = RefreshStateRequest{} +func (x *GetWorkflowsResponse) Reset() { + *x = GetWorkflowsResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4095,13 +4046,13 @@ func (x *RefreshStateRequest) Reset() { } } -func (x *RefreshStateRequest) String() string { +func (x *GetWorkflowsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RefreshStateRequest) ProtoMessage() {} +func (*GetWorkflowsResponse) ProtoMessage() {} -func (x *RefreshStateRequest) ProtoReflect() protoreflect.Message { +func (x *GetWorkflowsResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4113,36 +4064,29 @@ func (x *RefreshStateRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RefreshStateRequest.ProtoReflect.Descriptor instead. -func (*RefreshStateRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkflowsResponse.ProtoReflect.Descriptor instead. +func (*GetWorkflowsResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{69} } -func (x *RefreshStateRequest) GetAlias() *topodata.TabletAlias { - if x != nil { - return x.Alias - } - return nil -} - -func (x *RefreshStateRequest) GetClusterIds() []string { +func (x *GetWorkflowsResponse) GetWorkflowsByCluster() map[string]*ClusterWorkflows { if x != nil { - return x.ClusterIds + return x.WorkflowsByCluster } return nil } -type RefreshStateResponse struct { +type LaunchSchemaMigrationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.LaunchSchemaMigrationRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *RefreshStateResponse) Reset() { - *x = RefreshStateResponse{} +func (x *LaunchSchemaMigrationRequest) Reset() { + *x = LaunchSchemaMigrationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4150,13 +4094,13 @@ func (x *RefreshStateResponse) Reset() { } } -func (x *RefreshStateResponse) String() string { +func (x *LaunchSchemaMigrationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RefreshStateResponse) ProtoMessage() {} +func (*LaunchSchemaMigrationRequest) ProtoMessage() {} -func (x *RefreshStateResponse) ProtoReflect() protoreflect.Message { +func (x *LaunchSchemaMigrationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4168,76 +4112,40 @@ func (x *RefreshStateResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RefreshStateResponse.ProtoReflect.Descriptor instead. -func (*RefreshStateResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use LaunchSchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*LaunchSchemaMigrationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{70} } -func (x *RefreshStateResponse) GetStatus() string { +func (x *LaunchSchemaMigrationRequest) GetClusterId() string { if x != nil { - return x.Status + return x.ClusterId } return "" } -func (x *RefreshStateResponse) GetCluster() *Cluster { +func (x *LaunchSchemaMigrationRequest) GetRequest() *vtctldata.LaunchSchemaMigrationRequest { if x != nil { - return x.Cluster + return x.Request } return nil } -type ReloadSchemasRequest struct { +type PingTabletRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Keyspaces, if set, will reload schemas across one or more keyspaces. A - // keyspace not existing in a cluster will not fail the overall request. - // - // Superceded by KeyspaceShards and Tablets, in that order. - Keyspaces []string `protobuf:"bytes,1,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` - // KeyspaceShards, if set, will reload schemas across one or more shards. - // Each element must be a valid keyspace/shard according to - // topoproto.ParseKeyspaceShard. A shard not existing in a cluster will not - // fail the overall request. - // - // Supercedes Keyspaces, and is superceded by Tablets. - KeyspaceShards []string `protobuf:"bytes,2,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` - // Tablets, if set will reload schemas across one or more tablets. - // Supercedes both Keyspaces and KeyspaceShards. - Tablets []*topodata.TabletAlias `protobuf:"bytes,3,rep,name=tablets,proto3" json:"tablets,omitempty"` - // ClusterIds optionally restricts the reload operation to clusters with - // the specified IDs. An empty list of ClusterIds will operate on all - // clusters. - ClusterIds []string `protobuf:"bytes,4,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` - // Concurrency controls the number of tablets to reload at any given time. - // Its semantics depend on whether the request is for keyspace, shard, or - // tablet mode. - // - // In Keyspaces mode, Concurrency is the number of tablets to reload at once - // *per keyspace*. - // - // In KeyspaceShards mode, Concurrency is the number of tablets to reload at - // once *per shard*. - // - // In Tablets mode, Concurrency is the number of tablets to reload at once - // *per cluster*. - Concurrency uint32 `protobuf:"varint,5,opt,name=concurrency,proto3" json:"concurrency,omitempty"` - // WaitPosition is the replication position that replicating tablets should - // reach prior to reloading their schemas. - // - // Does not apply in Tablets mode. - WaitPosition string `protobuf:"bytes,6,opt,name=wait_position,json=waitPosition,proto3" json:"wait_position,omitempty"` - // IncludePrimary, if set, will reload the schemas on PRIMARY tablets as - // well as REPLICA and RDONLY. - // - // Does not apply in Tablets mode. - IncludePrimary bool `protobuf:"varint,7,opt,name=include_primary,json=includePrimary,proto3" json:"include_primary,omitempty"` + // Unique (per cluster) tablet alias of the standard form: "$cell-$uid" + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + // ClusterIDs is an optional parameter to narrow the scope of the search, if + // the caller knows which cluster the tablet may be in, or, to disambiguate + // if multiple clusters have a tablet with the same hostname. + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *ReloadSchemasRequest) Reset() { - *x = ReloadSchemasRequest{} +func (x *PingTabletRequest) Reset() { + *x = PingTabletRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4245,13 +4153,13 @@ func (x *ReloadSchemasRequest) Reset() { } } -func (x *ReloadSchemasRequest) String() string { +func (x *PingTabletRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemasRequest) ProtoMessage() {} +func (*PingTabletRequest) ProtoMessage() {} -func (x *ReloadSchemasRequest) ProtoReflect() protoreflect.Message { +func (x *PingTabletRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4263,81 +4171,36 @@ func (x *ReloadSchemasRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemasRequest.ProtoReflect.Descriptor instead. -func (*ReloadSchemasRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use PingTabletRequest.ProtoReflect.Descriptor instead. +func (*PingTabletRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{71} } -func (x *ReloadSchemasRequest) GetKeyspaces() []string { +func (x *PingTabletRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.Keyspaces + return x.Alias } return nil } -func (x *ReloadSchemasRequest) GetKeyspaceShards() []string { +func (x *PingTabletRequest) GetClusterIds() []string { if x != nil { - return x.KeyspaceShards + return x.ClusterIds } return nil } -func (x *ReloadSchemasRequest) GetTablets() []*topodata.TabletAlias { - if x != nil { - return x.Tablets - } - return nil -} +type PingTabletResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (x *ReloadSchemasRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -func (x *ReloadSchemasRequest) GetConcurrency() uint32 { - if x != nil { - return x.Concurrency - } - return 0 -} - -func (x *ReloadSchemasRequest) GetWaitPosition() string { - if x != nil { - return x.WaitPosition - } - return "" -} - -func (x *ReloadSchemasRequest) GetIncludePrimary() bool { - if x != nil { - return x.IncludePrimary - } - return false -} - -type ReloadSchemasResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // KeyspaceResults is the list of KeyspaceResult objects for a ReloadSchemas - // operation. It is only set when the request mandates Keyspaces mode (see - // ReloadSchemasRequest). - KeyspaceResults []*ReloadSchemasResponse_KeyspaceResult `protobuf:"bytes,1,rep,name=keyspace_results,json=keyspaceResults,proto3" json:"keyspace_results,omitempty"` - // ShardResults is the list of ShardResult objects for a ReloadSchemas - // operation. It is only set when the request mandates KeyspaceShards mode - // (see ReloadSchemasRequest). - ShardResults []*ReloadSchemasResponse_ShardResult `protobuf:"bytes,2,rep,name=shard_results,json=shardResults,proto3" json:"shard_results,omitempty"` - // TabletResults is the list of TabletResult objects for a ReloadSchemas - // operation. It is only set when the request mandates Tablets mode (see - // ReloadSchemasRequest). - TabletResults []*ReloadSchemasResponse_TabletResult `protobuf:"bytes,3,rep,name=tablet_results,json=tabletResults,proto3" json:"tablet_results,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *ReloadSchemasResponse) Reset() { - *x = ReloadSchemasResponse{} +func (x *PingTabletResponse) Reset() { + *x = PingTabletResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4345,13 +4208,13 @@ func (x *ReloadSchemasResponse) Reset() { } } -func (x *ReloadSchemasResponse) String() string { +func (x *PingTabletResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemasResponse) ProtoMessage() {} +func (*PingTabletResponse) ProtoMessage() {} -func (x *ReloadSchemasResponse) ProtoReflect() protoreflect.Message { +func (x *PingTabletResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4363,47 +4226,36 @@ func (x *ReloadSchemasResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemasResponse.ProtoReflect.Descriptor instead. -func (*ReloadSchemasResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use PingTabletResponse.ProtoReflect.Descriptor instead. +func (*PingTabletResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{72} } -func (x *ReloadSchemasResponse) GetKeyspaceResults() []*ReloadSchemasResponse_KeyspaceResult { - if x != nil { - return x.KeyspaceResults - } - return nil -} - -func (x *ReloadSchemasResponse) GetShardResults() []*ReloadSchemasResponse_ShardResult { +func (x *PingTabletResponse) GetStatus() string { if x != nil { - return x.ShardResults + return x.Status } - return nil + return "" } -func (x *ReloadSchemasResponse) GetTabletResults() []*ReloadSchemasResponse_TabletResult { +func (x *PingTabletResponse) GetCluster() *Cluster { if x != nil { - return x.TabletResults + return x.Cluster } return nil } -type ReloadSchemaShardRequest struct { +type PlannedFailoverShardRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - WaitPosition string `protobuf:"bytes,4,opt,name=wait_position,json=waitPosition,proto3" json:"wait_position,omitempty"` - IncludePrimary bool `protobuf:"varint,5,opt,name=include_primary,json=includePrimary,proto3" json:"include_primary,omitempty"` - Concurrency uint32 `protobuf:"varint,6,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Options *vtctldata.PlannedReparentShardRequest `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (x *ReloadSchemaShardRequest) Reset() { - *x = ReloadSchemaShardRequest{} +func (x *PlannedFailoverShardRequest) Reset() { + *x = PlannedFailoverShardRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4411,13 +4263,13 @@ func (x *ReloadSchemaShardRequest) Reset() { } } -func (x *ReloadSchemaShardRequest) String() string { +func (x *PlannedFailoverShardRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemaShardRequest) ProtoMessage() {} +func (*PlannedFailoverShardRequest) ProtoMessage() {} -func (x *ReloadSchemaShardRequest) ProtoReflect() protoreflect.Message { +func (x *PlannedFailoverShardRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4429,63 +4281,43 @@ func (x *ReloadSchemaShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemaShardRequest.ProtoReflect.Descriptor instead. -func (*ReloadSchemaShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use PlannedFailoverShardRequest.ProtoReflect.Descriptor instead. +func (*PlannedFailoverShardRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{73} } -func (x *ReloadSchemaShardRequest) GetClusterId() string { +func (x *PlannedFailoverShardRequest) GetClusterId() string { if x != nil { return x.ClusterId } return "" } -func (x *ReloadSchemaShardRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *ReloadSchemaShardRequest) GetShard() string { - if x != nil { - return x.Shard - } - return "" -} - -func (x *ReloadSchemaShardRequest) GetWaitPosition() string { - if x != nil { - return x.WaitPosition - } - return "" -} - -func (x *ReloadSchemaShardRequest) GetIncludePrimary() bool { - if x != nil { - return x.IncludePrimary - } - return false -} - -func (x *ReloadSchemaShardRequest) GetConcurrency() uint32 { +func (x *PlannedFailoverShardRequest) GetOptions() *vtctldata.PlannedReparentShardRequest { if x != nil { - return x.Concurrency + return x.Options } - return 0 + return nil } -type ReloadSchemaShardResponse struct { +type PlannedFailoverShardResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Events []*logutil.Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + // PromotedPrimary is the tablet alias that was promoted to shard primary. + // If NewPrimary was set in the request options, then this will be the + // same tablet alias. Otherwise, it will be the alias of the tablet found + // to be most up-to-date in the shard. + PromotedPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=promoted_primary,json=promotedPrimary,proto3" json:"promoted_primary,omitempty"` + Events []*logutil.Event `protobuf:"bytes,5,rep,name=events,proto3" json:"events,omitempty"` } -func (x *ReloadSchemaShardResponse) Reset() { - *x = ReloadSchemaShardResponse{} +func (x *PlannedFailoverShardResponse) Reset() { + *x = PlannedFailoverShardResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4493,13 +4325,13 @@ func (x *ReloadSchemaShardResponse) Reset() { } } -func (x *ReloadSchemaShardResponse) String() string { +func (x *PlannedFailoverShardResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemaShardResponse) ProtoMessage() {} +func (*PlannedFailoverShardResponse) ProtoMessage() {} -func (x *ReloadSchemaShardResponse) ProtoReflect() protoreflect.Message { +func (x *PlannedFailoverShardResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4511,29 +4343,59 @@ func (x *ReloadSchemaShardResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemaShardResponse.ProtoReflect.Descriptor instead. -func (*ReloadSchemaShardResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use PlannedFailoverShardResponse.ProtoReflect.Descriptor instead. +func (*PlannedFailoverShardResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{74} } -func (x *ReloadSchemaShardResponse) GetEvents() []*logutil.Event { +func (x *PlannedFailoverShardResponse) GetCluster() *Cluster { + if x != nil { + return x.Cluster + } + return nil +} + +func (x *PlannedFailoverShardResponse) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *PlannedFailoverShardResponse) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +func (x *PlannedFailoverShardResponse) GetPromotedPrimary() *topodata.TabletAlias { + if x != nil { + return x.PromotedPrimary + } + return nil +} + +func (x *PlannedFailoverShardResponse) GetEvents() []*logutil.Event { if x != nil { return x.Events } return nil } -type RefreshTabletReplicationSourceRequest struct { +type RebuildKeyspaceGraphRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` + AllowPartial bool `protobuf:"varint,4,opt,name=allow_partial,json=allowPartial,proto3" json:"allow_partial,omitempty"` } -func (x *RefreshTabletReplicationSourceRequest) Reset() { - *x = RefreshTabletReplicationSourceRequest{} +func (x *RebuildKeyspaceGraphRequest) Reset() { + *x = RebuildKeyspaceGraphRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4541,13 +4403,13 @@ func (x *RefreshTabletReplicationSourceRequest) Reset() { } } -func (x *RefreshTabletReplicationSourceRequest) String() string { +func (x *RebuildKeyspaceGraphRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RefreshTabletReplicationSourceRequest) ProtoMessage() {} +func (*RebuildKeyspaceGraphRequest) ProtoMessage() {} -func (x *RefreshTabletReplicationSourceRequest) ProtoReflect() protoreflect.Message { +func (x *RebuildKeyspaceGraphRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4559,38 +4421,49 @@ func (x *RefreshTabletReplicationSourceRequest) ProtoReflect() protoreflect.Mess return mi.MessageOf(x) } -// Deprecated: Use RefreshTabletReplicationSourceRequest.ProtoReflect.Descriptor instead. -func (*RefreshTabletReplicationSourceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RebuildKeyspaceGraphRequest.ProtoReflect.Descriptor instead. +func (*RebuildKeyspaceGraphRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{75} } -func (x *RefreshTabletReplicationSourceRequest) GetAlias() *topodata.TabletAlias { +func (x *RebuildKeyspaceGraphRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *RefreshTabletReplicationSourceRequest) GetClusterIds() []string { +func (x *RebuildKeyspaceGraphRequest) GetKeyspace() string { if x != nil { - return x.ClusterIds + return x.Keyspace + } + return "" +} + +func (x *RebuildKeyspaceGraphRequest) GetCells() []string { + if x != nil { + return x.Cells } return nil } -type RefreshTabletReplicationSourceResponse struct { +func (x *RebuildKeyspaceGraphRequest) GetAllowPartial() bool { + if x != nil { + return x.AllowPartial + } + return false +} + +type RebuildKeyspaceGraphResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` - Primary *topodata.TabletAlias `protobuf:"bytes,3,opt,name=primary,proto3" json:"primary,omitempty"` - Cluster *Cluster `protobuf:"bytes,4,opt,name=cluster,proto3" json:"cluster,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` } -func (x *RefreshTabletReplicationSourceResponse) Reset() { - *x = RefreshTabletReplicationSourceResponse{} +func (x *RebuildKeyspaceGraphResponse) Reset() { + *x = RebuildKeyspaceGraphResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4598,13 +4471,13 @@ func (x *RefreshTabletReplicationSourceResponse) Reset() { } } -func (x *RefreshTabletReplicationSourceResponse) String() string { +func (x *RebuildKeyspaceGraphResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RefreshTabletReplicationSourceResponse) ProtoMessage() {} +func (*RebuildKeyspaceGraphResponse) ProtoMessage() {} -func (x *RefreshTabletReplicationSourceResponse) ProtoReflect() protoreflect.Message { +func (x *RebuildKeyspaceGraphResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4616,53 +4489,29 @@ func (x *RefreshTabletReplicationSourceResponse) ProtoReflect() protoreflect.Mes return mi.MessageOf(x) } -// Deprecated: Use RefreshTabletReplicationSourceResponse.ProtoReflect.Descriptor instead. -func (*RefreshTabletReplicationSourceResponse) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{76} -} - -func (x *RefreshTabletReplicationSourceResponse) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" +// Deprecated: Use RebuildKeyspaceGraphResponse.ProtoReflect.Descriptor instead. +func (*RebuildKeyspaceGraphResponse) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{76} } -func (x *RefreshTabletReplicationSourceResponse) GetShard() string { +func (x *RebuildKeyspaceGraphResponse) GetStatus() string { if x != nil { - return x.Shard + return x.Status } return "" } -func (x *RefreshTabletReplicationSourceResponse) GetPrimary() *topodata.TabletAlias { - if x != nil { - return x.Primary - } - return nil -} - -func (x *RefreshTabletReplicationSourceResponse) GetCluster() *Cluster { - if x != nil { - return x.Cluster - } - return nil -} - -type RemoveKeyspaceCellRequest struct { +type RefreshStateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Cell string `protobuf:"bytes,3,opt,name=cell,proto3" json:"cell,omitempty"` - Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` - Recursive bool `protobuf:"varint,5,opt,name=recursive,proto3" json:"recursive,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *RemoveKeyspaceCellRequest) Reset() { - *x = RemoveKeyspaceCellRequest{} +func (x *RefreshStateRequest) Reset() { + *x = RefreshStateRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4670,13 +4519,13 @@ func (x *RemoveKeyspaceCellRequest) Reset() { } } -func (x *RemoveKeyspaceCellRequest) String() string { +func (x *RefreshStateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveKeyspaceCellRequest) ProtoMessage() {} +func (*RefreshStateRequest) ProtoMessage() {} -func (x *RemoveKeyspaceCellRequest) ProtoReflect() protoreflect.Message { +func (x *RefreshStateRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4688,56 +4537,36 @@ func (x *RemoveKeyspaceCellRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveKeyspaceCellRequest.ProtoReflect.Descriptor instead. -func (*RemoveKeyspaceCellRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RefreshStateRequest.ProtoReflect.Descriptor instead. +func (*RefreshStateRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{77} } -func (x *RemoveKeyspaceCellRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *RemoveKeyspaceCellRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *RemoveKeyspaceCellRequest) GetCell() string { - if x != nil { - return x.Cell - } - return "" -} - -func (x *RemoveKeyspaceCellRequest) GetForce() bool { +func (x *RefreshStateRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.Force + return x.Alias } - return false + return nil } -func (x *RemoveKeyspaceCellRequest) GetRecursive() bool { +func (x *RefreshStateRequest) GetClusterIds() []string { if x != nil { - return x.Recursive + return x.ClusterIds } - return false + return nil } -type RemoveKeyspaceCellResponse struct { +type RefreshStateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *RemoveKeyspaceCellResponse) Reset() { - *x = RemoveKeyspaceCellResponse{} +func (x *RefreshStateResponse) Reset() { + *x = RefreshStateResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4745,13 +4574,13 @@ func (x *RemoveKeyspaceCellResponse) Reset() { } } -func (x *RemoveKeyspaceCellResponse) String() string { +func (x *RefreshStateResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RemoveKeyspaceCellResponse) ProtoMessage() {} +func (*RefreshStateResponse) ProtoMessage() {} -func (x *RemoveKeyspaceCellResponse) ProtoReflect() protoreflect.Message { +func (x *RefreshStateResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4763,29 +4592,76 @@ func (x *RemoveKeyspaceCellResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RemoveKeyspaceCellResponse.ProtoReflect.Descriptor instead. -func (*RemoveKeyspaceCellResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use RefreshStateResponse.ProtoReflect.Descriptor instead. +func (*RefreshStateResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{78} } -func (x *RemoveKeyspaceCellResponse) GetStatus() string { +func (x *RefreshStateResponse) GetStatus() string { if x != nil { return x.Status } return "" } -type RunHealthCheckRequest struct { +func (x *RefreshStateResponse) GetCluster() *Cluster { + if x != nil { + return x.Cluster + } + return nil +} + +type ReloadSchemasRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Keyspaces, if set, will reload schemas across one or more keyspaces. A + // keyspace not existing in a cluster will not fail the overall request. + // + // Superceded by KeyspaceShards and Tablets, in that order. + Keyspaces []string `protobuf:"bytes,1,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + // KeyspaceShards, if set, will reload schemas across one or more shards. + // Each element must be a valid keyspace/shard according to + // topoproto.ParseKeyspaceShard. A shard not existing in a cluster will not + // fail the overall request. + // + // Supercedes Keyspaces, and is superceded by Tablets. + KeyspaceShards []string `protobuf:"bytes,2,rep,name=keyspace_shards,json=keyspaceShards,proto3" json:"keyspace_shards,omitempty"` + // Tablets, if set will reload schemas across one or more tablets. + // Supercedes both Keyspaces and KeyspaceShards. + Tablets []*topodata.TabletAlias `protobuf:"bytes,3,rep,name=tablets,proto3" json:"tablets,omitempty"` + // ClusterIds optionally restricts the reload operation to clusters with + // the specified IDs. An empty list of ClusterIds will operate on all + // clusters. + ClusterIds []string `protobuf:"bytes,4,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + // Concurrency controls the number of tablets to reload at any given time. + // Its semantics depend on whether the request is for keyspace, shard, or + // tablet mode. + // + // In Keyspaces mode, Concurrency is the number of tablets to reload at once + // *per keyspace*. + // + // In KeyspaceShards mode, Concurrency is the number of tablets to reload at + // once *per shard*. + // + // In Tablets mode, Concurrency is the number of tablets to reload at once + // *per cluster*. + Concurrency int32 `protobuf:"varint,5,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + // WaitPosition is the replication position that replicating tablets should + // reach prior to reloading their schemas. + // + // Does not apply in Tablets mode. + WaitPosition string `protobuf:"bytes,6,opt,name=wait_position,json=waitPosition,proto3" json:"wait_position,omitempty"` + // IncludePrimary, if set, will reload the schemas on PRIMARY tablets as + // well as REPLICA and RDONLY. + // + // Does not apply in Tablets mode. + IncludePrimary bool `protobuf:"varint,7,opt,name=include_primary,json=includePrimary,proto3" json:"include_primary,omitempty"` } -func (x *RunHealthCheckRequest) Reset() { - *x = RunHealthCheckRequest{} +func (x *ReloadSchemasRequest) Reset() { + *x = ReloadSchemasRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4793,13 +4669,13 @@ func (x *RunHealthCheckRequest) Reset() { } } -func (x *RunHealthCheckRequest) String() string { +func (x *ReloadSchemasRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RunHealthCheckRequest) ProtoMessage() {} +func (*ReloadSchemasRequest) ProtoMessage() {} -func (x *RunHealthCheckRequest) ProtoReflect() protoreflect.Message { +func (x *ReloadSchemasRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4811,36 +4687,81 @@ func (x *RunHealthCheckRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RunHealthCheckRequest.ProtoReflect.Descriptor instead. -func (*RunHealthCheckRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ReloadSchemasRequest.ProtoReflect.Descriptor instead. +func (*ReloadSchemasRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{79} } -func (x *RunHealthCheckRequest) GetAlias() *topodata.TabletAlias { +func (x *ReloadSchemasRequest) GetKeyspaces() []string { if x != nil { - return x.Alias + return x.Keyspaces } return nil } -func (x *RunHealthCheckRequest) GetClusterIds() []string { +func (x *ReloadSchemasRequest) GetKeyspaceShards() []string { + if x != nil { + return x.KeyspaceShards + } + return nil +} + +func (x *ReloadSchemasRequest) GetTablets() []*topodata.TabletAlias { + if x != nil { + return x.Tablets + } + return nil +} + +func (x *ReloadSchemasRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type RunHealthCheckResponse struct { +func (x *ReloadSchemasRequest) GetConcurrency() int32 { + if x != nil { + return x.Concurrency + } + return 0 +} + +func (x *ReloadSchemasRequest) GetWaitPosition() string { + if x != nil { + return x.WaitPosition + } + return "" +} + +func (x *ReloadSchemasRequest) GetIncludePrimary() bool { + if x != nil { + return x.IncludePrimary + } + return false +} + +type ReloadSchemasResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + // KeyspaceResults is the list of KeyspaceResult objects for a ReloadSchemas + // operation. It is only set when the request mandates Keyspaces mode (see + // ReloadSchemasRequest). + KeyspaceResults []*ReloadSchemasResponse_KeyspaceResult `protobuf:"bytes,1,rep,name=keyspace_results,json=keyspaceResults,proto3" json:"keyspace_results,omitempty"` + // ShardResults is the list of ShardResult objects for a ReloadSchemas + // operation. It is only set when the request mandates KeyspaceShards mode + // (see ReloadSchemasRequest). + ShardResults []*ReloadSchemasResponse_ShardResult `protobuf:"bytes,2,rep,name=shard_results,json=shardResults,proto3" json:"shard_results,omitempty"` + // TabletResults is the list of TabletResult objects for a ReloadSchemas + // operation. It is only set when the request mandates Tablets mode (see + // ReloadSchemasRequest). + TabletResults []*ReloadSchemasResponse_TabletResult `protobuf:"bytes,3,rep,name=tablet_results,json=tabletResults,proto3" json:"tablet_results,omitempty"` } -func (x *RunHealthCheckResponse) Reset() { - *x = RunHealthCheckResponse{} +func (x *ReloadSchemasResponse) Reset() { + *x = ReloadSchemasResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4848,13 +4769,13 @@ func (x *RunHealthCheckResponse) Reset() { } } -func (x *RunHealthCheckResponse) String() string { +func (x *ReloadSchemasResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RunHealthCheckResponse) ProtoMessage() {} +func (*ReloadSchemasResponse) ProtoMessage() {} -func (x *RunHealthCheckResponse) ProtoReflect() protoreflect.Message { +func (x *ReloadSchemasResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4866,36 +4787,47 @@ func (x *RunHealthCheckResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RunHealthCheckResponse.ProtoReflect.Descriptor instead. -func (*RunHealthCheckResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ReloadSchemasResponse.ProtoReflect.Descriptor instead. +func (*ReloadSchemasResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{80} } -func (x *RunHealthCheckResponse) GetStatus() string { +func (x *ReloadSchemasResponse) GetKeyspaceResults() []*ReloadSchemasResponse_KeyspaceResult { if x != nil { - return x.Status + return x.KeyspaceResults } - return "" + return nil } -func (x *RunHealthCheckResponse) GetCluster() *Cluster { +func (x *ReloadSchemasResponse) GetShardResults() []*ReloadSchemasResponse_ShardResult { if x != nil { - return x.Cluster + return x.ShardResults } return nil } -type SetReadOnlyRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` +func (x *ReloadSchemasResponse) GetTabletResults() []*ReloadSchemasResponse_TabletResult { + if x != nil { + return x.TabletResults + } + return nil } -func (x *SetReadOnlyRequest) Reset() { - *x = SetReadOnlyRequest{} +type ReloadSchemaShardRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + WaitPosition string `protobuf:"bytes,4,opt,name=wait_position,json=waitPosition,proto3" json:"wait_position,omitempty"` + IncludePrimary bool `protobuf:"varint,5,opt,name=include_primary,json=includePrimary,proto3" json:"include_primary,omitempty"` + Concurrency int32 `protobuf:"varint,6,opt,name=concurrency,proto3" json:"concurrency,omitempty"` +} + +func (x *ReloadSchemaShardRequest) Reset() { + *x = ReloadSchemaShardRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4903,13 +4835,13 @@ func (x *SetReadOnlyRequest) Reset() { } } -func (x *SetReadOnlyRequest) String() string { +func (x *ReloadSchemaShardRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetReadOnlyRequest) ProtoMessage() {} +func (*ReloadSchemaShardRequest) ProtoMessage() {} -func (x *SetReadOnlyRequest) ProtoReflect() protoreflect.Message { +func (x *ReloadSchemaShardRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4921,33 +4853,63 @@ func (x *SetReadOnlyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetReadOnlyRequest.ProtoReflect.Descriptor instead. -func (*SetReadOnlyRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ReloadSchemaShardRequest.ProtoReflect.Descriptor instead. +func (*ReloadSchemaShardRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{81} } -func (x *SetReadOnlyRequest) GetAlias() *topodata.TabletAlias { +func (x *ReloadSchemaShardRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *SetReadOnlyRequest) GetClusterIds() []string { +func (x *ReloadSchemaShardRequest) GetKeyspace() string { if x != nil { - return x.ClusterIds + return x.Keyspace } - return nil + return "" } -type SetReadOnlyResponse struct { +func (x *ReloadSchemaShardRequest) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +func (x *ReloadSchemaShardRequest) GetWaitPosition() string { + if x != nil { + return x.WaitPosition + } + return "" +} + +func (x *ReloadSchemaShardRequest) GetIncludePrimary() bool { + if x != nil { + return x.IncludePrimary + } + return false +} + +func (x *ReloadSchemaShardRequest) GetConcurrency() int32 { + if x != nil { + return x.Concurrency + } + return 0 +} + +type ReloadSchemaShardResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Events []*logutil.Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` } -func (x *SetReadOnlyResponse) Reset() { - *x = SetReadOnlyResponse{} +func (x *ReloadSchemaShardResponse) Reset() { + *x = ReloadSchemaShardResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4955,13 +4917,13 @@ func (x *SetReadOnlyResponse) Reset() { } } -func (x *SetReadOnlyResponse) String() string { +func (x *ReloadSchemaShardResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetReadOnlyResponse) ProtoMessage() {} +func (*ReloadSchemaShardResponse) ProtoMessage() {} -func (x *SetReadOnlyResponse) ProtoReflect() protoreflect.Message { +func (x *ReloadSchemaShardResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4973,12 +4935,19 @@ func (x *SetReadOnlyResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetReadOnlyResponse.ProtoReflect.Descriptor instead. -func (*SetReadOnlyResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ReloadSchemaShardResponse.ProtoReflect.Descriptor instead. +func (*ReloadSchemaShardResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{82} } -type SetReadWriteRequest struct { +func (x *ReloadSchemaShardResponse) GetEvents() []*logutil.Event { + if x != nil { + return x.Events + } + return nil +} + +type RefreshTabletReplicationSourceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -4987,8 +4956,8 @@ type SetReadWriteRequest struct { ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *SetReadWriteRequest) Reset() { - *x = SetReadWriteRequest{} +func (x *RefreshTabletReplicationSourceRequest) Reset() { + *x = RefreshTabletReplicationSourceRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -4996,13 +4965,13 @@ func (x *SetReadWriteRequest) Reset() { } } -func (x *SetReadWriteRequest) String() string { +func (x *RefreshTabletReplicationSourceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetReadWriteRequest) ProtoMessage() {} +func (*RefreshTabletReplicationSourceRequest) ProtoMessage() {} -func (x *SetReadWriteRequest) ProtoReflect() protoreflect.Message { +func (x *RefreshTabletReplicationSourceRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5014,33 +4983,38 @@ func (x *SetReadWriteRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetReadWriteRequest.ProtoReflect.Descriptor instead. -func (*SetReadWriteRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RefreshTabletReplicationSourceRequest.ProtoReflect.Descriptor instead. +func (*RefreshTabletReplicationSourceRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{83} } -func (x *SetReadWriteRequest) GetAlias() *topodata.TabletAlias { +func (x *RefreshTabletReplicationSourceRequest) GetAlias() *topodata.TabletAlias { if x != nil { return x.Alias } return nil } -func (x *SetReadWriteRequest) GetClusterIds() []string { +func (x *RefreshTabletReplicationSourceRequest) GetClusterIds() []string { if x != nil { return x.ClusterIds } return nil } -type SetReadWriteResponse struct { +type RefreshTabletReplicationSourceResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + Primary *topodata.TabletAlias `protobuf:"bytes,3,opt,name=primary,proto3" json:"primary,omitempty"` + Cluster *Cluster `protobuf:"bytes,4,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *SetReadWriteResponse) Reset() { - *x = SetReadWriteResponse{} +func (x *RefreshTabletReplicationSourceResponse) Reset() { + *x = RefreshTabletReplicationSourceResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5048,13 +5022,13 @@ func (x *SetReadWriteResponse) Reset() { } } -func (x *SetReadWriteResponse) String() string { +func (x *RefreshTabletReplicationSourceResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetReadWriteResponse) ProtoMessage() {} +func (*RefreshTabletReplicationSourceResponse) ProtoMessage() {} -func (x *SetReadWriteResponse) ProtoReflect() protoreflect.Message { +func (x *RefreshTabletReplicationSourceResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5066,22 +5040,53 @@ func (x *SetReadWriteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetReadWriteResponse.ProtoReflect.Descriptor instead. -func (*SetReadWriteResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use RefreshTabletReplicationSourceResponse.ProtoReflect.Descriptor instead. +func (*RefreshTabletReplicationSourceResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{84} } -type StartReplicationRequest struct { +func (x *RefreshTabletReplicationSourceResponse) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *RefreshTabletReplicationSourceResponse) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +func (x *RefreshTabletReplicationSourceResponse) GetPrimary() *topodata.TabletAlias { + if x != nil { + return x.Primary + } + return nil +} + +func (x *RefreshTabletReplicationSourceResponse) GetCluster() *Cluster { + if x != nil { + return x.Cluster + } + return nil +} + +type RemoveKeyspaceCellRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Cell string `protobuf:"bytes,3,opt,name=cell,proto3" json:"cell,omitempty"` + Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` + Recursive bool `protobuf:"varint,5,opt,name=recursive,proto3" json:"recursive,omitempty"` } -func (x *StartReplicationRequest) Reset() { - *x = StartReplicationRequest{} +func (x *RemoveKeyspaceCellRequest) Reset() { + *x = RemoveKeyspaceCellRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5089,13 +5094,13 @@ func (x *StartReplicationRequest) Reset() { } } -func (x *StartReplicationRequest) String() string { +func (x *RemoveKeyspaceCellRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StartReplicationRequest) ProtoMessage() {} +func (*RemoveKeyspaceCellRequest) ProtoMessage() {} -func (x *StartReplicationRequest) ProtoReflect() protoreflect.Message { +func (x *RemoveKeyspaceCellRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5107,36 +5112,56 @@ func (x *StartReplicationRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StartReplicationRequest.ProtoReflect.Descriptor instead. -func (*StartReplicationRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RemoveKeyspaceCellRequest.ProtoReflect.Descriptor instead. +func (*RemoveKeyspaceCellRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{85} } -func (x *StartReplicationRequest) GetAlias() *topodata.TabletAlias { +func (x *RemoveKeyspaceCellRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *StartReplicationRequest) GetClusterIds() []string { +func (x *RemoveKeyspaceCellRequest) GetKeyspace() string { if x != nil { - return x.ClusterIds + return x.Keyspace } - return nil + return "" } -type StartReplicationResponse struct { +func (x *RemoveKeyspaceCellRequest) GetCell() string { + if x != nil { + return x.Cell + } + return "" +} + +func (x *RemoveKeyspaceCellRequest) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *RemoveKeyspaceCellRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +type RemoveKeyspaceCellResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` } -func (x *StartReplicationResponse) Reset() { - *x = StartReplicationResponse{} +func (x *RemoveKeyspaceCellResponse) Reset() { + *x = RemoveKeyspaceCellResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5144,13 +5169,13 @@ func (x *StartReplicationResponse) Reset() { } } -func (x *StartReplicationResponse) String() string { +func (x *RemoveKeyspaceCellResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StartReplicationResponse) ProtoMessage() {} +func (*RemoveKeyspaceCellResponse) ProtoMessage() {} -func (x *StartReplicationResponse) ProtoReflect() protoreflect.Message { +func (x *RemoveKeyspaceCellResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5162,36 +5187,29 @@ func (x *StartReplicationResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StartReplicationResponse.ProtoReflect.Descriptor instead. -func (*StartReplicationResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use RemoveKeyspaceCellResponse.ProtoReflect.Descriptor instead. +func (*RemoveKeyspaceCellResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{86} } -func (x *StartReplicationResponse) GetStatus() string { +func (x *RemoveKeyspaceCellResponse) GetStatus() string { if x != nil { return x.Status } return "" } -func (x *StartReplicationResponse) GetCluster() *Cluster { - if x != nil { - return x.Cluster - } - return nil -} - -type StopReplicationRequest struct { +type RetrySchemaMigrationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.RetrySchemaMigrationRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` } -func (x *StopReplicationRequest) Reset() { - *x = StopReplicationRequest{} +func (x *RetrySchemaMigrationRequest) Reset() { + *x = RetrySchemaMigrationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5199,13 +5217,13 @@ func (x *StopReplicationRequest) Reset() { } } -func (x *StopReplicationRequest) String() string { +func (x *RetrySchemaMigrationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StopReplicationRequest) ProtoMessage() {} +func (*RetrySchemaMigrationRequest) ProtoMessage() {} -func (x *StopReplicationRequest) ProtoReflect() protoreflect.Message { +func (x *RetrySchemaMigrationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5217,36 +5235,36 @@ func (x *StopReplicationRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StopReplicationRequest.ProtoReflect.Descriptor instead. -func (*StopReplicationRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RetrySchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*RetrySchemaMigrationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{87} } -func (x *StopReplicationRequest) GetAlias() *topodata.TabletAlias { +func (x *RetrySchemaMigrationRequest) GetClusterId() string { if x != nil { - return x.Alias + return x.ClusterId } - return nil + return "" } -func (x *StopReplicationRequest) GetClusterIds() []string { +func (x *RetrySchemaMigrationRequest) GetRequest() *vtctldata.RetrySchemaMigrationRequest { if x != nil { - return x.ClusterIds + return x.Request } return nil } -type StopReplicationResponse struct { +type RunHealthCheckRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *StopReplicationResponse) Reset() { - *x = StopReplicationResponse{} +func (x *RunHealthCheckRequest) Reset() { + *x = RunHealthCheckRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5254,13 +5272,13 @@ func (x *StopReplicationResponse) Reset() { } } -func (x *StopReplicationResponse) String() string { +func (x *RunHealthCheckRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*StopReplicationResponse) ProtoMessage() {} +func (*RunHealthCheckRequest) ProtoMessage() {} -func (x *StopReplicationResponse) ProtoReflect() protoreflect.Message { +func (x *RunHealthCheckRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5272,38 +5290,36 @@ func (x *StopReplicationResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use StopReplicationResponse.ProtoReflect.Descriptor instead. -func (*StopReplicationResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use RunHealthCheckRequest.ProtoReflect.Descriptor instead. +func (*RunHealthCheckRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{88} } -func (x *StopReplicationResponse) GetStatus() string { +func (x *RunHealthCheckRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.Status + return x.Alias } - return "" + return nil } -func (x *StopReplicationResponse) GetCluster() *Cluster { +func (x *RunHealthCheckRequest) GetClusterIds() []string { if x != nil { - return x.Cluster + return x.ClusterIds } return nil } -type TabletExternallyPromotedRequest struct { +type RunHealthCheckResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Tablet is the alias of the tablet that was promoted externally and should - // be updated to the shard primary in the topo. - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *TabletExternallyPromotedRequest) Reset() { - *x = TabletExternallyPromotedRequest{} +func (x *RunHealthCheckResponse) Reset() { + *x = RunHealthCheckResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5311,13 +5327,13 @@ func (x *TabletExternallyPromotedRequest) Reset() { } } -func (x *TabletExternallyPromotedRequest) String() string { +func (x *RunHealthCheckResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TabletExternallyPromotedRequest) ProtoMessage() {} +func (*RunHealthCheckResponse) ProtoMessage() {} -func (x *TabletExternallyPromotedRequest) ProtoReflect() protoreflect.Message { +func (x *RunHealthCheckResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5329,39 +5345,36 @@ func (x *TabletExternallyPromotedRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TabletExternallyPromotedRequest.ProtoReflect.Descriptor instead. -func (*TabletExternallyPromotedRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use RunHealthCheckResponse.ProtoReflect.Descriptor instead. +func (*RunHealthCheckResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{89} } -func (x *TabletExternallyPromotedRequest) GetAlias() *topodata.TabletAlias { +func (x *RunHealthCheckResponse) GetStatus() string { if x != nil { - return x.Alias + return x.Status } - return nil + return "" } -func (x *TabletExternallyPromotedRequest) GetClusterIds() []string { +func (x *RunHealthCheckResponse) GetCluster() *Cluster { if x != nil { - return x.ClusterIds + return x.Cluster } return nil } -type TabletExternallyPromotedResponse struct { +type SetReadOnlyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - NewPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=new_primary,json=newPrimary,proto3" json:"new_primary,omitempty"` - OldPrimary *topodata.TabletAlias `protobuf:"bytes,5,opt,name=old_primary,json=oldPrimary,proto3" json:"old_primary,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *TabletExternallyPromotedResponse) Reset() { - *x = TabletExternallyPromotedResponse{} +func (x *SetReadOnlyRequest) Reset() { + *x = SetReadOnlyRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5369,13 +5382,13 @@ func (x *TabletExternallyPromotedResponse) Reset() { } } -func (x *TabletExternallyPromotedResponse) String() string { +func (x *SetReadOnlyRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TabletExternallyPromotedResponse) ProtoMessage() {} +func (*SetReadOnlyRequest) ProtoMessage() {} -func (x *TabletExternallyPromotedResponse) ProtoReflect() protoreflect.Message { +func (x *SetReadOnlyRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5387,57 +5400,33 @@ func (x *TabletExternallyPromotedResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TabletExternallyPromotedResponse.ProtoReflect.Descriptor instead. -func (*TabletExternallyPromotedResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SetReadOnlyRequest.ProtoReflect.Descriptor instead. +func (*SetReadOnlyRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{90} } -func (x *TabletExternallyPromotedResponse) GetCluster() *Cluster { - if x != nil { - return x.Cluster - } - return nil -} - -func (x *TabletExternallyPromotedResponse) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *TabletExternallyPromotedResponse) GetShard() string { - if x != nil { - return x.Shard - } - return "" -} - -func (x *TabletExternallyPromotedResponse) GetNewPrimary() *topodata.TabletAlias { +func (x *SetReadOnlyRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.NewPrimary + return x.Alias } return nil } -func (x *TabletExternallyPromotedResponse) GetOldPrimary() *topodata.TabletAlias { +func (x *SetReadOnlyRequest) GetClusterIds() []string { if x != nil { - return x.OldPrimary + return x.ClusterIds } return nil } -type TabletExternallyReparentedRequest struct { +type SetReadOnlyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` - ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *TabletExternallyReparentedRequest) Reset() { - *x = TabletExternallyReparentedRequest{} +func (x *SetReadOnlyResponse) Reset() { + *x = SetReadOnlyResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5445,13 +5434,13 @@ func (x *TabletExternallyReparentedRequest) Reset() { } } -func (x *TabletExternallyReparentedRequest) String() string { +func (x *SetReadOnlyResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TabletExternallyReparentedRequest) ProtoMessage() {} +func (*SetReadOnlyResponse) ProtoMessage() {} -func (x *TabletExternallyReparentedRequest) ProtoReflect() protoreflect.Message { +func (x *SetReadOnlyResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5463,36 +5452,22 @@ func (x *TabletExternallyReparentedRequest) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use TabletExternallyReparentedRequest.ProtoReflect.Descriptor instead. -func (*TabletExternallyReparentedRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SetReadOnlyResponse.ProtoReflect.Descriptor instead. +func (*SetReadOnlyResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{91} } -func (x *TabletExternallyReparentedRequest) GetAlias() *topodata.TabletAlias { - if x != nil { - return x.Alias - } - return nil -} - -func (x *TabletExternallyReparentedRequest) GetClusterIds() []string { - if x != nil { - return x.ClusterIds - } - return nil -} - -type ValidateRequest struct { +type SetReadWriteRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - PingTablets bool `protobuf:"varint,2,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *ValidateRequest) Reset() { - *x = ValidateRequest{} +func (x *SetReadWriteRequest) Reset() { + *x = SetReadWriteRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5500,13 +5475,13 @@ func (x *ValidateRequest) Reset() { } } -func (x *ValidateRequest) String() string { +func (x *SetReadWriteRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateRequest) ProtoMessage() {} +func (*SetReadWriteRequest) ProtoMessage() {} -func (x *ValidateRequest) ProtoReflect() protoreflect.Message { +func (x *SetReadWriteRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5518,37 +5493,33 @@ func (x *ValidateRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateRequest.ProtoReflect.Descriptor instead. -func (*ValidateRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SetReadWriteRequest.ProtoReflect.Descriptor instead. +func (*SetReadWriteRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{92} } -func (x *ValidateRequest) GetClusterId() string { +func (x *SetReadWriteRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.ClusterId + return x.Alias } - return "" + return nil } -func (x *ValidateRequest) GetPingTablets() bool { +func (x *SetReadWriteRequest) GetClusterIds() []string { if x != nil { - return x.PingTablets + return x.ClusterIds } - return false + return nil } -type ValidateKeyspaceRequest struct { +type SetReadWriteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - PingTablets bool `protobuf:"varint,3,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` } -func (x *ValidateKeyspaceRequest) Reset() { - *x = ValidateKeyspaceRequest{} +func (x *SetReadWriteResponse) Reset() { + *x = SetReadWriteResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5556,13 +5527,13 @@ func (x *ValidateKeyspaceRequest) Reset() { } } -func (x *ValidateKeyspaceRequest) String() string { +func (x *SetReadWriteResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateKeyspaceRequest) ProtoMessage() {} +func (*SetReadWriteResponse) ProtoMessage() {} -func (x *ValidateKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *SetReadWriteResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5574,43 +5545,22 @@ func (x *ValidateKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*ValidateKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SetReadWriteResponse.ProtoReflect.Descriptor instead. +func (*SetReadWriteResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{93} } -func (x *ValidateKeyspaceRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *ValidateKeyspaceRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *ValidateKeyspaceRequest) GetPingTablets() bool { - if x != nil { - return x.PingTablets - } - return false -} - -type ValidateSchemaKeyspaceRequest struct { +type StartReplicationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *ValidateSchemaKeyspaceRequest) Reset() { - *x = ValidateSchemaKeyspaceRequest{} +func (x *StartReplicationRequest) Reset() { + *x = StartReplicationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5618,13 +5568,13 @@ func (x *ValidateSchemaKeyspaceRequest) Reset() { } } -func (x *ValidateSchemaKeyspaceRequest) String() string { +func (x *StartReplicationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateSchemaKeyspaceRequest) ProtoMessage() {} +func (*StartReplicationRequest) ProtoMessage() {} -func (x *ValidateSchemaKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *StartReplicationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5636,38 +5586,36 @@ func (x *ValidateSchemaKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateSchemaKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*ValidateSchemaKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use StartReplicationRequest.ProtoReflect.Descriptor instead. +func (*StartReplicationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{94} } -func (x *ValidateSchemaKeyspaceRequest) GetClusterId() string { +func (x *StartReplicationRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.ClusterId + return x.Alias } - return "" + return nil } -func (x *ValidateSchemaKeyspaceRequest) GetKeyspace() string { +func (x *StartReplicationRequest) GetClusterIds() []string { if x != nil { - return x.Keyspace + return x.ClusterIds } - return "" + return nil } -type ValidateShardRequest struct { +type StartReplicationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - PingTablets bool `protobuf:"varint,4,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *ValidateShardRequest) Reset() { - *x = ValidateShardRequest{} +func (x *StartReplicationResponse) Reset() { + *x = StartReplicationResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5675,13 +5623,13 @@ func (x *ValidateShardRequest) Reset() { } } -func (x *ValidateShardRequest) String() string { +func (x *StartReplicationResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateShardRequest) ProtoMessage() {} +func (*StartReplicationResponse) ProtoMessage() {} -func (x *ValidateShardRequest) ProtoReflect() protoreflect.Message { +func (x *StartReplicationResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5693,50 +5641,36 @@ func (x *ValidateShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateShardRequest.ProtoReflect.Descriptor instead. -func (*ValidateShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use StartReplicationResponse.ProtoReflect.Descriptor instead. +func (*StartReplicationResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{95} } -func (x *ValidateShardRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *ValidateShardRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *ValidateShardRequest) GetShard() string { +func (x *StartReplicationResponse) GetStatus() string { if x != nil { - return x.Shard + return x.Status } return "" } -func (x *ValidateShardRequest) GetPingTablets() bool { +func (x *StartReplicationResponse) GetCluster() *Cluster { if x != nil { - return x.PingTablets + return x.Cluster } - return false + return nil } -type ValidateVersionKeyspaceRequest struct { +type StopReplicationRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *ValidateVersionKeyspaceRequest) Reset() { - *x = ValidateVersionKeyspaceRequest{} +func (x *StopReplicationRequest) Reset() { + *x = StopReplicationRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5744,13 +5678,13 @@ func (x *ValidateVersionKeyspaceRequest) Reset() { } } -func (x *ValidateVersionKeyspaceRequest) String() string { +func (x *StopReplicationRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateVersionKeyspaceRequest) ProtoMessage() {} +func (*StopReplicationRequest) ProtoMessage() {} -func (x *ValidateVersionKeyspaceRequest) ProtoReflect() protoreflect.Message { +func (x *StopReplicationRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5762,37 +5696,36 @@ func (x *ValidateVersionKeyspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateVersionKeyspaceRequest.ProtoReflect.Descriptor instead. -func (*ValidateVersionKeyspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use StopReplicationRequest.ProtoReflect.Descriptor instead. +func (*StopReplicationRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{96} } -func (x *ValidateVersionKeyspaceRequest) GetClusterId() string { +func (x *StopReplicationRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.ClusterId + return x.Alias } - return "" + return nil } -func (x *ValidateVersionKeyspaceRequest) GetKeyspace() string { +func (x *StopReplicationRequest) GetClusterIds() []string { if x != nil { - return x.Keyspace + return x.ClusterIds } - return "" + return nil } -type ValidateVersionShardRequest struct { +type StopReplicationResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Cluster *Cluster `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (x *ValidateVersionShardRequest) Reset() { - *x = ValidateVersionShardRequest{} +func (x *StopReplicationResponse) Reset() { + *x = StopReplicationResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5800,13 +5733,13 @@ func (x *ValidateVersionShardRequest) Reset() { } } -func (x *ValidateVersionShardRequest) String() string { +func (x *StopReplicationResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ValidateVersionShardRequest) ProtoMessage() {} +func (*StopReplicationResponse) ProtoMessage() {} -func (x *ValidateVersionShardRequest) ProtoReflect() protoreflect.Message { +func (x *StopReplicationResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5818,44 +5751,38 @@ func (x *ValidateVersionShardRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ValidateVersionShardRequest.ProtoReflect.Descriptor instead. -func (*ValidateVersionShardRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use StopReplicationResponse.ProtoReflect.Descriptor instead. +func (*StopReplicationResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{97} } -func (x *ValidateVersionShardRequest) GetClusterId() string { - if x != nil { - return x.ClusterId - } - return "" -} - -func (x *ValidateVersionShardRequest) GetKeyspace() string { +func (x *StopReplicationResponse) GetStatus() string { if x != nil { - return x.Keyspace + return x.Status } return "" } -func (x *ValidateVersionShardRequest) GetShard() string { +func (x *StopReplicationResponse) GetCluster() *Cluster { if x != nil { - return x.Shard + return x.Cluster } - return "" + return nil } -type VTExplainRequest struct { +type TabletExternallyPromotedRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Cluster string `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Sql string `protobuf:"bytes,3,opt,name=sql,proto3" json:"sql,omitempty"` + // Tablet is the alias of the tablet that was promoted externally and should + // be updated to the shard primary in the topo. + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *VTExplainRequest) Reset() { - *x = VTExplainRequest{} +func (x *TabletExternallyPromotedRequest) Reset() { + *x = TabletExternallyPromotedRequest{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5863,13 +5790,13 @@ func (x *VTExplainRequest) Reset() { } } -func (x *VTExplainRequest) String() string { +func (x *TabletExternallyPromotedRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VTExplainRequest) ProtoMessage() {} +func (*TabletExternallyPromotedRequest) ProtoMessage() {} -func (x *VTExplainRequest) ProtoReflect() protoreflect.Message { +func (x *TabletExternallyPromotedRequest) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5881,42 +5808,39 @@ func (x *VTExplainRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VTExplainRequest.ProtoReflect.Descriptor instead. -func (*VTExplainRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use TabletExternallyPromotedRequest.ProtoReflect.Descriptor instead. +func (*TabletExternallyPromotedRequest) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{98} } -func (x *VTExplainRequest) GetCluster() string { - if x != nil { - return x.Cluster - } - return "" -} - -func (x *VTExplainRequest) GetKeyspace() string { +func (x *TabletExternallyPromotedRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.Keyspace + return x.Alias } - return "" + return nil } -func (x *VTExplainRequest) GetSql() string { +func (x *TabletExternallyPromotedRequest) GetClusterIds() []string { if x != nil { - return x.Sql + return x.ClusterIds } - return "" + return nil } -type VTExplainResponse struct { +type TabletExternallyPromotedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Response string `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + NewPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=new_primary,json=newPrimary,proto3" json:"new_primary,omitempty"` + OldPrimary *topodata.TabletAlias `protobuf:"bytes,5,opt,name=old_primary,json=oldPrimary,proto3" json:"old_primary,omitempty"` } -func (x *VTExplainResponse) Reset() { - *x = VTExplainResponse{} +func (x *TabletExternallyPromotedResponse) Reset() { + *x = TabletExternallyPromotedResponse{} if protoimpl.UnsafeEnabled { mi := &file_vtadmin_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5924,13 +5848,13 @@ func (x *VTExplainResponse) Reset() { } } -func (x *VTExplainResponse) String() string { +func (x *TabletExternallyPromotedResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VTExplainResponse) ProtoMessage() {} +func (*TabletExternallyPromotedResponse) ProtoMessage() {} -func (x *VTExplainResponse) ProtoReflect() protoreflect.Message { +func (x *TabletExternallyPromotedResponse) ProtoReflect() protoreflect.Message { mi := &file_vtadmin_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -5942,44 +5866,72 @@ func (x *VTExplainResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VTExplainResponse.ProtoReflect.Descriptor instead. -func (*VTExplainResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use TabletExternallyPromotedResponse.ProtoReflect.Descriptor instead. +func (*TabletExternallyPromotedResponse) Descriptor() ([]byte, []int) { return file_vtadmin_proto_rawDescGZIP(), []int{99} } -func (x *VTExplainResponse) GetResponse() string { +func (x *TabletExternallyPromotedResponse) GetCluster() *Cluster { if x != nil { - return x.Response + return x.Cluster + } + return nil +} + +func (x *TabletExternallyPromotedResponse) GetKeyspace() string { + if x != nil { + return x.Keyspace } return "" } -type Schema_ShardTableSize struct { +func (x *TabletExternallyPromotedResponse) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +func (x *TabletExternallyPromotedResponse) GetNewPrimary() *topodata.TabletAlias { + if x != nil { + return x.NewPrimary + } + return nil +} + +func (x *TabletExternallyPromotedResponse) GetOldPrimary() *topodata.TabletAlias { + if x != nil { + return x.OldPrimary + } + return nil +} + +type TabletExternallyReparentedRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RowCount uint64 `protobuf:"varint,1,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` - DataLength uint64 `protobuf:"varint,2,opt,name=data_length,json=dataLength,proto3" json:"data_length,omitempty"` + Alias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` + ClusterIds []string `protobuf:"bytes,2,rep,name=cluster_ids,json=clusterIds,proto3" json:"cluster_ids,omitempty"` } -func (x *Schema_ShardTableSize) Reset() { - *x = Schema_ShardTableSize{} +func (x *TabletExternallyReparentedRequest) Reset() { + *x = TabletExternallyReparentedRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[103] + mi := &file_vtadmin_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Schema_ShardTableSize) String() string { +func (x *TabletExternallyReparentedRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Schema_ShardTableSize) ProtoMessage() {} +func (*TabletExternallyReparentedRequest) ProtoMessage() {} -func (x *Schema_ShardTableSize) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[103] +func (x *TabletExternallyReparentedRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5990,54 +5942,51 @@ func (x *Schema_ShardTableSize) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Schema_ShardTableSize.ProtoReflect.Descriptor instead. -func (*Schema_ShardTableSize) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{7, 1} +// Deprecated: Use TabletExternallyReparentedRequest.ProtoReflect.Descriptor instead. +func (*TabletExternallyReparentedRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{100} } -func (x *Schema_ShardTableSize) GetRowCount() uint64 { +func (x *TabletExternallyReparentedRequest) GetAlias() *topodata.TabletAlias { if x != nil { - return x.RowCount + return x.Alias } - return 0 + return nil } -func (x *Schema_ShardTableSize) GetDataLength() uint64 { +func (x *TabletExternallyReparentedRequest) GetClusterIds() []string { if x != nil { - return x.DataLength + return x.ClusterIds } - return 0 + return nil } -// TableSize aggregates table size information across all shards containing -// in the given keyspace and cluster, as well as per-shard size information. -type Schema_TableSize struct { +type ValidateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RowCount uint64 `protobuf:"varint,1,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` - DataLength uint64 `protobuf:"varint,2,opt,name=data_length,json=dataLength,proto3" json:"data_length,omitempty"` - ByShard map[string]*Schema_ShardTableSize `protobuf:"bytes,3,rep,name=by_shard,json=byShard,proto3" json:"by_shard,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + PingTablets bool `protobuf:"varint,2,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` } -func (x *Schema_TableSize) Reset() { - *x = Schema_TableSize{} +func (x *ValidateRequest) Reset() { + *x = ValidateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[104] + mi := &file_vtadmin_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Schema_TableSize) String() string { +func (x *ValidateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Schema_TableSize) ProtoMessage() {} +func (*ValidateRequest) ProtoMessage() {} -func (x *Schema_TableSize) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[104] +func (x *ValidateRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6048,64 +5997,114 @@ func (x *Schema_TableSize) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Schema_TableSize.ProtoReflect.Descriptor instead. -func (*Schema_TableSize) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{7, 2} +// Deprecated: Use ValidateRequest.ProtoReflect.Descriptor instead. +func (*ValidateRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{101} } -func (x *Schema_TableSize) GetRowCount() uint64 { +func (x *ValidateRequest) GetClusterId() string { if x != nil { - return x.RowCount + return x.ClusterId } - return 0 + return "" } -func (x *Schema_TableSize) GetDataLength() uint64 { +func (x *ValidateRequest) GetPingTablets() bool { if x != nil { - return x.DataLength + return x.PingTablets } - return 0 + return false } -func (x *Schema_TableSize) GetByShard() map[string]*Schema_ShardTableSize { +type ValidateKeyspaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + PingTablets bool `protobuf:"varint,3,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` +} + +func (x *ValidateKeyspaceRequest) Reset() { + *x = ValidateKeyspaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[102] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateKeyspaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateKeyspaceRequest) ProtoMessage() {} + +func (x *ValidateKeyspaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[102] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*ValidateKeyspaceRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{102} +} + +func (x *ValidateKeyspaceRequest) GetClusterId() string { if x != nil { - return x.ByShard + return x.ClusterId } - return nil + return "" } -// KeyspaceResult is a grouping of a Keyspace and any log events that -// occurred in that keyspace during a schema reload (usually associated with -// partial errors - ReloadSchemas requests are best-effort). -// -// It is only set when a ReloadSchemas request mandates Keyspaces mode -// (see ReloadSchemasRequest). -type ReloadSchemasResponse_KeyspaceResult struct { +func (x *ValidateKeyspaceRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *ValidateKeyspaceRequest) GetPingTablets() bool { + if x != nil { + return x.PingTablets + } + return false +} + +type ValidateSchemaKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Events []*logutil.Event `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *ReloadSchemasResponse_KeyspaceResult) Reset() { - *x = ReloadSchemasResponse_KeyspaceResult{} +func (x *ValidateSchemaKeyspaceRequest) Reset() { + *x = ValidateSchemaKeyspaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[108] + mi := &file_vtadmin_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ReloadSchemasResponse_KeyspaceResult) String() string { +func (x *ValidateSchemaKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemasResponse_KeyspaceResult) ProtoMessage() {} +func (*ValidateSchemaKeyspaceRequest) ProtoMessage() {} -func (x *ReloadSchemasResponse_KeyspaceResult) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[108] +func (x *ValidateSchemaKeyspaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6116,57 +6115,53 @@ func (x *ReloadSchemasResponse_KeyspaceResult) ProtoReflect() protoreflect.Messa return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemasResponse_KeyspaceResult.ProtoReflect.Descriptor instead. -func (*ReloadSchemasResponse_KeyspaceResult) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{72, 0} +// Deprecated: Use ValidateSchemaKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*ValidateSchemaKeyspaceRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{103} } -func (x *ReloadSchemasResponse_KeyspaceResult) GetKeyspace() *Keyspace { +func (x *ValidateSchemaKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Keyspace + return x.ClusterId } - return nil + return "" } -func (x *ReloadSchemasResponse_KeyspaceResult) GetEvents() []*logutil.Event { +func (x *ValidateSchemaKeyspaceRequest) GetKeyspace() string { if x != nil { - return x.Events + return x.Keyspace } - return nil + return "" } -// ShardResult is a grouping of a Shard and any log events that occurred in -// that shard during a schema reload (usually associated with partial -// errors - ReloadSchemas requests are best-effort). -// -// It is only set when a ReloadSchemas request mandates KeyspaceShards mode -// (see ReloadSchemasRequest). -type ReloadSchemasResponse_ShardResult struct { +type ValidateShardRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Shard *Shard `protobuf:"bytes,1,opt,name=shard,proto3" json:"shard,omitempty"` - Events []*logutil.Event `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + PingTablets bool `protobuf:"varint,4,opt,name=ping_tablets,json=pingTablets,proto3" json:"ping_tablets,omitempty"` } -func (x *ReloadSchemasResponse_ShardResult) Reset() { - *x = ReloadSchemasResponse_ShardResult{} +func (x *ValidateShardRequest) Reset() { + *x = ValidateShardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[109] + mi := &file_vtadmin_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ReloadSchemasResponse_ShardResult) String() string { +func (x *ValidateShardRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemasResponse_ShardResult) ProtoMessage() {} +func (*ValidateShardRequest) ProtoMessage() {} -func (x *ReloadSchemasResponse_ShardResult) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[109] +func (x *ValidateShardRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6177,58 +6172,65 @@ func (x *ReloadSchemasResponse_ShardResult) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemasResponse_ShardResult.ProtoReflect.Descriptor instead. -func (*ReloadSchemasResponse_ShardResult) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{72, 1} +// Deprecated: Use ValidateShardRequest.ProtoReflect.Descriptor instead. +func (*ValidateShardRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{104} } -func (x *ReloadSchemasResponse_ShardResult) GetShard() *Shard { +func (x *ValidateShardRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *ValidateShardRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *ValidateShardRequest) GetShard() string { if x != nil { return x.Shard } - return nil + return "" } -func (x *ReloadSchemasResponse_ShardResult) GetEvents() []*logutil.Event { +func (x *ValidateShardRequest) GetPingTablets() bool { if x != nil { - return x.Events + return x.PingTablets } - return nil + return false } -// TabletResult is a grouping of a Tablet and the result of reloading that -// Tablet's schema. Result will either be the string "ok", or the error -// message from that tablet. Note ReloadSchemas is best-effort, so tablet's -// failing to reload is not treated as an overall failure. -// -// It is only set when a ReloadSchemas request mandates Tablets mode (see -// ReloadSchemasRequest). -type ReloadSchemasResponse_TabletResult struct { +type ValidateVersionKeyspaceRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Tablet *Tablet `protobuf:"bytes,1,opt,name=tablet,proto3" json:"tablet,omitempty"` - Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` } -func (x *ReloadSchemasResponse_TabletResult) Reset() { - *x = ReloadSchemasResponse_TabletResult{} +func (x *ValidateVersionKeyspaceRequest) Reset() { + *x = ValidateVersionKeyspaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtadmin_proto_msgTypes[110] + mi := &file_vtadmin_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ReloadSchemasResponse_TabletResult) String() string { +func (x *ValidateVersionKeyspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReloadSchemasResponse_TabletResult) ProtoMessage() {} +func (*ValidateVersionKeyspaceRequest) ProtoMessage() {} -func (x *ReloadSchemasResponse_TabletResult) ProtoReflect() protoreflect.Message { - mi := &file_vtadmin_proto_msgTypes[110] +func (x *ValidateVersionKeyspaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6239,49 +6241,581 @@ func (x *ReloadSchemasResponse_TabletResult) ProtoReflect() protoreflect.Message return mi.MessageOf(x) } -// Deprecated: Use ReloadSchemasResponse_TabletResult.ProtoReflect.Descriptor instead. -func (*ReloadSchemasResponse_TabletResult) Descriptor() ([]byte, []int) { - return file_vtadmin_proto_rawDescGZIP(), []int{72, 2} +// Deprecated: Use ValidateVersionKeyspaceRequest.ProtoReflect.Descriptor instead. +func (*ValidateVersionKeyspaceRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{105} } -func (x *ReloadSchemasResponse_TabletResult) GetTablet() *Tablet { +func (x *ValidateVersionKeyspaceRequest) GetClusterId() string { if x != nil { - return x.Tablet + return x.ClusterId } - return nil + return "" } -func (x *ReloadSchemasResponse_TabletResult) GetResult() string { +func (x *ValidateVersionKeyspaceRequest) GetKeyspace() string { if x != nil { - return x.Result + return x.Keyspace } return "" } -var File_vtadmin_proto protoreflect.FileDescriptor +type ValidateVersionShardRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -var file_vtadmin_proto_rawDesc = []byte{ - 0x0a, 0x0d, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x07, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x1a, 0x0d, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, - 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, - 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x0e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x0d, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x0f, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x2d, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x69, 0x0a, 0x0d, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x06, - 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, - 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0xd8, 0x01, 0x0a, 0x13, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` +} + +func (x *ValidateVersionShardRequest) Reset() { + *x = ValidateVersionShardRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[106] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateVersionShardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateVersionShardRequest) ProtoMessage() {} + +func (x *ValidateVersionShardRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[106] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateVersionShardRequest.ProtoReflect.Descriptor instead. +func (*ValidateVersionShardRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{106} +} + +func (x *ValidateVersionShardRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *ValidateVersionShardRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *ValidateVersionShardRequest) GetShard() string { + if x != nil { + return x.Shard + } + return "" +} + +type VTExplainRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cluster string `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Sql string `protobuf:"bytes,3,opt,name=sql,proto3" json:"sql,omitempty"` +} + +func (x *VTExplainRequest) Reset() { + *x = VTExplainRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[107] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VTExplainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VTExplainRequest) ProtoMessage() {} + +func (x *VTExplainRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[107] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VTExplainRequest.ProtoReflect.Descriptor instead. +func (*VTExplainRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{107} +} + +func (x *VTExplainRequest) GetCluster() string { + if x != nil { + return x.Cluster + } + return "" +} + +func (x *VTExplainRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VTExplainRequest) GetSql() string { + if x != nil { + return x.Sql + } + return "" +} + +type VTExplainResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Response string `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` +} + +func (x *VTExplainResponse) Reset() { + *x = VTExplainResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[108] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VTExplainResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VTExplainResponse) ProtoMessage() {} + +func (x *VTExplainResponse) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[108] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VTExplainResponse.ProtoReflect.Descriptor instead. +func (*VTExplainResponse) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{108} +} + +func (x *VTExplainResponse) GetResponse() string { + if x != nil { + return x.Response + } + return "" +} + +type Schema_ShardTableSize struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RowCount uint64 `protobuf:"varint,1,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` + DataLength uint64 `protobuf:"varint,2,opt,name=data_length,json=dataLength,proto3" json:"data_length,omitempty"` +} + +func (x *Schema_ShardTableSize) Reset() { + *x = Schema_ShardTableSize{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[112] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_ShardTableSize) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_ShardTableSize) ProtoMessage() {} + +func (x *Schema_ShardTableSize) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[112] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_ShardTableSize.ProtoReflect.Descriptor instead. +func (*Schema_ShardTableSize) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{7, 1} +} + +func (x *Schema_ShardTableSize) GetRowCount() uint64 { + if x != nil { + return x.RowCount + } + return 0 +} + +func (x *Schema_ShardTableSize) GetDataLength() uint64 { + if x != nil { + return x.DataLength + } + return 0 +} + +// TableSize aggregates table size information across all shards containing +// in the given keyspace and cluster, as well as per-shard size information. +type Schema_TableSize struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RowCount uint64 `protobuf:"varint,1,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` + DataLength uint64 `protobuf:"varint,2,opt,name=data_length,json=dataLength,proto3" json:"data_length,omitempty"` + ByShard map[string]*Schema_ShardTableSize `protobuf:"bytes,3,rep,name=by_shard,json=byShard,proto3" json:"by_shard,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Schema_TableSize) Reset() { + *x = Schema_TableSize{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[113] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_TableSize) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_TableSize) ProtoMessage() {} + +func (x *Schema_TableSize) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[113] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_TableSize.ProtoReflect.Descriptor instead. +func (*Schema_TableSize) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{7, 2} +} + +func (x *Schema_TableSize) GetRowCount() uint64 { + if x != nil { + return x.RowCount + } + return 0 +} + +func (x *Schema_TableSize) GetDataLength() uint64 { + if x != nil { + return x.DataLength + } + return 0 +} + +func (x *Schema_TableSize) GetByShard() map[string]*Schema_ShardTableSize { + if x != nil { + return x.ByShard + } + return nil +} + +type GetSchemaMigrationsRequest_ClusterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + Request *vtctldata.GetSchemaMigrationsRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` +} + +func (x *GetSchemaMigrationsRequest_ClusterRequest) Reset() { + *x = GetSchemaMigrationsRequest_ClusterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[115] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSchemaMigrationsRequest_ClusterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSchemaMigrationsRequest_ClusterRequest) ProtoMessage() {} + +func (x *GetSchemaMigrationsRequest_ClusterRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[115] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSchemaMigrationsRequest_ClusterRequest.ProtoReflect.Descriptor instead. +func (*GetSchemaMigrationsRequest_ClusterRequest) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{47, 0} +} + +func (x *GetSchemaMigrationsRequest_ClusterRequest) GetClusterId() string { + if x != nil { + return x.ClusterId + } + return "" +} + +func (x *GetSchemaMigrationsRequest_ClusterRequest) GetRequest() *vtctldata.GetSchemaMigrationsRequest { + if x != nil { + return x.Request + } + return nil +} + +// KeyspaceResult is a grouping of a Keyspace and any log events that +// occurred in that keyspace during a schema reload (usually associated with +// partial errors - ReloadSchemas requests are best-effort). +// +// It is only set when a ReloadSchemas request mandates Keyspaces mode +// (see ReloadSchemasRequest). +type ReloadSchemasResponse_KeyspaceResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Events []*logutil.Event `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` +} + +func (x *ReloadSchemasResponse_KeyspaceResult) Reset() { + *x = ReloadSchemasResponse_KeyspaceResult{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[118] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReloadSchemasResponse_KeyspaceResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadSchemasResponse_KeyspaceResult) ProtoMessage() {} + +func (x *ReloadSchemasResponse_KeyspaceResult) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[118] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadSchemasResponse_KeyspaceResult.ProtoReflect.Descriptor instead. +func (*ReloadSchemasResponse_KeyspaceResult) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{80, 0} +} + +func (x *ReloadSchemasResponse_KeyspaceResult) GetKeyspace() *Keyspace { + if x != nil { + return x.Keyspace + } + return nil +} + +func (x *ReloadSchemasResponse_KeyspaceResult) GetEvents() []*logutil.Event { + if x != nil { + return x.Events + } + return nil +} + +// ShardResult is a grouping of a Shard and any log events that occurred in +// that shard during a schema reload (usually associated with partial +// errors - ReloadSchemas requests are best-effort). +// +// It is only set when a ReloadSchemas request mandates KeyspaceShards mode +// (see ReloadSchemasRequest). +type ReloadSchemasResponse_ShardResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Shard *Shard `protobuf:"bytes,1,opt,name=shard,proto3" json:"shard,omitempty"` + Events []*logutil.Event `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` +} + +func (x *ReloadSchemasResponse_ShardResult) Reset() { + *x = ReloadSchemasResponse_ShardResult{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[119] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReloadSchemasResponse_ShardResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadSchemasResponse_ShardResult) ProtoMessage() {} + +func (x *ReloadSchemasResponse_ShardResult) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[119] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadSchemasResponse_ShardResult.ProtoReflect.Descriptor instead. +func (*ReloadSchemasResponse_ShardResult) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{80, 1} +} + +func (x *ReloadSchemasResponse_ShardResult) GetShard() *Shard { + if x != nil { + return x.Shard + } + return nil +} + +func (x *ReloadSchemasResponse_ShardResult) GetEvents() []*logutil.Event { + if x != nil { + return x.Events + } + return nil +} + +// TabletResult is a grouping of a Tablet and the result of reloading that +// Tablet's schema. Result will either be the string "ok", or the error +// message from that tablet. Note ReloadSchemas is best-effort, so tablet's +// failing to reload is not treated as an overall failure. +// +// It is only set when a ReloadSchemas request mandates Tablets mode (see +// ReloadSchemasRequest). +type ReloadSchemasResponse_TabletResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tablet *Tablet `protobuf:"bytes,1,opt,name=tablet,proto3" json:"tablet,omitempty"` + Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *ReloadSchemasResponse_TabletResult) Reset() { + *x = ReloadSchemasResponse_TabletResult{} + if protoimpl.UnsafeEnabled { + mi := &file_vtadmin_proto_msgTypes[120] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReloadSchemasResponse_TabletResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadSchemasResponse_TabletResult) ProtoMessage() {} + +func (x *ReloadSchemasResponse_TabletResult) ProtoReflect() protoreflect.Message { + mi := &file_vtadmin_proto_msgTypes[120] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadSchemasResponse_TabletResult.ProtoReflect.Descriptor instead. +func (*ReloadSchemasResponse_TabletResult) Descriptor() ([]byte, []int) { + return file_vtadmin_proto_rawDescGZIP(), []int{80, 2} +} + +func (x *ReloadSchemasResponse_TabletResult) GetTablet() *Tablet { + if x != nil { + return x.Tablet + } + return nil +} + +func (x *ReloadSchemasResponse_TabletResult) GetResult() string { + if x != nil { + return x.Result + } + return "" +} + +var File_vtadmin_proto protoreflect.FileDescriptor + +var file_vtadmin_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x07, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x1a, 0x0d, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, + 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, + 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x0e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x0d, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x0f, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x2d, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x69, 0x0a, 0x0d, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x06, + 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, + 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0xd8, 0x01, 0x0a, 0x13, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, @@ -6374,947 +6908,1070 @@ var file_vtadmin_proto_rawDesc = []byte{ 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5b, 0x0a, 0x05, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x26, - 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x0a, 0x53, 0x72, 0x76, 0x56, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0c, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x52, 0x0a, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0xe1, 0x01, 0x0a, - 0x06, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x32, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x46, 0x51, 0x44, 0x4e, 0x22, 0x39, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, - 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, - 0x22, 0x77, 0x0a, 0x07, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x2a, 0x0a, 0x07, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x07, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x64, 0x0a, 0x06, 0x56, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x46, - 0x51, 0x44, 0x4e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x22, - 0xaa, 0x01, 0x0a, 0x06, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, - 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, - 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, - 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x2a, - 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x51, 0x44, 0x4e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x22, 0x83, 0x01, 0x0a, - 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x84, 0x01, 0x0a, 0x0f, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, + 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x5b, 0x0a, 0x05, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x22, 0x72, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x47, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, - 0x6c, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x72, 0x0a, - 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x83, 0x01, + 0x0a, 0x0a, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x12, 0x0a, 0x04, + 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, + 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0c, + 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x72, 0x76, + 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0a, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x22, 0xe1, 0x01, 0x0a, 0x06, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x2a, + 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x06, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x51, 0x44, 0x4e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x22, 0x39, 0x0a, 0x0c, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, + 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x45, + 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x77, 0x0a, 0x07, 0x56, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x22, 0x64, 0x0a, 0x06, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x22, 0xaa, 0x01, 0x0a, 0x06, 0x56, 0x54, 0x47, 0x61, 0x74, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6f, + 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x46, 0x51, 0x44, 0x4e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, + 0x51, 0x44, 0x4e, 0x22, 0x83, 0x01, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, + 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x6c, 0x0a, 0x12, 0x41, 0x70, 0x70, + 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, + 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x1c, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x88, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, - 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x5a, 0x0a, 0x14, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, - 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x82, 0x01, 0x0a, 0x1d, 0x45, 0x6d, 0x65, - 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x82, 0x01, 0x0a, 0x1d, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x84, 0x01, 0x0a, 0x1e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x43, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x72, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3a, + 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x47, 0x0a, 0x16, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0x6c, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x72, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, - 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe8, 0x01, - 0x0a, 0x1e, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, - 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6e, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, + 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x22, 0x5a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, - 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x46, 0x69, 0x6e, - 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x50, 0x0a, 0x12, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc2, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1c, - 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x46, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x07, 0x62, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x73, 0x22, 0x6b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, - 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, - 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x4f, 0x6e, 0x6c, - 0x79, 0x22, 0x4f, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x65, 0x6c, - 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x82, 0x01, 0x0a, + 0x1d, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, + 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x42, 0x0a, + 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x6d, 0x65, 0x72, 0x67, + 0x65, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0xe8, 0x01, 0x0a, 0x1e, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, + 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x12, 0x40, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x9c, 0x01, 0x0a, + 0x11, 0x46, 0x69, 0x6e, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x50, 0x0a, 0x12, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc2, 0x01, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x46, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x6b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, + 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x4f, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, + 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, + 0x0a, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x63, 0x65, 0x6c, + 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x22, 0x39, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, + 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x73, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x07, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, - 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, - 0x6f, 0x73, 0x22, 0x39, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x51, 0x0a, - 0x17, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x65, 0x6c, 0x6c, 0x73, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, - 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, 0x62, 0x0a, 0x14, 0x47, - 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x22, - 0x32, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x22, 0x39, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x67, 0x61, 0x74, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x52, 0x05, 0x67, 0x61, 0x74, 0x65, 0x73, 0x22, 0x4f, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, - 0x36, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x22, + 0x62, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, + 0x69, 0x61, 0x73, 0x22, 0x32, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x47, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x22, 0xb5, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x50, 0x0a, 0x12, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, - 0x50, 0x0a, 0x12, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x39, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x67, + 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x54, 0x47, 0x61, 0x74, 0x65, 0x52, 0x05, 0x67, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0x36, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x24, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x15, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, - 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x4f, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, - 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xd7, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x57, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x72, 0x76, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x73, 0x72, - 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x63, 0x0a, 0x11, 0x53, 0x72, - 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x47, 0x0a, 0x14, 0x47, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x50, 0x0a, 0x12, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x86, 0x01, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x73, 0x12, 0x50, 0x0a, 0x12, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, + 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x07, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x10, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x32, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x1a, 0x70, 0x0a, 0x0e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x3f, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x64, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x11, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, + 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8d, 0x01, 0x0a, + 0x23, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0x85, 0x01, 0x0a, + 0x24, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x14, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x4f, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, + 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, + 0xd7, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0d, 0x73, + 0x72, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x49, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x4e, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x73, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x1a, 0x63, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65, 0x74, + 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x4e, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, + 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x51, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, + 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0b, 0x73, 0x72, 0x76, 0x56, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x73, 0x12, 0x3b, + 0x0a, 0x1a, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x6f, 0x6e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0x60, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x34, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x73, 0x22, 0x4b, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x22, 0x4e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x22, 0x35, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x44, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x56, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2d, 0x0a, 0x09, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x34, + 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x51, 0x0a, 0x16, 0x47, 0x65, - 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x52, 0x0b, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x81, 0x01, - 0x0a, 0x19, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x53, 0x69, 0x7a, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, - 0x69, 0x7a, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x1a, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, - 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x4e, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x22, 0x60, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, - 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x22, 0x34, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x29, 0x0a, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x52, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x4b, 0x0a, 0x16, 0x47, 0x65, - 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x4e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x49, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x52, 0x07, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x35, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x56, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x44, - 0x0a, 0x13, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x09, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x76, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x73, 0x22, 0x34, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x29, 0x0a, 0x07, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x52, 0x07, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0xa0, 0x01, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, + 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, + 0xe1, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x42, + 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x77, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x1a, 0x60, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x42, 0x79, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x80, 0x01, 0x0a, 0x1c, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x11, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, + 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x12, 0x50, 0x69, 0x6e, + 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x22, 0x7e, 0x0a, 0x1b, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4f, 0x6e, - 0x6c, 0x79, 0x22, 0xa0, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x67, - 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, - 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x42, 0x79, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x1a, 0x60, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x73, 0x42, 0x79, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x61, 0x0a, 0x11, 0x50, 0x69, 0x6e, - 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, - 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x12, - 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x64, 0x12, 0x40, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, + 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x1c, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, + 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x12, 0x40, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x93, 0x01, 0x0a, + 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x23, 0x0a, + 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x74, 0x69, + 0x61, 0x6c, 0x22, 0x36, 0x0a, 0x1c, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x7e, 0x0a, 0x1b, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, - 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x1c, 0x50, 0x6c, 0x61, 0x6e, 0x6e, - 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, - 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, - 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, - 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x93, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, - 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x61, - 0x72, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x36, 0x0a, 0x1c, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x63, 0x0a, - 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x73, 0x22, 0x5a, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x9f, - 0x02, 0x0a, 0x14, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x2f, - 0x0a, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, - 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x22, 0xad, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x10, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, - 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x52, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x1a, 0x67, 0x0a, 0x0e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, - 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x1a, 0x5b, 0x0a, 0x0b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x63, 0x0a, 0x13, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, + 0x5a, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x9f, 0x02, 0x0a, 0x14, + 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x07, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x20, 0x0a, + 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0xad, 0x04, + 0x0a, 0x15, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x0f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x1a, 0x67, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, - 0x4f, 0x0a, 0x0c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, - 0x27, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x22, 0xdb, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, - 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x43, - 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, - 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x75, 0x0a, 0x25, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, - 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x26, 0x52, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x5b, 0x0a, 0x0b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x24, + 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x4f, 0x0a, 0x0c, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x27, 0x0a, 0x06, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, + 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x06, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xdb, 0x01, + 0x0a, 0x18, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x77, + 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, + 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x43, 0x0a, 0x19, 0x52, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, + 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x75, 0x0a, 0x25, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x22, 0x9e, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, - 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, - 0x73, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, - 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x34, 0x0a, 0x1a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x65, 0x0a, 0x15, 0x52, - 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x73, 0x22, 0x5c, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x22, 0x62, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, - 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x49, 0x64, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, - 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x63, 0x0a, 0x13, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, - 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, - 0x73, 0x22, 0x5e, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x26, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x22, 0x66, 0x0a, 0x16, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, - 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x5d, 0x0a, 0x17, 0x53, 0x74, 0x6f, - 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, - 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x1f, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, - 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, - 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xf0, 0x01, 0x0a, 0x20, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, - 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, - 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, - 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x50, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x52, 0x0a, 0x6f, 0x6c, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x71, 0x0a, 0x21, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, - 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x72, 0x22, 0x9e, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, + 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, + 0x76, 0x65, 0x22, 0x34, 0x0a, 0x1a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x7e, 0x0a, 0x1b, 0x52, 0x65, 0x74, 0x72, + 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x65, 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, - 0x53, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x73, 0x22, 0x77, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5c, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x62, 0x0a, + 0x12, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x73, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x63, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x16, 0x0a, + 0x14, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x5e, + 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x66, + 0x0a, 0x16, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x5d, 0x0a, 0x17, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x1f, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xf0, 0x01, 0x0a, 0x20, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, + 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, + 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x36, 0x0a, 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6f, + 0x6c, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x71, 0x0a, 0x21, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, + 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0x53, 0x0a, 0x0f, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x73, 0x22, 0x77, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, + 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x5a, 0x0a, 0x1d, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x14, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, - 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x5a, 0x0a, - 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x14, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x5b, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x22, 0x6e, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x73, 0x71, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x22, - 0x2f, 0x0a, 0x11, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x32, 0xd9, 0x21, 0x0a, 0x07, 0x56, 0x54, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x53, 0x0a, 0x0e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x55, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x16, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, - 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x12, 0x26, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x65, 0x72, 0x67, - 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x73, 0x22, 0x5b, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x22, 0x6e, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x22, 0x5a, 0x0a, 0x10, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x22, 0x2f, 0x0a, 0x11, + 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xad, 0x27, + 0x0a, 0x07, 0x56, 0x54, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x4c, 0x0a, 0x0b, 0x41, 0x70, 0x70, + 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x15, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x16, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x70, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, + 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x1c, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x4d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, + 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, + 0x0a, 0x16, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, + 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x26, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, - 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0a, 0x46, 0x69, 0x6e, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, - 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1a, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, - 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4a, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x12, 0x18, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, - 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x12, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x47, - 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1a, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x53, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, - 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x39, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, - 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x22, 0x00, 0x12, 0x47, 0x0a, - 0x0a, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, - 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, - 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, - 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x3c, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x4a, - 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1b, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, - 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, - 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x6e, 0x67, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, - 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, - 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, - 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x24, 0x2e, 0x76, 0x74, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x52, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x83, 0x01, 0x0a, 0x1e, 0x52, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, - 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x50, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, - 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, - 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x5c, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5f, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, - 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x75, 0x6e, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x75, 0x6e, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, - 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, - 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, - 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x59, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0f, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x12, - 0x28, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x26, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, + 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x65, 0x72, 0x67, + 0x65, 0x6e, 0x63, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0a, 0x46, + 0x69, 0x6e, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, + 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, + 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x56, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, + 0x61, 0x74, 0x65, 0x73, 0x12, 0x18, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x76, + 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x09, 0x47, + 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x72, 0x76, + 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1e, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, + 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x0a, + 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x73, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, + 0x65, 0x74, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x3f, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1b, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0x00, + 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, + 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x6a, 0x0a, 0x15, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x28, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x75, 0x6e, + 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x50, + 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, + 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x2e, 0x76, + 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x52, + 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x12, 0x24, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, + 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x83, 0x01, 0x0a, 0x1e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x11, 0x52, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x21, + 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x22, 0x2e, + 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x14, 0x52, 0x65, 0x74, 0x72, + 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x24, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x75, + 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x52, 0x75, + 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, + 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1b, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, + 0x61, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x59, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0f, + 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1f, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x18, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, + 0x12, 0x28, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x08, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x20, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, - 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x17, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, - 0x14, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x74, + 0x61, 0x63, 0x65, 0x12, 0x26, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, - 0x61, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x54, - 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, - 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, - 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x17, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, + 0x0a, 0x14, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x56, 0x54, 0x45, 0x78, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, + 0x54, 0x45, 0x78, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x76, 0x74, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x56, 0x54, 0x45, 0x78, 0x70, 0x6c, + 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, + 0x24, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, + 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7330,361 +7987,410 @@ func file_vtadmin_proto_rawDescGZIP() []byte { } var file_vtadmin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_vtadmin_proto_msgTypes = make([]protoimpl.MessageInfo, 111) +var file_vtadmin_proto_msgTypes = make([]protoimpl.MessageInfo, 121) var file_vtadmin_proto_goTypes = []interface{}{ - (Tablet_ServingState)(0), // 0: vtadmin.Tablet.ServingState - (*Cluster)(nil), // 1: vtadmin.Cluster - (*ClusterBackup)(nil), // 2: vtadmin.ClusterBackup - (*ClusterCellsAliases)(nil), // 3: vtadmin.ClusterCellsAliases - (*ClusterCellInfo)(nil), // 4: vtadmin.ClusterCellInfo - (*ClusterShardReplicationPosition)(nil), // 5: vtadmin.ClusterShardReplicationPosition - (*ClusterWorkflows)(nil), // 6: vtadmin.ClusterWorkflows - (*Keyspace)(nil), // 7: vtadmin.Keyspace - (*Schema)(nil), // 8: vtadmin.Schema - (*Shard)(nil), // 9: vtadmin.Shard - (*SrvVSchema)(nil), // 10: vtadmin.SrvVSchema - (*Tablet)(nil), // 11: vtadmin.Tablet - (*VSchema)(nil), // 12: vtadmin.VSchema - (*Vtctld)(nil), // 13: vtadmin.Vtctld - (*VTGate)(nil), // 14: vtadmin.VTGate - (*Workflow)(nil), // 15: vtadmin.Workflow - (*CreateKeyspaceRequest)(nil), // 16: vtadmin.CreateKeyspaceRequest - (*CreateKeyspaceResponse)(nil), // 17: vtadmin.CreateKeyspaceResponse - (*CreateShardRequest)(nil), // 18: vtadmin.CreateShardRequest - (*DeleteKeyspaceRequest)(nil), // 19: vtadmin.DeleteKeyspaceRequest - (*DeleteShardsRequest)(nil), // 20: vtadmin.DeleteShardsRequest - (*DeleteTabletRequest)(nil), // 21: vtadmin.DeleteTabletRequest - (*DeleteTabletResponse)(nil), // 22: vtadmin.DeleteTabletResponse - (*EmergencyFailoverShardRequest)(nil), // 23: vtadmin.EmergencyFailoverShardRequest - (*EmergencyFailoverShardResponse)(nil), // 24: vtadmin.EmergencyFailoverShardResponse - (*FindSchemaRequest)(nil), // 25: vtadmin.FindSchemaRequest - (*GetBackupsRequest)(nil), // 26: vtadmin.GetBackupsRequest - (*GetBackupsResponse)(nil), // 27: vtadmin.GetBackupsResponse - (*GetCellInfosRequest)(nil), // 28: vtadmin.GetCellInfosRequest - (*GetCellInfosResponse)(nil), // 29: vtadmin.GetCellInfosResponse - (*GetCellsAliasesRequest)(nil), // 30: vtadmin.GetCellsAliasesRequest - (*GetCellsAliasesResponse)(nil), // 31: vtadmin.GetCellsAliasesResponse - (*GetClustersRequest)(nil), // 32: vtadmin.GetClustersRequest - (*GetClustersResponse)(nil), // 33: vtadmin.GetClustersResponse - (*GetFullStatusRequest)(nil), // 34: vtadmin.GetFullStatusRequest - (*GetGatesRequest)(nil), // 35: vtadmin.GetGatesRequest - (*GetGatesResponse)(nil), // 36: vtadmin.GetGatesResponse - (*GetKeyspaceRequest)(nil), // 37: vtadmin.GetKeyspaceRequest - (*GetKeyspacesRequest)(nil), // 38: vtadmin.GetKeyspacesRequest - (*GetKeyspacesResponse)(nil), // 39: vtadmin.GetKeyspacesResponse - (*GetSchemaRequest)(nil), // 40: vtadmin.GetSchemaRequest - (*GetSchemasRequest)(nil), // 41: vtadmin.GetSchemasRequest - (*GetSchemasResponse)(nil), // 42: vtadmin.GetSchemasResponse - (*GetShardReplicationPositionsRequest)(nil), // 43: vtadmin.GetShardReplicationPositionsRequest - (*GetShardReplicationPositionsResponse)(nil), // 44: vtadmin.GetShardReplicationPositionsResponse - (*GetSrvKeyspaceRequest)(nil), // 45: vtadmin.GetSrvKeyspaceRequest - (*GetSrvKeyspacesRequest)(nil), // 46: vtadmin.GetSrvKeyspacesRequest - (*GetSrvKeyspacesResponse)(nil), // 47: vtadmin.GetSrvKeyspacesResponse - (*GetSrvVSchemaRequest)(nil), // 48: vtadmin.GetSrvVSchemaRequest - (*GetSrvVSchemasRequest)(nil), // 49: vtadmin.GetSrvVSchemasRequest - (*GetSrvVSchemasResponse)(nil), // 50: vtadmin.GetSrvVSchemasResponse - (*GetSchemaTableSizeOptions)(nil), // 51: vtadmin.GetSchemaTableSizeOptions - (*GetTabletRequest)(nil), // 52: vtadmin.GetTabletRequest - (*GetTabletsRequest)(nil), // 53: vtadmin.GetTabletsRequest - (*GetTabletsResponse)(nil), // 54: vtadmin.GetTabletsResponse - (*GetTopologyPathRequest)(nil), // 55: vtadmin.GetTopologyPathRequest - (*GetVSchemaRequest)(nil), // 56: vtadmin.GetVSchemaRequest - (*GetVSchemasRequest)(nil), // 57: vtadmin.GetVSchemasRequest - (*GetVSchemasResponse)(nil), // 58: vtadmin.GetVSchemasResponse - (*GetVtctldsRequest)(nil), // 59: vtadmin.GetVtctldsRequest - (*GetVtctldsResponse)(nil), // 60: vtadmin.GetVtctldsResponse - (*GetWorkflowRequest)(nil), // 61: vtadmin.GetWorkflowRequest - (*GetWorkflowsRequest)(nil), // 62: vtadmin.GetWorkflowsRequest - (*GetWorkflowsResponse)(nil), // 63: vtadmin.GetWorkflowsResponse - (*PingTabletRequest)(nil), // 64: vtadmin.PingTabletRequest - (*PingTabletResponse)(nil), // 65: vtadmin.PingTabletResponse - (*PlannedFailoverShardRequest)(nil), // 66: vtadmin.PlannedFailoverShardRequest - (*PlannedFailoverShardResponse)(nil), // 67: vtadmin.PlannedFailoverShardResponse - (*RebuildKeyspaceGraphRequest)(nil), // 68: vtadmin.RebuildKeyspaceGraphRequest - (*RebuildKeyspaceGraphResponse)(nil), // 69: vtadmin.RebuildKeyspaceGraphResponse - (*RefreshStateRequest)(nil), // 70: vtadmin.RefreshStateRequest - (*RefreshStateResponse)(nil), // 71: vtadmin.RefreshStateResponse - (*ReloadSchemasRequest)(nil), // 72: vtadmin.ReloadSchemasRequest - (*ReloadSchemasResponse)(nil), // 73: vtadmin.ReloadSchemasResponse - (*ReloadSchemaShardRequest)(nil), // 74: vtadmin.ReloadSchemaShardRequest - (*ReloadSchemaShardResponse)(nil), // 75: vtadmin.ReloadSchemaShardResponse - (*RefreshTabletReplicationSourceRequest)(nil), // 76: vtadmin.RefreshTabletReplicationSourceRequest - (*RefreshTabletReplicationSourceResponse)(nil), // 77: vtadmin.RefreshTabletReplicationSourceResponse - (*RemoveKeyspaceCellRequest)(nil), // 78: vtadmin.RemoveKeyspaceCellRequest - (*RemoveKeyspaceCellResponse)(nil), // 79: vtadmin.RemoveKeyspaceCellResponse - (*RunHealthCheckRequest)(nil), // 80: vtadmin.RunHealthCheckRequest - (*RunHealthCheckResponse)(nil), // 81: vtadmin.RunHealthCheckResponse - (*SetReadOnlyRequest)(nil), // 82: vtadmin.SetReadOnlyRequest - (*SetReadOnlyResponse)(nil), // 83: vtadmin.SetReadOnlyResponse - (*SetReadWriteRequest)(nil), // 84: vtadmin.SetReadWriteRequest - (*SetReadWriteResponse)(nil), // 85: vtadmin.SetReadWriteResponse - (*StartReplicationRequest)(nil), // 86: vtadmin.StartReplicationRequest - (*StartReplicationResponse)(nil), // 87: vtadmin.StartReplicationResponse - (*StopReplicationRequest)(nil), // 88: vtadmin.StopReplicationRequest - (*StopReplicationResponse)(nil), // 89: vtadmin.StopReplicationResponse - (*TabletExternallyPromotedRequest)(nil), // 90: vtadmin.TabletExternallyPromotedRequest - (*TabletExternallyPromotedResponse)(nil), // 91: vtadmin.TabletExternallyPromotedResponse - (*TabletExternallyReparentedRequest)(nil), // 92: vtadmin.TabletExternallyReparentedRequest - (*ValidateRequest)(nil), // 93: vtadmin.ValidateRequest - (*ValidateKeyspaceRequest)(nil), // 94: vtadmin.ValidateKeyspaceRequest - (*ValidateSchemaKeyspaceRequest)(nil), // 95: vtadmin.ValidateSchemaKeyspaceRequest - (*ValidateShardRequest)(nil), // 96: vtadmin.ValidateShardRequest - (*ValidateVersionKeyspaceRequest)(nil), // 97: vtadmin.ValidateVersionKeyspaceRequest - (*ValidateVersionShardRequest)(nil), // 98: vtadmin.ValidateVersionShardRequest - (*VTExplainRequest)(nil), // 99: vtadmin.VTExplainRequest - (*VTExplainResponse)(nil), // 100: vtadmin.VTExplainResponse - nil, // 101: vtadmin.ClusterCellsAliases.AliasesEntry - nil, // 102: vtadmin.Keyspace.ShardsEntry - nil, // 103: vtadmin.Schema.TableSizesEntry - (*Schema_ShardTableSize)(nil), // 104: vtadmin.Schema.ShardTableSize - (*Schema_TableSize)(nil), // 105: vtadmin.Schema.TableSize - nil, // 106: vtadmin.Schema.TableSize.ByShardEntry - nil, // 107: vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry - nil, // 108: vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry - (*ReloadSchemasResponse_KeyspaceResult)(nil), // 109: vtadmin.ReloadSchemasResponse.KeyspaceResult - (*ReloadSchemasResponse_ShardResult)(nil), // 110: vtadmin.ReloadSchemasResponse.ShardResult - (*ReloadSchemasResponse_TabletResult)(nil), // 111: vtadmin.ReloadSchemasResponse.TabletResult - (*mysqlctl.BackupInfo)(nil), // 112: mysqlctl.BackupInfo - (*topodata.CellInfo)(nil), // 113: topodata.CellInfo - (*vtctldata.ShardReplicationPositionsResponse)(nil), // 114: vtctldata.ShardReplicationPositionsResponse - (*vtctldata.Keyspace)(nil), // 115: vtctldata.Keyspace - (*tabletmanagerdata.TableDefinition)(nil), // 116: tabletmanagerdata.TableDefinition - (*vtctldata.Shard)(nil), // 117: vtctldata.Shard - (*vschema.SrvVSchema)(nil), // 118: vschema.SrvVSchema - (*topodata.Tablet)(nil), // 119: topodata.Tablet - (*vschema.Keyspace)(nil), // 120: vschema.Keyspace - (*vtctldata.Workflow)(nil), // 121: vtctldata.Workflow - (*vtctldata.CreateKeyspaceRequest)(nil), // 122: vtctldata.CreateKeyspaceRequest - (*vtctldata.CreateShardRequest)(nil), // 123: vtctldata.CreateShardRequest - (*vtctldata.DeleteKeyspaceRequest)(nil), // 124: vtctldata.DeleteKeyspaceRequest - (*vtctldata.DeleteShardsRequest)(nil), // 125: vtctldata.DeleteShardsRequest - (*topodata.TabletAlias)(nil), // 126: topodata.TabletAlias - (*vtctldata.EmergencyReparentShardRequest)(nil), // 127: vtctldata.EmergencyReparentShardRequest - (*logutil.Event)(nil), // 128: logutil.Event - (*vtctldata.GetBackupsRequest)(nil), // 129: vtctldata.GetBackupsRequest - (*vtctldata.PlannedReparentShardRequest)(nil), // 130: vtctldata.PlannedReparentShardRequest - (*topodata.CellsAlias)(nil), // 131: topodata.CellsAlias - (*vtctldata.GetSrvKeyspacesResponse)(nil), // 132: vtctldata.GetSrvKeyspacesResponse - (*vtctldata.CreateShardResponse)(nil), // 133: vtctldata.CreateShardResponse - (*vtctldata.DeleteKeyspaceResponse)(nil), // 134: vtctldata.DeleteKeyspaceResponse - (*vtctldata.DeleteShardsResponse)(nil), // 135: vtctldata.DeleteShardsResponse - (*vtctldata.GetFullStatusResponse)(nil), // 136: vtctldata.GetFullStatusResponse - (*vtctldata.GetTopologyPathResponse)(nil), // 137: vtctldata.GetTopologyPathResponse - (*vtctldata.ValidateResponse)(nil), // 138: vtctldata.ValidateResponse - (*vtctldata.ValidateKeyspaceResponse)(nil), // 139: vtctldata.ValidateKeyspaceResponse - (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 140: vtctldata.ValidateSchemaKeyspaceResponse - (*vtctldata.ValidateShardResponse)(nil), // 141: vtctldata.ValidateShardResponse - (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 142: vtctldata.ValidateVersionKeyspaceResponse - (*vtctldata.ValidateVersionShardResponse)(nil), // 143: vtctldata.ValidateVersionShardResponse + (Tablet_ServingState)(0), // 0: vtadmin.Tablet.ServingState + (*Cluster)(nil), // 1: vtadmin.Cluster + (*ClusterBackup)(nil), // 2: vtadmin.ClusterBackup + (*ClusterCellsAliases)(nil), // 3: vtadmin.ClusterCellsAliases + (*ClusterCellInfo)(nil), // 4: vtadmin.ClusterCellInfo + (*ClusterShardReplicationPosition)(nil), // 5: vtadmin.ClusterShardReplicationPosition + (*ClusterWorkflows)(nil), // 6: vtadmin.ClusterWorkflows + (*Keyspace)(nil), // 7: vtadmin.Keyspace + (*Schema)(nil), // 8: vtadmin.Schema + (*SchemaMigration)(nil), // 9: vtadmin.SchemaMigration + (*Shard)(nil), // 10: vtadmin.Shard + (*SrvVSchema)(nil), // 11: vtadmin.SrvVSchema + (*Tablet)(nil), // 12: vtadmin.Tablet + (*VSchema)(nil), // 13: vtadmin.VSchema + (*Vtctld)(nil), // 14: vtadmin.Vtctld + (*VTGate)(nil), // 15: vtadmin.VTGate + (*Workflow)(nil), // 16: vtadmin.Workflow + (*ApplySchemaRequest)(nil), // 17: vtadmin.ApplySchemaRequest + (*CancelSchemaMigrationRequest)(nil), // 18: vtadmin.CancelSchemaMigrationRequest + (*CleanupSchemaMigrationRequest)(nil), // 19: vtadmin.CleanupSchemaMigrationRequest + (*CompleteSchemaMigrationRequest)(nil), // 20: vtadmin.CompleteSchemaMigrationRequest + (*CreateKeyspaceRequest)(nil), // 21: vtadmin.CreateKeyspaceRequest + (*CreateKeyspaceResponse)(nil), // 22: vtadmin.CreateKeyspaceResponse + (*CreateShardRequest)(nil), // 23: vtadmin.CreateShardRequest + (*DeleteKeyspaceRequest)(nil), // 24: vtadmin.DeleteKeyspaceRequest + (*DeleteShardsRequest)(nil), // 25: vtadmin.DeleteShardsRequest + (*DeleteTabletRequest)(nil), // 26: vtadmin.DeleteTabletRequest + (*DeleteTabletResponse)(nil), // 27: vtadmin.DeleteTabletResponse + (*EmergencyFailoverShardRequest)(nil), // 28: vtadmin.EmergencyFailoverShardRequest + (*EmergencyFailoverShardResponse)(nil), // 29: vtadmin.EmergencyFailoverShardResponse + (*FindSchemaRequest)(nil), // 30: vtadmin.FindSchemaRequest + (*GetBackupsRequest)(nil), // 31: vtadmin.GetBackupsRequest + (*GetBackupsResponse)(nil), // 32: vtadmin.GetBackupsResponse + (*GetCellInfosRequest)(nil), // 33: vtadmin.GetCellInfosRequest + (*GetCellInfosResponse)(nil), // 34: vtadmin.GetCellInfosResponse + (*GetCellsAliasesRequest)(nil), // 35: vtadmin.GetCellsAliasesRequest + (*GetCellsAliasesResponse)(nil), // 36: vtadmin.GetCellsAliasesResponse + (*GetClustersRequest)(nil), // 37: vtadmin.GetClustersRequest + (*GetClustersResponse)(nil), // 38: vtadmin.GetClustersResponse + (*GetFullStatusRequest)(nil), // 39: vtadmin.GetFullStatusRequest + (*GetGatesRequest)(nil), // 40: vtadmin.GetGatesRequest + (*GetGatesResponse)(nil), // 41: vtadmin.GetGatesResponse + (*GetKeyspaceRequest)(nil), // 42: vtadmin.GetKeyspaceRequest + (*GetKeyspacesRequest)(nil), // 43: vtadmin.GetKeyspacesRequest + (*GetKeyspacesResponse)(nil), // 44: vtadmin.GetKeyspacesResponse + (*GetSchemaRequest)(nil), // 45: vtadmin.GetSchemaRequest + (*GetSchemasRequest)(nil), // 46: vtadmin.GetSchemasRequest + (*GetSchemasResponse)(nil), // 47: vtadmin.GetSchemasResponse + (*GetSchemaMigrationsRequest)(nil), // 48: vtadmin.GetSchemaMigrationsRequest + (*GetSchemaMigrationsResponse)(nil), // 49: vtadmin.GetSchemaMigrationsResponse + (*GetShardReplicationPositionsRequest)(nil), // 50: vtadmin.GetShardReplicationPositionsRequest + (*GetShardReplicationPositionsResponse)(nil), // 51: vtadmin.GetShardReplicationPositionsResponse + (*GetSrvKeyspaceRequest)(nil), // 52: vtadmin.GetSrvKeyspaceRequest + (*GetSrvKeyspacesRequest)(nil), // 53: vtadmin.GetSrvKeyspacesRequest + (*GetSrvKeyspacesResponse)(nil), // 54: vtadmin.GetSrvKeyspacesResponse + (*GetSrvVSchemaRequest)(nil), // 55: vtadmin.GetSrvVSchemaRequest + (*GetSrvVSchemasRequest)(nil), // 56: vtadmin.GetSrvVSchemasRequest + (*GetSrvVSchemasResponse)(nil), // 57: vtadmin.GetSrvVSchemasResponse + (*GetSchemaTableSizeOptions)(nil), // 58: vtadmin.GetSchemaTableSizeOptions + (*GetTabletRequest)(nil), // 59: vtadmin.GetTabletRequest + (*GetTabletsRequest)(nil), // 60: vtadmin.GetTabletsRequest + (*GetTabletsResponse)(nil), // 61: vtadmin.GetTabletsResponse + (*GetTopologyPathRequest)(nil), // 62: vtadmin.GetTopologyPathRequest + (*GetVSchemaRequest)(nil), // 63: vtadmin.GetVSchemaRequest + (*GetVSchemasRequest)(nil), // 64: vtadmin.GetVSchemasRequest + (*GetVSchemasResponse)(nil), // 65: vtadmin.GetVSchemasResponse + (*GetVtctldsRequest)(nil), // 66: vtadmin.GetVtctldsRequest + (*GetVtctldsResponse)(nil), // 67: vtadmin.GetVtctldsResponse + (*GetWorkflowRequest)(nil), // 68: vtadmin.GetWorkflowRequest + (*GetWorkflowsRequest)(nil), // 69: vtadmin.GetWorkflowsRequest + (*GetWorkflowsResponse)(nil), // 70: vtadmin.GetWorkflowsResponse + (*LaunchSchemaMigrationRequest)(nil), // 71: vtadmin.LaunchSchemaMigrationRequest + (*PingTabletRequest)(nil), // 72: vtadmin.PingTabletRequest + (*PingTabletResponse)(nil), // 73: vtadmin.PingTabletResponse + (*PlannedFailoverShardRequest)(nil), // 74: vtadmin.PlannedFailoverShardRequest + (*PlannedFailoverShardResponse)(nil), // 75: vtadmin.PlannedFailoverShardResponse + (*RebuildKeyspaceGraphRequest)(nil), // 76: vtadmin.RebuildKeyspaceGraphRequest + (*RebuildKeyspaceGraphResponse)(nil), // 77: vtadmin.RebuildKeyspaceGraphResponse + (*RefreshStateRequest)(nil), // 78: vtadmin.RefreshStateRequest + (*RefreshStateResponse)(nil), // 79: vtadmin.RefreshStateResponse + (*ReloadSchemasRequest)(nil), // 80: vtadmin.ReloadSchemasRequest + (*ReloadSchemasResponse)(nil), // 81: vtadmin.ReloadSchemasResponse + (*ReloadSchemaShardRequest)(nil), // 82: vtadmin.ReloadSchemaShardRequest + (*ReloadSchemaShardResponse)(nil), // 83: vtadmin.ReloadSchemaShardResponse + (*RefreshTabletReplicationSourceRequest)(nil), // 84: vtadmin.RefreshTabletReplicationSourceRequest + (*RefreshTabletReplicationSourceResponse)(nil), // 85: vtadmin.RefreshTabletReplicationSourceResponse + (*RemoveKeyspaceCellRequest)(nil), // 86: vtadmin.RemoveKeyspaceCellRequest + (*RemoveKeyspaceCellResponse)(nil), // 87: vtadmin.RemoveKeyspaceCellResponse + (*RetrySchemaMigrationRequest)(nil), // 88: vtadmin.RetrySchemaMigrationRequest + (*RunHealthCheckRequest)(nil), // 89: vtadmin.RunHealthCheckRequest + (*RunHealthCheckResponse)(nil), // 90: vtadmin.RunHealthCheckResponse + (*SetReadOnlyRequest)(nil), // 91: vtadmin.SetReadOnlyRequest + (*SetReadOnlyResponse)(nil), // 92: vtadmin.SetReadOnlyResponse + (*SetReadWriteRequest)(nil), // 93: vtadmin.SetReadWriteRequest + (*SetReadWriteResponse)(nil), // 94: vtadmin.SetReadWriteResponse + (*StartReplicationRequest)(nil), // 95: vtadmin.StartReplicationRequest + (*StartReplicationResponse)(nil), // 96: vtadmin.StartReplicationResponse + (*StopReplicationRequest)(nil), // 97: vtadmin.StopReplicationRequest + (*StopReplicationResponse)(nil), // 98: vtadmin.StopReplicationResponse + (*TabletExternallyPromotedRequest)(nil), // 99: vtadmin.TabletExternallyPromotedRequest + (*TabletExternallyPromotedResponse)(nil), // 100: vtadmin.TabletExternallyPromotedResponse + (*TabletExternallyReparentedRequest)(nil), // 101: vtadmin.TabletExternallyReparentedRequest + (*ValidateRequest)(nil), // 102: vtadmin.ValidateRequest + (*ValidateKeyspaceRequest)(nil), // 103: vtadmin.ValidateKeyspaceRequest + (*ValidateSchemaKeyspaceRequest)(nil), // 104: vtadmin.ValidateSchemaKeyspaceRequest + (*ValidateShardRequest)(nil), // 105: vtadmin.ValidateShardRequest + (*ValidateVersionKeyspaceRequest)(nil), // 106: vtadmin.ValidateVersionKeyspaceRequest + (*ValidateVersionShardRequest)(nil), // 107: vtadmin.ValidateVersionShardRequest + (*VTExplainRequest)(nil), // 108: vtadmin.VTExplainRequest + (*VTExplainResponse)(nil), // 109: vtadmin.VTExplainResponse + nil, // 110: vtadmin.ClusterCellsAliases.AliasesEntry + nil, // 111: vtadmin.Keyspace.ShardsEntry + nil, // 112: vtadmin.Schema.TableSizesEntry + (*Schema_ShardTableSize)(nil), // 113: vtadmin.Schema.ShardTableSize + (*Schema_TableSize)(nil), // 114: vtadmin.Schema.TableSize + nil, // 115: vtadmin.Schema.TableSize.ByShardEntry + (*GetSchemaMigrationsRequest_ClusterRequest)(nil), // 116: vtadmin.GetSchemaMigrationsRequest.ClusterRequest + nil, // 117: vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry + nil, // 118: vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry + (*ReloadSchemasResponse_KeyspaceResult)(nil), // 119: vtadmin.ReloadSchemasResponse.KeyspaceResult + (*ReloadSchemasResponse_ShardResult)(nil), // 120: vtadmin.ReloadSchemasResponse.ShardResult + (*ReloadSchemasResponse_TabletResult)(nil), // 121: vtadmin.ReloadSchemasResponse.TabletResult + (*mysqlctl.BackupInfo)(nil), // 122: mysqlctl.BackupInfo + (*topodata.CellInfo)(nil), // 123: topodata.CellInfo + (*vtctldata.ShardReplicationPositionsResponse)(nil), // 124: vtctldata.ShardReplicationPositionsResponse + (*vtctldata.Keyspace)(nil), // 125: vtctldata.Keyspace + (*tabletmanagerdata.TableDefinition)(nil), // 126: tabletmanagerdata.TableDefinition + (*vtctldata.SchemaMigration)(nil), // 127: vtctldata.SchemaMigration + (*vtctldata.Shard)(nil), // 128: vtctldata.Shard + (*vschema.SrvVSchema)(nil), // 129: vschema.SrvVSchema + (*topodata.Tablet)(nil), // 130: topodata.Tablet + (*vschema.Keyspace)(nil), // 131: vschema.Keyspace + (*vtctldata.Workflow)(nil), // 132: vtctldata.Workflow + (*vtctldata.ApplySchemaRequest)(nil), // 133: vtctldata.ApplySchemaRequest + (*vtctldata.CancelSchemaMigrationRequest)(nil), // 134: vtctldata.CancelSchemaMigrationRequest + (*vtctldata.CleanupSchemaMigrationRequest)(nil), // 135: vtctldata.CleanupSchemaMigrationRequest + (*vtctldata.CompleteSchemaMigrationRequest)(nil), // 136: vtctldata.CompleteSchemaMigrationRequest + (*vtctldata.CreateKeyspaceRequest)(nil), // 137: vtctldata.CreateKeyspaceRequest + (*vtctldata.CreateShardRequest)(nil), // 138: vtctldata.CreateShardRequest + (*vtctldata.DeleteKeyspaceRequest)(nil), // 139: vtctldata.DeleteKeyspaceRequest + (*vtctldata.DeleteShardsRequest)(nil), // 140: vtctldata.DeleteShardsRequest + (*topodata.TabletAlias)(nil), // 141: topodata.TabletAlias + (*vtctldata.EmergencyReparentShardRequest)(nil), // 142: vtctldata.EmergencyReparentShardRequest + (*logutil.Event)(nil), // 143: logutil.Event + (*vtctldata.GetBackupsRequest)(nil), // 144: vtctldata.GetBackupsRequest + (*vtctldata.LaunchSchemaMigrationRequest)(nil), // 145: vtctldata.LaunchSchemaMigrationRequest + (*vtctldata.PlannedReparentShardRequest)(nil), // 146: vtctldata.PlannedReparentShardRequest + (*vtctldata.RetrySchemaMigrationRequest)(nil), // 147: vtctldata.RetrySchemaMigrationRequest + (*topodata.CellsAlias)(nil), // 148: topodata.CellsAlias + (*vtctldata.GetSchemaMigrationsRequest)(nil), // 149: vtctldata.GetSchemaMigrationsRequest + (*vtctldata.GetSrvKeyspacesResponse)(nil), // 150: vtctldata.GetSrvKeyspacesResponse + (*vtctldata.ApplySchemaResponse)(nil), // 151: vtctldata.ApplySchemaResponse + (*vtctldata.CancelSchemaMigrationResponse)(nil), // 152: vtctldata.CancelSchemaMigrationResponse + (*vtctldata.CleanupSchemaMigrationResponse)(nil), // 153: vtctldata.CleanupSchemaMigrationResponse + (*vtctldata.CompleteSchemaMigrationResponse)(nil), // 154: vtctldata.CompleteSchemaMigrationResponse + (*vtctldata.CreateShardResponse)(nil), // 155: vtctldata.CreateShardResponse + (*vtctldata.DeleteKeyspaceResponse)(nil), // 156: vtctldata.DeleteKeyspaceResponse + (*vtctldata.DeleteShardsResponse)(nil), // 157: vtctldata.DeleteShardsResponse + (*vtctldata.GetFullStatusResponse)(nil), // 158: vtctldata.GetFullStatusResponse + (*vtctldata.GetTopologyPathResponse)(nil), // 159: vtctldata.GetTopologyPathResponse + (*vtctldata.LaunchSchemaMigrationResponse)(nil), // 160: vtctldata.LaunchSchemaMigrationResponse + (*vtctldata.RetrySchemaMigrationResponse)(nil), // 161: vtctldata.RetrySchemaMigrationResponse + (*vtctldata.ValidateResponse)(nil), // 162: vtctldata.ValidateResponse + (*vtctldata.ValidateKeyspaceResponse)(nil), // 163: vtctldata.ValidateKeyspaceResponse + (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 164: vtctldata.ValidateSchemaKeyspaceResponse + (*vtctldata.ValidateShardResponse)(nil), // 165: vtctldata.ValidateShardResponse + (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 166: vtctldata.ValidateVersionKeyspaceResponse + (*vtctldata.ValidateVersionShardResponse)(nil), // 167: vtctldata.ValidateVersionShardResponse } var file_vtadmin_proto_depIdxs = []int32{ 1, // 0: vtadmin.ClusterBackup.cluster:type_name -> vtadmin.Cluster - 112, // 1: vtadmin.ClusterBackup.backup:type_name -> mysqlctl.BackupInfo + 122, // 1: vtadmin.ClusterBackup.backup:type_name -> mysqlctl.BackupInfo 1, // 2: vtadmin.ClusterCellsAliases.cluster:type_name -> vtadmin.Cluster - 101, // 3: vtadmin.ClusterCellsAliases.aliases:type_name -> vtadmin.ClusterCellsAliases.AliasesEntry + 110, // 3: vtadmin.ClusterCellsAliases.aliases:type_name -> vtadmin.ClusterCellsAliases.AliasesEntry 1, // 4: vtadmin.ClusterCellInfo.cluster:type_name -> vtadmin.Cluster - 113, // 5: vtadmin.ClusterCellInfo.cell_info:type_name -> topodata.CellInfo + 123, // 5: vtadmin.ClusterCellInfo.cell_info:type_name -> topodata.CellInfo 1, // 6: vtadmin.ClusterShardReplicationPosition.cluster:type_name -> vtadmin.Cluster - 114, // 7: vtadmin.ClusterShardReplicationPosition.position_info:type_name -> vtctldata.ShardReplicationPositionsResponse - 15, // 8: vtadmin.ClusterWorkflows.workflows:type_name -> vtadmin.Workflow + 124, // 7: vtadmin.ClusterShardReplicationPosition.position_info:type_name -> vtctldata.ShardReplicationPositionsResponse + 16, // 8: vtadmin.ClusterWorkflows.workflows:type_name -> vtadmin.Workflow 1, // 9: vtadmin.Keyspace.cluster:type_name -> vtadmin.Cluster - 115, // 10: vtadmin.Keyspace.keyspace:type_name -> vtctldata.Keyspace - 102, // 11: vtadmin.Keyspace.shards:type_name -> vtadmin.Keyspace.ShardsEntry + 125, // 10: vtadmin.Keyspace.keyspace:type_name -> vtctldata.Keyspace + 111, // 11: vtadmin.Keyspace.shards:type_name -> vtadmin.Keyspace.ShardsEntry 1, // 12: vtadmin.Schema.cluster:type_name -> vtadmin.Cluster - 116, // 13: vtadmin.Schema.table_definitions:type_name -> tabletmanagerdata.TableDefinition - 103, // 14: vtadmin.Schema.table_sizes:type_name -> vtadmin.Schema.TableSizesEntry - 1, // 15: vtadmin.Shard.cluster:type_name -> vtadmin.Cluster - 117, // 16: vtadmin.Shard.shard:type_name -> vtctldata.Shard - 1, // 17: vtadmin.SrvVSchema.cluster:type_name -> vtadmin.Cluster - 118, // 18: vtadmin.SrvVSchema.srv_v_schema:type_name -> vschema.SrvVSchema - 1, // 19: vtadmin.Tablet.cluster:type_name -> vtadmin.Cluster - 119, // 20: vtadmin.Tablet.tablet:type_name -> topodata.Tablet - 0, // 21: vtadmin.Tablet.state:type_name -> vtadmin.Tablet.ServingState - 1, // 22: vtadmin.VSchema.cluster:type_name -> vtadmin.Cluster - 120, // 23: vtadmin.VSchema.v_schema:type_name -> vschema.Keyspace - 1, // 24: vtadmin.Vtctld.cluster:type_name -> vtadmin.Cluster - 1, // 25: vtadmin.VTGate.cluster:type_name -> vtadmin.Cluster - 1, // 26: vtadmin.Workflow.cluster:type_name -> vtadmin.Cluster - 121, // 27: vtadmin.Workflow.workflow:type_name -> vtctldata.Workflow - 122, // 28: vtadmin.CreateKeyspaceRequest.options:type_name -> vtctldata.CreateKeyspaceRequest - 7, // 29: vtadmin.CreateKeyspaceResponse.keyspace:type_name -> vtadmin.Keyspace - 123, // 30: vtadmin.CreateShardRequest.options:type_name -> vtctldata.CreateShardRequest - 124, // 31: vtadmin.DeleteKeyspaceRequest.options:type_name -> vtctldata.DeleteKeyspaceRequest - 125, // 32: vtadmin.DeleteShardsRequest.options:type_name -> vtctldata.DeleteShardsRequest - 126, // 33: vtadmin.DeleteTabletRequest.alias:type_name -> topodata.TabletAlias - 1, // 34: vtadmin.DeleteTabletResponse.cluster:type_name -> vtadmin.Cluster - 127, // 35: vtadmin.EmergencyFailoverShardRequest.options:type_name -> vtctldata.EmergencyReparentShardRequest - 1, // 36: vtadmin.EmergencyFailoverShardResponse.cluster:type_name -> vtadmin.Cluster - 126, // 37: vtadmin.EmergencyFailoverShardResponse.promoted_primary:type_name -> topodata.TabletAlias - 128, // 38: vtadmin.EmergencyFailoverShardResponse.events:type_name -> logutil.Event - 51, // 39: vtadmin.FindSchemaRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions - 129, // 40: vtadmin.GetBackupsRequest.request_options:type_name -> vtctldata.GetBackupsRequest - 2, // 41: vtadmin.GetBackupsResponse.backups:type_name -> vtadmin.ClusterBackup - 4, // 42: vtadmin.GetCellInfosResponse.cell_infos:type_name -> vtadmin.ClusterCellInfo - 3, // 43: vtadmin.GetCellsAliasesResponse.aliases:type_name -> vtadmin.ClusterCellsAliases - 1, // 44: vtadmin.GetClustersResponse.clusters:type_name -> vtadmin.Cluster - 126, // 45: vtadmin.GetFullStatusRequest.alias:type_name -> topodata.TabletAlias - 14, // 46: vtadmin.GetGatesResponse.gates:type_name -> vtadmin.VTGate - 7, // 47: vtadmin.GetKeyspacesResponse.keyspaces:type_name -> vtadmin.Keyspace - 51, // 48: vtadmin.GetSchemaRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions - 51, // 49: vtadmin.GetSchemasRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions - 8, // 50: vtadmin.GetSchemasResponse.schemas:type_name -> vtadmin.Schema - 5, // 51: vtadmin.GetShardReplicationPositionsResponse.replication_positions:type_name -> vtadmin.ClusterShardReplicationPosition - 107, // 52: vtadmin.GetSrvKeyspacesResponse.srv_keyspaces:type_name -> vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry - 10, // 53: vtadmin.GetSrvVSchemasResponse.srv_v_schemas:type_name -> vtadmin.SrvVSchema - 126, // 54: vtadmin.GetTabletRequest.alias:type_name -> topodata.TabletAlias - 11, // 55: vtadmin.GetTabletsResponse.tablets:type_name -> vtadmin.Tablet - 12, // 56: vtadmin.GetVSchemasResponse.v_schemas:type_name -> vtadmin.VSchema - 13, // 57: vtadmin.GetVtctldsResponse.vtctlds:type_name -> vtadmin.Vtctld - 108, // 58: vtadmin.GetWorkflowsResponse.workflows_by_cluster:type_name -> vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry - 126, // 59: vtadmin.PingTabletRequest.alias:type_name -> topodata.TabletAlias - 1, // 60: vtadmin.PingTabletResponse.cluster:type_name -> vtadmin.Cluster - 130, // 61: vtadmin.PlannedFailoverShardRequest.options:type_name -> vtctldata.PlannedReparentShardRequest - 1, // 62: vtadmin.PlannedFailoverShardResponse.cluster:type_name -> vtadmin.Cluster - 126, // 63: vtadmin.PlannedFailoverShardResponse.promoted_primary:type_name -> topodata.TabletAlias - 128, // 64: vtadmin.PlannedFailoverShardResponse.events:type_name -> logutil.Event - 126, // 65: vtadmin.RefreshStateRequest.alias:type_name -> topodata.TabletAlias - 1, // 66: vtadmin.RefreshStateResponse.cluster:type_name -> vtadmin.Cluster - 126, // 67: vtadmin.ReloadSchemasRequest.tablets:type_name -> topodata.TabletAlias - 109, // 68: vtadmin.ReloadSchemasResponse.keyspace_results:type_name -> vtadmin.ReloadSchemasResponse.KeyspaceResult - 110, // 69: vtadmin.ReloadSchemasResponse.shard_results:type_name -> vtadmin.ReloadSchemasResponse.ShardResult - 111, // 70: vtadmin.ReloadSchemasResponse.tablet_results:type_name -> vtadmin.ReloadSchemasResponse.TabletResult - 128, // 71: vtadmin.ReloadSchemaShardResponse.events:type_name -> logutil.Event - 126, // 72: vtadmin.RefreshTabletReplicationSourceRequest.alias:type_name -> topodata.TabletAlias - 126, // 73: vtadmin.RefreshTabletReplicationSourceResponse.primary:type_name -> topodata.TabletAlias - 1, // 74: vtadmin.RefreshTabletReplicationSourceResponse.cluster:type_name -> vtadmin.Cluster - 126, // 75: vtadmin.RunHealthCheckRequest.alias:type_name -> topodata.TabletAlias - 1, // 76: vtadmin.RunHealthCheckResponse.cluster:type_name -> vtadmin.Cluster - 126, // 77: vtadmin.SetReadOnlyRequest.alias:type_name -> topodata.TabletAlias - 126, // 78: vtadmin.SetReadWriteRequest.alias:type_name -> topodata.TabletAlias - 126, // 79: vtadmin.StartReplicationRequest.alias:type_name -> topodata.TabletAlias - 1, // 80: vtadmin.StartReplicationResponse.cluster:type_name -> vtadmin.Cluster - 126, // 81: vtadmin.StopReplicationRequest.alias:type_name -> topodata.TabletAlias - 1, // 82: vtadmin.StopReplicationResponse.cluster:type_name -> vtadmin.Cluster - 126, // 83: vtadmin.TabletExternallyPromotedRequest.alias:type_name -> topodata.TabletAlias - 1, // 84: vtadmin.TabletExternallyPromotedResponse.cluster:type_name -> vtadmin.Cluster - 126, // 85: vtadmin.TabletExternallyPromotedResponse.new_primary:type_name -> topodata.TabletAlias - 126, // 86: vtadmin.TabletExternallyPromotedResponse.old_primary:type_name -> topodata.TabletAlias - 126, // 87: vtadmin.TabletExternallyReparentedRequest.alias:type_name -> topodata.TabletAlias - 131, // 88: vtadmin.ClusterCellsAliases.AliasesEntry.value:type_name -> topodata.CellsAlias - 117, // 89: vtadmin.Keyspace.ShardsEntry.value:type_name -> vtctldata.Shard - 105, // 90: vtadmin.Schema.TableSizesEntry.value:type_name -> vtadmin.Schema.TableSize - 106, // 91: vtadmin.Schema.TableSize.by_shard:type_name -> vtadmin.Schema.TableSize.ByShardEntry - 104, // 92: vtadmin.Schema.TableSize.ByShardEntry.value:type_name -> vtadmin.Schema.ShardTableSize - 132, // 93: vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry.value:type_name -> vtctldata.GetSrvKeyspacesResponse - 6, // 94: vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry.value:type_name -> vtadmin.ClusterWorkflows - 7, // 95: vtadmin.ReloadSchemasResponse.KeyspaceResult.keyspace:type_name -> vtadmin.Keyspace - 128, // 96: vtadmin.ReloadSchemasResponse.KeyspaceResult.events:type_name -> logutil.Event - 9, // 97: vtadmin.ReloadSchemasResponse.ShardResult.shard:type_name -> vtadmin.Shard - 128, // 98: vtadmin.ReloadSchemasResponse.ShardResult.events:type_name -> logutil.Event - 11, // 99: vtadmin.ReloadSchemasResponse.TabletResult.tablet:type_name -> vtadmin.Tablet - 16, // 100: vtadmin.VTAdmin.CreateKeyspace:input_type -> vtadmin.CreateKeyspaceRequest - 18, // 101: vtadmin.VTAdmin.CreateShard:input_type -> vtadmin.CreateShardRequest - 19, // 102: vtadmin.VTAdmin.DeleteKeyspace:input_type -> vtadmin.DeleteKeyspaceRequest - 20, // 103: vtadmin.VTAdmin.DeleteShards:input_type -> vtadmin.DeleteShardsRequest - 21, // 104: vtadmin.VTAdmin.DeleteTablet:input_type -> vtadmin.DeleteTabletRequest - 23, // 105: vtadmin.VTAdmin.EmergencyFailoverShard:input_type -> vtadmin.EmergencyFailoverShardRequest - 25, // 106: vtadmin.VTAdmin.FindSchema:input_type -> vtadmin.FindSchemaRequest - 26, // 107: vtadmin.VTAdmin.GetBackups:input_type -> vtadmin.GetBackupsRequest - 28, // 108: vtadmin.VTAdmin.GetCellInfos:input_type -> vtadmin.GetCellInfosRequest - 30, // 109: vtadmin.VTAdmin.GetCellsAliases:input_type -> vtadmin.GetCellsAliasesRequest - 32, // 110: vtadmin.VTAdmin.GetClusters:input_type -> vtadmin.GetClustersRequest - 34, // 111: vtadmin.VTAdmin.GetFullStatus:input_type -> vtadmin.GetFullStatusRequest - 35, // 112: vtadmin.VTAdmin.GetGates:input_type -> vtadmin.GetGatesRequest - 37, // 113: vtadmin.VTAdmin.GetKeyspace:input_type -> vtadmin.GetKeyspaceRequest - 38, // 114: vtadmin.VTAdmin.GetKeyspaces:input_type -> vtadmin.GetKeyspacesRequest - 40, // 115: vtadmin.VTAdmin.GetSchema:input_type -> vtadmin.GetSchemaRequest - 41, // 116: vtadmin.VTAdmin.GetSchemas:input_type -> vtadmin.GetSchemasRequest - 43, // 117: vtadmin.VTAdmin.GetShardReplicationPositions:input_type -> vtadmin.GetShardReplicationPositionsRequest - 45, // 118: vtadmin.VTAdmin.GetSrvKeyspace:input_type -> vtadmin.GetSrvKeyspaceRequest - 46, // 119: vtadmin.VTAdmin.GetSrvKeyspaces:input_type -> vtadmin.GetSrvKeyspacesRequest - 48, // 120: vtadmin.VTAdmin.GetSrvVSchema:input_type -> vtadmin.GetSrvVSchemaRequest - 49, // 121: vtadmin.VTAdmin.GetSrvVSchemas:input_type -> vtadmin.GetSrvVSchemasRequest - 52, // 122: vtadmin.VTAdmin.GetTablet:input_type -> vtadmin.GetTabletRequest - 53, // 123: vtadmin.VTAdmin.GetTablets:input_type -> vtadmin.GetTabletsRequest - 55, // 124: vtadmin.VTAdmin.GetTopologyPath:input_type -> vtadmin.GetTopologyPathRequest - 56, // 125: vtadmin.VTAdmin.GetVSchema:input_type -> vtadmin.GetVSchemaRequest - 57, // 126: vtadmin.VTAdmin.GetVSchemas:input_type -> vtadmin.GetVSchemasRequest - 59, // 127: vtadmin.VTAdmin.GetVtctlds:input_type -> vtadmin.GetVtctldsRequest - 61, // 128: vtadmin.VTAdmin.GetWorkflow:input_type -> vtadmin.GetWorkflowRequest - 62, // 129: vtadmin.VTAdmin.GetWorkflows:input_type -> vtadmin.GetWorkflowsRequest - 64, // 130: vtadmin.VTAdmin.PingTablet:input_type -> vtadmin.PingTabletRequest - 66, // 131: vtadmin.VTAdmin.PlannedFailoverShard:input_type -> vtadmin.PlannedFailoverShardRequest - 68, // 132: vtadmin.VTAdmin.RebuildKeyspaceGraph:input_type -> vtadmin.RebuildKeyspaceGraphRequest - 70, // 133: vtadmin.VTAdmin.RefreshState:input_type -> vtadmin.RefreshStateRequest - 76, // 134: vtadmin.VTAdmin.RefreshTabletReplicationSource:input_type -> vtadmin.RefreshTabletReplicationSourceRequest - 72, // 135: vtadmin.VTAdmin.ReloadSchemas:input_type -> vtadmin.ReloadSchemasRequest - 74, // 136: vtadmin.VTAdmin.ReloadSchemaShard:input_type -> vtadmin.ReloadSchemaShardRequest - 78, // 137: vtadmin.VTAdmin.RemoveKeyspaceCell:input_type -> vtadmin.RemoveKeyspaceCellRequest - 80, // 138: vtadmin.VTAdmin.RunHealthCheck:input_type -> vtadmin.RunHealthCheckRequest - 82, // 139: vtadmin.VTAdmin.SetReadOnly:input_type -> vtadmin.SetReadOnlyRequest - 84, // 140: vtadmin.VTAdmin.SetReadWrite:input_type -> vtadmin.SetReadWriteRequest - 86, // 141: vtadmin.VTAdmin.StartReplication:input_type -> vtadmin.StartReplicationRequest - 88, // 142: vtadmin.VTAdmin.StopReplication:input_type -> vtadmin.StopReplicationRequest - 90, // 143: vtadmin.VTAdmin.TabletExternallyPromoted:input_type -> vtadmin.TabletExternallyPromotedRequest - 93, // 144: vtadmin.VTAdmin.Validate:input_type -> vtadmin.ValidateRequest - 94, // 145: vtadmin.VTAdmin.ValidateKeyspace:input_type -> vtadmin.ValidateKeyspaceRequest - 95, // 146: vtadmin.VTAdmin.ValidateSchemaKeyspace:input_type -> vtadmin.ValidateSchemaKeyspaceRequest - 96, // 147: vtadmin.VTAdmin.ValidateShard:input_type -> vtadmin.ValidateShardRequest - 97, // 148: vtadmin.VTAdmin.ValidateVersionKeyspace:input_type -> vtadmin.ValidateVersionKeyspaceRequest - 98, // 149: vtadmin.VTAdmin.ValidateVersionShard:input_type -> vtadmin.ValidateVersionShardRequest - 99, // 150: vtadmin.VTAdmin.VTExplain:input_type -> vtadmin.VTExplainRequest - 17, // 151: vtadmin.VTAdmin.CreateKeyspace:output_type -> vtadmin.CreateKeyspaceResponse - 133, // 152: vtadmin.VTAdmin.CreateShard:output_type -> vtctldata.CreateShardResponse - 134, // 153: vtadmin.VTAdmin.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse - 135, // 154: vtadmin.VTAdmin.DeleteShards:output_type -> vtctldata.DeleteShardsResponse - 22, // 155: vtadmin.VTAdmin.DeleteTablet:output_type -> vtadmin.DeleteTabletResponse - 24, // 156: vtadmin.VTAdmin.EmergencyFailoverShard:output_type -> vtadmin.EmergencyFailoverShardResponse - 8, // 157: vtadmin.VTAdmin.FindSchema:output_type -> vtadmin.Schema - 27, // 158: vtadmin.VTAdmin.GetBackups:output_type -> vtadmin.GetBackupsResponse - 29, // 159: vtadmin.VTAdmin.GetCellInfos:output_type -> vtadmin.GetCellInfosResponse - 31, // 160: vtadmin.VTAdmin.GetCellsAliases:output_type -> vtadmin.GetCellsAliasesResponse - 33, // 161: vtadmin.VTAdmin.GetClusters:output_type -> vtadmin.GetClustersResponse - 136, // 162: vtadmin.VTAdmin.GetFullStatus:output_type -> vtctldata.GetFullStatusResponse - 36, // 163: vtadmin.VTAdmin.GetGates:output_type -> vtadmin.GetGatesResponse - 7, // 164: vtadmin.VTAdmin.GetKeyspace:output_type -> vtadmin.Keyspace - 39, // 165: vtadmin.VTAdmin.GetKeyspaces:output_type -> vtadmin.GetKeyspacesResponse - 8, // 166: vtadmin.VTAdmin.GetSchema:output_type -> vtadmin.Schema - 42, // 167: vtadmin.VTAdmin.GetSchemas:output_type -> vtadmin.GetSchemasResponse - 44, // 168: vtadmin.VTAdmin.GetShardReplicationPositions:output_type -> vtadmin.GetShardReplicationPositionsResponse - 132, // 169: vtadmin.VTAdmin.GetSrvKeyspace:output_type -> vtctldata.GetSrvKeyspacesResponse - 47, // 170: vtadmin.VTAdmin.GetSrvKeyspaces:output_type -> vtadmin.GetSrvKeyspacesResponse - 10, // 171: vtadmin.VTAdmin.GetSrvVSchema:output_type -> vtadmin.SrvVSchema - 50, // 172: vtadmin.VTAdmin.GetSrvVSchemas:output_type -> vtadmin.GetSrvVSchemasResponse - 11, // 173: vtadmin.VTAdmin.GetTablet:output_type -> vtadmin.Tablet - 54, // 174: vtadmin.VTAdmin.GetTablets:output_type -> vtadmin.GetTabletsResponse - 137, // 175: vtadmin.VTAdmin.GetTopologyPath:output_type -> vtctldata.GetTopologyPathResponse - 12, // 176: vtadmin.VTAdmin.GetVSchema:output_type -> vtadmin.VSchema - 58, // 177: vtadmin.VTAdmin.GetVSchemas:output_type -> vtadmin.GetVSchemasResponse - 60, // 178: vtadmin.VTAdmin.GetVtctlds:output_type -> vtadmin.GetVtctldsResponse - 15, // 179: vtadmin.VTAdmin.GetWorkflow:output_type -> vtadmin.Workflow - 63, // 180: vtadmin.VTAdmin.GetWorkflows:output_type -> vtadmin.GetWorkflowsResponse - 65, // 181: vtadmin.VTAdmin.PingTablet:output_type -> vtadmin.PingTabletResponse - 67, // 182: vtadmin.VTAdmin.PlannedFailoverShard:output_type -> vtadmin.PlannedFailoverShardResponse - 69, // 183: vtadmin.VTAdmin.RebuildKeyspaceGraph:output_type -> vtadmin.RebuildKeyspaceGraphResponse - 71, // 184: vtadmin.VTAdmin.RefreshState:output_type -> vtadmin.RefreshStateResponse - 77, // 185: vtadmin.VTAdmin.RefreshTabletReplicationSource:output_type -> vtadmin.RefreshTabletReplicationSourceResponse - 73, // 186: vtadmin.VTAdmin.ReloadSchemas:output_type -> vtadmin.ReloadSchemasResponse - 75, // 187: vtadmin.VTAdmin.ReloadSchemaShard:output_type -> vtadmin.ReloadSchemaShardResponse - 79, // 188: vtadmin.VTAdmin.RemoveKeyspaceCell:output_type -> vtadmin.RemoveKeyspaceCellResponse - 81, // 189: vtadmin.VTAdmin.RunHealthCheck:output_type -> vtadmin.RunHealthCheckResponse - 83, // 190: vtadmin.VTAdmin.SetReadOnly:output_type -> vtadmin.SetReadOnlyResponse - 85, // 191: vtadmin.VTAdmin.SetReadWrite:output_type -> vtadmin.SetReadWriteResponse - 87, // 192: vtadmin.VTAdmin.StartReplication:output_type -> vtadmin.StartReplicationResponse - 89, // 193: vtadmin.VTAdmin.StopReplication:output_type -> vtadmin.StopReplicationResponse - 91, // 194: vtadmin.VTAdmin.TabletExternallyPromoted:output_type -> vtadmin.TabletExternallyPromotedResponse - 138, // 195: vtadmin.VTAdmin.Validate:output_type -> vtctldata.ValidateResponse - 139, // 196: vtadmin.VTAdmin.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse - 140, // 197: vtadmin.VTAdmin.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse - 141, // 198: vtadmin.VTAdmin.ValidateShard:output_type -> vtctldata.ValidateShardResponse - 142, // 199: vtadmin.VTAdmin.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse - 143, // 200: vtadmin.VTAdmin.ValidateVersionShard:output_type -> vtctldata.ValidateVersionShardResponse - 100, // 201: vtadmin.VTAdmin.VTExplain:output_type -> vtadmin.VTExplainResponse - 151, // [151:202] is the sub-list for method output_type - 100, // [100:151] is the sub-list for method input_type - 100, // [100:100] is the sub-list for extension type_name - 100, // [100:100] is the sub-list for extension extendee - 0, // [0:100] is the sub-list for field type_name + 126, // 13: vtadmin.Schema.table_definitions:type_name -> tabletmanagerdata.TableDefinition + 112, // 14: vtadmin.Schema.table_sizes:type_name -> vtadmin.Schema.TableSizesEntry + 1, // 15: vtadmin.SchemaMigration.cluster:type_name -> vtadmin.Cluster + 127, // 16: vtadmin.SchemaMigration.schema_migration:type_name -> vtctldata.SchemaMigration + 1, // 17: vtadmin.Shard.cluster:type_name -> vtadmin.Cluster + 128, // 18: vtadmin.Shard.shard:type_name -> vtctldata.Shard + 1, // 19: vtadmin.SrvVSchema.cluster:type_name -> vtadmin.Cluster + 129, // 20: vtadmin.SrvVSchema.srv_v_schema:type_name -> vschema.SrvVSchema + 1, // 21: vtadmin.Tablet.cluster:type_name -> vtadmin.Cluster + 130, // 22: vtadmin.Tablet.tablet:type_name -> topodata.Tablet + 0, // 23: vtadmin.Tablet.state:type_name -> vtadmin.Tablet.ServingState + 1, // 24: vtadmin.VSchema.cluster:type_name -> vtadmin.Cluster + 131, // 25: vtadmin.VSchema.v_schema:type_name -> vschema.Keyspace + 1, // 26: vtadmin.Vtctld.cluster:type_name -> vtadmin.Cluster + 1, // 27: vtadmin.VTGate.cluster:type_name -> vtadmin.Cluster + 1, // 28: vtadmin.Workflow.cluster:type_name -> vtadmin.Cluster + 132, // 29: vtadmin.Workflow.workflow:type_name -> vtctldata.Workflow + 133, // 30: vtadmin.ApplySchemaRequest.request:type_name -> vtctldata.ApplySchemaRequest + 134, // 31: vtadmin.CancelSchemaMigrationRequest.request:type_name -> vtctldata.CancelSchemaMigrationRequest + 135, // 32: vtadmin.CleanupSchemaMigrationRequest.request:type_name -> vtctldata.CleanupSchemaMigrationRequest + 136, // 33: vtadmin.CompleteSchemaMigrationRequest.request:type_name -> vtctldata.CompleteSchemaMigrationRequest + 137, // 34: vtadmin.CreateKeyspaceRequest.options:type_name -> vtctldata.CreateKeyspaceRequest + 7, // 35: vtadmin.CreateKeyspaceResponse.keyspace:type_name -> vtadmin.Keyspace + 138, // 36: vtadmin.CreateShardRequest.options:type_name -> vtctldata.CreateShardRequest + 139, // 37: vtadmin.DeleteKeyspaceRequest.options:type_name -> vtctldata.DeleteKeyspaceRequest + 140, // 38: vtadmin.DeleteShardsRequest.options:type_name -> vtctldata.DeleteShardsRequest + 141, // 39: vtadmin.DeleteTabletRequest.alias:type_name -> topodata.TabletAlias + 1, // 40: vtadmin.DeleteTabletResponse.cluster:type_name -> vtadmin.Cluster + 142, // 41: vtadmin.EmergencyFailoverShardRequest.options:type_name -> vtctldata.EmergencyReparentShardRequest + 1, // 42: vtadmin.EmergencyFailoverShardResponse.cluster:type_name -> vtadmin.Cluster + 141, // 43: vtadmin.EmergencyFailoverShardResponse.promoted_primary:type_name -> topodata.TabletAlias + 143, // 44: vtadmin.EmergencyFailoverShardResponse.events:type_name -> logutil.Event + 58, // 45: vtadmin.FindSchemaRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions + 144, // 46: vtadmin.GetBackupsRequest.request_options:type_name -> vtctldata.GetBackupsRequest + 2, // 47: vtadmin.GetBackupsResponse.backups:type_name -> vtadmin.ClusterBackup + 4, // 48: vtadmin.GetCellInfosResponse.cell_infos:type_name -> vtadmin.ClusterCellInfo + 3, // 49: vtadmin.GetCellsAliasesResponse.aliases:type_name -> vtadmin.ClusterCellsAliases + 1, // 50: vtadmin.GetClustersResponse.clusters:type_name -> vtadmin.Cluster + 141, // 51: vtadmin.GetFullStatusRequest.alias:type_name -> topodata.TabletAlias + 15, // 52: vtadmin.GetGatesResponse.gates:type_name -> vtadmin.VTGate + 7, // 53: vtadmin.GetKeyspacesResponse.keyspaces:type_name -> vtadmin.Keyspace + 58, // 54: vtadmin.GetSchemaRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions + 58, // 55: vtadmin.GetSchemasRequest.table_size_options:type_name -> vtadmin.GetSchemaTableSizeOptions + 8, // 56: vtadmin.GetSchemasResponse.schemas:type_name -> vtadmin.Schema + 116, // 57: vtadmin.GetSchemaMigrationsRequest.cluster_requests:type_name -> vtadmin.GetSchemaMigrationsRequest.ClusterRequest + 9, // 58: vtadmin.GetSchemaMigrationsResponse.schema_migrations:type_name -> vtadmin.SchemaMigration + 5, // 59: vtadmin.GetShardReplicationPositionsResponse.replication_positions:type_name -> vtadmin.ClusterShardReplicationPosition + 117, // 60: vtadmin.GetSrvKeyspacesResponse.srv_keyspaces:type_name -> vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry + 11, // 61: vtadmin.GetSrvVSchemasResponse.srv_v_schemas:type_name -> vtadmin.SrvVSchema + 141, // 62: vtadmin.GetTabletRequest.alias:type_name -> topodata.TabletAlias + 12, // 63: vtadmin.GetTabletsResponse.tablets:type_name -> vtadmin.Tablet + 13, // 64: vtadmin.GetVSchemasResponse.v_schemas:type_name -> vtadmin.VSchema + 14, // 65: vtadmin.GetVtctldsResponse.vtctlds:type_name -> vtadmin.Vtctld + 118, // 66: vtadmin.GetWorkflowsResponse.workflows_by_cluster:type_name -> vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry + 145, // 67: vtadmin.LaunchSchemaMigrationRequest.request:type_name -> vtctldata.LaunchSchemaMigrationRequest + 141, // 68: vtadmin.PingTabletRequest.alias:type_name -> topodata.TabletAlias + 1, // 69: vtadmin.PingTabletResponse.cluster:type_name -> vtadmin.Cluster + 146, // 70: vtadmin.PlannedFailoverShardRequest.options:type_name -> vtctldata.PlannedReparentShardRequest + 1, // 71: vtadmin.PlannedFailoverShardResponse.cluster:type_name -> vtadmin.Cluster + 141, // 72: vtadmin.PlannedFailoverShardResponse.promoted_primary:type_name -> topodata.TabletAlias + 143, // 73: vtadmin.PlannedFailoverShardResponse.events:type_name -> logutil.Event + 141, // 74: vtadmin.RefreshStateRequest.alias:type_name -> topodata.TabletAlias + 1, // 75: vtadmin.RefreshStateResponse.cluster:type_name -> vtadmin.Cluster + 141, // 76: vtadmin.ReloadSchemasRequest.tablets:type_name -> topodata.TabletAlias + 119, // 77: vtadmin.ReloadSchemasResponse.keyspace_results:type_name -> vtadmin.ReloadSchemasResponse.KeyspaceResult + 120, // 78: vtadmin.ReloadSchemasResponse.shard_results:type_name -> vtadmin.ReloadSchemasResponse.ShardResult + 121, // 79: vtadmin.ReloadSchemasResponse.tablet_results:type_name -> vtadmin.ReloadSchemasResponse.TabletResult + 143, // 80: vtadmin.ReloadSchemaShardResponse.events:type_name -> logutil.Event + 141, // 81: vtadmin.RefreshTabletReplicationSourceRequest.alias:type_name -> topodata.TabletAlias + 141, // 82: vtadmin.RefreshTabletReplicationSourceResponse.primary:type_name -> topodata.TabletAlias + 1, // 83: vtadmin.RefreshTabletReplicationSourceResponse.cluster:type_name -> vtadmin.Cluster + 147, // 84: vtadmin.RetrySchemaMigrationRequest.request:type_name -> vtctldata.RetrySchemaMigrationRequest + 141, // 85: vtadmin.RunHealthCheckRequest.alias:type_name -> topodata.TabletAlias + 1, // 86: vtadmin.RunHealthCheckResponse.cluster:type_name -> vtadmin.Cluster + 141, // 87: vtadmin.SetReadOnlyRequest.alias:type_name -> topodata.TabletAlias + 141, // 88: vtadmin.SetReadWriteRequest.alias:type_name -> topodata.TabletAlias + 141, // 89: vtadmin.StartReplicationRequest.alias:type_name -> topodata.TabletAlias + 1, // 90: vtadmin.StartReplicationResponse.cluster:type_name -> vtadmin.Cluster + 141, // 91: vtadmin.StopReplicationRequest.alias:type_name -> topodata.TabletAlias + 1, // 92: vtadmin.StopReplicationResponse.cluster:type_name -> vtadmin.Cluster + 141, // 93: vtadmin.TabletExternallyPromotedRequest.alias:type_name -> topodata.TabletAlias + 1, // 94: vtadmin.TabletExternallyPromotedResponse.cluster:type_name -> vtadmin.Cluster + 141, // 95: vtadmin.TabletExternallyPromotedResponse.new_primary:type_name -> topodata.TabletAlias + 141, // 96: vtadmin.TabletExternallyPromotedResponse.old_primary:type_name -> topodata.TabletAlias + 141, // 97: vtadmin.TabletExternallyReparentedRequest.alias:type_name -> topodata.TabletAlias + 148, // 98: vtadmin.ClusterCellsAliases.AliasesEntry.value:type_name -> topodata.CellsAlias + 128, // 99: vtadmin.Keyspace.ShardsEntry.value:type_name -> vtctldata.Shard + 114, // 100: vtadmin.Schema.TableSizesEntry.value:type_name -> vtadmin.Schema.TableSize + 115, // 101: vtadmin.Schema.TableSize.by_shard:type_name -> vtadmin.Schema.TableSize.ByShardEntry + 113, // 102: vtadmin.Schema.TableSize.ByShardEntry.value:type_name -> vtadmin.Schema.ShardTableSize + 149, // 103: vtadmin.GetSchemaMigrationsRequest.ClusterRequest.request:type_name -> vtctldata.GetSchemaMigrationsRequest + 150, // 104: vtadmin.GetSrvKeyspacesResponse.SrvKeyspacesEntry.value:type_name -> vtctldata.GetSrvKeyspacesResponse + 6, // 105: vtadmin.GetWorkflowsResponse.WorkflowsByClusterEntry.value:type_name -> vtadmin.ClusterWorkflows + 7, // 106: vtadmin.ReloadSchemasResponse.KeyspaceResult.keyspace:type_name -> vtadmin.Keyspace + 143, // 107: vtadmin.ReloadSchemasResponse.KeyspaceResult.events:type_name -> logutil.Event + 10, // 108: vtadmin.ReloadSchemasResponse.ShardResult.shard:type_name -> vtadmin.Shard + 143, // 109: vtadmin.ReloadSchemasResponse.ShardResult.events:type_name -> logutil.Event + 12, // 110: vtadmin.ReloadSchemasResponse.TabletResult.tablet:type_name -> vtadmin.Tablet + 17, // 111: vtadmin.VTAdmin.ApplySchema:input_type -> vtadmin.ApplySchemaRequest + 18, // 112: vtadmin.VTAdmin.CancelSchemaMigration:input_type -> vtadmin.CancelSchemaMigrationRequest + 19, // 113: vtadmin.VTAdmin.CleanupSchemaMigration:input_type -> vtadmin.CleanupSchemaMigrationRequest + 20, // 114: vtadmin.VTAdmin.CompleteSchemaMigration:input_type -> vtadmin.CompleteSchemaMigrationRequest + 21, // 115: vtadmin.VTAdmin.CreateKeyspace:input_type -> vtadmin.CreateKeyspaceRequest + 23, // 116: vtadmin.VTAdmin.CreateShard:input_type -> vtadmin.CreateShardRequest + 24, // 117: vtadmin.VTAdmin.DeleteKeyspace:input_type -> vtadmin.DeleteKeyspaceRequest + 25, // 118: vtadmin.VTAdmin.DeleteShards:input_type -> vtadmin.DeleteShardsRequest + 26, // 119: vtadmin.VTAdmin.DeleteTablet:input_type -> vtadmin.DeleteTabletRequest + 28, // 120: vtadmin.VTAdmin.EmergencyFailoverShard:input_type -> vtadmin.EmergencyFailoverShardRequest + 30, // 121: vtadmin.VTAdmin.FindSchema:input_type -> vtadmin.FindSchemaRequest + 31, // 122: vtadmin.VTAdmin.GetBackups:input_type -> vtadmin.GetBackupsRequest + 33, // 123: vtadmin.VTAdmin.GetCellInfos:input_type -> vtadmin.GetCellInfosRequest + 35, // 124: vtadmin.VTAdmin.GetCellsAliases:input_type -> vtadmin.GetCellsAliasesRequest + 37, // 125: vtadmin.VTAdmin.GetClusters:input_type -> vtadmin.GetClustersRequest + 39, // 126: vtadmin.VTAdmin.GetFullStatus:input_type -> vtadmin.GetFullStatusRequest + 40, // 127: vtadmin.VTAdmin.GetGates:input_type -> vtadmin.GetGatesRequest + 42, // 128: vtadmin.VTAdmin.GetKeyspace:input_type -> vtadmin.GetKeyspaceRequest + 43, // 129: vtadmin.VTAdmin.GetKeyspaces:input_type -> vtadmin.GetKeyspacesRequest + 45, // 130: vtadmin.VTAdmin.GetSchema:input_type -> vtadmin.GetSchemaRequest + 46, // 131: vtadmin.VTAdmin.GetSchemas:input_type -> vtadmin.GetSchemasRequest + 48, // 132: vtadmin.VTAdmin.GetSchemaMigrations:input_type -> vtadmin.GetSchemaMigrationsRequest + 50, // 133: vtadmin.VTAdmin.GetShardReplicationPositions:input_type -> vtadmin.GetShardReplicationPositionsRequest + 52, // 134: vtadmin.VTAdmin.GetSrvKeyspace:input_type -> vtadmin.GetSrvKeyspaceRequest + 53, // 135: vtadmin.VTAdmin.GetSrvKeyspaces:input_type -> vtadmin.GetSrvKeyspacesRequest + 55, // 136: vtadmin.VTAdmin.GetSrvVSchema:input_type -> vtadmin.GetSrvVSchemaRequest + 56, // 137: vtadmin.VTAdmin.GetSrvVSchemas:input_type -> vtadmin.GetSrvVSchemasRequest + 59, // 138: vtadmin.VTAdmin.GetTablet:input_type -> vtadmin.GetTabletRequest + 60, // 139: vtadmin.VTAdmin.GetTablets:input_type -> vtadmin.GetTabletsRequest + 62, // 140: vtadmin.VTAdmin.GetTopologyPath:input_type -> vtadmin.GetTopologyPathRequest + 63, // 141: vtadmin.VTAdmin.GetVSchema:input_type -> vtadmin.GetVSchemaRequest + 64, // 142: vtadmin.VTAdmin.GetVSchemas:input_type -> vtadmin.GetVSchemasRequest + 66, // 143: vtadmin.VTAdmin.GetVtctlds:input_type -> vtadmin.GetVtctldsRequest + 68, // 144: vtadmin.VTAdmin.GetWorkflow:input_type -> vtadmin.GetWorkflowRequest + 69, // 145: vtadmin.VTAdmin.GetWorkflows:input_type -> vtadmin.GetWorkflowsRequest + 71, // 146: vtadmin.VTAdmin.LaunchSchemaMigration:input_type -> vtadmin.LaunchSchemaMigrationRequest + 72, // 147: vtadmin.VTAdmin.PingTablet:input_type -> vtadmin.PingTabletRequest + 74, // 148: vtadmin.VTAdmin.PlannedFailoverShard:input_type -> vtadmin.PlannedFailoverShardRequest + 76, // 149: vtadmin.VTAdmin.RebuildKeyspaceGraph:input_type -> vtadmin.RebuildKeyspaceGraphRequest + 78, // 150: vtadmin.VTAdmin.RefreshState:input_type -> vtadmin.RefreshStateRequest + 84, // 151: vtadmin.VTAdmin.RefreshTabletReplicationSource:input_type -> vtadmin.RefreshTabletReplicationSourceRequest + 80, // 152: vtadmin.VTAdmin.ReloadSchemas:input_type -> vtadmin.ReloadSchemasRequest + 82, // 153: vtadmin.VTAdmin.ReloadSchemaShard:input_type -> vtadmin.ReloadSchemaShardRequest + 86, // 154: vtadmin.VTAdmin.RemoveKeyspaceCell:input_type -> vtadmin.RemoveKeyspaceCellRequest + 88, // 155: vtadmin.VTAdmin.RetrySchemaMigration:input_type -> vtadmin.RetrySchemaMigrationRequest + 89, // 156: vtadmin.VTAdmin.RunHealthCheck:input_type -> vtadmin.RunHealthCheckRequest + 91, // 157: vtadmin.VTAdmin.SetReadOnly:input_type -> vtadmin.SetReadOnlyRequest + 93, // 158: vtadmin.VTAdmin.SetReadWrite:input_type -> vtadmin.SetReadWriteRequest + 95, // 159: vtadmin.VTAdmin.StartReplication:input_type -> vtadmin.StartReplicationRequest + 97, // 160: vtadmin.VTAdmin.StopReplication:input_type -> vtadmin.StopReplicationRequest + 99, // 161: vtadmin.VTAdmin.TabletExternallyPromoted:input_type -> vtadmin.TabletExternallyPromotedRequest + 102, // 162: vtadmin.VTAdmin.Validate:input_type -> vtadmin.ValidateRequest + 103, // 163: vtadmin.VTAdmin.ValidateKeyspace:input_type -> vtadmin.ValidateKeyspaceRequest + 104, // 164: vtadmin.VTAdmin.ValidateSchemaKeyspace:input_type -> vtadmin.ValidateSchemaKeyspaceRequest + 105, // 165: vtadmin.VTAdmin.ValidateShard:input_type -> vtadmin.ValidateShardRequest + 106, // 166: vtadmin.VTAdmin.ValidateVersionKeyspace:input_type -> vtadmin.ValidateVersionKeyspaceRequest + 107, // 167: vtadmin.VTAdmin.ValidateVersionShard:input_type -> vtadmin.ValidateVersionShardRequest + 108, // 168: vtadmin.VTAdmin.VTExplain:input_type -> vtadmin.VTExplainRequest + 151, // 169: vtadmin.VTAdmin.ApplySchema:output_type -> vtctldata.ApplySchemaResponse + 152, // 170: vtadmin.VTAdmin.CancelSchemaMigration:output_type -> vtctldata.CancelSchemaMigrationResponse + 153, // 171: vtadmin.VTAdmin.CleanupSchemaMigration:output_type -> vtctldata.CleanupSchemaMigrationResponse + 154, // 172: vtadmin.VTAdmin.CompleteSchemaMigration:output_type -> vtctldata.CompleteSchemaMigrationResponse + 22, // 173: vtadmin.VTAdmin.CreateKeyspace:output_type -> vtadmin.CreateKeyspaceResponse + 155, // 174: vtadmin.VTAdmin.CreateShard:output_type -> vtctldata.CreateShardResponse + 156, // 175: vtadmin.VTAdmin.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse + 157, // 176: vtadmin.VTAdmin.DeleteShards:output_type -> vtctldata.DeleteShardsResponse + 27, // 177: vtadmin.VTAdmin.DeleteTablet:output_type -> vtadmin.DeleteTabletResponse + 29, // 178: vtadmin.VTAdmin.EmergencyFailoverShard:output_type -> vtadmin.EmergencyFailoverShardResponse + 8, // 179: vtadmin.VTAdmin.FindSchema:output_type -> vtadmin.Schema + 32, // 180: vtadmin.VTAdmin.GetBackups:output_type -> vtadmin.GetBackupsResponse + 34, // 181: vtadmin.VTAdmin.GetCellInfos:output_type -> vtadmin.GetCellInfosResponse + 36, // 182: vtadmin.VTAdmin.GetCellsAliases:output_type -> vtadmin.GetCellsAliasesResponse + 38, // 183: vtadmin.VTAdmin.GetClusters:output_type -> vtadmin.GetClustersResponse + 158, // 184: vtadmin.VTAdmin.GetFullStatus:output_type -> vtctldata.GetFullStatusResponse + 41, // 185: vtadmin.VTAdmin.GetGates:output_type -> vtadmin.GetGatesResponse + 7, // 186: vtadmin.VTAdmin.GetKeyspace:output_type -> vtadmin.Keyspace + 44, // 187: vtadmin.VTAdmin.GetKeyspaces:output_type -> vtadmin.GetKeyspacesResponse + 8, // 188: vtadmin.VTAdmin.GetSchema:output_type -> vtadmin.Schema + 47, // 189: vtadmin.VTAdmin.GetSchemas:output_type -> vtadmin.GetSchemasResponse + 49, // 190: vtadmin.VTAdmin.GetSchemaMigrations:output_type -> vtadmin.GetSchemaMigrationsResponse + 51, // 191: vtadmin.VTAdmin.GetShardReplicationPositions:output_type -> vtadmin.GetShardReplicationPositionsResponse + 150, // 192: vtadmin.VTAdmin.GetSrvKeyspace:output_type -> vtctldata.GetSrvKeyspacesResponse + 54, // 193: vtadmin.VTAdmin.GetSrvKeyspaces:output_type -> vtadmin.GetSrvKeyspacesResponse + 11, // 194: vtadmin.VTAdmin.GetSrvVSchema:output_type -> vtadmin.SrvVSchema + 57, // 195: vtadmin.VTAdmin.GetSrvVSchemas:output_type -> vtadmin.GetSrvVSchemasResponse + 12, // 196: vtadmin.VTAdmin.GetTablet:output_type -> vtadmin.Tablet + 61, // 197: vtadmin.VTAdmin.GetTablets:output_type -> vtadmin.GetTabletsResponse + 159, // 198: vtadmin.VTAdmin.GetTopologyPath:output_type -> vtctldata.GetTopologyPathResponse + 13, // 199: vtadmin.VTAdmin.GetVSchema:output_type -> vtadmin.VSchema + 65, // 200: vtadmin.VTAdmin.GetVSchemas:output_type -> vtadmin.GetVSchemasResponse + 67, // 201: vtadmin.VTAdmin.GetVtctlds:output_type -> vtadmin.GetVtctldsResponse + 16, // 202: vtadmin.VTAdmin.GetWorkflow:output_type -> vtadmin.Workflow + 70, // 203: vtadmin.VTAdmin.GetWorkflows:output_type -> vtadmin.GetWorkflowsResponse + 160, // 204: vtadmin.VTAdmin.LaunchSchemaMigration:output_type -> vtctldata.LaunchSchemaMigrationResponse + 73, // 205: vtadmin.VTAdmin.PingTablet:output_type -> vtadmin.PingTabletResponse + 75, // 206: vtadmin.VTAdmin.PlannedFailoverShard:output_type -> vtadmin.PlannedFailoverShardResponse + 77, // 207: vtadmin.VTAdmin.RebuildKeyspaceGraph:output_type -> vtadmin.RebuildKeyspaceGraphResponse + 79, // 208: vtadmin.VTAdmin.RefreshState:output_type -> vtadmin.RefreshStateResponse + 85, // 209: vtadmin.VTAdmin.RefreshTabletReplicationSource:output_type -> vtadmin.RefreshTabletReplicationSourceResponse + 81, // 210: vtadmin.VTAdmin.ReloadSchemas:output_type -> vtadmin.ReloadSchemasResponse + 83, // 211: vtadmin.VTAdmin.ReloadSchemaShard:output_type -> vtadmin.ReloadSchemaShardResponse + 87, // 212: vtadmin.VTAdmin.RemoveKeyspaceCell:output_type -> vtadmin.RemoveKeyspaceCellResponse + 161, // 213: vtadmin.VTAdmin.RetrySchemaMigration:output_type -> vtctldata.RetrySchemaMigrationResponse + 90, // 214: vtadmin.VTAdmin.RunHealthCheck:output_type -> vtadmin.RunHealthCheckResponse + 92, // 215: vtadmin.VTAdmin.SetReadOnly:output_type -> vtadmin.SetReadOnlyResponse + 94, // 216: vtadmin.VTAdmin.SetReadWrite:output_type -> vtadmin.SetReadWriteResponse + 96, // 217: vtadmin.VTAdmin.StartReplication:output_type -> vtadmin.StartReplicationResponse + 98, // 218: vtadmin.VTAdmin.StopReplication:output_type -> vtadmin.StopReplicationResponse + 100, // 219: vtadmin.VTAdmin.TabletExternallyPromoted:output_type -> vtadmin.TabletExternallyPromotedResponse + 162, // 220: vtadmin.VTAdmin.Validate:output_type -> vtctldata.ValidateResponse + 163, // 221: vtadmin.VTAdmin.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse + 164, // 222: vtadmin.VTAdmin.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse + 165, // 223: vtadmin.VTAdmin.ValidateShard:output_type -> vtctldata.ValidateShardResponse + 166, // 224: vtadmin.VTAdmin.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse + 167, // 225: vtadmin.VTAdmin.ValidateVersionShard:output_type -> vtctldata.ValidateVersionShardResponse + 109, // 226: vtadmin.VTAdmin.VTExplain:output_type -> vtadmin.VTExplainResponse + 169, // [169:227] is the sub-list for method output_type + 111, // [111:169] is the sub-list for method input_type + 111, // [111:111] is the sub-list for extension type_name + 111, // [111:111] is the sub-list for extension extendee + 0, // [0:111] is the sub-list for field type_name } func init() { file_vtadmin_proto_init() } @@ -7765,8 +8471,68 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Keyspace); i { + file_vtadmin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Keyspace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SchemaMigration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Shard); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SrvVSchema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Tablet); i { case 0: return &v.state case 1: @@ -7777,8 +8543,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Schema); i { + file_vtadmin_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VSchema); i { case 0: return &v.state case 1: @@ -7789,8 +8555,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Shard); i { + file_vtadmin_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Vtctld); i { case 0: return &v.state case 1: @@ -7801,8 +8567,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SrvVSchema); i { + file_vtadmin_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VTGate); i { case 0: return &v.state case 1: @@ -7813,8 +8579,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Tablet); i { + file_vtadmin_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Workflow); i { case 0: return &v.state case 1: @@ -7825,8 +8591,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VSchema); i { + file_vtadmin_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplySchemaRequest); i { case 0: return &v.state case 1: @@ -7837,8 +8603,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Vtctld); i { + file_vtadmin_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelSchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -7849,8 +8615,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VTGate); i { + file_vtadmin_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanupSchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -7861,8 +8627,8 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Workflow); i { + file_vtadmin_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompleteSchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -7873,7 +8639,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateKeyspaceRequest); i { case 0: return &v.state @@ -7885,7 +8651,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateKeyspaceResponse); i { case 0: return &v.state @@ -7897,7 +8663,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateShardRequest); i { case 0: return &v.state @@ -7909,7 +8675,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteKeyspaceRequest); i { case 0: return &v.state @@ -7921,7 +8687,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteShardsRequest); i { case 0: return &v.state @@ -7933,7 +8699,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteTabletRequest); i { case 0: return &v.state @@ -7945,7 +8711,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteTabletResponse); i { case 0: return &v.state @@ -7957,7 +8723,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EmergencyFailoverShardRequest); i { case 0: return &v.state @@ -7969,7 +8735,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EmergencyFailoverShardResponse); i { case 0: return &v.state @@ -7981,7 +8747,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*FindSchemaRequest); i { case 0: return &v.state @@ -7993,7 +8759,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetBackupsRequest); i { case 0: return &v.state @@ -8005,7 +8771,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetBackupsResponse); i { case 0: return &v.state @@ -8017,7 +8783,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetCellInfosRequest); i { case 0: return &v.state @@ -8029,7 +8795,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetCellInfosResponse); i { case 0: return &v.state @@ -8041,7 +8807,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetCellsAliasesRequest); i { case 0: return &v.state @@ -8053,7 +8819,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetCellsAliasesResponse); i { case 0: return &v.state @@ -8065,7 +8831,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClustersRequest); i { case 0: return &v.state @@ -8077,7 +8843,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetClustersResponse); i { case 0: return &v.state @@ -8089,7 +8855,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetFullStatusRequest); i { case 0: return &v.state @@ -8101,7 +8867,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGatesRequest); i { case 0: return &v.state @@ -8113,7 +8879,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGatesResponse); i { case 0: return &v.state @@ -8125,7 +8891,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetKeyspaceRequest); i { case 0: return &v.state @@ -8137,7 +8903,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetKeyspacesRequest); i { case 0: return &v.state @@ -8149,7 +8915,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetKeyspacesResponse); i { case 0: return &v.state @@ -8161,7 +8927,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSchemaRequest); i { case 0: return &v.state @@ -8173,7 +8939,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSchemasRequest); i { case 0: return &v.state @@ -8185,7 +8951,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSchemasResponse); i { case 0: return &v.state @@ -8197,7 +8963,31 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSchemaMigrationsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSchemaMigrationsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetShardReplicationPositionsRequest); i { case 0: return &v.state @@ -8209,7 +8999,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetShardReplicationPositionsResponse); i { case 0: return &v.state @@ -8221,7 +9011,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvKeyspaceRequest); i { case 0: return &v.state @@ -8233,7 +9023,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvKeyspacesRequest); i { case 0: return &v.state @@ -8245,7 +9035,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvKeyspacesResponse); i { case 0: return &v.state @@ -8257,7 +9047,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvVSchemaRequest); i { case 0: return &v.state @@ -8269,7 +9059,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvVSchemasRequest); i { case 0: return &v.state @@ -8281,7 +9071,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvVSchemasResponse); i { case 0: return &v.state @@ -8293,7 +9083,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSchemaTableSizeOptions); i { case 0: return &v.state @@ -8305,7 +9095,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTabletRequest); i { case 0: return &v.state @@ -8317,7 +9107,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTabletsRequest); i { case 0: return &v.state @@ -8329,7 +9119,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTabletsResponse); i { case 0: return &v.state @@ -8341,7 +9131,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetTopologyPathRequest); i { case 0: return &v.state @@ -8353,7 +9143,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetVSchemaRequest); i { case 0: return &v.state @@ -8365,7 +9155,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetVSchemasRequest); i { case 0: return &v.state @@ -8377,7 +9167,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetVSchemasResponse); i { case 0: return &v.state @@ -8389,7 +9179,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetVtctldsRequest); i { case 0: return &v.state @@ -8401,7 +9191,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetVtctldsResponse); i { case 0: return &v.state @@ -8413,7 +9203,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkflowRequest); i { case 0: return &v.state @@ -8425,7 +9215,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkflowsRequest); i { case 0: return &v.state @@ -8437,7 +9227,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetWorkflowsResponse); i { case 0: return &v.state @@ -8449,7 +9239,19 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LaunchSchemaMigrationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PingTabletRequest); i { case 0: return &v.state @@ -8461,7 +9263,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PingTabletResponse); i { case 0: return &v.state @@ -8473,7 +9275,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlannedFailoverShardRequest); i { case 0: return &v.state @@ -8485,7 +9287,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlannedFailoverShardResponse); i { case 0: return &v.state @@ -8497,7 +9299,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RebuildKeyspaceGraphRequest); i { case 0: return &v.state @@ -8509,7 +9311,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RebuildKeyspaceGraphResponse); i { case 0: return &v.state @@ -8521,7 +9323,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshStateRequest); i { case 0: return &v.state @@ -8533,7 +9335,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshStateResponse); i { case 0: return &v.state @@ -8545,7 +9347,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemasRequest); i { case 0: return &v.state @@ -8557,7 +9359,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemasResponse); i { case 0: return &v.state @@ -8569,7 +9371,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemaShardRequest); i { case 0: return &v.state @@ -8581,7 +9383,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemaShardResponse); i { case 0: return &v.state @@ -8593,7 +9395,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshTabletReplicationSourceRequest); i { case 0: return &v.state @@ -8605,7 +9407,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RefreshTabletReplicationSourceResponse); i { case 0: return &v.state @@ -8617,7 +9419,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveKeyspaceCellRequest); i { case 0: return &v.state @@ -8629,7 +9431,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RemoveKeyspaceCellResponse); i { case 0: return &v.state @@ -8641,7 +9443,19 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RetrySchemaMigrationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RunHealthCheckRequest); i { case 0: return &v.state @@ -8653,7 +9467,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RunHealthCheckResponse); i { case 0: return &v.state @@ -8665,7 +9479,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetReadOnlyRequest); i { case 0: return &v.state @@ -8677,7 +9491,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetReadOnlyResponse); i { case 0: return &v.state @@ -8689,7 +9503,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetReadWriteRequest); i { case 0: return &v.state @@ -8701,7 +9515,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SetReadWriteResponse); i { case 0: return &v.state @@ -8713,7 +9527,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StartReplicationRequest); i { case 0: return &v.state @@ -8725,7 +9539,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StartReplicationResponse); i { case 0: return &v.state @@ -8737,7 +9551,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StopReplicationRequest); i { case 0: return &v.state @@ -8749,7 +9563,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StopReplicationResponse); i { case 0: return &v.state @@ -8761,7 +9575,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TabletExternallyPromotedRequest); i { case 0: return &v.state @@ -8773,7 +9587,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TabletExternallyPromotedResponse); i { case 0: return &v.state @@ -8785,7 +9599,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TabletExternallyReparentedRequest); i { case 0: return &v.state @@ -8797,7 +9611,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateRequest); i { case 0: return &v.state @@ -8809,7 +9623,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateKeyspaceRequest); i { case 0: return &v.state @@ -8821,7 +9635,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateSchemaKeyspaceRequest); i { case 0: return &v.state @@ -8833,7 +9647,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateShardRequest); i { case 0: return &v.state @@ -8845,7 +9659,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateVersionKeyspaceRequest); i { case 0: return &v.state @@ -8857,7 +9671,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateVersionShardRequest); i { case 0: return &v.state @@ -8869,7 +9683,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VTExplainRequest); i { case 0: return &v.state @@ -8881,7 +9695,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VTExplainResponse); i { case 0: return &v.state @@ -8893,7 +9707,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_ShardTableSize); i { case 0: return &v.state @@ -8905,7 +9719,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_TableSize); i { case 0: return &v.state @@ -8917,7 +9731,19 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSchemaMigrationsRequest_ClusterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtadmin_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemasResponse_KeyspaceResult); i { case 0: return &v.state @@ -8929,7 +9755,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemasResponse_ShardResult); i { case 0: return &v.state @@ -8941,7 +9767,7 @@ func file_vtadmin_proto_init() { return nil } } - file_vtadmin_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { + file_vtadmin_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReloadSchemasResponse_TabletResult); i { case 0: return &v.state @@ -8960,7 +9786,7 @@ func file_vtadmin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_vtadmin_proto_rawDesc, NumEnums: 1, - NumMessages: 111, + NumMessages: 121, NumExtensions: 0, NumServices: 1, }, diff --git a/go/vt/proto/vtadmin/vtadmin_grpc.pb.go b/go/vt/proto/vtadmin/vtadmin_grpc.pb.go index e0e2ce2f44f..1e377d0659f 100644 --- a/go/vt/proto/vtadmin/vtadmin_grpc.pb.go +++ b/go/vt/proto/vtadmin/vtadmin_grpc.pb.go @@ -23,6 +23,17 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type VTAdminClient interface { + // ApplySchema applies a schema to a keyspace in the given cluster. + ApplySchema(ctx context.Context, in *ApplySchemaRequest, opts ...grpc.CallOption) (*vtctldata.ApplySchemaResponse, error) + // CancelSchemaMigration cancels one or all schema migrations in the given + // cluster, terminating any running ones as needed. + CancelSchemaMigration(ctx context.Context, in *CancelSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CancelSchemaMigrationResponse, error) + // CleanupSchemaMigration marks a schema migration in the given cluster as + // ready for artifact cleanup. + CleanupSchemaMigration(ctx context.Context, in *CleanupSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CleanupSchemaMigrationResponse, error) + // CompleteSchemaMigration completes one or all migrations in the given + // cluster executed with --postpone-completion. + CompleteSchemaMigration(ctx context.Context, in *CompleteSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CompleteSchemaMigrationResponse, error) // CreateKeyspace creates a new keyspace in the given cluster. CreateKeyspace(ctx context.Context, in *CreateKeyspaceRequest, opts ...grpc.CallOption) (*CreateKeyspaceResponse, error) // CreateShard creates a new shard in the given cluster and keyspace. @@ -67,6 +78,13 @@ type VTAdminClient interface { GetSchema(ctx context.Context, in *GetSchemaRequest, opts ...grpc.CallOption) (*Schema, error) // GetSchemas returns all schemas across the specified clusters. GetSchemas(ctx context.Context, in *GetSchemasRequest, opts ...grpc.CallOption) (*GetSchemasResponse, error) + // GetSchemaMigrations returns one or more online schema migrations for the + // set of keyspaces (or all keyspaces) in the given clusters, analagous to + // repeated executions of `SHOW VITESS_MIGRATIONS`. + // + // Different fields in the request message result in different behaviors. + // See the documentation on vtctldata.GetSchemaMigrationsRequest for details. + GetSchemaMigrations(ctx context.Context, in *GetSchemaMigrationsRequest, opts ...grpc.CallOption) (*GetSchemaMigrationsResponse, error) // GetShardReplicationPositions returns shard replication positions grouped // by cluster. GetShardReplicationPositions(ctx context.Context, in *GetShardReplicationPositionsRequest, opts ...grpc.CallOption) (*GetShardReplicationPositionsResponse, error) @@ -98,6 +116,9 @@ type VTAdminClient interface { GetWorkflow(ctx context.Context, in *GetWorkflowRequest, opts ...grpc.CallOption) (*Workflow, error) // GetWorkflows returns the Workflows for all specified clusters. GetWorkflows(ctx context.Context, in *GetWorkflowsRequest, opts ...grpc.CallOption) (*GetWorkflowsResponse, error) + // LaunchSchemaMigration launches one or all migrations in the given + // cluster executed with --postpone-launch. + LaunchSchemaMigration(ctx context.Context, in *LaunchSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.LaunchSchemaMigrationResponse, error) // PingTablet checks that the specified tablet is awake and responding to // RPCs. This command can be blocked by other in-flight operations. PingTablet(ctx context.Context, in *PingTabletRequest, opts ...grpc.CallOption) (*PingTabletResponse, error) @@ -124,6 +145,9 @@ type VTAdminClient interface { ReloadSchemaShard(ctx context.Context, in *ReloadSchemaShardRequest, opts ...grpc.CallOption) (*ReloadSchemaShardResponse, error) // RemoveKeyspaceCell removes the cell from the Cells list for all shards in the keyspace, and the SrvKeyspace for that keyspace in that cell. RemoveKeyspaceCell(ctx context.Context, in *RemoveKeyspaceCellRequest, opts ...grpc.CallOption) (*RemoveKeyspaceCellResponse, error) + // RetrySchemaMigration marks a given schema migration in the given cluster + // for retry. + RetrySchemaMigration(ctx context.Context, in *RetrySchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.RetrySchemaMigrationResponse, error) // RunHealthCheck runs a healthcheck on the tablet. RunHealthCheck(ctx context.Context, in *RunHealthCheckRequest, opts ...grpc.CallOption) (*RunHealthCheckResponse, error) // SetReadOnly sets the tablet to read-only mode. @@ -176,6 +200,42 @@ func NewVTAdminClient(cc grpc.ClientConnInterface) VTAdminClient { return &vTAdminClient{cc} } +func (c *vTAdminClient) ApplySchema(ctx context.Context, in *ApplySchemaRequest, opts ...grpc.CallOption) (*vtctldata.ApplySchemaResponse, error) { + out := new(vtctldata.ApplySchemaResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/ApplySchema", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vTAdminClient) CancelSchemaMigration(ctx context.Context, in *CancelSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CancelSchemaMigrationResponse, error) { + out := new(vtctldata.CancelSchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/CancelSchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vTAdminClient) CleanupSchemaMigration(ctx context.Context, in *CleanupSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CleanupSchemaMigrationResponse, error) { + out := new(vtctldata.CleanupSchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/CleanupSchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vTAdminClient) CompleteSchemaMigration(ctx context.Context, in *CompleteSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CompleteSchemaMigrationResponse, error) { + out := new(vtctldata.CompleteSchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/CompleteSchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vTAdminClient) CreateKeyspace(ctx context.Context, in *CreateKeyspaceRequest, opts ...grpc.CallOption) (*CreateKeyspaceResponse, error) { out := new(CreateKeyspaceResponse) err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/CreateKeyspace", in, out, opts...) @@ -329,6 +389,15 @@ func (c *vTAdminClient) GetSchemas(ctx context.Context, in *GetSchemasRequest, o return out, nil } +func (c *vTAdminClient) GetSchemaMigrations(ctx context.Context, in *GetSchemaMigrationsRequest, opts ...grpc.CallOption) (*GetSchemaMigrationsResponse, error) { + out := new(GetSchemaMigrationsResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/GetSchemaMigrations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vTAdminClient) GetShardReplicationPositions(ctx context.Context, in *GetShardReplicationPositionsRequest, opts ...grpc.CallOption) (*GetShardReplicationPositionsResponse, error) { out := new(GetShardReplicationPositionsResponse) err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/GetShardReplicationPositions", in, out, opts...) @@ -446,6 +515,15 @@ func (c *vTAdminClient) GetWorkflows(ctx context.Context, in *GetWorkflowsReques return out, nil } +func (c *vTAdminClient) LaunchSchemaMigration(ctx context.Context, in *LaunchSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.LaunchSchemaMigrationResponse, error) { + out := new(vtctldata.LaunchSchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/LaunchSchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vTAdminClient) PingTablet(ctx context.Context, in *PingTabletRequest, opts ...grpc.CallOption) (*PingTabletResponse, error) { out := new(PingTabletResponse) err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/PingTablet", in, out, opts...) @@ -518,6 +596,15 @@ func (c *vTAdminClient) RemoveKeyspaceCell(ctx context.Context, in *RemoveKeyspa return out, nil } +func (c *vTAdminClient) RetrySchemaMigration(ctx context.Context, in *RetrySchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.RetrySchemaMigrationResponse, error) { + out := new(vtctldata.RetrySchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/RetrySchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vTAdminClient) RunHealthCheck(ctx context.Context, in *RunHealthCheckRequest, opts ...grpc.CallOption) (*RunHealthCheckResponse, error) { out := new(RunHealthCheckResponse) err := c.cc.Invoke(ctx, "/vtadmin.VTAdmin/RunHealthCheck", in, out, opts...) @@ -639,6 +726,17 @@ func (c *vTAdminClient) VTExplain(ctx context.Context, in *VTExplainRequest, opt // All implementations must embed UnimplementedVTAdminServer // for forward compatibility type VTAdminServer interface { + // ApplySchema applies a schema to a keyspace in the given cluster. + ApplySchema(context.Context, *ApplySchemaRequest) (*vtctldata.ApplySchemaResponse, error) + // CancelSchemaMigration cancels one or all schema migrations in the given + // cluster, terminating any running ones as needed. + CancelSchemaMigration(context.Context, *CancelSchemaMigrationRequest) (*vtctldata.CancelSchemaMigrationResponse, error) + // CleanupSchemaMigration marks a schema migration in the given cluster as + // ready for artifact cleanup. + CleanupSchemaMigration(context.Context, *CleanupSchemaMigrationRequest) (*vtctldata.CleanupSchemaMigrationResponse, error) + // CompleteSchemaMigration completes one or all migrations in the given + // cluster executed with --postpone-completion. + CompleteSchemaMigration(context.Context, *CompleteSchemaMigrationRequest) (*vtctldata.CompleteSchemaMigrationResponse, error) // CreateKeyspace creates a new keyspace in the given cluster. CreateKeyspace(context.Context, *CreateKeyspaceRequest) (*CreateKeyspaceResponse, error) // CreateShard creates a new shard in the given cluster and keyspace. @@ -683,6 +781,13 @@ type VTAdminServer interface { GetSchema(context.Context, *GetSchemaRequest) (*Schema, error) // GetSchemas returns all schemas across the specified clusters. GetSchemas(context.Context, *GetSchemasRequest) (*GetSchemasResponse, error) + // GetSchemaMigrations returns one or more online schema migrations for the + // set of keyspaces (or all keyspaces) in the given clusters, analagous to + // repeated executions of `SHOW VITESS_MIGRATIONS`. + // + // Different fields in the request message result in different behaviors. + // See the documentation on vtctldata.GetSchemaMigrationsRequest for details. + GetSchemaMigrations(context.Context, *GetSchemaMigrationsRequest) (*GetSchemaMigrationsResponse, error) // GetShardReplicationPositions returns shard replication positions grouped // by cluster. GetShardReplicationPositions(context.Context, *GetShardReplicationPositionsRequest) (*GetShardReplicationPositionsResponse, error) @@ -714,6 +819,9 @@ type VTAdminServer interface { GetWorkflow(context.Context, *GetWorkflowRequest) (*Workflow, error) // GetWorkflows returns the Workflows for all specified clusters. GetWorkflows(context.Context, *GetWorkflowsRequest) (*GetWorkflowsResponse, error) + // LaunchSchemaMigration launches one or all migrations in the given + // cluster executed with --postpone-launch. + LaunchSchemaMigration(context.Context, *LaunchSchemaMigrationRequest) (*vtctldata.LaunchSchemaMigrationResponse, error) // PingTablet checks that the specified tablet is awake and responding to // RPCs. This command can be blocked by other in-flight operations. PingTablet(context.Context, *PingTabletRequest) (*PingTabletResponse, error) @@ -740,6 +848,9 @@ type VTAdminServer interface { ReloadSchemaShard(context.Context, *ReloadSchemaShardRequest) (*ReloadSchemaShardResponse, error) // RemoveKeyspaceCell removes the cell from the Cells list for all shards in the keyspace, and the SrvKeyspace for that keyspace in that cell. RemoveKeyspaceCell(context.Context, *RemoveKeyspaceCellRequest) (*RemoveKeyspaceCellResponse, error) + // RetrySchemaMigration marks a given schema migration in the given cluster + // for retry. + RetrySchemaMigration(context.Context, *RetrySchemaMigrationRequest) (*vtctldata.RetrySchemaMigrationResponse, error) // RunHealthCheck runs a healthcheck on the tablet. RunHealthCheck(context.Context, *RunHealthCheckRequest) (*RunHealthCheckResponse, error) // SetReadOnly sets the tablet to read-only mode. @@ -789,6 +900,18 @@ type VTAdminServer interface { type UnimplementedVTAdminServer struct { } +func (UnimplementedVTAdminServer) ApplySchema(context.Context, *ApplySchemaRequest) (*vtctldata.ApplySchemaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ApplySchema not implemented") +} +func (UnimplementedVTAdminServer) CancelSchemaMigration(context.Context, *CancelSchemaMigrationRequest) (*vtctldata.CancelSchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelSchemaMigration not implemented") +} +func (UnimplementedVTAdminServer) CleanupSchemaMigration(context.Context, *CleanupSchemaMigrationRequest) (*vtctldata.CleanupSchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CleanupSchemaMigration not implemented") +} +func (UnimplementedVTAdminServer) CompleteSchemaMigration(context.Context, *CompleteSchemaMigrationRequest) (*vtctldata.CompleteSchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CompleteSchemaMigration not implemented") +} func (UnimplementedVTAdminServer) CreateKeyspace(context.Context, *CreateKeyspaceRequest) (*CreateKeyspaceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateKeyspace not implemented") } @@ -840,6 +963,9 @@ func (UnimplementedVTAdminServer) GetSchema(context.Context, *GetSchemaRequest) func (UnimplementedVTAdminServer) GetSchemas(context.Context, *GetSchemasRequest) (*GetSchemasResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSchemas not implemented") } +func (UnimplementedVTAdminServer) GetSchemaMigrations(context.Context, *GetSchemaMigrationsRequest) (*GetSchemaMigrationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSchemaMigrations not implemented") +} func (UnimplementedVTAdminServer) GetShardReplicationPositions(context.Context, *GetShardReplicationPositionsRequest) (*GetShardReplicationPositionsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetShardReplicationPositions not implemented") } @@ -879,6 +1005,9 @@ func (UnimplementedVTAdminServer) GetWorkflow(context.Context, *GetWorkflowReque func (UnimplementedVTAdminServer) GetWorkflows(context.Context, *GetWorkflowsRequest) (*GetWorkflowsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetWorkflows not implemented") } +func (UnimplementedVTAdminServer) LaunchSchemaMigration(context.Context, *LaunchSchemaMigrationRequest) (*vtctldata.LaunchSchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LaunchSchemaMigration not implemented") +} func (UnimplementedVTAdminServer) PingTablet(context.Context, *PingTabletRequest) (*PingTabletResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method PingTablet not implemented") } @@ -903,6 +1032,9 @@ func (UnimplementedVTAdminServer) ReloadSchemaShard(context.Context, *ReloadSche func (UnimplementedVTAdminServer) RemoveKeyspaceCell(context.Context, *RemoveKeyspaceCellRequest) (*RemoveKeyspaceCellResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveKeyspaceCell not implemented") } +func (UnimplementedVTAdminServer) RetrySchemaMigration(context.Context, *RetrySchemaMigrationRequest) (*vtctldata.RetrySchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RetrySchemaMigration not implemented") +} func (UnimplementedVTAdminServer) RunHealthCheck(context.Context, *RunHealthCheckRequest) (*RunHealthCheckResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RunHealthCheck not implemented") } @@ -955,6 +1087,78 @@ func RegisterVTAdminServer(s grpc.ServiceRegistrar, srv VTAdminServer) { s.RegisterService(&VTAdmin_ServiceDesc, srv) } +func _VTAdmin_ApplySchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApplySchemaRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).ApplySchema(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/ApplySchema", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).ApplySchema(ctx, req.(*ApplySchemaRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VTAdmin_CancelSchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CancelSchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).CancelSchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/CancelSchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).CancelSchemaMigration(ctx, req.(*CancelSchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VTAdmin_CleanupSchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CleanupSchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).CleanupSchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/CleanupSchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).CleanupSchemaMigration(ctx, req.(*CleanupSchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VTAdmin_CompleteSchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteSchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).CompleteSchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/CompleteSchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).CompleteSchemaMigration(ctx, req.(*CompleteSchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VTAdmin_CreateKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateKeyspaceRequest) if err := dec(in); err != nil { @@ -1261,6 +1465,24 @@ func _VTAdmin_GetSchemas_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _VTAdmin_GetSchemaMigrations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSchemaMigrationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).GetSchemaMigrations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/GetSchemaMigrations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).GetSchemaMigrations(ctx, req.(*GetSchemaMigrationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VTAdmin_GetShardReplicationPositions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetShardReplicationPositionsRequest) if err := dec(in); err != nil { @@ -1495,6 +1717,24 @@ func _VTAdmin_GetWorkflows_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _VTAdmin_LaunchSchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LaunchSchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).LaunchSchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/LaunchSchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).LaunchSchemaMigration(ctx, req.(*LaunchSchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VTAdmin_PingTablet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(PingTabletRequest) if err := dec(in); err != nil { @@ -1639,6 +1879,24 @@ func _VTAdmin_RemoveKeyspaceCell_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _VTAdmin_RetrySchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RetrySchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTAdminServer).RetrySchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtadmin.VTAdmin/RetrySchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTAdminServer).RetrySchemaMigration(ctx, req.(*RetrySchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VTAdmin_RunHealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RunHealthCheckRequest) if err := dec(in); err != nil { @@ -1880,6 +2138,22 @@ var VTAdmin_ServiceDesc = grpc.ServiceDesc{ ServiceName: "vtadmin.VTAdmin", HandlerType: (*VTAdminServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "ApplySchema", + Handler: _VTAdmin_ApplySchema_Handler, + }, + { + MethodName: "CancelSchemaMigration", + Handler: _VTAdmin_CancelSchemaMigration_Handler, + }, + { + MethodName: "CleanupSchemaMigration", + Handler: _VTAdmin_CleanupSchemaMigration_Handler, + }, + { + MethodName: "CompleteSchemaMigration", + Handler: _VTAdmin_CompleteSchemaMigration_Handler, + }, { MethodName: "CreateKeyspace", Handler: _VTAdmin_CreateKeyspace_Handler, @@ -1948,6 +2222,10 @@ var VTAdmin_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetSchemas", Handler: _VTAdmin_GetSchemas_Handler, }, + { + MethodName: "GetSchemaMigrations", + Handler: _VTAdmin_GetSchemaMigrations_Handler, + }, { MethodName: "GetShardReplicationPositions", Handler: _VTAdmin_GetShardReplicationPositions_Handler, @@ -2000,6 +2278,10 @@ var VTAdmin_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetWorkflows", Handler: _VTAdmin_GetWorkflows_Handler, }, + { + MethodName: "LaunchSchemaMigration", + Handler: _VTAdmin_LaunchSchemaMigration_Handler, + }, { MethodName: "PingTablet", Handler: _VTAdmin_PingTablet_Handler, @@ -2032,6 +2314,10 @@ var VTAdmin_ServiceDesc = grpc.ServiceDesc{ MethodName: "RemoveKeyspaceCell", Handler: _VTAdmin_RemoveKeyspaceCell_Handler, }, + { + MethodName: "RetrySchemaMigration", + Handler: _VTAdmin_RetrySchemaMigration_Handler, + }, { MethodName: "RunHealthCheck", Handler: _VTAdmin_RunHealthCheck_Handler, diff --git a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go index 0e4b4c6e84b..ce506cb7215 100644 --- a/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go +++ b/go/vt/proto/vtadmin/vtadmin_vtproto.pb.go @@ -261,6 +261,25 @@ func (m *Schema) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *SchemaMigration) CloneVT() *SchemaMigration { + if m == nil { + return (*SchemaMigration)(nil) + } + r := &SchemaMigration{ + Cluster: m.Cluster.CloneVT(), + SchemaMigration: m.SchemaMigration.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *SchemaMigration) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *Shard) CloneVT() *Shard { if m == nil { return (*Shard)(nil) @@ -408,6 +427,82 @@ func (m *Workflow) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *ApplySchemaRequest) CloneVT() *ApplySchemaRequest { + if m == nil { + return (*ApplySchemaRequest)(nil) + } + r := &ApplySchemaRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *ApplySchemaRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *CancelSchemaMigrationRequest) CloneVT() *CancelSchemaMigrationRequest { + if m == nil { + return (*CancelSchemaMigrationRequest)(nil) + } + r := &CancelSchemaMigrationRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CancelSchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *CleanupSchemaMigrationRequest) CloneVT() *CleanupSchemaMigrationRequest { + if m == nil { + return (*CleanupSchemaMigrationRequest)(nil) + } + r := &CleanupSchemaMigrationRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CleanupSchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *CompleteSchemaMigrationRequest) CloneVT() *CompleteSchemaMigrationRequest { + if m == nil { + return (*CompleteSchemaMigrationRequest)(nil) + } + r := &CompleteSchemaMigrationRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CompleteSchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *CreateKeyspaceRequest) CloneVT() *CreateKeyspaceRequest { if m == nil { return (*CreateKeyspaceRequest)(nil) @@ -999,6 +1094,71 @@ func (m *GetSchemasResponse) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *GetSchemaMigrationsRequest_ClusterRequest) CloneVT() *GetSchemaMigrationsRequest_ClusterRequest { + if m == nil { + return (*GetSchemaMigrationsRequest_ClusterRequest)(nil) + } + r := &GetSchemaMigrationsRequest_ClusterRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *GetSchemaMigrationsRequest_ClusterRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *GetSchemaMigrationsRequest) CloneVT() *GetSchemaMigrationsRequest { + if m == nil { + return (*GetSchemaMigrationsRequest)(nil) + } + r := &GetSchemaMigrationsRequest{} + if rhs := m.ClusterRequests; rhs != nil { + tmpContainer := make([]*GetSchemaMigrationsRequest_ClusterRequest, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.ClusterRequests = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *GetSchemaMigrationsRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *GetSchemaMigrationsResponse) CloneVT() *GetSchemaMigrationsResponse { + if m == nil { + return (*GetSchemaMigrationsResponse)(nil) + } + r := &GetSchemaMigrationsResponse{} + if rhs := m.SchemaMigrations; rhs != nil { + tmpContainer := make([]*SchemaMigration, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.SchemaMigrations = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *GetSchemaMigrationsResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *GetShardReplicationPositionsRequest) CloneVT() *GetShardReplicationPositionsRequest { if m == nil { return (*GetShardReplicationPositionsRequest)(nil) @@ -1483,6 +1643,25 @@ func (m *GetWorkflowsResponse) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *LaunchSchemaMigrationRequest) CloneVT() *LaunchSchemaMigrationRequest { + if m == nil { + return (*LaunchSchemaMigrationRequest)(nil) + } + r := &LaunchSchemaMigrationRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *LaunchSchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *PingTabletRequest) CloneVT() *PingTabletRequest { if m == nil { return (*PingTabletRequest)(nil) @@ -1935,6 +2114,25 @@ func (m *RemoveKeyspaceCellResponse) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *RetrySchemaMigrationRequest) CloneVT() *RetrySchemaMigrationRequest { + if m == nil { + return (*RetrySchemaMigrationRequest)(nil) + } + r := &RetrySchemaMigrationRequest{ + ClusterId: m.ClusterId, + Request: m.Request.CloneVT(), + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *RetrySchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *RunHealthCheckRequest) CloneVT() *RunHealthCheckRequest { if m == nil { return (*RunHealthCheckRequest)(nil) @@ -2976,6 +3174,59 @@ func (m *Schema) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SchemaMigration) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SchemaMigration) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SchemaMigration) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SchemaMigration != nil { + size, err := m.SchemaMigration.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Cluster != nil { + size, err := m.Cluster.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *Shard) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -3411,7 +3662,7 @@ func (m *Workflow) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *CreateKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { +func (m *ApplySchemaRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3424,12 +3675,12 @@ func (m *CreateKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *CreateKeyspaceRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *ApplySchemaRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *CreateKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *ApplySchemaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3441,8 +3692,8 @@ func (m *CreateKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.Options != nil { - size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -3461,7 +3712,7 @@ func (m *CreateKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *CreateKeyspaceResponse) MarshalVT() (dAtA []byte, err error) { +func (m *CancelSchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3474,12 +3725,12 @@ func (m *CreateKeyspaceResponse) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *CreateKeyspaceResponse) MarshalToVT(dAtA []byte) (int, error) { +func (m *CancelSchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *CreateKeyspaceResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *CancelSchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3491,20 +3742,27 @@ func (m *CreateKeyspaceResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.Keyspace != nil { - size, err := m.Keyspace.MarshalToSizedBufferVT(dAtA[:i]) + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = encodeVarint(dAtA, i, uint64(size)) i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *CreateShardRequest) MarshalVT() (dAtA []byte, err error) { +func (m *CleanupSchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3517,12 +3775,12 @@ func (m *CreateShardRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *CreateShardRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *CleanupSchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *CreateShardRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *CleanupSchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3534,8 +3792,8 @@ func (m *CreateShardRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.Options != nil { - size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -3554,7 +3812,7 @@ func (m *CreateShardRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DeleteKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { +func (m *CompleteSchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3567,12 +3825,12 @@ func (m *DeleteKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DeleteKeyspaceRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *CompleteSchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DeleteKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *CompleteSchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3584,8 +3842,8 @@ func (m *DeleteKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.Options != nil { - size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -3604,7 +3862,7 @@ func (m *DeleteKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *DeleteShardsRequest) MarshalVT() (dAtA []byte, err error) { +func (m *CreateKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3617,12 +3875,12 @@ func (m *DeleteShardsRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DeleteShardsRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *CreateKeyspaceRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DeleteShardsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *CreateKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3654,7 +3912,7 @@ func (m *DeleteShardsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DeleteTabletRequest) MarshalVT() (dAtA []byte, err error) { +func (m *CreateKeyspaceResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -3667,12 +3925,12 @@ func (m *DeleteTabletRequest) MarshalVT() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DeleteTabletRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *CreateKeyspaceResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *DeleteTabletRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *CreateKeyspaceResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -3684,27 +3942,8 @@ func (m *DeleteTabletRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.AllowPrimary { - i-- - if m.AllowPrimary { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 - } - if len(m.ClusterIds) > 0 { - for iNdEx := len(m.ClusterIds) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ClusterIds[iNdEx]) - copy(dAtA[i:], m.ClusterIds[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.ClusterIds[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - if m.Alias != nil { - size, err := m.Alias.MarshalToSizedBufferVT(dAtA[:i]) + if m.Keyspace != nil { + size, err := m.Keyspace.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } @@ -3716,7 +3955,219 @@ func (m *DeleteTabletRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DeleteTabletResponse) MarshalVT() (dAtA []byte, err error) { +func (m *CreateShardRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateShardRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CreateShardRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Options != nil { + size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DeleteKeyspaceRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteKeyspaceRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DeleteKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Options != nil { + size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DeleteShardsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteShardsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DeleteShardsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Options != nil { + size, err := m.Options.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DeleteTabletRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteTabletRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DeleteTabletRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.AllowPrimary { + i-- + if m.AllowPrimary { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.ClusterIds) > 0 { + for iNdEx := len(m.ClusterIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClusterIds[iNdEx]) + copy(dAtA[i:], m.ClusterIds[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterIds[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.Alias != nil { + size, err := m.Alias.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DeleteTabletResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -4772,7 +5223,7 @@ func (m *GetSchemasResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *GetShardReplicationPositionsRequest) MarshalVT() (dAtA []byte, err error) { +func (m *GetSchemaMigrationsRequest_ClusterRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -4785,12 +5236,12 @@ func (m *GetShardReplicationPositionsRequest) MarshalVT() (dAtA []byte, err erro return dAtA[:n], nil } -func (m *GetShardReplicationPositionsRequest) MarshalToVT(dAtA []byte) (int, error) { +func (m *GetSchemaMigrationsRequest_ClusterRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *GetShardReplicationPositionsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *GetSchemaMigrationsRequest_ClusterRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -4802,37 +5253,27 @@ func (m *GetShardReplicationPositionsRequest) MarshalToSizedBufferVT(dAtA []byte i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.KeyspaceShards) > 0 { - for iNdEx := len(m.KeyspaceShards) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.KeyspaceShards[iNdEx]) - copy(dAtA[i:], m.KeyspaceShards[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.KeyspaceShards[iNdEx]))) - i-- - dAtA[i] = 0x1a - } - } - if len(m.Keyspaces) > 0 { - for iNdEx := len(m.Keyspaces) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Keyspaces[iNdEx]) - copy(dAtA[i:], m.Keyspaces[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.Keyspaces[iNdEx]))) - i-- - dAtA[i] = 0x12 + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 } - if len(m.ClusterIds) > 0 { - for iNdEx := len(m.ClusterIds) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ClusterIds[iNdEx]) - copy(dAtA[i:], m.ClusterIds[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.ClusterIds[iNdEx]))) - i-- - dAtA[i] = 0xa - } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *GetShardReplicationPositionsResponse) MarshalVT() (dAtA []byte, err error) { +func (m *GetSchemaMigrationsRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } @@ -4845,12 +5286,12 @@ func (m *GetShardReplicationPositionsResponse) MarshalVT() (dAtA []byte, err err return dAtA[:n], nil } -func (m *GetShardReplicationPositionsResponse) MarshalToVT(dAtA []byte) (int, error) { +func (m *GetSchemaMigrationsRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } -func (m *GetShardReplicationPositionsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { +func (m *GetSchemaMigrationsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } @@ -4862,8 +5303,158 @@ func (m *GetShardReplicationPositionsResponse) MarshalToSizedBufferVT(dAtA []byt i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if len(m.ReplicationPositions) > 0 { - for iNdEx := len(m.ReplicationPositions) - 1; iNdEx >= 0; iNdEx-- { + if len(m.ClusterRequests) > 0 { + for iNdEx := len(m.ClusterRequests) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.ClusterRequests[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetSchemaMigrationsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSchemaMigrationsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *GetSchemaMigrationsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.SchemaMigrations) > 0 { + for iNdEx := len(m.SchemaMigrations) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.SchemaMigrations[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetShardReplicationPositionsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetShardReplicationPositionsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *GetShardReplicationPositionsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.KeyspaceShards) > 0 { + for iNdEx := len(m.KeyspaceShards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.KeyspaceShards[iNdEx]) + copy(dAtA[i:], m.KeyspaceShards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.KeyspaceShards[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Keyspaces) > 0 { + for iNdEx := len(m.Keyspaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Keyspaces[iNdEx]) + copy(dAtA[i:], m.Keyspaces[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Keyspaces[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClusterIds) > 0 { + for iNdEx := len(m.ClusterIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClusterIds[iNdEx]) + copy(dAtA[i:], m.ClusterIds[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterIds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetShardReplicationPositionsResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetShardReplicationPositionsResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *GetShardReplicationPositionsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ReplicationPositions) > 0 { + for iNdEx := len(m.ReplicationPositions) - 1; iNdEx >= 0; iNdEx-- { size, err := m.ReplicationPositions[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err @@ -5831,6 +6422,56 @@ func (m *GetWorkflowsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *LaunchSchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LaunchSchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *LaunchSchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PingTabletRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -6947,6 +7588,56 @@ func (m *RemoveKeyspaceCellResponse) MarshalToSizedBufferVT(dAtA []byte) (int, e return len(dAtA) - i, nil } +func (m *RetrySchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrySchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RetrySchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.ClusterId) > 0 { + i -= len(m.ClusterId) + copy(dAtA[i:], m.ClusterId) + i = encodeVarint(dAtA, i, uint64(len(m.ClusterId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *RunHealthCheckRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -8274,6 +8965,24 @@ func (m *Schema) SizeVT() (n int) { return n } +func (m *SchemaMigration) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Cluster != nil { + l = m.Cluster.SizeVT() + n += 1 + l + sov(uint64(l)) + } + if m.SchemaMigration != nil { + l = m.SchemaMigration.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *Shard) SizeVT() (n int) { if m == nil { return 0 @@ -8441,7 +9150,7 @@ func (m *Workflow) SizeVT() (n int) { return n } -func (m *CreateKeyspaceRequest) SizeVT() (n int) { +func (m *ApplySchemaRequest) SizeVT() (n int) { if m == nil { return 0 } @@ -8451,22 +9160,94 @@ func (m *CreateKeyspaceRequest) SizeVT() (n int) { if l > 0 { n += 1 + l + sov(uint64(l)) } - if m.Options != nil { - l = m.Options.SizeVT() + if m.Request != nil { + l = m.Request.SizeVT() n += 1 + l + sov(uint64(l)) } n += len(m.unknownFields) return n } -func (m *CreateKeyspaceResponse) SizeVT() (n int) { +func (m *CancelSchemaMigrationRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l - if m.Keyspace != nil { - l = m.Keyspace.SizeVT() + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CleanupSchemaMigrationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CompleteSchemaMigrationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CreateKeyspaceRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Options != nil { + l = m.Options.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CreateKeyspaceResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Keyspace != nil { + l = m.Keyspace.SizeVT() n += 1 + l + sov(uint64(l)) } n += len(m.unknownFields) @@ -8951,6 +9732,56 @@ func (m *GetSchemasResponse) SizeVT() (n int) { return n } +func (m *GetSchemaMigrationsRequest_ClusterRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *GetSchemaMigrationsRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ClusterRequests) > 0 { + for _, e := range m.ClusterRequests { + l = e.SizeVT() + n += 1 + l + sov(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *GetSchemaMigrationsResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SchemaMigrations) > 0 { + for _, e := range m.SchemaMigrations { + l = e.SizeVT() + n += 1 + l + sov(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + func (m *GetShardReplicationPositionsRequest) SizeVT() (n int) { if m == nil { return 0 @@ -9367,6 +10198,24 @@ func (m *GetWorkflowsResponse) SizeVT() (n int) { return n } +func (m *LaunchSchemaMigrationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *PingTabletRequest) SizeVT() (n int) { if m == nil { return 0 @@ -9800,6 +10649,24 @@ func (m *RemoveKeyspaceCellResponse) SizeVT() (n int) { return n } +func (m *RetrySchemaMigrationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterId) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + func (m *RunHealthCheckRequest) SizeVT() (n int) { if m == nil { return 0 @@ -11961,6 +12828,129 @@ func (m *Schema) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *SchemaMigration) UnmarshalVT(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 ErrIntOverflow + } + 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: SchemaMigration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SchemaMigration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Cluster == nil { + m.Cluster = &Cluster{} + } + if err := m.Cluster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SchemaMigration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SchemaMigration == nil { + m.SchemaMigration = &vtctldata.SchemaMigration{} + } + if err := m.SchemaMigration.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Shard) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -13121,7 +14111,7 @@ func (m *Workflow) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { +func (m *ApplySchemaRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13144,10 +14134,10 @@ func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateKeyspaceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ApplySchemaRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ApplySchemaRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13184,7 +14174,7 @@ func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13211,10 +14201,10 @@ func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Options == nil { - m.Options = &vtctldata.CreateKeyspaceRequest{} + if m.Request == nil { + m.Request = &vtctldata.ApplySchemaRequest{} } - if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13240,7 +14230,7 @@ func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *CreateKeyspaceResponse) UnmarshalVT(dAtA []byte) error { +func (m *CancelSchemaMigrationRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13263,15 +14253,47 @@ func (m *CreateKeyspaceResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateKeyspaceResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CancelSchemaMigrationRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateKeyspaceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CancelSchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13298,10 +14320,10 @@ func (m *CreateKeyspaceResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Keyspace == nil { - m.Keyspace = &Keyspace{} + if m.Request == nil { + m.Request = &vtctldata.CancelSchemaMigrationRequest{} } - if err := m.Keyspace.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13327,7 +14349,7 @@ func (m *CreateKeyspaceResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *CreateShardRequest) UnmarshalVT(dAtA []byte) error { +func (m *CleanupSchemaMigrationRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13350,10 +14372,10 @@ func (m *CreateShardRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateShardRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CleanupSchemaMigrationRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateShardRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CleanupSchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13390,7 +14412,7 @@ func (m *CreateShardRequest) UnmarshalVT(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13417,10 +14439,10 @@ func (m *CreateShardRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Options == nil { - m.Options = &vtctldata.CreateShardRequest{} + if m.Request == nil { + m.Request = &vtctldata.CleanupSchemaMigrationRequest{} } - if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13446,7 +14468,7 @@ func (m *CreateShardRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { +func (m *CompleteSchemaMigrationRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13469,10 +14491,10 @@ func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeleteKeyspaceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CompleteSchemaMigrationRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CompleteSchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13509,7 +14531,7 @@ func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13536,10 +14558,10 @@ func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Options == nil { - m.Options = &vtctldata.DeleteKeyspaceRequest{} + if m.Request == nil { + m.Request = &vtctldata.CompleteSchemaMigrationRequest{} } - if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13565,7 +14587,7 @@ func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DeleteShardsRequest) UnmarshalVT(dAtA []byte) error { +func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13588,10 +14610,10 @@ func (m *DeleteShardsRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeleteShardsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateKeyspaceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteShardsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13656,7 +14678,7 @@ func (m *DeleteShardsRequest) UnmarshalVT(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &vtctldata.DeleteShardsRequest{} + m.Options = &vtctldata.CreateKeyspaceRequest{} } if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err @@ -13684,7 +14706,7 @@ func (m *DeleteShardsRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { +func (m *CreateKeyspaceResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13707,15 +14729,15 @@ func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeleteTabletRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateKeyspaceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteTabletRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateKeyspaceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13742,16 +14764,67 @@ func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Alias == nil { - m.Alias = &topodata.TabletAlias{} + if m.Keyspace == nil { + m.Keyspace = &Keyspace{} } - if err := m.Alias.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Keyspace.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 2: + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateShardRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: CreateShardRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateShardRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -13779,13 +14852,13 @@ func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AllowPrimary", wireType) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -13795,12 +14868,28 @@ func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.AllowPrimary = bool(v != 0) + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Options == nil { + m.Options = &vtctldata.CreateShardRequest{} + } + if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -13823,7 +14912,7 @@ func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *DeleteTabletResponse) UnmarshalVT(dAtA []byte) error { +func (m *DeleteKeyspaceRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13846,15 +14935,15 @@ func (m *DeleteTabletResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeleteTabletResponse: wiretype end group for non-group") + return fmt.Errorf("proto: DeleteKeyspaceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteTabletResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DeleteKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -13882,11 +14971,11 @@ func (m *DeleteTabletResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Status = string(dAtA[iNdEx:postIndex]) + m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13913,10 +15002,10 @@ func (m *DeleteTabletResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Cluster == nil { - m.Cluster = &Cluster{} + if m.Options == nil { + m.Options = &vtctldata.DeleteKeyspaceRequest{} } - if err := m.Cluster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13942,7 +15031,7 @@ func (m *DeleteTabletResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *EmergencyFailoverShardRequest) UnmarshalVT(dAtA []byte) error { +func (m *DeleteShardsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13965,10 +15054,10 @@ func (m *EmergencyFailoverShardRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: EmergencyFailoverShardRequest: wiretype end group for non-group") + return fmt.Errorf("proto: DeleteShardsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: EmergencyFailoverShardRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DeleteShardsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -14033,7 +15122,7 @@ func (m *EmergencyFailoverShardRequest) UnmarshalVT(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &vtctldata.EmergencyReparentShardRequest{} + m.Options = &vtctldata.DeleteShardsRequest{} } if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err @@ -14061,7 +15150,7 @@ func (m *EmergencyFailoverShardRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { +func (m *DeleteTabletRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14084,15 +15173,15 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: EmergencyFailoverShardResponse: wiretype end group for non-group") + return fmt.Errorf("proto: DeleteTabletRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: EmergencyFailoverShardResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DeleteTabletRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14119,16 +15208,16 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Cluster == nil { - m.Cluster = &Cluster{} + if m.Alias == nil { + m.Alias = &topodata.TabletAlias{} } - if err := m.Cluster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Alias.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14156,13 +15245,13 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Keyspace = string(dAtA[iNdEx:postIndex]) + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Shard", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowPrimary", wireType) } - var stringLen uint64 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -14172,63 +15261,98 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLength + m.AllowPrimary = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err } - postIndex := iNdEx + intStringLen - if postIndex < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLength } - if postIndex > l { + if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.Shard = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PromotedPrimary", wireType) + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteTabletResponse) UnmarshalVT(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 ErrIntOverflow } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow + 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: DeleteTabletResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteTabletResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLength } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - if m.PromotedPrimary == nil { - m.PromotedPrimary = &topodata.TabletAlias{} - } - if err := m.PromotedPrimary.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14255,8 +15379,10 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Events = append(m.Events, &logutil.Event{}) - if err := m.Events[len(m.Events)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if m.Cluster == nil { + m.Cluster = &Cluster{} + } + if err := m.Cluster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14282,7 +15408,7 @@ func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { +func (m *EmergencyFailoverShardRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14305,15 +15431,15 @@ func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: FindSchemaRequest: wiretype end group for non-group") + return fmt.Errorf("proto: EmergencyFailoverShardRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: FindSchemaRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EmergencyFailoverShardRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14341,43 +15467,11 @@ func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Table = string(dAtA[iNdEx:postIndex]) + m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14404,10 +15498,10 @@ func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.TableSizeOptions == nil { - m.TableSizeOptions = &GetSchemaTableSizeOptions{} + if m.Options == nil { + m.Options = &vtctldata.EmergencyReparentShardRequest{} } - if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Options.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14433,7 +15527,7 @@ func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { +func (m *EmergencyFailoverShardResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14456,17 +15550,17 @@ func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetBackupsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: EmergencyFailoverShardResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetBackupsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EmergencyFailoverShardResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -14476,27 +15570,31 @@ func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + if m.Cluster == nil { + m.Cluster = &Cluster{} + } + if err := m.Cluster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspaces", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14524,11 +15622,11 @@ func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Keyspaces = append(m.Keyspaces, string(dAtA[iNdEx:postIndex])) + m.Keyspace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KeyspaceShards", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Shard", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14556,11 +15654,11 @@ func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.KeyspaceShards = append(m.KeyspaceShards, string(dAtA[iNdEx:postIndex])) + m.Shard = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RequestOptions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PromotedPrimary", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14587,67 +15685,16 @@ func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.RequestOptions == nil { - m.RequestOptions = &vtctldata.GetBackupsRequest{} + if m.PromotedPrimary == nil { + m.PromotedPrimary = &topodata.TabletAlias{} } - if err := m.RequestOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.PromotedPrimary.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetBackupsResponse) UnmarshalVT(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 ErrIntOverflow - } - 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: GetBackupsResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetBackupsResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Backups", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14674,8 +15721,8 @@ func (m *GetBackupsResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Backups = append(m.Backups, &ClusterBackup{}) - if err := m.Backups[len(m.Backups)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.Events = append(m.Events, &logutil.Event{}) + if err := m.Events[len(m.Events)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14701,7 +15748,7 @@ func (m *GetBackupsResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { +func (m *FindSchemaRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14724,15 +15771,15 @@ func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetCellInfosRequest: wiretype end group for non-group") + return fmt.Errorf("proto: FindSchemaRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetCellInfosRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: FindSchemaRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14760,11 +15807,11 @@ func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + m.Table = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14792,13 +15839,13 @@ func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field NamesOnly", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -14808,12 +15855,28 @@ func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.NamesOnly = bool(v != 0) + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TableSizeOptions == nil { + m.TableSizeOptions = &GetSchemaTableSizeOptions{} + } + if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -14836,7 +15899,7 @@ func (m *GetCellInfosRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetCellInfosResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14859,17 +15922,17 @@ func (m *GetCellInfosResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetCellInfosResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetBackupsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetCellInfosResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetBackupsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CellInfos", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -14879,23 +15942,594 @@ func (m *GetCellInfosResponse) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLength } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Keyspaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Keyspaces = append(m.Keyspaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KeyspaceShards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KeyspaceShards = append(m.KeyspaceShards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RequestOptions == nil { + m.RequestOptions = &vtctldata.GetBackupsRequest{} + } + if err := m.RequestOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetBackupsResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: GetBackupsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetBackupsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Backups", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Backups = append(m.Backups, &ClusterBackup{}) + if err := m.Backups[len(m.Backups)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetCellInfosRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: GetCellInfosRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetCellInfosRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NamesOnly", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NamesOnly = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetCellInfosResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: GetCellInfosResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetCellInfosResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CellInfos", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CellInfos = append(m.CellInfos, &ClusterCellInfo{}) + if err := m.CellInfos[len(m.CellInfos)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetCellsAliasesRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: GetCellsAliasesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetCellsAliasesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetCellsAliasesResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: GetCellsAliasesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetCellsAliasesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Aliases", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.CellInfos = append(m.CellInfos, &ClusterCellInfo{}) - if err := m.CellInfos[len(m.CellInfos)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.Aliases = append(m.Aliases, &ClusterCellsAliases{}) + if err := m.Aliases[len(m.Aliases)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14921,7 +16555,7 @@ func (m *GetCellInfosResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetCellsAliasesRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetClustersRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14944,17 +16578,68 @@ func (m *GetCellsAliasesRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetCellsAliasesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetClustersRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetCellsAliasesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetClustersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetClustersResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: GetClustersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetClustersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Clusters", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -14964,23 +16649,25 @@ func (m *GetCellsAliasesRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + m.Clusters = append(m.Clusters, &Cluster{}) + if err := m.Clusters[len(m.Clusters)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -15004,7 +16691,7 @@ func (m *GetCellsAliasesRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetCellsAliasesResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15027,15 +16714,47 @@ func (m *GetCellsAliasesResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetCellsAliasesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetFullStatusRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetCellsAliasesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetFullStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Aliases", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15062,8 +16781,10 @@ func (m *GetCellsAliasesResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Aliases = append(m.Aliases, &ClusterCellsAliases{}) - if err := m.Aliases[len(m.Aliases)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if m.Alias == nil { + m.Alias = &topodata.TabletAlias{} + } + if err := m.Alias.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15089,7 +16810,7 @@ func (m *GetCellsAliasesResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetClustersRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetGatesRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15112,12 +16833,44 @@ func (m *GetClustersRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetClustersRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetGatesRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetClustersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetGatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -15140,7 +16893,7 @@ func (m *GetClustersRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetClustersResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetGatesResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15163,15 +16916,15 @@ func (m *GetClustersResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetClustersResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetGatesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetClustersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetGatesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Clusters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Gates", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15198,8 +16951,8 @@ func (m *GetClustersResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Clusters = append(m.Clusters, &Cluster{}) - if err := m.Clusters[len(m.Clusters)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.Gates = append(m.Gates, &VTGate{}) + if err := m.Gates[len(m.Gates)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15225,7 +16978,7 @@ func (m *GetClustersResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetKeyspaceRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15248,10 +17001,10 @@ func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetFullStatusRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetKeyspaceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetFullStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15288,9 +17041,9 @@ func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -15300,27 +17053,23 @@ func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLength } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Alias == nil { - m.Alias = &topodata.TabletAlias{} - } - if err := m.Alias.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Keyspace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -15344,7 +17093,7 @@ func (m *GetFullStatusRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetGatesRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetKeyspacesRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15367,10 +17116,10 @@ func (m *GetGatesRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetGatesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetKeyspacesRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetGatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetKeyspacesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15427,7 +17176,7 @@ func (m *GetGatesRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetGatesResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetKeyspacesResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15450,15 +17199,15 @@ func (m *GetGatesResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetGatesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetKeyspacesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetGatesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetKeyspacesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Gates", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Keyspaces", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15485,8 +17234,8 @@ func (m *GetGatesResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Gates = append(m.Gates, &VTGate{}) - if err := m.Gates[len(m.Gates)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.Keyspaces = append(m.Keyspaces, &Keyspace{}) + if err := m.Keyspaces[len(m.Keyspaces)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15512,7 +17261,7 @@ func (m *GetGatesResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetKeyspaceRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15535,10 +17284,10 @@ func (m *GetKeyspaceRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetKeyspaceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetSchemaRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetKeyspaceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSchemaRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15605,6 +17354,74 @@ func (m *GetKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } m.Keyspace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Table = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TableSizeOptions == nil { + m.TableSizeOptions = &GetSchemaTableSizeOptions{} + } + if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -15627,7 +17444,7 @@ func (m *GetKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetKeyspacesRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemasRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15650,10 +17467,10 @@ func (m *GetKeyspacesRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetKeyspacesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetSchemasRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetKeyspacesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSchemasRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15688,6 +17505,42 @@ func (m *GetKeyspacesRequest) UnmarshalVT(dAtA []byte) error { } m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TableSizeOptions == nil { + m.TableSizeOptions = &GetSchemaTableSizeOptions{} + } + if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -15710,7 +17563,7 @@ func (m *GetKeyspacesRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetKeyspacesResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemasResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15733,15 +17586,15 @@ func (m *GetKeyspacesResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetKeyspacesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetSchemasResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetKeyspacesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSchemasResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspaces", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Schemas", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15768,8 +17621,8 @@ func (m *GetKeyspacesResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Keyspaces = append(m.Keyspaces, &Keyspace{}) - if err := m.Keyspaces[len(m.Keyspaces)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.Schemas = append(m.Schemas, &Schema{}) + if err := m.Schemas[len(m.Schemas)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15795,7 +17648,7 @@ func (m *GetKeyspacesResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemaMigrationsRequest_ClusterRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15818,10 +17671,10 @@ func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetSchemaRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetSchemaMigrationsRequest_ClusterRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetSchemaRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSchemaMigrationsRequest_ClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15858,71 +17711,7 @@ func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Keyspace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Table = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15949,10 +17738,10 @@ func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.TableSizeOptions == nil { - m.TableSizeOptions = &GetSchemaTableSizeOptions{} + if m.Request == nil { + m.Request = &vtctldata.GetSchemaMigrationsRequest{} } - if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15978,7 +17767,7 @@ func (m *GetSchemaRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetSchemasRequest) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemaMigrationsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15986,62 +17775,30 @@ func (m *GetSchemasRequest) UnmarshalVT(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflow - } - 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: GetSchemasRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetSchemasRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength + return ErrIntOverflow } - if postIndex > l { + if iNdEx >= l { return io.ErrUnexpectedEOF } - m.ClusterIds = append(m.ClusterIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: + 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: GetSchemaMigrationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSchemaMigrationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TableSizeOptions", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterRequests", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -16068,10 +17825,8 @@ func (m *GetSchemasRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.TableSizeOptions == nil { - m.TableSizeOptions = &GetSchemaTableSizeOptions{} - } - if err := m.TableSizeOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.ClusterRequests = append(m.ClusterRequests, &GetSchemaMigrationsRequest_ClusterRequest{}) + if err := m.ClusterRequests[len(m.ClusterRequests)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -16097,7 +17852,7 @@ func (m *GetSchemasRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *GetSchemasResponse) UnmarshalVT(dAtA []byte) error { +func (m *GetSchemaMigrationsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -16120,15 +17875,15 @@ func (m *GetSchemasResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetSchemasResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetSchemaMigrationsResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetSchemasResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSchemaMigrationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Schemas", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SchemaMigrations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -16155,8 +17910,8 @@ func (m *GetSchemasResponse) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Schemas = append(m.Schemas, &Schema{}) - if err := m.Schemas[len(m.Schemas)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + m.SchemaMigrations = append(m.SchemaMigrations, &SchemaMigration{}) + if err := m.SchemaMigrations[len(m.SchemaMigrations)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -18629,6 +20384,125 @@ func (m *GetWorkflowsResponse) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *LaunchSchemaMigrationRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: LaunchSchemaMigrationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LaunchSchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Request == nil { + m.Request = &vtctldata.LaunchSchemaMigrationRequest{} + } + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *PingTabletRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -19868,7 +21742,7 @@ func (m *ReloadSchemasRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint32(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -20652,7 +22526,7 @@ func (m *ReloadSchemaShardRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint32(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -21340,6 +23214,125 @@ func (m *RemoveKeyspaceCellResponse) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *RetrySchemaMigrationRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: RetrySchemaMigrationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrySchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Request == nil { + m.Request = &vtctldata.RetrySchemaMigrationRequest{} + } + if err := m.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *RunHealthCheckRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/go/vt/proto/vtctldata/vtctldata.pb.go b/go/vt/proto/vtctldata/vtctldata.pb.go index 5f70d625ffa..bec6a89d360 100644 --- a/go/vt/proto/vtctldata/vtctldata.pb.go +++ b/go/vt/proto/vtctldata/vtctldata.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtctldata.proto @@ -1920,6 +1920,8 @@ type ApplyVSchemaRequest struct { Cells []string `protobuf:"bytes,4,rep,name=cells,proto3" json:"cells,omitempty"` VSchema *vschema.Keyspace `protobuf:"bytes,5,opt,name=v_schema,json=vSchema,proto3" json:"v_schema,omitempty"` Sql string `protobuf:"bytes,6,opt,name=sql,proto3" json:"sql,omitempty"` + // Strict returns an error if there are unknown vindex params. + Strict bool `protobuf:"varint,7,opt,name=strict,proto3" json:"strict,omitempty"` } func (x *ApplyVSchemaRequest) Reset() { @@ -1996,12 +1998,28 @@ func (x *ApplyVSchemaRequest) GetSql() string { return "" } +func (x *ApplyVSchemaRequest) GetStrict() bool { + if x != nil { + return x.Strict + } + return false +} + type ApplyVSchemaResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields VSchema *vschema.Keyspace `protobuf:"bytes,1,opt,name=v_schema,json=vSchema,proto3" json:"v_schema,omitempty"` + // UnknownVindexParams is a map of vindex name to params that were not recognized by the vindex + // type. E.g.: + // + // { + // "lookup_vdx": { + // "params": ["raed_lock", "not_verify"] + // } + // } + UnknownVindexParams map[string]*ApplyVSchemaResponse_ParamList `protobuf:"bytes,2,rep,name=unknown_vindex_params,json=unknownVindexParams,proto3" json:"unknown_vindex_params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplyVSchemaResponse) Reset() { @@ -2043,6 +2061,13 @@ func (x *ApplyVSchemaResponse) GetVSchema() *vschema.Keyspace { return nil } +func (x *ApplyVSchemaResponse) GetUnknownVindexParams() map[string]*ApplyVSchemaResponse_ParamList { + if x != nil { + return x.UnknownVindexParams + } + return nil +} + type BackupRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2056,7 +2081,7 @@ type BackupRequest struct { AllowPrimary bool `protobuf:"varint,2,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` // Concurrency specifies the number of compression/checksum jobs to run // simultaneously. - Concurrency uint64 `protobuf:"varint,3,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + Concurrency int32 `protobuf:"varint,3,opt,name=concurrency,proto3" json:"concurrency,omitempty"` // IncrementalFromPos indicates a position of a previous backup. When this value is non-empty // then the backup becomes incremental and applies as of given position. IncrementalFromPos string `protobuf:"bytes,4,opt,name=incremental_from_pos,json=incrementalFromPos,proto3" json:"incremental_from_pos,omitempty"` @@ -2111,7 +2136,7 @@ func (x *BackupRequest) GetAllowPrimary() bool { return false } -func (x *BackupRequest) GetConcurrency() uint64 { +func (x *BackupRequest) GetConcurrency() int32 { if x != nil { return x.Concurrency } @@ -2216,7 +2241,7 @@ type BackupShardRequest struct { AllowPrimary bool `protobuf:"varint,3,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` // Concurrency specifies the number of compression/checksum jobs to run // simultaneously. - Concurrency uint64 `protobuf:"varint,4,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + Concurrency int32 `protobuf:"varint,4,opt,name=concurrency,proto3" json:"concurrency,omitempty"` // UpgradeSafe indicates if the backup should be taken with innodb_fast_shutdown=0 // so that it's a backup that can be used for an upgrade. UpgradeSafe bool `protobuf:"varint,5,opt,name=upgrade_safe,json=upgradeSafe,proto3" json:"upgrade_safe,omitempty"` @@ -2278,7 +2303,7 @@ func (x *BackupShardRequest) GetAllowPrimary() bool { return false } -func (x *BackupShardRequest) GetConcurrency() uint64 { +func (x *BackupShardRequest) GetConcurrency() int32 { if x != nil { return x.Concurrency } @@ -2742,9 +2767,6 @@ type CreateKeyspaceRequest struct { Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` // AllowEmptyVSchema allows a keyspace to be created with no vschema. AllowEmptyVSchema bool `protobuf:"varint,3,opt,name=allow_empty_v_schema,json=allowEmptyVSchema,proto3" json:"allow_empty_v_schema,omitempty"` - // ServedFroms specifies a set of db_type:keyspace pairs used to serve - // traffic for the keyspace. - ServedFroms []*topodata.Keyspace_ServedFrom `protobuf:"bytes,6,rep,name=served_froms,json=servedFroms,proto3" json:"served_froms,omitempty"` // Type is the type of the keyspace to create. Type topodata.KeyspaceType `protobuf:"varint,7,opt,name=type,proto3,enum=topodata.KeyspaceType" json:"type,omitempty"` // BaseKeyspace specifies the base keyspace for SNAPSHOT keyspaces. It is @@ -2814,13 +2836,6 @@ func (x *CreateKeyspaceRequest) GetAllowEmptyVSchema() bool { return false } -func (x *CreateKeyspaceRequest) GetServedFroms() []*topodata.Keyspace_ServedFrom { - if x != nil { - return x.ServedFroms - } - return nil -} - func (x *CreateKeyspaceRequest) GetType() topodata.KeyspaceType { if x != nil { return x.Type @@ -4276,6 +4291,108 @@ func (x *FindAllShardsInKeyspaceResponse) GetShards() map[string]*Shard { return nil } +type ForceCutOverSchemaMigrationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Uuid string `protobuf:"bytes,2,opt,name=uuid,proto3" json:"uuid,omitempty"` +} + +func (x *ForceCutOverSchemaMigrationRequest) Reset() { + *x = ForceCutOverSchemaMigrationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vtctldata_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForceCutOverSchemaMigrationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForceCutOverSchemaMigrationRequest) ProtoMessage() {} + +func (x *ForceCutOverSchemaMigrationRequest) ProtoReflect() protoreflect.Message { + mi := &file_vtctldata_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForceCutOverSchemaMigrationRequest.ProtoReflect.Descriptor instead. +func (*ForceCutOverSchemaMigrationRequest) Descriptor() ([]byte, []int) { + return file_vtctldata_proto_rawDescGZIP(), []int{57} +} + +func (x *ForceCutOverSchemaMigrationRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *ForceCutOverSchemaMigrationRequest) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +type ForceCutOverSchemaMigrationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RowsAffectedByShard map[string]uint64 `protobuf:"bytes,1,rep,name=rows_affected_by_shard,json=rowsAffectedByShard,proto3" json:"rows_affected_by_shard,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *ForceCutOverSchemaMigrationResponse) Reset() { + *x = ForceCutOverSchemaMigrationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vtctldata_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForceCutOverSchemaMigrationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForceCutOverSchemaMigrationResponse) ProtoMessage() {} + +func (x *ForceCutOverSchemaMigrationResponse) ProtoReflect() protoreflect.Message { + mi := &file_vtctldata_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForceCutOverSchemaMigrationResponse.ProtoReflect.Descriptor instead. +func (*ForceCutOverSchemaMigrationResponse) Descriptor() ([]byte, []int) { + return file_vtctldata_proto_rawDescGZIP(), []int{58} +} + +func (x *ForceCutOverSchemaMigrationResponse) GetRowsAffectedByShard() map[string]uint64 { + if x != nil { + return x.RowsAffectedByShard + } + return nil +} + type GetBackupsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4302,7 +4419,7 @@ type GetBackupsRequest struct { func (x *GetBackupsRequest) Reset() { *x = GetBackupsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[57] + mi := &file_vtctldata_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4315,7 +4432,7 @@ func (x *GetBackupsRequest) String() string { func (*GetBackupsRequest) ProtoMessage() {} func (x *GetBackupsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[57] + mi := &file_vtctldata_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4328,7 +4445,7 @@ func (x *GetBackupsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBackupsRequest.ProtoReflect.Descriptor instead. func (*GetBackupsRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{57} + return file_vtctldata_proto_rawDescGZIP(), []int{59} } func (x *GetBackupsRequest) GetKeyspace() string { @@ -4377,7 +4494,7 @@ type GetBackupsResponse struct { func (x *GetBackupsResponse) Reset() { *x = GetBackupsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[58] + mi := &file_vtctldata_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4390,7 +4507,7 @@ func (x *GetBackupsResponse) String() string { func (*GetBackupsResponse) ProtoMessage() {} func (x *GetBackupsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[58] + mi := &file_vtctldata_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4403,7 +4520,7 @@ func (x *GetBackupsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBackupsResponse.ProtoReflect.Descriptor instead. func (*GetBackupsResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{58} + return file_vtctldata_proto_rawDescGZIP(), []int{60} } func (x *GetBackupsResponse) GetBackups() []*mysqlctl.BackupInfo { @@ -4424,7 +4541,7 @@ type GetCellInfoRequest struct { func (x *GetCellInfoRequest) Reset() { *x = GetCellInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[59] + mi := &file_vtctldata_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4437,7 +4554,7 @@ func (x *GetCellInfoRequest) String() string { func (*GetCellInfoRequest) ProtoMessage() {} func (x *GetCellInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[59] + mi := &file_vtctldata_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4450,7 +4567,7 @@ func (x *GetCellInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellInfoRequest.ProtoReflect.Descriptor instead. func (*GetCellInfoRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{59} + return file_vtctldata_proto_rawDescGZIP(), []int{61} } func (x *GetCellInfoRequest) GetCell() string { @@ -4471,7 +4588,7 @@ type GetCellInfoResponse struct { func (x *GetCellInfoResponse) Reset() { *x = GetCellInfoResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[60] + mi := &file_vtctldata_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4484,7 +4601,7 @@ func (x *GetCellInfoResponse) String() string { func (*GetCellInfoResponse) ProtoMessage() {} func (x *GetCellInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[60] + mi := &file_vtctldata_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4497,7 +4614,7 @@ func (x *GetCellInfoResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellInfoResponse.ProtoReflect.Descriptor instead. func (*GetCellInfoResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{60} + return file_vtctldata_proto_rawDescGZIP(), []int{62} } func (x *GetCellInfoResponse) GetCellInfo() *topodata.CellInfo { @@ -4516,7 +4633,7 @@ type GetCellInfoNamesRequest struct { func (x *GetCellInfoNamesRequest) Reset() { *x = GetCellInfoNamesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[61] + mi := &file_vtctldata_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4529,7 +4646,7 @@ func (x *GetCellInfoNamesRequest) String() string { func (*GetCellInfoNamesRequest) ProtoMessage() {} func (x *GetCellInfoNamesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[61] + mi := &file_vtctldata_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4542,7 +4659,7 @@ func (x *GetCellInfoNamesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellInfoNamesRequest.ProtoReflect.Descriptor instead. func (*GetCellInfoNamesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{61} + return file_vtctldata_proto_rawDescGZIP(), []int{63} } type GetCellInfoNamesResponse struct { @@ -4556,7 +4673,7 @@ type GetCellInfoNamesResponse struct { func (x *GetCellInfoNamesResponse) Reset() { *x = GetCellInfoNamesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[62] + mi := &file_vtctldata_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4569,7 +4686,7 @@ func (x *GetCellInfoNamesResponse) String() string { func (*GetCellInfoNamesResponse) ProtoMessage() {} func (x *GetCellInfoNamesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[62] + mi := &file_vtctldata_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4582,7 +4699,7 @@ func (x *GetCellInfoNamesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellInfoNamesResponse.ProtoReflect.Descriptor instead. func (*GetCellInfoNamesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{62} + return file_vtctldata_proto_rawDescGZIP(), []int{64} } func (x *GetCellInfoNamesResponse) GetNames() []string { @@ -4601,7 +4718,7 @@ type GetCellsAliasesRequest struct { func (x *GetCellsAliasesRequest) Reset() { *x = GetCellsAliasesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[63] + mi := &file_vtctldata_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4614,7 +4731,7 @@ func (x *GetCellsAliasesRequest) String() string { func (*GetCellsAliasesRequest) ProtoMessage() {} func (x *GetCellsAliasesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[63] + mi := &file_vtctldata_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4627,7 +4744,7 @@ func (x *GetCellsAliasesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellsAliasesRequest.ProtoReflect.Descriptor instead. func (*GetCellsAliasesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{63} + return file_vtctldata_proto_rawDescGZIP(), []int{65} } type GetCellsAliasesResponse struct { @@ -4641,7 +4758,7 @@ type GetCellsAliasesResponse struct { func (x *GetCellsAliasesResponse) Reset() { *x = GetCellsAliasesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[64] + mi := &file_vtctldata_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4654,7 +4771,7 @@ func (x *GetCellsAliasesResponse) String() string { func (*GetCellsAliasesResponse) ProtoMessage() {} func (x *GetCellsAliasesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[64] + mi := &file_vtctldata_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4667,7 +4784,7 @@ func (x *GetCellsAliasesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCellsAliasesResponse.ProtoReflect.Descriptor instead. func (*GetCellsAliasesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{64} + return file_vtctldata_proto_rawDescGZIP(), []int{66} } func (x *GetCellsAliasesResponse) GetAliases() map[string]*topodata.CellsAlias { @@ -4688,7 +4805,7 @@ type GetFullStatusRequest struct { func (x *GetFullStatusRequest) Reset() { *x = GetFullStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[65] + mi := &file_vtctldata_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4701,7 +4818,7 @@ func (x *GetFullStatusRequest) String() string { func (*GetFullStatusRequest) ProtoMessage() {} func (x *GetFullStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[65] + mi := &file_vtctldata_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4714,7 +4831,7 @@ func (x *GetFullStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFullStatusRequest.ProtoReflect.Descriptor instead. func (*GetFullStatusRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{65} + return file_vtctldata_proto_rawDescGZIP(), []int{67} } func (x *GetFullStatusRequest) GetTabletAlias() *topodata.TabletAlias { @@ -4735,7 +4852,7 @@ type GetFullStatusResponse struct { func (x *GetFullStatusResponse) Reset() { *x = GetFullStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[66] + mi := &file_vtctldata_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4748,7 +4865,7 @@ func (x *GetFullStatusResponse) String() string { func (*GetFullStatusResponse) ProtoMessage() {} func (x *GetFullStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[66] + mi := &file_vtctldata_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4761,7 +4878,7 @@ func (x *GetFullStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFullStatusResponse.ProtoReflect.Descriptor instead. func (*GetFullStatusResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{66} + return file_vtctldata_proto_rawDescGZIP(), []int{68} } func (x *GetFullStatusResponse) GetStatus() *replicationdata.FullStatus { @@ -4780,7 +4897,7 @@ type GetKeyspacesRequest struct { func (x *GetKeyspacesRequest) Reset() { *x = GetKeyspacesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[67] + mi := &file_vtctldata_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4793,7 +4910,7 @@ func (x *GetKeyspacesRequest) String() string { func (*GetKeyspacesRequest) ProtoMessage() {} func (x *GetKeyspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[67] + mi := &file_vtctldata_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4806,7 +4923,7 @@ func (x *GetKeyspacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetKeyspacesRequest.ProtoReflect.Descriptor instead. func (*GetKeyspacesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{67} + return file_vtctldata_proto_rawDescGZIP(), []int{69} } type GetKeyspacesResponse struct { @@ -4820,7 +4937,7 @@ type GetKeyspacesResponse struct { func (x *GetKeyspacesResponse) Reset() { *x = GetKeyspacesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[68] + mi := &file_vtctldata_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4833,7 +4950,7 @@ func (x *GetKeyspacesResponse) String() string { func (*GetKeyspacesResponse) ProtoMessage() {} func (x *GetKeyspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[68] + mi := &file_vtctldata_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4846,7 +4963,7 @@ func (x *GetKeyspacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetKeyspacesResponse.ProtoReflect.Descriptor instead. func (*GetKeyspacesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{68} + return file_vtctldata_proto_rawDescGZIP(), []int{70} } func (x *GetKeyspacesResponse) GetKeyspaces() []*Keyspace { @@ -4867,7 +4984,7 @@ type GetKeyspaceRequest struct { func (x *GetKeyspaceRequest) Reset() { *x = GetKeyspaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[69] + mi := &file_vtctldata_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4880,7 +4997,7 @@ func (x *GetKeyspaceRequest) String() string { func (*GetKeyspaceRequest) ProtoMessage() {} func (x *GetKeyspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[69] + mi := &file_vtctldata_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4893,7 +5010,7 @@ func (x *GetKeyspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetKeyspaceRequest.ProtoReflect.Descriptor instead. func (*GetKeyspaceRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{69} + return file_vtctldata_proto_rawDescGZIP(), []int{71} } func (x *GetKeyspaceRequest) GetKeyspace() string { @@ -4914,7 +5031,7 @@ type GetKeyspaceResponse struct { func (x *GetKeyspaceResponse) Reset() { *x = GetKeyspaceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[70] + mi := &file_vtctldata_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4927,7 +5044,7 @@ func (x *GetKeyspaceResponse) String() string { func (*GetKeyspaceResponse) ProtoMessage() {} func (x *GetKeyspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[70] + mi := &file_vtctldata_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4940,7 +5057,7 @@ func (x *GetKeyspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetKeyspaceResponse.ProtoReflect.Descriptor instead. func (*GetKeyspaceResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{70} + return file_vtctldata_proto_rawDescGZIP(), []int{72} } func (x *GetKeyspaceResponse) GetKeyspace() *Keyspace { @@ -4961,7 +5078,7 @@ type GetPermissionsRequest struct { func (x *GetPermissionsRequest) Reset() { *x = GetPermissionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[71] + mi := &file_vtctldata_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4974,7 +5091,7 @@ func (x *GetPermissionsRequest) String() string { func (*GetPermissionsRequest) ProtoMessage() {} func (x *GetPermissionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[71] + mi := &file_vtctldata_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4987,7 +5104,7 @@ func (x *GetPermissionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPermissionsRequest.ProtoReflect.Descriptor instead. func (*GetPermissionsRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{71} + return file_vtctldata_proto_rawDescGZIP(), []int{73} } func (x *GetPermissionsRequest) GetTabletAlias() *topodata.TabletAlias { @@ -5008,7 +5125,7 @@ type GetPermissionsResponse struct { func (x *GetPermissionsResponse) Reset() { *x = GetPermissionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[72] + mi := &file_vtctldata_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5021,7 +5138,7 @@ func (x *GetPermissionsResponse) String() string { func (*GetPermissionsResponse) ProtoMessage() {} func (x *GetPermissionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[72] + mi := &file_vtctldata_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5034,7 +5151,7 @@ func (x *GetPermissionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPermissionsResponse.ProtoReflect.Descriptor instead. func (*GetPermissionsResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{72} + return file_vtctldata_proto_rawDescGZIP(), []int{74} } func (x *GetPermissionsResponse) GetPermissions() *tabletmanagerdata.Permissions { @@ -5053,7 +5170,7 @@ type GetRoutingRulesRequest struct { func (x *GetRoutingRulesRequest) Reset() { *x = GetRoutingRulesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[73] + mi := &file_vtctldata_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5066,7 +5183,7 @@ func (x *GetRoutingRulesRequest) String() string { func (*GetRoutingRulesRequest) ProtoMessage() {} func (x *GetRoutingRulesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[73] + mi := &file_vtctldata_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5079,7 +5196,7 @@ func (x *GetRoutingRulesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoutingRulesRequest.ProtoReflect.Descriptor instead. func (*GetRoutingRulesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{73} + return file_vtctldata_proto_rawDescGZIP(), []int{75} } type GetRoutingRulesResponse struct { @@ -5093,7 +5210,7 @@ type GetRoutingRulesResponse struct { func (x *GetRoutingRulesResponse) Reset() { *x = GetRoutingRulesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[74] + mi := &file_vtctldata_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5106,7 +5223,7 @@ func (x *GetRoutingRulesResponse) String() string { func (*GetRoutingRulesResponse) ProtoMessage() {} func (x *GetRoutingRulesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[74] + mi := &file_vtctldata_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5119,7 +5236,7 @@ func (x *GetRoutingRulesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRoutingRulesResponse.ProtoReflect.Descriptor instead. func (*GetRoutingRulesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{74} + return file_vtctldata_proto_rawDescGZIP(), []int{76} } func (x *GetRoutingRulesResponse) GetRoutingRules() *vschema.RoutingRules { @@ -5158,7 +5275,7 @@ type GetSchemaRequest struct { func (x *GetSchemaRequest) Reset() { *x = GetSchemaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[75] + mi := &file_vtctldata_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5171,7 +5288,7 @@ func (x *GetSchemaRequest) String() string { func (*GetSchemaRequest) ProtoMessage() {} func (x *GetSchemaRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[75] + mi := &file_vtctldata_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5184,7 +5301,7 @@ func (x *GetSchemaRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSchemaRequest.ProtoReflect.Descriptor instead. func (*GetSchemaRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{75} + return file_vtctldata_proto_rawDescGZIP(), []int{77} } func (x *GetSchemaRequest) GetTabletAlias() *topodata.TabletAlias { @@ -5247,7 +5364,7 @@ type GetSchemaResponse struct { func (x *GetSchemaResponse) Reset() { *x = GetSchemaResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[76] + mi := &file_vtctldata_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5260,7 +5377,7 @@ func (x *GetSchemaResponse) String() string { func (*GetSchemaResponse) ProtoMessage() {} func (x *GetSchemaResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[76] + mi := &file_vtctldata_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5273,7 +5390,7 @@ func (x *GetSchemaResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSchemaResponse.ProtoReflect.Descriptor instead. func (*GetSchemaResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{76} + return file_vtctldata_proto_rawDescGZIP(), []int{78} } func (x *GetSchemaResponse) GetSchema() *tabletmanagerdata.SchemaDefinition { @@ -5319,7 +5436,7 @@ type GetSchemaMigrationsRequest struct { func (x *GetSchemaMigrationsRequest) Reset() { *x = GetSchemaMigrationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[77] + mi := &file_vtctldata_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5332,7 +5449,7 @@ func (x *GetSchemaMigrationsRequest) String() string { func (*GetSchemaMigrationsRequest) ProtoMessage() {} func (x *GetSchemaMigrationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[77] + mi := &file_vtctldata_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5345,7 +5462,7 @@ func (x *GetSchemaMigrationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSchemaMigrationsRequest.ProtoReflect.Descriptor instead. func (*GetSchemaMigrationsRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{77} + return file_vtctldata_proto_rawDescGZIP(), []int{79} } func (x *GetSchemaMigrationsRequest) GetKeyspace() string { @@ -5415,7 +5532,7 @@ type GetSchemaMigrationsResponse struct { func (x *GetSchemaMigrationsResponse) Reset() { *x = GetSchemaMigrationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[78] + mi := &file_vtctldata_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5428,7 +5545,7 @@ func (x *GetSchemaMigrationsResponse) String() string { func (*GetSchemaMigrationsResponse) ProtoMessage() {} func (x *GetSchemaMigrationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[78] + mi := &file_vtctldata_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5441,7 +5558,7 @@ func (x *GetSchemaMigrationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSchemaMigrationsResponse.ProtoReflect.Descriptor instead. func (*GetSchemaMigrationsResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{78} + return file_vtctldata_proto_rawDescGZIP(), []int{80} } func (x *GetSchemaMigrationsResponse) GetMigrations() []*SchemaMigration { @@ -5463,7 +5580,7 @@ type GetShardRequest struct { func (x *GetShardRequest) Reset() { *x = GetShardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[79] + mi := &file_vtctldata_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5476,7 +5593,7 @@ func (x *GetShardRequest) String() string { func (*GetShardRequest) ProtoMessage() {} func (x *GetShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[79] + mi := &file_vtctldata_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5489,7 +5606,7 @@ func (x *GetShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardRequest.ProtoReflect.Descriptor instead. func (*GetShardRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{79} + return file_vtctldata_proto_rawDescGZIP(), []int{81} } func (x *GetShardRequest) GetKeyspace() string { @@ -5517,7 +5634,7 @@ type GetShardResponse struct { func (x *GetShardResponse) Reset() { *x = GetShardResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[80] + mi := &file_vtctldata_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5530,7 +5647,7 @@ func (x *GetShardResponse) String() string { func (*GetShardResponse) ProtoMessage() {} func (x *GetShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[80] + mi := &file_vtctldata_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5543,7 +5660,7 @@ func (x *GetShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardResponse.ProtoReflect.Descriptor instead. func (*GetShardResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{80} + return file_vtctldata_proto_rawDescGZIP(), []int{82} } func (x *GetShardResponse) GetShard() *Shard { @@ -5562,7 +5679,7 @@ type GetShardRoutingRulesRequest struct { func (x *GetShardRoutingRulesRequest) Reset() { *x = GetShardRoutingRulesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[81] + mi := &file_vtctldata_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5575,7 +5692,7 @@ func (x *GetShardRoutingRulesRequest) String() string { func (*GetShardRoutingRulesRequest) ProtoMessage() {} func (x *GetShardRoutingRulesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[81] + mi := &file_vtctldata_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5588,7 +5705,7 @@ func (x *GetShardRoutingRulesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardRoutingRulesRequest.ProtoReflect.Descriptor instead. func (*GetShardRoutingRulesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{81} + return file_vtctldata_proto_rawDescGZIP(), []int{83} } type GetShardRoutingRulesResponse struct { @@ -5602,7 +5719,7 @@ type GetShardRoutingRulesResponse struct { func (x *GetShardRoutingRulesResponse) Reset() { *x = GetShardRoutingRulesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[82] + mi := &file_vtctldata_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5615,7 +5732,7 @@ func (x *GetShardRoutingRulesResponse) String() string { func (*GetShardRoutingRulesResponse) ProtoMessage() {} func (x *GetShardRoutingRulesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[82] + mi := &file_vtctldata_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5628,7 +5745,7 @@ func (x *GetShardRoutingRulesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetShardRoutingRulesResponse.ProtoReflect.Descriptor instead. func (*GetShardRoutingRulesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{82} + return file_vtctldata_proto_rawDescGZIP(), []int{84} } func (x *GetShardRoutingRulesResponse) GetShardRoutingRules() *vschema.ShardRoutingRules { @@ -5649,7 +5766,7 @@ type GetSrvKeyspaceNamesRequest struct { func (x *GetSrvKeyspaceNamesRequest) Reset() { *x = GetSrvKeyspaceNamesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[83] + mi := &file_vtctldata_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5662,7 +5779,7 @@ func (x *GetSrvKeyspaceNamesRequest) String() string { func (*GetSrvKeyspaceNamesRequest) ProtoMessage() {} func (x *GetSrvKeyspaceNamesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[83] + mi := &file_vtctldata_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5675,7 +5792,7 @@ func (x *GetSrvKeyspaceNamesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvKeyspaceNamesRequest.ProtoReflect.Descriptor instead. func (*GetSrvKeyspaceNamesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{83} + return file_vtctldata_proto_rawDescGZIP(), []int{85} } func (x *GetSrvKeyspaceNamesRequest) GetCells() []string { @@ -5697,7 +5814,7 @@ type GetSrvKeyspaceNamesResponse struct { func (x *GetSrvKeyspaceNamesResponse) Reset() { *x = GetSrvKeyspaceNamesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[84] + mi := &file_vtctldata_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5710,7 +5827,7 @@ func (x *GetSrvKeyspaceNamesResponse) String() string { func (*GetSrvKeyspaceNamesResponse) ProtoMessage() {} func (x *GetSrvKeyspaceNamesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[84] + mi := &file_vtctldata_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5723,7 +5840,7 @@ func (x *GetSrvKeyspaceNamesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvKeyspaceNamesResponse.ProtoReflect.Descriptor instead. func (*GetSrvKeyspaceNamesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{84} + return file_vtctldata_proto_rawDescGZIP(), []int{86} } func (x *GetSrvKeyspaceNamesResponse) GetNames() map[string]*GetSrvKeyspaceNamesResponse_NameList { @@ -5747,7 +5864,7 @@ type GetSrvKeyspacesRequest struct { func (x *GetSrvKeyspacesRequest) Reset() { *x = GetSrvKeyspacesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[85] + mi := &file_vtctldata_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5760,7 +5877,7 @@ func (x *GetSrvKeyspacesRequest) String() string { func (*GetSrvKeyspacesRequest) ProtoMessage() {} func (x *GetSrvKeyspacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[85] + mi := &file_vtctldata_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5773,7 +5890,7 @@ func (x *GetSrvKeyspacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvKeyspacesRequest.ProtoReflect.Descriptor instead. func (*GetSrvKeyspacesRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{85} + return file_vtctldata_proto_rawDescGZIP(), []int{87} } func (x *GetSrvKeyspacesRequest) GetKeyspace() string { @@ -5802,7 +5919,7 @@ type GetSrvKeyspacesResponse struct { func (x *GetSrvKeyspacesResponse) Reset() { *x = GetSrvKeyspacesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[86] + mi := &file_vtctldata_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5815,7 +5932,7 @@ func (x *GetSrvKeyspacesResponse) String() string { func (*GetSrvKeyspacesResponse) ProtoMessage() {} func (x *GetSrvKeyspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[86] + mi := &file_vtctldata_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5828,7 +5945,7 @@ func (x *GetSrvKeyspacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvKeyspacesResponse.ProtoReflect.Descriptor instead. func (*GetSrvKeyspacesResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{86} + return file_vtctldata_proto_rawDescGZIP(), []int{88} } func (x *GetSrvKeyspacesResponse) GetSrvKeyspaces() map[string]*topodata.SrvKeyspace { @@ -5865,7 +5982,7 @@ type UpdateThrottlerConfigRequest struct { func (x *UpdateThrottlerConfigRequest) Reset() { *x = UpdateThrottlerConfigRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[87] + mi := &file_vtctldata_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5878,7 +5995,7 @@ func (x *UpdateThrottlerConfigRequest) String() string { func (*UpdateThrottlerConfigRequest) ProtoMessage() {} func (x *UpdateThrottlerConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[87] + mi := &file_vtctldata_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5891,7 +6008,7 @@ func (x *UpdateThrottlerConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateThrottlerConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateThrottlerConfigRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{87} + return file_vtctldata_proto_rawDescGZIP(), []int{89} } func (x *UpdateThrottlerConfigRequest) GetKeyspace() string { @@ -5966,7 +6083,7 @@ type UpdateThrottlerConfigResponse struct { func (x *UpdateThrottlerConfigResponse) Reset() { *x = UpdateThrottlerConfigResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[88] + mi := &file_vtctldata_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5979,7 +6096,7 @@ func (x *UpdateThrottlerConfigResponse) String() string { func (*UpdateThrottlerConfigResponse) ProtoMessage() {} func (x *UpdateThrottlerConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[88] + mi := &file_vtctldata_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5992,7 +6109,7 @@ func (x *UpdateThrottlerConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateThrottlerConfigResponse.ProtoReflect.Descriptor instead. func (*UpdateThrottlerConfigResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{88} + return file_vtctldata_proto_rawDescGZIP(), []int{90} } type GetSrvVSchemaRequest struct { @@ -6006,7 +6123,7 @@ type GetSrvVSchemaRequest struct { func (x *GetSrvVSchemaRequest) Reset() { *x = GetSrvVSchemaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[89] + mi := &file_vtctldata_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6019,7 +6136,7 @@ func (x *GetSrvVSchemaRequest) String() string { func (*GetSrvVSchemaRequest) ProtoMessage() {} func (x *GetSrvVSchemaRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[89] + mi := &file_vtctldata_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6032,7 +6149,7 @@ func (x *GetSrvVSchemaRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvVSchemaRequest.ProtoReflect.Descriptor instead. func (*GetSrvVSchemaRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{89} + return file_vtctldata_proto_rawDescGZIP(), []int{91} } func (x *GetSrvVSchemaRequest) GetCell() string { @@ -6053,7 +6170,7 @@ type GetSrvVSchemaResponse struct { func (x *GetSrvVSchemaResponse) Reset() { *x = GetSrvVSchemaResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[90] + mi := &file_vtctldata_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6066,7 +6183,7 @@ func (x *GetSrvVSchemaResponse) String() string { func (*GetSrvVSchemaResponse) ProtoMessage() {} func (x *GetSrvVSchemaResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[90] + mi := &file_vtctldata_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6079,7 +6196,7 @@ func (x *GetSrvVSchemaResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvVSchemaResponse.ProtoReflect.Descriptor instead. func (*GetSrvVSchemaResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{90} + return file_vtctldata_proto_rawDescGZIP(), []int{92} } func (x *GetSrvVSchemaResponse) GetSrvVSchema() *vschema.SrvVSchema { @@ -6100,7 +6217,7 @@ type GetSrvVSchemasRequest struct { func (x *GetSrvVSchemasRequest) Reset() { *x = GetSrvVSchemasRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[91] + mi := &file_vtctldata_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6113,7 +6230,7 @@ func (x *GetSrvVSchemasRequest) String() string { func (*GetSrvVSchemasRequest) ProtoMessage() {} func (x *GetSrvVSchemasRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[91] + mi := &file_vtctldata_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6126,7 +6243,7 @@ func (x *GetSrvVSchemasRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvVSchemasRequest.ProtoReflect.Descriptor instead. func (*GetSrvVSchemasRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{91} + return file_vtctldata_proto_rawDescGZIP(), []int{93} } func (x *GetSrvVSchemasRequest) GetCells() []string { @@ -6148,7 +6265,7 @@ type GetSrvVSchemasResponse struct { func (x *GetSrvVSchemasResponse) Reset() { *x = GetSrvVSchemasResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[92] + mi := &file_vtctldata_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6161,7 +6278,7 @@ func (x *GetSrvVSchemasResponse) String() string { func (*GetSrvVSchemasResponse) ProtoMessage() {} func (x *GetSrvVSchemasResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[92] + mi := &file_vtctldata_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6174,7 +6291,7 @@ func (x *GetSrvVSchemasResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSrvVSchemasResponse.ProtoReflect.Descriptor instead. func (*GetSrvVSchemasResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{92} + return file_vtctldata_proto_rawDescGZIP(), []int{94} } func (x *GetSrvVSchemasResponse) GetSrvVSchemas() map[string]*vschema.SrvVSchema { @@ -6195,7 +6312,7 @@ type GetTabletRequest struct { func (x *GetTabletRequest) Reset() { *x = GetTabletRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[93] + mi := &file_vtctldata_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6208,7 +6325,7 @@ func (x *GetTabletRequest) String() string { func (*GetTabletRequest) ProtoMessage() {} func (x *GetTabletRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[93] + mi := &file_vtctldata_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6221,7 +6338,7 @@ func (x *GetTabletRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTabletRequest.ProtoReflect.Descriptor instead. func (*GetTabletRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{93} + return file_vtctldata_proto_rawDescGZIP(), []int{95} } func (x *GetTabletRequest) GetTabletAlias() *topodata.TabletAlias { @@ -6242,7 +6359,7 @@ type GetTabletResponse struct { func (x *GetTabletResponse) Reset() { *x = GetTabletResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[94] + mi := &file_vtctldata_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6255,7 +6372,7 @@ func (x *GetTabletResponse) String() string { func (*GetTabletResponse) ProtoMessage() {} func (x *GetTabletResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[94] + mi := &file_vtctldata_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6268,7 +6385,7 @@ func (x *GetTabletResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTabletResponse.ProtoReflect.Descriptor instead. func (*GetTabletResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{94} + return file_vtctldata_proto_rawDescGZIP(), []int{96} } func (x *GetTabletResponse) GetTablet() *topodata.Tablet { @@ -6310,7 +6427,7 @@ type GetTabletsRequest struct { func (x *GetTabletsRequest) Reset() { *x = GetTabletsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[95] + mi := &file_vtctldata_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6323,7 +6440,7 @@ func (x *GetTabletsRequest) String() string { func (*GetTabletsRequest) ProtoMessage() {} func (x *GetTabletsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[95] + mi := &file_vtctldata_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6336,7 +6453,7 @@ func (x *GetTabletsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTabletsRequest.ProtoReflect.Descriptor instead. func (*GetTabletsRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{95} + return file_vtctldata_proto_rawDescGZIP(), []int{97} } func (x *GetTabletsRequest) GetKeyspace() string { @@ -6392,7 +6509,7 @@ type GetTabletsResponse struct { func (x *GetTabletsResponse) Reset() { *x = GetTabletsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[96] + mi := &file_vtctldata_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6405,7 +6522,7 @@ func (x *GetTabletsResponse) String() string { func (*GetTabletsResponse) ProtoMessage() {} func (x *GetTabletsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[96] + mi := &file_vtctldata_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6418,7 +6535,7 @@ func (x *GetTabletsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTabletsResponse.ProtoReflect.Descriptor instead. func (*GetTabletsResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{96} + return file_vtctldata_proto_rawDescGZIP(), []int{98} } func (x *GetTabletsResponse) GetTablets() []*topodata.Tablet { @@ -6439,7 +6556,7 @@ type GetTopologyPathRequest struct { func (x *GetTopologyPathRequest) Reset() { *x = GetTopologyPathRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[97] + mi := &file_vtctldata_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6452,7 +6569,7 @@ func (x *GetTopologyPathRequest) String() string { func (*GetTopologyPathRequest) ProtoMessage() {} func (x *GetTopologyPathRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[97] + mi := &file_vtctldata_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6465,7 +6582,7 @@ func (x *GetTopologyPathRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTopologyPathRequest.ProtoReflect.Descriptor instead. func (*GetTopologyPathRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{97} + return file_vtctldata_proto_rawDescGZIP(), []int{99} } func (x *GetTopologyPathRequest) GetPath() string { @@ -6486,7 +6603,7 @@ type GetTopologyPathResponse struct { func (x *GetTopologyPathResponse) Reset() { *x = GetTopologyPathResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[98] + mi := &file_vtctldata_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6499,7 +6616,7 @@ func (x *GetTopologyPathResponse) String() string { func (*GetTopologyPathResponse) ProtoMessage() {} func (x *GetTopologyPathResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[98] + mi := &file_vtctldata_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6512,7 +6629,7 @@ func (x *GetTopologyPathResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTopologyPathResponse.ProtoReflect.Descriptor instead. func (*GetTopologyPathResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{98} + return file_vtctldata_proto_rawDescGZIP(), []int{100} } func (x *GetTopologyPathResponse) GetCell() *TopologyCell { @@ -6538,7 +6655,7 @@ type TopologyCell struct { func (x *TopologyCell) Reset() { *x = TopologyCell{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[99] + mi := &file_vtctldata_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6551,7 +6668,7 @@ func (x *TopologyCell) String() string { func (*TopologyCell) ProtoMessage() {} func (x *TopologyCell) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[99] + mi := &file_vtctldata_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6564,7 +6681,7 @@ func (x *TopologyCell) ProtoReflect() protoreflect.Message { // Deprecated: Use TopologyCell.ProtoReflect.Descriptor instead. func (*TopologyCell) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{99} + return file_vtctldata_proto_rawDescGZIP(), []int{101} } func (x *TopologyCell) GetName() string { @@ -6606,7 +6723,7 @@ type GetVSchemaRequest struct { func (x *GetVSchemaRequest) Reset() { *x = GetVSchemaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[100] + mi := &file_vtctldata_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6619,7 +6736,7 @@ func (x *GetVSchemaRequest) String() string { func (*GetVSchemaRequest) ProtoMessage() {} func (x *GetVSchemaRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[100] + mi := &file_vtctldata_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6632,7 +6749,7 @@ func (x *GetVSchemaRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVSchemaRequest.ProtoReflect.Descriptor instead. func (*GetVSchemaRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{100} + return file_vtctldata_proto_rawDescGZIP(), []int{102} } func (x *GetVSchemaRequest) GetKeyspace() string { @@ -6653,7 +6770,7 @@ type GetVersionRequest struct { func (x *GetVersionRequest) Reset() { *x = GetVersionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[101] + mi := &file_vtctldata_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6666,7 +6783,7 @@ func (x *GetVersionRequest) String() string { func (*GetVersionRequest) ProtoMessage() {} func (x *GetVersionRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[101] + mi := &file_vtctldata_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6679,7 +6796,7 @@ func (x *GetVersionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVersionRequest.ProtoReflect.Descriptor instead. func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{101} + return file_vtctldata_proto_rawDescGZIP(), []int{103} } func (x *GetVersionRequest) GetTabletAlias() *topodata.TabletAlias { @@ -6700,7 +6817,7 @@ type GetVersionResponse struct { func (x *GetVersionResponse) Reset() { *x = GetVersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[102] + mi := &file_vtctldata_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6713,7 +6830,7 @@ func (x *GetVersionResponse) String() string { func (*GetVersionResponse) ProtoMessage() {} func (x *GetVersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[102] + mi := &file_vtctldata_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6726,7 +6843,7 @@ func (x *GetVersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVersionResponse.ProtoReflect.Descriptor instead. func (*GetVersionResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{102} + return file_vtctldata_proto_rawDescGZIP(), []int{104} } func (x *GetVersionResponse) GetVersion() string { @@ -6747,7 +6864,7 @@ type GetVSchemaResponse struct { func (x *GetVSchemaResponse) Reset() { *x = GetVSchemaResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[103] + mi := &file_vtctldata_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6760,7 +6877,7 @@ func (x *GetVSchemaResponse) String() string { func (*GetVSchemaResponse) ProtoMessage() {} func (x *GetVSchemaResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[103] + mi := &file_vtctldata_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6773,7 +6890,7 @@ func (x *GetVSchemaResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVSchemaResponse.ProtoReflect.Descriptor instead. func (*GetVSchemaResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{103} + return file_vtctldata_proto_rawDescGZIP(), []int{105} } func (x *GetVSchemaResponse) GetVSchema() *vschema.Keyspace { @@ -6792,14 +6909,15 @@ type GetWorkflowsRequest struct { ActiveOnly bool `protobuf:"varint,2,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` NameOnly bool `protobuf:"varint,3,opt,name=name_only,json=nameOnly,proto3" json:"name_only,omitempty"` // If you only want a specific workflow then set this field. - Workflow string `protobuf:"bytes,4,opt,name=workflow,proto3" json:"workflow,omitempty"` - IncludeLogs bool `protobuf:"varint,5,opt,name=include_logs,json=includeLogs,proto3" json:"include_logs,omitempty"` + Workflow string `protobuf:"bytes,4,opt,name=workflow,proto3" json:"workflow,omitempty"` + IncludeLogs bool `protobuf:"varint,5,opt,name=include_logs,json=includeLogs,proto3" json:"include_logs,omitempty"` + Shards []string `protobuf:"bytes,6,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *GetWorkflowsRequest) Reset() { *x = GetWorkflowsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[104] + mi := &file_vtctldata_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6812,7 +6930,7 @@ func (x *GetWorkflowsRequest) String() string { func (*GetWorkflowsRequest) ProtoMessage() {} func (x *GetWorkflowsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[104] + mi := &file_vtctldata_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6825,7 +6943,7 @@ func (x *GetWorkflowsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkflowsRequest.ProtoReflect.Descriptor instead. func (*GetWorkflowsRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{104} + return file_vtctldata_proto_rawDescGZIP(), []int{106} } func (x *GetWorkflowsRequest) GetKeyspace() string { @@ -6863,6 +6981,13 @@ func (x *GetWorkflowsRequest) GetIncludeLogs() bool { return false } +func (x *GetWorkflowsRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type GetWorkflowsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6874,7 +6999,7 @@ type GetWorkflowsResponse struct { func (x *GetWorkflowsResponse) Reset() { *x = GetWorkflowsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[105] + mi := &file_vtctldata_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6887,7 +7012,7 @@ func (x *GetWorkflowsResponse) String() string { func (*GetWorkflowsResponse) ProtoMessage() {} func (x *GetWorkflowsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[105] + mi := &file_vtctldata_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6900,7 +7025,7 @@ func (x *GetWorkflowsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkflowsResponse.ProtoReflect.Descriptor instead. func (*GetWorkflowsResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{105} + return file_vtctldata_proto_rawDescGZIP(), []int{107} } func (x *GetWorkflowsResponse) GetWorkflows() []*Workflow { @@ -6925,7 +7050,7 @@ type InitShardPrimaryRequest struct { func (x *InitShardPrimaryRequest) Reset() { *x = InitShardPrimaryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[106] + mi := &file_vtctldata_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6938,7 +7063,7 @@ func (x *InitShardPrimaryRequest) String() string { func (*InitShardPrimaryRequest) ProtoMessage() {} func (x *InitShardPrimaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[106] + mi := &file_vtctldata_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6951,7 +7076,7 @@ func (x *InitShardPrimaryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use InitShardPrimaryRequest.ProtoReflect.Descriptor instead. func (*InitShardPrimaryRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{106} + return file_vtctldata_proto_rawDescGZIP(), []int{108} } func (x *InitShardPrimaryRequest) GetKeyspace() string { @@ -7000,7 +7125,7 @@ type InitShardPrimaryResponse struct { func (x *InitShardPrimaryResponse) Reset() { *x = InitShardPrimaryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[107] + mi := &file_vtctldata_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7013,7 +7138,7 @@ func (x *InitShardPrimaryResponse) String() string { func (*InitShardPrimaryResponse) ProtoMessage() {} func (x *InitShardPrimaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[107] + mi := &file_vtctldata_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7026,7 +7151,7 @@ func (x *InitShardPrimaryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InitShardPrimaryResponse.ProtoReflect.Descriptor instead. func (*InitShardPrimaryResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{107} + return file_vtctldata_proto_rawDescGZIP(), []int{109} } func (x *InitShardPrimaryResponse) GetEvents() []*logutil.Event { @@ -7048,7 +7173,7 @@ type LaunchSchemaMigrationRequest struct { func (x *LaunchSchemaMigrationRequest) Reset() { *x = LaunchSchemaMigrationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[108] + mi := &file_vtctldata_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7061,7 +7186,7 @@ func (x *LaunchSchemaMigrationRequest) String() string { func (*LaunchSchemaMigrationRequest) ProtoMessage() {} func (x *LaunchSchemaMigrationRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[108] + mi := &file_vtctldata_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7074,7 +7199,7 @@ func (x *LaunchSchemaMigrationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LaunchSchemaMigrationRequest.ProtoReflect.Descriptor instead. func (*LaunchSchemaMigrationRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{108} + return file_vtctldata_proto_rawDescGZIP(), []int{110} } func (x *LaunchSchemaMigrationRequest) GetKeyspace() string { @@ -7102,7 +7227,7 @@ type LaunchSchemaMigrationResponse struct { func (x *LaunchSchemaMigrationResponse) Reset() { *x = LaunchSchemaMigrationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[109] + mi := &file_vtctldata_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7115,7 +7240,7 @@ func (x *LaunchSchemaMigrationResponse) String() string { func (*LaunchSchemaMigrationResponse) ProtoMessage() {} func (x *LaunchSchemaMigrationResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[109] + mi := &file_vtctldata_proto_msgTypes[111] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7128,7 +7253,7 @@ func (x *LaunchSchemaMigrationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LaunchSchemaMigrationResponse.ProtoReflect.Descriptor instead. func (*LaunchSchemaMigrationResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{109} + return file_vtctldata_proto_rawDescGZIP(), []int{111} } func (x *LaunchSchemaMigrationResponse) GetRowsAffectedByShard() map[string]uint64 { @@ -7155,7 +7280,7 @@ type LookupVindexCreateRequest struct { func (x *LookupVindexCreateRequest) Reset() { *x = LookupVindexCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[110] + mi := &file_vtctldata_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7168,7 +7293,7 @@ func (x *LookupVindexCreateRequest) String() string { func (*LookupVindexCreateRequest) ProtoMessage() {} func (x *LookupVindexCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[110] + mi := &file_vtctldata_proto_msgTypes[112] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7181,7 +7306,7 @@ func (x *LookupVindexCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LookupVindexCreateRequest.ProtoReflect.Descriptor instead. func (*LookupVindexCreateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{110} + return file_vtctldata_proto_rawDescGZIP(), []int{112} } func (x *LookupVindexCreateRequest) GetKeyspace() string { @@ -7242,7 +7367,7 @@ type LookupVindexCreateResponse struct { func (x *LookupVindexCreateResponse) Reset() { *x = LookupVindexCreateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[111] + mi := &file_vtctldata_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7255,7 +7380,7 @@ func (x *LookupVindexCreateResponse) String() string { func (*LookupVindexCreateResponse) ProtoMessage() {} func (x *LookupVindexCreateResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[111] + mi := &file_vtctldata_proto_msgTypes[113] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7268,7 +7393,7 @@ func (x *LookupVindexCreateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LookupVindexCreateResponse.ProtoReflect.Descriptor instead. func (*LookupVindexCreateResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{111} + return file_vtctldata_proto_rawDescGZIP(), []int{113} } type LookupVindexExternalizeRequest struct { @@ -7287,7 +7412,7 @@ type LookupVindexExternalizeRequest struct { func (x *LookupVindexExternalizeRequest) Reset() { *x = LookupVindexExternalizeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[112] + mi := &file_vtctldata_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7300,7 +7425,7 @@ func (x *LookupVindexExternalizeRequest) String() string { func (*LookupVindexExternalizeRequest) ProtoMessage() {} func (x *LookupVindexExternalizeRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[112] + mi := &file_vtctldata_proto_msgTypes[114] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7313,7 +7438,7 @@ func (x *LookupVindexExternalizeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LookupVindexExternalizeRequest.ProtoReflect.Descriptor instead. func (*LookupVindexExternalizeRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{112} + return file_vtctldata_proto_rawDescGZIP(), []int{114} } func (x *LookupVindexExternalizeRequest) GetKeyspace() string { @@ -7349,7 +7474,7 @@ type LookupVindexExternalizeResponse struct { func (x *LookupVindexExternalizeResponse) Reset() { *x = LookupVindexExternalizeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[113] + mi := &file_vtctldata_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7362,7 +7487,7 @@ func (x *LookupVindexExternalizeResponse) String() string { func (*LookupVindexExternalizeResponse) ProtoMessage() {} func (x *LookupVindexExternalizeResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[113] + mi := &file_vtctldata_proto_msgTypes[115] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7375,7 +7500,7 @@ func (x *LookupVindexExternalizeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LookupVindexExternalizeResponse.ProtoReflect.Descriptor instead. func (*LookupVindexExternalizeResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{113} + return file_vtctldata_proto_rawDescGZIP(), []int{115} } func (x *LookupVindexExternalizeResponse) GetWorkflowDeleted() bool { @@ -7396,7 +7521,7 @@ type MaterializeCreateRequest struct { func (x *MaterializeCreateRequest) Reset() { *x = MaterializeCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[114] + mi := &file_vtctldata_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7409,7 +7534,7 @@ func (x *MaterializeCreateRequest) String() string { func (*MaterializeCreateRequest) ProtoMessage() {} func (x *MaterializeCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[114] + mi := &file_vtctldata_proto_msgTypes[116] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7422,7 +7547,7 @@ func (x *MaterializeCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MaterializeCreateRequest.ProtoReflect.Descriptor instead. func (*MaterializeCreateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{114} + return file_vtctldata_proto_rawDescGZIP(), []int{116} } func (x *MaterializeCreateRequest) GetSettings() *MaterializeSettings { @@ -7441,7 +7566,7 @@ type MaterializeCreateResponse struct { func (x *MaterializeCreateResponse) Reset() { *x = MaterializeCreateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[115] + mi := &file_vtctldata_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7454,7 +7579,7 @@ func (x *MaterializeCreateResponse) String() string { func (*MaterializeCreateResponse) ProtoMessage() {} func (x *MaterializeCreateResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[115] + mi := &file_vtctldata_proto_msgTypes[117] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7467,7 +7592,7 @@ func (x *MaterializeCreateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MaterializeCreateResponse.ProtoReflect.Descriptor instead. func (*MaterializeCreateResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{115} + return file_vtctldata_proto_rawDescGZIP(), []int{117} } type MigrateCreateRequest struct { @@ -7506,7 +7631,7 @@ type MigrateCreateRequest struct { func (x *MigrateCreateRequest) Reset() { *x = MigrateCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[116] + mi := &file_vtctldata_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7519,7 +7644,7 @@ func (x *MigrateCreateRequest) String() string { func (*MigrateCreateRequest) ProtoMessage() {} func (x *MigrateCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[116] + mi := &file_vtctldata_proto_msgTypes[118] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7532,7 +7657,7 @@ func (x *MigrateCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MigrateCreateRequest.ProtoReflect.Descriptor instead. func (*MigrateCreateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{116} + return file_vtctldata_proto_rawDescGZIP(), []int{118} } func (x *MigrateCreateRequest) GetWorkflow() string { @@ -7670,7 +7795,7 @@ type MigrateCompleteRequest struct { func (x *MigrateCompleteRequest) Reset() { *x = MigrateCompleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[117] + mi := &file_vtctldata_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7683,7 +7808,7 @@ func (x *MigrateCompleteRequest) String() string { func (*MigrateCompleteRequest) ProtoMessage() {} func (x *MigrateCompleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[117] + mi := &file_vtctldata_proto_msgTypes[119] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7696,7 +7821,7 @@ func (x *MigrateCompleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MigrateCompleteRequest.ProtoReflect.Descriptor instead. func (*MigrateCompleteRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{117} + return file_vtctldata_proto_rawDescGZIP(), []int{119} } func (x *MigrateCompleteRequest) GetWorkflow() string { @@ -7753,7 +7878,7 @@ type MigrateCompleteResponse struct { func (x *MigrateCompleteResponse) Reset() { *x = MigrateCompleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[118] + mi := &file_vtctldata_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7766,7 +7891,7 @@ func (x *MigrateCompleteResponse) String() string { func (*MigrateCompleteResponse) ProtoMessage() {} func (x *MigrateCompleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[118] + mi := &file_vtctldata_proto_msgTypes[120] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7779,7 +7904,7 @@ func (x *MigrateCompleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MigrateCompleteResponse.ProtoReflect.Descriptor instead. func (*MigrateCompleteResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{118} + return file_vtctldata_proto_rawDescGZIP(), []int{120} } func (x *MigrateCompleteResponse) GetSummary() string { @@ -7810,7 +7935,7 @@ type MountRegisterRequest struct { func (x *MountRegisterRequest) Reset() { *x = MountRegisterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[119] + mi := &file_vtctldata_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7823,7 +7948,7 @@ func (x *MountRegisterRequest) String() string { func (*MountRegisterRequest) ProtoMessage() {} func (x *MountRegisterRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[119] + mi := &file_vtctldata_proto_msgTypes[121] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7836,7 +7961,7 @@ func (x *MountRegisterRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MountRegisterRequest.ProtoReflect.Descriptor instead. func (*MountRegisterRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{119} + return file_vtctldata_proto_rawDescGZIP(), []int{121} } func (x *MountRegisterRequest) GetTopoType() string { @@ -7876,7 +8001,7 @@ type MountRegisterResponse struct { func (x *MountRegisterResponse) Reset() { *x = MountRegisterResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[120] + mi := &file_vtctldata_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7889,7 +8014,7 @@ func (x *MountRegisterResponse) String() string { func (*MountRegisterResponse) ProtoMessage() {} func (x *MountRegisterResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[120] + mi := &file_vtctldata_proto_msgTypes[122] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7902,7 +8027,7 @@ func (x *MountRegisterResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MountRegisterResponse.ProtoReflect.Descriptor instead. func (*MountRegisterResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{120} + return file_vtctldata_proto_rawDescGZIP(), []int{122} } type MountUnregisterRequest struct { @@ -7916,7 +8041,7 @@ type MountUnregisterRequest struct { func (x *MountUnregisterRequest) Reset() { *x = MountUnregisterRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[121] + mi := &file_vtctldata_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7929,7 +8054,7 @@ func (x *MountUnregisterRequest) String() string { func (*MountUnregisterRequest) ProtoMessage() {} func (x *MountUnregisterRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[121] + mi := &file_vtctldata_proto_msgTypes[123] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7942,7 +8067,7 @@ func (x *MountUnregisterRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MountUnregisterRequest.ProtoReflect.Descriptor instead. func (*MountUnregisterRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{121} + return file_vtctldata_proto_rawDescGZIP(), []int{123} } func (x *MountUnregisterRequest) GetName() string { @@ -7961,7 +8086,7 @@ type MountUnregisterResponse struct { func (x *MountUnregisterResponse) Reset() { *x = MountUnregisterResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[122] + mi := &file_vtctldata_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7974,7 +8099,7 @@ func (x *MountUnregisterResponse) String() string { func (*MountUnregisterResponse) ProtoMessage() {} func (x *MountUnregisterResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[122] + mi := &file_vtctldata_proto_msgTypes[124] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7987,7 +8112,7 @@ func (x *MountUnregisterResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MountUnregisterResponse.ProtoReflect.Descriptor instead. func (*MountUnregisterResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{122} + return file_vtctldata_proto_rawDescGZIP(), []int{124} } type MountShowRequest struct { @@ -8001,7 +8126,7 @@ type MountShowRequest struct { func (x *MountShowRequest) Reset() { *x = MountShowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[123] + mi := &file_vtctldata_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8014,7 +8139,7 @@ func (x *MountShowRequest) String() string { func (*MountShowRequest) ProtoMessage() {} func (x *MountShowRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[123] + mi := &file_vtctldata_proto_msgTypes[125] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8027,7 +8152,7 @@ func (x *MountShowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MountShowRequest.ProtoReflect.Descriptor instead. func (*MountShowRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{123} + return file_vtctldata_proto_rawDescGZIP(), []int{125} } func (x *MountShowRequest) GetName() string { @@ -8051,7 +8176,7 @@ type MountShowResponse struct { func (x *MountShowResponse) Reset() { *x = MountShowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[124] + mi := &file_vtctldata_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8064,7 +8189,7 @@ func (x *MountShowResponse) String() string { func (*MountShowResponse) ProtoMessage() {} func (x *MountShowResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[124] + mi := &file_vtctldata_proto_msgTypes[126] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8077,7 +8202,7 @@ func (x *MountShowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MountShowResponse.ProtoReflect.Descriptor instead. func (*MountShowResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{124} + return file_vtctldata_proto_rawDescGZIP(), []int{126} } func (x *MountShowResponse) GetTopoType() string { @@ -8117,7 +8242,7 @@ type MountListRequest struct { func (x *MountListRequest) Reset() { *x = MountListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[125] + mi := &file_vtctldata_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8130,7 +8255,7 @@ func (x *MountListRequest) String() string { func (*MountListRequest) ProtoMessage() {} func (x *MountListRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[125] + mi := &file_vtctldata_proto_msgTypes[127] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8143,7 +8268,7 @@ func (x *MountListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MountListRequest.ProtoReflect.Descriptor instead. func (*MountListRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{125} + return file_vtctldata_proto_rawDescGZIP(), []int{127} } type MountListResponse struct { @@ -8157,7 +8282,7 @@ type MountListResponse struct { func (x *MountListResponse) Reset() { *x = MountListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[126] + mi := &file_vtctldata_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8170,7 +8295,7 @@ func (x *MountListResponse) String() string { func (*MountListResponse) ProtoMessage() {} func (x *MountListResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[126] + mi := &file_vtctldata_proto_msgTypes[128] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8183,7 +8308,7 @@ func (x *MountListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MountListResponse.ProtoReflect.Descriptor instead. func (*MountListResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{126} + return file_vtctldata_proto_rawDescGZIP(), []int{128} } func (x *MountListResponse) GetNames() []string { @@ -8233,7 +8358,7 @@ type MoveTablesCreateRequest struct { func (x *MoveTablesCreateRequest) Reset() { *x = MoveTablesCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[127] + mi := &file_vtctldata_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8246,7 +8371,7 @@ func (x *MoveTablesCreateRequest) String() string { func (*MoveTablesCreateRequest) ProtoMessage() {} func (x *MoveTablesCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[127] + mi := &file_vtctldata_proto_msgTypes[129] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8259,7 +8384,7 @@ func (x *MoveTablesCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveTablesCreateRequest.ProtoReflect.Descriptor instead. func (*MoveTablesCreateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{127} + return file_vtctldata_proto_rawDescGZIP(), []int{129} } func (x *MoveTablesCreateRequest) GetWorkflow() string { @@ -8407,7 +8532,7 @@ type MoveTablesCreateResponse struct { func (x *MoveTablesCreateResponse) Reset() { *x = MoveTablesCreateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[128] + mi := &file_vtctldata_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8420,7 +8545,7 @@ func (x *MoveTablesCreateResponse) String() string { func (*MoveTablesCreateResponse) ProtoMessage() {} func (x *MoveTablesCreateResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[128] + mi := &file_vtctldata_proto_msgTypes[130] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8433,7 +8558,7 @@ func (x *MoveTablesCreateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveTablesCreateResponse.ProtoReflect.Descriptor instead. func (*MoveTablesCreateResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{128} + return file_vtctldata_proto_rawDescGZIP(), []int{130} } func (x *MoveTablesCreateResponse) GetSummary() string { @@ -8455,18 +8580,19 @@ type MoveTablesCompleteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Workflow string `protobuf:"bytes,1,opt,name=workflow,proto3" json:"workflow,omitempty"` - TargetKeyspace string `protobuf:"bytes,3,opt,name=target_keyspace,json=targetKeyspace,proto3" json:"target_keyspace,omitempty"` - KeepData bool `protobuf:"varint,4,opt,name=keep_data,json=keepData,proto3" json:"keep_data,omitempty"` - KeepRoutingRules bool `protobuf:"varint,5,opt,name=keep_routing_rules,json=keepRoutingRules,proto3" json:"keep_routing_rules,omitempty"` - RenameTables bool `protobuf:"varint,6,opt,name=rename_tables,json=renameTables,proto3" json:"rename_tables,omitempty"` - DryRun bool `protobuf:"varint,7,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` + Workflow string `protobuf:"bytes,1,opt,name=workflow,proto3" json:"workflow,omitempty"` + TargetKeyspace string `protobuf:"bytes,3,opt,name=target_keyspace,json=targetKeyspace,proto3" json:"target_keyspace,omitempty"` + KeepData bool `protobuf:"varint,4,opt,name=keep_data,json=keepData,proto3" json:"keep_data,omitempty"` + KeepRoutingRules bool `protobuf:"varint,5,opt,name=keep_routing_rules,json=keepRoutingRules,proto3" json:"keep_routing_rules,omitempty"` + RenameTables bool `protobuf:"varint,6,opt,name=rename_tables,json=renameTables,proto3" json:"rename_tables,omitempty"` + DryRun bool `protobuf:"varint,7,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` + Shards []string `protobuf:"bytes,8,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *MoveTablesCompleteRequest) Reset() { *x = MoveTablesCompleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[129] + mi := &file_vtctldata_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8479,7 +8605,7 @@ func (x *MoveTablesCompleteRequest) String() string { func (*MoveTablesCompleteRequest) ProtoMessage() {} func (x *MoveTablesCompleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[129] + mi := &file_vtctldata_proto_msgTypes[131] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8492,7 +8618,7 @@ func (x *MoveTablesCompleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveTablesCompleteRequest.ProtoReflect.Descriptor instead. func (*MoveTablesCompleteRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{129} + return file_vtctldata_proto_rawDescGZIP(), []int{131} } func (x *MoveTablesCompleteRequest) GetWorkflow() string { @@ -8537,6 +8663,13 @@ func (x *MoveTablesCompleteRequest) GetDryRun() bool { return false } +func (x *MoveTablesCompleteRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type MoveTablesCompleteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -8549,7 +8682,7 @@ type MoveTablesCompleteResponse struct { func (x *MoveTablesCompleteResponse) Reset() { *x = MoveTablesCompleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[130] + mi := &file_vtctldata_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8562,7 +8695,7 @@ func (x *MoveTablesCompleteResponse) String() string { func (*MoveTablesCompleteResponse) ProtoMessage() {} func (x *MoveTablesCompleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[130] + mi := &file_vtctldata_proto_msgTypes[132] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8575,7 +8708,7 @@ func (x *MoveTablesCompleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveTablesCompleteResponse.ProtoReflect.Descriptor instead. func (*MoveTablesCompleteResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{130} + return file_vtctldata_proto_rawDescGZIP(), []int{132} } func (x *MoveTablesCompleteResponse) GetSummary() string { @@ -8603,7 +8736,7 @@ type PingTabletRequest struct { func (x *PingTabletRequest) Reset() { *x = PingTabletRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[131] + mi := &file_vtctldata_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8616,7 +8749,7 @@ func (x *PingTabletRequest) String() string { func (*PingTabletRequest) ProtoMessage() {} func (x *PingTabletRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[131] + mi := &file_vtctldata_proto_msgTypes[133] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8629,7 +8762,7 @@ func (x *PingTabletRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PingTabletRequest.ProtoReflect.Descriptor instead. func (*PingTabletRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{131} + return file_vtctldata_proto_rawDescGZIP(), []int{133} } func (x *PingTabletRequest) GetTabletAlias() *topodata.TabletAlias { @@ -8648,7 +8781,7 @@ type PingTabletResponse struct { func (x *PingTabletResponse) Reset() { *x = PingTabletResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[132] + mi := &file_vtctldata_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8661,7 +8794,7 @@ func (x *PingTabletResponse) String() string { func (*PingTabletResponse) ProtoMessage() {} func (x *PingTabletResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[132] + mi := &file_vtctldata_proto_msgTypes[134] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8674,7 +8807,7 @@ func (x *PingTabletResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PingTabletResponse.ProtoReflect.Descriptor instead. func (*PingTabletResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{132} + return file_vtctldata_proto_rawDescGZIP(), []int{134} } type PlannedReparentShardRequest struct { @@ -8704,12 +8837,16 @@ type PlannedReparentShardRequest struct { // WaitReplicasTimeout time to catch up before the reparent, and an additional // WaitReplicasTimeout time to catch up after the reparent. WaitReplicasTimeout *vttime.Duration `protobuf:"bytes,5,opt,name=wait_replicas_timeout,json=waitReplicasTimeout,proto3" json:"wait_replicas_timeout,omitempty"` + // TolerableReplicationLag is the amount of replication lag that is considered + // acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary. + // A value of 0 indicates that Vitess shouldn't consider the replication lag at all. + TolerableReplicationLag *vttime.Duration `protobuf:"bytes,6,opt,name=tolerable_replication_lag,json=tolerableReplicationLag,proto3" json:"tolerable_replication_lag,omitempty"` } func (x *PlannedReparentShardRequest) Reset() { *x = PlannedReparentShardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[133] + mi := &file_vtctldata_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8722,7 +8859,7 @@ func (x *PlannedReparentShardRequest) String() string { func (*PlannedReparentShardRequest) ProtoMessage() {} func (x *PlannedReparentShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[133] + mi := &file_vtctldata_proto_msgTypes[135] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8735,7 +8872,7 @@ func (x *PlannedReparentShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PlannedReparentShardRequest.ProtoReflect.Descriptor instead. func (*PlannedReparentShardRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{133} + return file_vtctldata_proto_rawDescGZIP(), []int{135} } func (x *PlannedReparentShardRequest) GetKeyspace() string { @@ -8773,6 +8910,13 @@ func (x *PlannedReparentShardRequest) GetWaitReplicasTimeout() *vttime.Duration return nil } +func (x *PlannedReparentShardRequest) GetTolerableReplicationLag() *vttime.Duration { + if x != nil { + return x.TolerableReplicationLag + } + return nil +} + type PlannedReparentShardResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -8793,7 +8937,7 @@ type PlannedReparentShardResponse struct { func (x *PlannedReparentShardResponse) Reset() { *x = PlannedReparentShardResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[134] + mi := &file_vtctldata_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8806,7 +8950,7 @@ func (x *PlannedReparentShardResponse) String() string { func (*PlannedReparentShardResponse) ProtoMessage() {} func (x *PlannedReparentShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[134] + mi := &file_vtctldata_proto_msgTypes[136] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8819,7 +8963,7 @@ func (x *PlannedReparentShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PlannedReparentShardResponse.ProtoReflect.Descriptor instead. func (*PlannedReparentShardResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{134} + return file_vtctldata_proto_rawDescGZIP(), []int{136} } func (x *PlannedReparentShardResponse) GetKeyspace() string { @@ -8865,7 +9009,7 @@ type RebuildKeyspaceGraphRequest struct { func (x *RebuildKeyspaceGraphRequest) Reset() { *x = RebuildKeyspaceGraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[135] + mi := &file_vtctldata_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8878,7 +9022,7 @@ func (x *RebuildKeyspaceGraphRequest) String() string { func (*RebuildKeyspaceGraphRequest) ProtoMessage() {} func (x *RebuildKeyspaceGraphRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[135] + mi := &file_vtctldata_proto_msgTypes[137] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8891,7 +9035,7 @@ func (x *RebuildKeyspaceGraphRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildKeyspaceGraphRequest.ProtoReflect.Descriptor instead. func (*RebuildKeyspaceGraphRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{135} + return file_vtctldata_proto_rawDescGZIP(), []int{137} } func (x *RebuildKeyspaceGraphRequest) GetKeyspace() string { @@ -8924,7 +9068,7 @@ type RebuildKeyspaceGraphResponse struct { func (x *RebuildKeyspaceGraphResponse) Reset() { *x = RebuildKeyspaceGraphResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[136] + mi := &file_vtctldata_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8937,7 +9081,7 @@ func (x *RebuildKeyspaceGraphResponse) String() string { func (*RebuildKeyspaceGraphResponse) ProtoMessage() {} func (x *RebuildKeyspaceGraphResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[136] + mi := &file_vtctldata_proto_msgTypes[138] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8950,7 +9094,7 @@ func (x *RebuildKeyspaceGraphResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildKeyspaceGraphResponse.ProtoReflect.Descriptor instead. func (*RebuildKeyspaceGraphResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{136} + return file_vtctldata_proto_rawDescGZIP(), []int{138} } type RebuildVSchemaGraphRequest struct { @@ -8966,7 +9110,7 @@ type RebuildVSchemaGraphRequest struct { func (x *RebuildVSchemaGraphRequest) Reset() { *x = RebuildVSchemaGraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[137] + mi := &file_vtctldata_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8979,7 +9123,7 @@ func (x *RebuildVSchemaGraphRequest) String() string { func (*RebuildVSchemaGraphRequest) ProtoMessage() {} func (x *RebuildVSchemaGraphRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[137] + mi := &file_vtctldata_proto_msgTypes[139] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8992,7 +9136,7 @@ func (x *RebuildVSchemaGraphRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildVSchemaGraphRequest.ProtoReflect.Descriptor instead. func (*RebuildVSchemaGraphRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{137} + return file_vtctldata_proto_rawDescGZIP(), []int{139} } func (x *RebuildVSchemaGraphRequest) GetCells() []string { @@ -9011,7 +9155,7 @@ type RebuildVSchemaGraphResponse struct { func (x *RebuildVSchemaGraphResponse) Reset() { *x = RebuildVSchemaGraphResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[138] + mi := &file_vtctldata_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9024,7 +9168,7 @@ func (x *RebuildVSchemaGraphResponse) String() string { func (*RebuildVSchemaGraphResponse) ProtoMessage() {} func (x *RebuildVSchemaGraphResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[138] + mi := &file_vtctldata_proto_msgTypes[140] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9037,7 +9181,7 @@ func (x *RebuildVSchemaGraphResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RebuildVSchemaGraphResponse.ProtoReflect.Descriptor instead. func (*RebuildVSchemaGraphResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{138} + return file_vtctldata_proto_rawDescGZIP(), []int{140} } type RefreshStateRequest struct { @@ -9051,7 +9195,7 @@ type RefreshStateRequest struct { func (x *RefreshStateRequest) Reset() { *x = RefreshStateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[139] + mi := &file_vtctldata_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9064,7 +9208,7 @@ func (x *RefreshStateRequest) String() string { func (*RefreshStateRequest) ProtoMessage() {} func (x *RefreshStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[139] + mi := &file_vtctldata_proto_msgTypes[141] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9077,7 +9221,7 @@ func (x *RefreshStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshStateRequest.ProtoReflect.Descriptor instead. func (*RefreshStateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{139} + return file_vtctldata_proto_rawDescGZIP(), []int{141} } func (x *RefreshStateRequest) GetTabletAlias() *topodata.TabletAlias { @@ -9096,7 +9240,7 @@ type RefreshStateResponse struct { func (x *RefreshStateResponse) Reset() { *x = RefreshStateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[140] + mi := &file_vtctldata_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9109,7 +9253,7 @@ func (x *RefreshStateResponse) String() string { func (*RefreshStateResponse) ProtoMessage() {} func (x *RefreshStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[140] + mi := &file_vtctldata_proto_msgTypes[142] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9122,7 +9266,7 @@ func (x *RefreshStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshStateResponse.ProtoReflect.Descriptor instead. func (*RefreshStateResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{140} + return file_vtctldata_proto_rawDescGZIP(), []int{142} } type RefreshStateByShardRequest struct { @@ -9138,7 +9282,7 @@ type RefreshStateByShardRequest struct { func (x *RefreshStateByShardRequest) Reset() { *x = RefreshStateByShardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[141] + mi := &file_vtctldata_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9151,7 +9295,7 @@ func (x *RefreshStateByShardRequest) String() string { func (*RefreshStateByShardRequest) ProtoMessage() {} func (x *RefreshStateByShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[141] + mi := &file_vtctldata_proto_msgTypes[143] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9164,7 +9308,7 @@ func (x *RefreshStateByShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshStateByShardRequest.ProtoReflect.Descriptor instead. func (*RefreshStateByShardRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{141} + return file_vtctldata_proto_rawDescGZIP(), []int{143} } func (x *RefreshStateByShardRequest) GetKeyspace() string { @@ -9201,7 +9345,7 @@ type RefreshStateByShardResponse struct { func (x *RefreshStateByShardResponse) Reset() { *x = RefreshStateByShardResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[142] + mi := &file_vtctldata_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9214,7 +9358,7 @@ func (x *RefreshStateByShardResponse) String() string { func (*RefreshStateByShardResponse) ProtoMessage() {} func (x *RefreshStateByShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[142] + mi := &file_vtctldata_proto_msgTypes[144] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9227,7 +9371,7 @@ func (x *RefreshStateByShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshStateByShardResponse.ProtoReflect.Descriptor instead. func (*RefreshStateByShardResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{142} + return file_vtctldata_proto_rawDescGZIP(), []int{144} } func (x *RefreshStateByShardResponse) GetIsPartialRefresh() bool { @@ -9255,7 +9399,7 @@ type ReloadSchemaRequest struct { func (x *ReloadSchemaRequest) Reset() { *x = ReloadSchemaRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[143] + mi := &file_vtctldata_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9268,7 +9412,7 @@ func (x *ReloadSchemaRequest) String() string { func (*ReloadSchemaRequest) ProtoMessage() {} func (x *ReloadSchemaRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[143] + mi := &file_vtctldata_proto_msgTypes[145] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9281,7 +9425,7 @@ func (x *ReloadSchemaRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaRequest.ProtoReflect.Descriptor instead. func (*ReloadSchemaRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{143} + return file_vtctldata_proto_rawDescGZIP(), []int{145} } func (x *ReloadSchemaRequest) GetTabletAlias() *topodata.TabletAlias { @@ -9300,7 +9444,7 @@ type ReloadSchemaResponse struct { func (x *ReloadSchemaResponse) Reset() { *x = ReloadSchemaResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[144] + mi := &file_vtctldata_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9313,7 +9457,7 @@ func (x *ReloadSchemaResponse) String() string { func (*ReloadSchemaResponse) ProtoMessage() {} func (x *ReloadSchemaResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[144] + mi := &file_vtctldata_proto_msgTypes[146] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9326,7 +9470,7 @@ func (x *ReloadSchemaResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaResponse.ProtoReflect.Descriptor instead. func (*ReloadSchemaResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{144} + return file_vtctldata_proto_rawDescGZIP(), []int{146} } type ReloadSchemaKeyspaceRequest struct { @@ -9340,13 +9484,13 @@ type ReloadSchemaKeyspaceRequest struct { // Concurrency is the global concurrency across all shards in the keyspace // (so, at most this many tablets will be reloaded across the keyspace at any // given point). - Concurrency uint32 `protobuf:"varint,4,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + Concurrency int32 `protobuf:"varint,4,opt,name=concurrency,proto3" json:"concurrency,omitempty"` } func (x *ReloadSchemaKeyspaceRequest) Reset() { *x = ReloadSchemaKeyspaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[145] + mi := &file_vtctldata_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9359,7 +9503,7 @@ func (x *ReloadSchemaKeyspaceRequest) String() string { func (*ReloadSchemaKeyspaceRequest) ProtoMessage() {} func (x *ReloadSchemaKeyspaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[145] + mi := &file_vtctldata_proto_msgTypes[147] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9372,7 +9516,7 @@ func (x *ReloadSchemaKeyspaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaKeyspaceRequest.ProtoReflect.Descriptor instead. func (*ReloadSchemaKeyspaceRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{145} + return file_vtctldata_proto_rawDescGZIP(), []int{147} } func (x *ReloadSchemaKeyspaceRequest) GetKeyspace() string { @@ -9396,7 +9540,7 @@ func (x *ReloadSchemaKeyspaceRequest) GetIncludePrimary() bool { return false } -func (x *ReloadSchemaKeyspaceRequest) GetConcurrency() uint32 { +func (x *ReloadSchemaKeyspaceRequest) GetConcurrency() int32 { if x != nil { return x.Concurrency } @@ -9414,7 +9558,7 @@ type ReloadSchemaKeyspaceResponse struct { func (x *ReloadSchemaKeyspaceResponse) Reset() { *x = ReloadSchemaKeyspaceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[146] + mi := &file_vtctldata_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9427,7 +9571,7 @@ func (x *ReloadSchemaKeyspaceResponse) String() string { func (*ReloadSchemaKeyspaceResponse) ProtoMessage() {} func (x *ReloadSchemaKeyspaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[146] + mi := &file_vtctldata_proto_msgTypes[148] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9440,7 +9584,7 @@ func (x *ReloadSchemaKeyspaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaKeyspaceResponse.ProtoReflect.Descriptor instead. func (*ReloadSchemaKeyspaceResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{146} + return file_vtctldata_proto_rawDescGZIP(), []int{148} } func (x *ReloadSchemaKeyspaceResponse) GetEvents() []*logutil.Event { @@ -9460,13 +9604,13 @@ type ReloadSchemaShardRequest struct { WaitPosition string `protobuf:"bytes,3,opt,name=wait_position,json=waitPosition,proto3" json:"wait_position,omitempty"` IncludePrimary bool `protobuf:"varint,4,opt,name=include_primary,json=includePrimary,proto3" json:"include_primary,omitempty"` // Concurrency is the maximum number of tablets to reload at one time. - Concurrency uint32 `protobuf:"varint,5,opt,name=concurrency,proto3" json:"concurrency,omitempty"` + Concurrency int32 `protobuf:"varint,5,opt,name=concurrency,proto3" json:"concurrency,omitempty"` } func (x *ReloadSchemaShardRequest) Reset() { *x = ReloadSchemaShardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[147] + mi := &file_vtctldata_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9479,7 +9623,7 @@ func (x *ReloadSchemaShardRequest) String() string { func (*ReloadSchemaShardRequest) ProtoMessage() {} func (x *ReloadSchemaShardRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[147] + mi := &file_vtctldata_proto_msgTypes[149] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9492,7 +9636,7 @@ func (x *ReloadSchemaShardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaShardRequest.ProtoReflect.Descriptor instead. func (*ReloadSchemaShardRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{147} + return file_vtctldata_proto_rawDescGZIP(), []int{149} } func (x *ReloadSchemaShardRequest) GetKeyspace() string { @@ -9523,7 +9667,7 @@ func (x *ReloadSchemaShardRequest) GetIncludePrimary() bool { return false } -func (x *ReloadSchemaShardRequest) GetConcurrency() uint32 { +func (x *ReloadSchemaShardRequest) GetConcurrency() int32 { if x != nil { return x.Concurrency } @@ -9541,7 +9685,7 @@ type ReloadSchemaShardResponse struct { func (x *ReloadSchemaShardResponse) Reset() { *x = ReloadSchemaShardResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[148] + mi := &file_vtctldata_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9554,7 +9698,7 @@ func (x *ReloadSchemaShardResponse) String() string { func (*ReloadSchemaShardResponse) ProtoMessage() {} func (x *ReloadSchemaShardResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[148] + mi := &file_vtctldata_proto_msgTypes[150] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9567,7 +9711,7 @@ func (x *ReloadSchemaShardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSchemaShardResponse.ProtoReflect.Descriptor instead. func (*ReloadSchemaShardResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{148} + return file_vtctldata_proto_rawDescGZIP(), []int{150} } func (x *ReloadSchemaShardResponse) GetEvents() []*logutil.Event { @@ -9590,7 +9734,7 @@ type RemoveBackupRequest struct { func (x *RemoveBackupRequest) Reset() { *x = RemoveBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[149] + mi := &file_vtctldata_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9603,7 +9747,7 @@ func (x *RemoveBackupRequest) String() string { func (*RemoveBackupRequest) ProtoMessage() {} func (x *RemoveBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[149] + mi := &file_vtctldata_proto_msgTypes[151] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9616,7 +9760,7 @@ func (x *RemoveBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveBackupRequest.ProtoReflect.Descriptor instead. func (*RemoveBackupRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{149} + return file_vtctldata_proto_rawDescGZIP(), []int{151} } func (x *RemoveBackupRequest) GetKeyspace() string { @@ -9649,7 +9793,7 @@ type RemoveBackupResponse struct { func (x *RemoveBackupResponse) Reset() { *x = RemoveBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[150] + mi := &file_vtctldata_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9662,7 +9806,7 @@ func (x *RemoveBackupResponse) String() string { func (*RemoveBackupResponse) ProtoMessage() {} func (x *RemoveBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[150] + mi := &file_vtctldata_proto_msgTypes[152] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9675,7 +9819,7 @@ func (x *RemoveBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveBackupResponse.ProtoReflect.Descriptor instead. func (*RemoveBackupResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{150} + return file_vtctldata_proto_rawDescGZIP(), []int{152} } type RemoveKeyspaceCellRequest struct { @@ -9697,7 +9841,7 @@ type RemoveKeyspaceCellRequest struct { func (x *RemoveKeyspaceCellRequest) Reset() { *x = RemoveKeyspaceCellRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[151] + mi := &file_vtctldata_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9710,7 +9854,7 @@ func (x *RemoveKeyspaceCellRequest) String() string { func (*RemoveKeyspaceCellRequest) ProtoMessage() {} func (x *RemoveKeyspaceCellRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[151] + mi := &file_vtctldata_proto_msgTypes[153] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9723,7 +9867,7 @@ func (x *RemoveKeyspaceCellRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveKeyspaceCellRequest.ProtoReflect.Descriptor instead. func (*RemoveKeyspaceCellRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{151} + return file_vtctldata_proto_rawDescGZIP(), []int{153} } func (x *RemoveKeyspaceCellRequest) GetKeyspace() string { @@ -9763,7 +9907,7 @@ type RemoveKeyspaceCellResponse struct { func (x *RemoveKeyspaceCellResponse) Reset() { *x = RemoveKeyspaceCellResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[152] + mi := &file_vtctldata_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9776,7 +9920,7 @@ func (x *RemoveKeyspaceCellResponse) String() string { func (*RemoveKeyspaceCellResponse) ProtoMessage() {} func (x *RemoveKeyspaceCellResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[152] + mi := &file_vtctldata_proto_msgTypes[154] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9789,7 +9933,7 @@ func (x *RemoveKeyspaceCellResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveKeyspaceCellResponse.ProtoReflect.Descriptor instead. func (*RemoveKeyspaceCellResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{152} + return file_vtctldata_proto_rawDescGZIP(), []int{154} } type RemoveShardCellRequest struct { @@ -9812,7 +9956,7 @@ type RemoveShardCellRequest struct { func (x *RemoveShardCellRequest) Reset() { *x = RemoveShardCellRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[153] + mi := &file_vtctldata_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9825,7 +9969,7 @@ func (x *RemoveShardCellRequest) String() string { func (*RemoveShardCellRequest) ProtoMessage() {} func (x *RemoveShardCellRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[153] + mi := &file_vtctldata_proto_msgTypes[155] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9838,7 +9982,7 @@ func (x *RemoveShardCellRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveShardCellRequest.ProtoReflect.Descriptor instead. func (*RemoveShardCellRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{153} + return file_vtctldata_proto_rawDescGZIP(), []int{155} } func (x *RemoveShardCellRequest) GetKeyspace() string { @@ -9885,7 +10029,7 @@ type RemoveShardCellResponse struct { func (x *RemoveShardCellResponse) Reset() { *x = RemoveShardCellResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[154] + mi := &file_vtctldata_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9898,7 +10042,7 @@ func (x *RemoveShardCellResponse) String() string { func (*RemoveShardCellResponse) ProtoMessage() {} func (x *RemoveShardCellResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[154] + mi := &file_vtctldata_proto_msgTypes[156] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9911,7 +10055,7 @@ func (x *RemoveShardCellResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoveShardCellResponse.ProtoReflect.Descriptor instead. func (*RemoveShardCellResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{154} + return file_vtctldata_proto_rawDescGZIP(), []int{156} } type ReparentTabletRequest struct { @@ -9927,7 +10071,7 @@ type ReparentTabletRequest struct { func (x *ReparentTabletRequest) Reset() { *x = ReparentTabletRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[155] + mi := &file_vtctldata_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9940,7 +10084,7 @@ func (x *ReparentTabletRequest) String() string { func (*ReparentTabletRequest) ProtoMessage() {} func (x *ReparentTabletRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[155] + mi := &file_vtctldata_proto_msgTypes[157] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9953,7 +10097,7 @@ func (x *ReparentTabletRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReparentTabletRequest.ProtoReflect.Descriptor instead. func (*ReparentTabletRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{155} + return file_vtctldata_proto_rawDescGZIP(), []int{157} } func (x *ReparentTabletRequest) GetTablet() *topodata.TabletAlias { @@ -9979,7 +10123,7 @@ type ReparentTabletResponse struct { func (x *ReparentTabletResponse) Reset() { *x = ReparentTabletResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[156] + mi := &file_vtctldata_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9992,7 +10136,7 @@ func (x *ReparentTabletResponse) String() string { func (*ReparentTabletResponse) ProtoMessage() {} func (x *ReparentTabletResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[156] + mi := &file_vtctldata_proto_msgTypes[158] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10005,7 +10149,7 @@ func (x *ReparentTabletResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReparentTabletResponse.ProtoReflect.Descriptor instead. func (*ReparentTabletResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{156} + return file_vtctldata_proto_rawDescGZIP(), []int{158} } func (x *ReparentTabletResponse) GetKeyspace() string { @@ -10057,7 +10201,7 @@ type ReshardCreateRequest struct { func (x *ReshardCreateRequest) Reset() { *x = ReshardCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[157] + mi := &file_vtctldata_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10070,7 +10214,7 @@ func (x *ReshardCreateRequest) String() string { func (*ReshardCreateRequest) ProtoMessage() {} func (x *ReshardCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[157] + mi := &file_vtctldata_proto_msgTypes[159] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10083,7 +10227,7 @@ func (x *ReshardCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReshardCreateRequest.ProtoReflect.Descriptor instead. func (*ReshardCreateRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{157} + return file_vtctldata_proto_rawDescGZIP(), []int{159} } func (x *ReshardCreateRequest) GetWorkflow() string { @@ -10193,7 +10337,7 @@ type RestoreFromBackupRequest struct { func (x *RestoreFromBackupRequest) Reset() { *x = RestoreFromBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[158] + mi := &file_vtctldata_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10206,7 +10350,7 @@ func (x *RestoreFromBackupRequest) String() string { func (*RestoreFromBackupRequest) ProtoMessage() {} func (x *RestoreFromBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[158] + mi := &file_vtctldata_proto_msgTypes[160] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10219,7 +10363,7 @@ func (x *RestoreFromBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreFromBackupRequest.ProtoReflect.Descriptor instead. func (*RestoreFromBackupRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{158} + return file_vtctldata_proto_rawDescGZIP(), []int{160} } func (x *RestoreFromBackupRequest) GetTabletAlias() *topodata.TabletAlias { @@ -10272,7 +10416,7 @@ type RestoreFromBackupResponse struct { func (x *RestoreFromBackupResponse) Reset() { *x = RestoreFromBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[159] + mi := &file_vtctldata_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10285,7 +10429,7 @@ func (x *RestoreFromBackupResponse) String() string { func (*RestoreFromBackupResponse) ProtoMessage() {} func (x *RestoreFromBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[159] + mi := &file_vtctldata_proto_msgTypes[161] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10298,7 +10442,7 @@ func (x *RestoreFromBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreFromBackupResponse.ProtoReflect.Descriptor instead. func (*RestoreFromBackupResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{159} + return file_vtctldata_proto_rawDescGZIP(), []int{161} } func (x *RestoreFromBackupResponse) GetTabletAlias() *topodata.TabletAlias { @@ -10341,7 +10485,7 @@ type RetrySchemaMigrationRequest struct { func (x *RetrySchemaMigrationRequest) Reset() { *x = RetrySchemaMigrationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[160] + mi := &file_vtctldata_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10354,7 +10498,7 @@ func (x *RetrySchemaMigrationRequest) String() string { func (*RetrySchemaMigrationRequest) ProtoMessage() {} func (x *RetrySchemaMigrationRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[160] + mi := &file_vtctldata_proto_msgTypes[162] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10367,7 +10511,7 @@ func (x *RetrySchemaMigrationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RetrySchemaMigrationRequest.ProtoReflect.Descriptor instead. func (*RetrySchemaMigrationRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{160} + return file_vtctldata_proto_rawDescGZIP(), []int{162} } func (x *RetrySchemaMigrationRequest) GetKeyspace() string { @@ -10395,7 +10539,7 @@ type RetrySchemaMigrationResponse struct { func (x *RetrySchemaMigrationResponse) Reset() { *x = RetrySchemaMigrationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[161] + mi := &file_vtctldata_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10408,7 +10552,7 @@ func (x *RetrySchemaMigrationResponse) String() string { func (*RetrySchemaMigrationResponse) ProtoMessage() {} func (x *RetrySchemaMigrationResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[161] + mi := &file_vtctldata_proto_msgTypes[163] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10421,7 +10565,7 @@ func (x *RetrySchemaMigrationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RetrySchemaMigrationResponse.ProtoReflect.Descriptor instead. func (*RetrySchemaMigrationResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{161} + return file_vtctldata_proto_rawDescGZIP(), []int{163} } func (x *RetrySchemaMigrationResponse) GetRowsAffectedByShard() map[string]uint64 { @@ -10442,7 +10586,7 @@ type RunHealthCheckRequest struct { func (x *RunHealthCheckRequest) Reset() { *x = RunHealthCheckRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[162] + mi := &file_vtctldata_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10455,7 +10599,7 @@ func (x *RunHealthCheckRequest) String() string { func (*RunHealthCheckRequest) ProtoMessage() {} func (x *RunHealthCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[162] + mi := &file_vtctldata_proto_msgTypes[164] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10468,7 +10612,7 @@ func (x *RunHealthCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunHealthCheckRequest.ProtoReflect.Descriptor instead. func (*RunHealthCheckRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{162} + return file_vtctldata_proto_rawDescGZIP(), []int{164} } func (x *RunHealthCheckRequest) GetTabletAlias() *topodata.TabletAlias { @@ -10487,7 +10631,7 @@ type RunHealthCheckResponse struct { func (x *RunHealthCheckResponse) Reset() { *x = RunHealthCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[163] + mi := &file_vtctldata_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10500,7 +10644,7 @@ func (x *RunHealthCheckResponse) String() string { func (*RunHealthCheckResponse) ProtoMessage() {} func (x *RunHealthCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[163] + mi := &file_vtctldata_proto_msgTypes[165] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10513,7 +10657,7 @@ func (x *RunHealthCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunHealthCheckResponse.ProtoReflect.Descriptor instead. func (*RunHealthCheckResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{163} + return file_vtctldata_proto_rawDescGZIP(), []int{165} } type SetKeyspaceDurabilityPolicyRequest struct { @@ -10528,7 +10672,7 @@ type SetKeyspaceDurabilityPolicyRequest struct { func (x *SetKeyspaceDurabilityPolicyRequest) Reset() { *x = SetKeyspaceDurabilityPolicyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[164] + mi := &file_vtctldata_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10541,7 +10685,7 @@ func (x *SetKeyspaceDurabilityPolicyRequest) String() string { func (*SetKeyspaceDurabilityPolicyRequest) ProtoMessage() {} func (x *SetKeyspaceDurabilityPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[164] + mi := &file_vtctldata_proto_msgTypes[166] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10554,7 +10698,7 @@ func (x *SetKeyspaceDurabilityPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use SetKeyspaceDurabilityPolicyRequest.ProtoReflect.Descriptor instead. func (*SetKeyspaceDurabilityPolicyRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{164} + return file_vtctldata_proto_rawDescGZIP(), []int{166} } func (x *SetKeyspaceDurabilityPolicyRequest) GetKeyspace() string { @@ -10583,7 +10727,7 @@ type SetKeyspaceDurabilityPolicyResponse struct { func (x *SetKeyspaceDurabilityPolicyResponse) Reset() { *x = SetKeyspaceDurabilityPolicyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[165] + mi := &file_vtctldata_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10596,7 +10740,7 @@ func (x *SetKeyspaceDurabilityPolicyResponse) String() string { func (*SetKeyspaceDurabilityPolicyResponse) ProtoMessage() {} func (x *SetKeyspaceDurabilityPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[165] + mi := &file_vtctldata_proto_msgTypes[167] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10609,137 +10753,10 @@ func (x *SetKeyspaceDurabilityPolicyResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use SetKeyspaceDurabilityPolicyResponse.ProtoReflect.Descriptor instead. func (*SetKeyspaceDurabilityPolicyResponse) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{165} -} - -func (x *SetKeyspaceDurabilityPolicyResponse) GetKeyspace() *topodata.Keyspace { - if x != nil { - return x.Keyspace - } - return nil -} - -type SetKeyspaceServedFromRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - TabletType topodata.TabletType `protobuf:"varint,2,opt,name=tablet_type,json=tabletType,proto3,enum=topodata.TabletType" json:"tablet_type,omitempty"` - Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` - Remove bool `protobuf:"varint,4,opt,name=remove,proto3" json:"remove,omitempty"` - SourceKeyspace string `protobuf:"bytes,5,opt,name=source_keyspace,json=sourceKeyspace,proto3" json:"source_keyspace,omitempty"` -} - -func (x *SetKeyspaceServedFromRequest) Reset() { - *x = SetKeyspaceServedFromRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[166] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetKeyspaceServedFromRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetKeyspaceServedFromRequest) ProtoMessage() {} - -func (x *SetKeyspaceServedFromRequest) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[166] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetKeyspaceServedFromRequest.ProtoReflect.Descriptor instead. -func (*SetKeyspaceServedFromRequest) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{166} -} - -func (x *SetKeyspaceServedFromRequest) GetKeyspace() string { - if x != nil { - return x.Keyspace - } - return "" -} - -func (x *SetKeyspaceServedFromRequest) GetTabletType() topodata.TabletType { - if x != nil { - return x.TabletType - } - return topodata.TabletType(0) -} - -func (x *SetKeyspaceServedFromRequest) GetCells() []string { - if x != nil { - return x.Cells - } - return nil -} - -func (x *SetKeyspaceServedFromRequest) GetRemove() bool { - if x != nil { - return x.Remove - } - return false -} - -func (x *SetKeyspaceServedFromRequest) GetSourceKeyspace() string { - if x != nil { - return x.SourceKeyspace - } - return "" -} - -type SetKeyspaceServedFromResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Keyspace is the updated keyspace record. - Keyspace *topodata.Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` -} - -func (x *SetKeyspaceServedFromResponse) Reset() { - *x = SetKeyspaceServedFromResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[167] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetKeyspaceServedFromResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetKeyspaceServedFromResponse) ProtoMessage() {} - -func (x *SetKeyspaceServedFromResponse) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[167] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetKeyspaceServedFromResponse.ProtoReflect.Descriptor instead. -func (*SetKeyspaceServedFromResponse) Descriptor() ([]byte, []int) { return file_vtctldata_proto_rawDescGZIP(), []int{167} } -func (x *SetKeyspaceServedFromResponse) GetKeyspace() *topodata.Keyspace { +func (x *SetKeyspaceDurabilityPolicyResponse) GetKeyspace() *topodata.Keyspace { if x != nil { return x.Keyspace } @@ -13309,6 +13326,8 @@ type VDiffCreateRequest struct { WaitUpdateInterval *vttime.Duration `protobuf:"bytes,16,opt,name=wait_update_interval,json=waitUpdateInterval,proto3" json:"wait_update_interval,omitempty"` AutoRetry bool `protobuf:"varint,17,opt,name=auto_retry,json=autoRetry,proto3" json:"auto_retry,omitempty"` Verbose bool `protobuf:"varint,18,opt,name=verbose,proto3" json:"verbose,omitempty"` + MaxReportSampleRows int64 `protobuf:"varint,19,opt,name=max_report_sample_rows,json=maxReportSampleRows,proto3" json:"max_report_sample_rows,omitempty"` + MaxDiffDuration *vttime.Duration `protobuf:"bytes,20,opt,name=max_diff_duration,json=maxDiffDuration,proto3" json:"max_diff_duration,omitempty"` } func (x *VDiffCreateRequest) Reset() { @@ -13469,6 +13488,20 @@ func (x *VDiffCreateRequest) GetVerbose() bool { return false } +func (x *VDiffCreateRequest) GetMaxReportSampleRows() int64 { + if x != nil { + return x.MaxReportSampleRows + } + return 0 +} + +func (x *VDiffCreateRequest) GetMaxDiffDuration() *vttime.Duration { + if x != nil { + return x.MaxDiffDuration + } + return nil +} + type VDiffCreateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -13939,10 +13972,11 @@ type WorkflowDeleteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Workflow string `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` - KeepData bool `protobuf:"varint,3,opt,name=keep_data,json=keepData,proto3" json:"keep_data,omitempty"` - KeepRoutingRules bool `protobuf:"varint,4,opt,name=keep_routing_rules,json=keepRoutingRules,proto3" json:"keep_routing_rules,omitempty"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Workflow string `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` + KeepData bool `protobuf:"varint,3,opt,name=keep_data,json=keepData,proto3" json:"keep_data,omitempty"` + KeepRoutingRules bool `protobuf:"varint,4,opt,name=keep_routing_rules,json=keepRoutingRules,proto3" json:"keep_routing_rules,omitempty"` + Shards []string `protobuf:"bytes,5,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *WorkflowDeleteRequest) Reset() { @@ -14005,6 +14039,13 @@ func (x *WorkflowDeleteRequest) GetKeepRoutingRules() bool { return false } +func (x *WorkflowDeleteRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type WorkflowDeleteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14065,8 +14106,9 @@ type WorkflowStatusRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - Workflow string `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Workflow string `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` + Shards []string `protobuf:"bytes,3,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *WorkflowStatusRequest) Reset() { @@ -14115,6 +14157,13 @@ func (x *WorkflowStatusRequest) GetWorkflow() string { return "" } +func (x *WorkflowStatusRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type WorkflowStatusResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14194,6 +14243,7 @@ type WorkflowSwitchTrafficRequest struct { Timeout *vttime.Duration `protobuf:"bytes,8,opt,name=timeout,proto3" json:"timeout,omitempty"` DryRun bool `protobuf:"varint,9,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` InitializeTargetSequences bool `protobuf:"varint,10,opt,name=initialize_target_sequences,json=initializeTargetSequences,proto3" json:"initialize_target_sequences,omitempty"` + Shards []string `protobuf:"bytes,11,rep,name=shards,proto3" json:"shards,omitempty"` } func (x *WorkflowSwitchTrafficRequest) Reset() { @@ -14298,6 +14348,13 @@ func (x *WorkflowSwitchTrafficRequest) GetInitializeTargetSequences() bool { return false } +func (x *WorkflowSwitchTrafficRequest) GetShards() []string { + if x != nil { + return x.Shards + } + return nil +} + type WorkflowSwitchTrafficResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14625,10 +14682,13 @@ type Workflow_Stream struct { // Note that this field being set does not necessarily mean that Logs is nil; // if there are N logs that exist for the stream, and we fail to fetch the // ith log, we will still return logs in [0, i) + (i, N]. - LogFetchError string `protobuf:"bytes,14,opt,name=log_fetch_error,json=logFetchError,proto3" json:"log_fetch_error,omitempty"` - Tags []string `protobuf:"bytes,15,rep,name=tags,proto3" json:"tags,omitempty"` - RowsCopied int64 `protobuf:"varint,16,opt,name=rows_copied,json=rowsCopied,proto3" json:"rows_copied,omitempty"` - ThrottlerStatus *Workflow_Stream_ThrottlerStatus `protobuf:"bytes,17,opt,name=throttler_status,json=throttlerStatus,proto3" json:"throttler_status,omitempty"` + LogFetchError string `protobuf:"bytes,14,opt,name=log_fetch_error,json=logFetchError,proto3" json:"log_fetch_error,omitempty"` + Tags []string `protobuf:"bytes,15,rep,name=tags,proto3" json:"tags,omitempty"` + RowsCopied int64 `protobuf:"varint,16,opt,name=rows_copied,json=rowsCopied,proto3" json:"rows_copied,omitempty"` + ThrottlerStatus *Workflow_Stream_ThrottlerStatus `protobuf:"bytes,17,opt,name=throttler_status,json=throttlerStatus,proto3" json:"throttler_status,omitempty"` + TabletTypes []topodata.TabletType `protobuf:"varint,18,rep,packed,name=tablet_types,json=tabletTypes,proto3,enum=topodata.TabletType" json:"tablet_types,omitempty"` + TabletSelectionPreference tabletmanagerdata.TabletSelectionPreference `protobuf:"varint,19,opt,name=tablet_selection_preference,json=tabletSelectionPreference,proto3,enum=tabletmanagerdata.TabletSelectionPreference" json:"tablet_selection_preference,omitempty"` + Cells []string `protobuf:"bytes,20,rep,name=cells,proto3" json:"cells,omitempty"` } func (x *Workflow_Stream) Reset() { @@ -14782,13 +14842,35 @@ func (x *Workflow_Stream) GetThrottlerStatus() *Workflow_Stream_ThrottlerStatus return nil } +func (x *Workflow_Stream) GetTabletTypes() []topodata.TabletType { + if x != nil { + return x.TabletTypes + } + return nil +} + +func (x *Workflow_Stream) GetTabletSelectionPreference() tabletmanagerdata.TabletSelectionPreference { + if x != nil { + return x.TabletSelectionPreference + } + return tabletmanagerdata.TabletSelectionPreference(0) +} + +func (x *Workflow_Stream) GetCells() []string { + if x != nil { + return x.Cells + } + return nil +} + type Workflow_Stream_CopyState struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` - LastPk string `protobuf:"bytes,2,opt,name=last_pk,json=lastPk,proto3" json:"last_pk,omitempty"` + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + LastPk string `protobuf:"bytes,2,opt,name=last_pk,json=lastPk,proto3" json:"last_pk,omitempty"` + StreamId int64 `protobuf:"varint,3,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` } func (x *Workflow_Stream_CopyState) Reset() { @@ -14837,6 +14919,13 @@ func (x *Workflow_Stream_CopyState) GetLastPk() string { return "" } +func (x *Workflow_Stream_CopyState) GetStreamId() int64 { + if x != nil { + return x.StreamId + } + return 0 +} + type Workflow_Stream_Log struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14995,6 +15084,53 @@ func (x *Workflow_Stream_ThrottlerStatus) GetTimeThrottled() *vttime.Time { return nil } +type ApplyVSchemaResponse_ParamList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Params []string `protobuf:"bytes,1,rep,name=params,proto3" json:"params,omitempty"` +} + +func (x *ApplyVSchemaResponse_ParamList) Reset() { + *x = ApplyVSchemaResponse_ParamList{} + if protoimpl.UnsafeEnabled { + mi := &file_vtctldata_proto_msgTypes[241] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyVSchemaResponse_ParamList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyVSchemaResponse_ParamList) ProtoMessage() {} + +func (x *ApplyVSchemaResponse_ParamList) ProtoReflect() protoreflect.Message { + mi := &file_vtctldata_proto_msgTypes[241] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyVSchemaResponse_ParamList.ProtoReflect.Descriptor instead. +func (*ApplyVSchemaResponse_ParamList) Descriptor() ([]byte, []int) { + return file_vtctldata_proto_rawDescGZIP(), []int{19, 1} +} + +func (x *ApplyVSchemaResponse_ParamList) GetParams() []string { + if x != nil { + return x.Params + } + return nil +} + type GetSrvKeyspaceNamesResponse_NameList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -15006,7 +15142,7 @@ type GetSrvKeyspaceNamesResponse_NameList struct { func (x *GetSrvKeyspaceNamesResponse_NameList) Reset() { *x = GetSrvKeyspaceNamesResponse_NameList{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[246] + mi := &file_vtctldata_proto_msgTypes[249] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15019,7 +15155,7 @@ func (x *GetSrvKeyspaceNamesResponse_NameList) String() string { func (*GetSrvKeyspaceNamesResponse_NameList) ProtoMessage() {} func (x *GetSrvKeyspaceNamesResponse_NameList) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[246] + mi := &file_vtctldata_proto_msgTypes[249] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15032,7 +15168,7 @@ func (x *GetSrvKeyspaceNamesResponse_NameList) ProtoReflect() protoreflect.Messa // Deprecated: Use GetSrvKeyspaceNamesResponse_NameList.ProtoReflect.Descriptor instead. func (*GetSrvKeyspaceNamesResponse_NameList) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{84, 1} + return file_vtctldata_proto_rawDescGZIP(), []int{86, 1} } func (x *GetSrvKeyspaceNamesResponse_NameList) GetNames() []string { @@ -15055,7 +15191,7 @@ type MoveTablesCreateResponse_TabletInfo struct { func (x *MoveTablesCreateResponse_TabletInfo) Reset() { *x = MoveTablesCreateResponse_TabletInfo{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[250] + mi := &file_vtctldata_proto_msgTypes[253] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15068,7 +15204,7 @@ func (x *MoveTablesCreateResponse_TabletInfo) String() string { func (*MoveTablesCreateResponse_TabletInfo) ProtoMessage() {} func (x *MoveTablesCreateResponse_TabletInfo) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[250] + mi := &file_vtctldata_proto_msgTypes[253] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15081,7 +15217,7 @@ func (x *MoveTablesCreateResponse_TabletInfo) ProtoReflect() protoreflect.Messag // Deprecated: Use MoveTablesCreateResponse_TabletInfo.ProtoReflect.Descriptor instead. func (*MoveTablesCreateResponse_TabletInfo) Descriptor() ([]byte, []int) { - return file_vtctldata_proto_rawDescGZIP(), []int{128, 0} + return file_vtctldata_proto_rawDescGZIP(), []int{130, 0} } func (x *MoveTablesCreateResponse_TabletInfo) GetTablet() *topodata.TabletAlias { @@ -15111,7 +15247,7 @@ type WorkflowDeleteResponse_TabletInfo struct { func (x *WorkflowDeleteResponse_TabletInfo) Reset() { *x = WorkflowDeleteResponse_TabletInfo{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[260] + mi := &file_vtctldata_proto_msgTypes[263] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15124,7 +15260,7 @@ func (x *WorkflowDeleteResponse_TabletInfo) String() string { func (*WorkflowDeleteResponse_TabletInfo) ProtoMessage() {} func (x *WorkflowDeleteResponse_TabletInfo) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[260] + mi := &file_vtctldata_proto_msgTypes[263] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15170,7 +15306,7 @@ type WorkflowStatusResponse_TableCopyState struct { func (x *WorkflowStatusResponse_TableCopyState) Reset() { *x = WorkflowStatusResponse_TableCopyState{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[261] + mi := &file_vtctldata_proto_msgTypes[264] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15183,7 +15319,7 @@ func (x *WorkflowStatusResponse_TableCopyState) String() string { func (*WorkflowStatusResponse_TableCopyState) ProtoMessage() {} func (x *WorkflowStatusResponse_TableCopyState) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[261] + mi := &file_vtctldata_proto_msgTypes[264] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15257,7 +15393,7 @@ type WorkflowStatusResponse_ShardStreamState struct { func (x *WorkflowStatusResponse_ShardStreamState) Reset() { *x = WorkflowStatusResponse_ShardStreamState{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[262] + mi := &file_vtctldata_proto_msgTypes[265] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15270,7 +15406,7 @@ func (x *WorkflowStatusResponse_ShardStreamState) String() string { func (*WorkflowStatusResponse_ShardStreamState) ProtoMessage() {} func (x *WorkflowStatusResponse_ShardStreamState) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[262] + mi := &file_vtctldata_proto_msgTypes[265] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15339,7 +15475,7 @@ type WorkflowStatusResponse_ShardStreams struct { func (x *WorkflowStatusResponse_ShardStreams) Reset() { *x = WorkflowStatusResponse_ShardStreams{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[263] + mi := &file_vtctldata_proto_msgTypes[266] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15352,7 +15488,7 @@ func (x *WorkflowStatusResponse_ShardStreams) String() string { func (*WorkflowStatusResponse_ShardStreams) ProtoMessage() {} func (x *WorkflowStatusResponse_ShardStreams) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[263] + mi := &file_vtctldata_proto_msgTypes[266] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15389,7 +15525,7 @@ type WorkflowUpdateResponse_TabletInfo struct { func (x *WorkflowUpdateResponse_TabletInfo) Reset() { *x = WorkflowUpdateResponse_TabletInfo{} if protoimpl.UnsafeEnabled { - mi := &file_vtctldata_proto_msgTypes[266] + mi := &file_vtctldata_proto_msgTypes[269] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15402,7 +15538,7 @@ func (x *WorkflowUpdateResponse_TabletInfo) String() string { func (*WorkflowUpdateResponse_TabletInfo) ProtoMessage() {} func (x *WorkflowUpdateResponse_TabletInfo) ProtoReflect() protoreflect.Message { - mi := &file_vtctldata_proto_msgTypes[266] + mi := &file_vtctldata_proto_msgTypes[269] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15681,7 +15817,7 @@ var file_vtctldata_proto_rawDesc = []byte{ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0xbf, 0x0f, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, + 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x99, 0x11, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, @@ -15735,7 +15871,7 @@ var file_vtctldata_proto_rawDesc = []byte{ 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x1a, - 0xe7, 0x08, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0xc1, 0x0a, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, @@ -15779,1616 +15915,1620 @@ var file_vtctldata_proto_rawDesc = []byte{ 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0f, 0x74, 0x68, 0x72, 0x6f, - 0x74, 0x74, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3a, 0x0a, 0x09, 0x43, - 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x17, - 0x0a, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x6b, 0x1a, 0xe6, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x12, 0x2b, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x1a, 0x77, 0x0a, 0x0f, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x6f, 0x74, - 0x74, 0x6c, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x68, 0x72, - 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, - 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, - 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x22, 0x59, 0x0a, 0x12, 0x41, 0x64, 0x64, - 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, - 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x15, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x0a, 0x14, 0x41, - 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x17, 0x0a, - 0x15, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9e, 0x01, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x79, - 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x65, - 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x41, 0x70, 0x70, 0x6c, 0x79, - 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, - 0x11, 0x73, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, - 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x20, 0x0a, 0x1e, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, - 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xce, 0x02, 0x0a, - 0x12, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, - 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x64, 0x6c, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x64, 0x6c, 0x53, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6c, 0x69, 0x73, - 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x75, 0x75, 0x69, 0x64, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x44, - 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, - 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0xe8, 0x01, - 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6c, 0x69, - 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x75, 0x75, 0x69, 0x64, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x6c, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, - 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, - 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x41, 0x70, 0x70, - 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x74, 0x74, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x1a, 0x57, 0x0a, 0x09, 0x43, 0x6f, 0x70, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, + 0x73, 0x74, 0x50, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, + 0x64, 0x1a, 0xe6, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x2b, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2b, 0x0a, + 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, + 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x77, 0x0a, 0x0f, 0x54, 0x68, + 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, + 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, + 0x74, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x33, + 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, + 0x6c, 0x65, 0x64, 0x22, 0x59, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, + 0x09, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x15, + 0x0a, 0x13, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, + 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x43, 0x65, + 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x9e, 0x01, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, + 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, + 0x70, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x65, 0x6c, 0x6c, + 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb3, + 0x01, 0x0a, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, - 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x2c, - 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a, 0x03, - 0x73, 0x71, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x22, 0x44, - 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x22, 0xe5, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x72, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x61, 0x6c, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x67, - 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x53, 0x61, 0x66, 0x65, 0x22, 0xa2, 0x01, 0x0a, - 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, + 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x20, 0x0a, 0x1e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xce, 0x02, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x6c, 0x79, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x64, 0x6c, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x64, 0x6c, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1b, + 0x0a, 0x09, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x08, 0x75, 0x75, 0x69, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x44, 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, + 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2c, + 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, + 0x49, 0x44, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, + 0x03, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0xe8, 0x01, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x75, 0x75, 0x69, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x75, 0x69, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x6c, 0x0a, 0x16, + 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, + 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, + 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, + 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0xdb, 0x01, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, + 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x6b, + 0x69, 0x70, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, + 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, + 0x75, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, + 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, + 0x22, 0xca, 0x02, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, + 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x6c, 0x0a, 0x15, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x5f, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x56, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x13, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x71, 0x0a, 0x18, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x23, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0xe5, 0x01, + 0x0a, 0x0d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, - 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0xe2, 0x01, 0x0a, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, - 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, 0x66, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, - 0x53, 0x61, 0x66, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, - 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x73, 0x22, 0x4e, 0x0a, 0x1c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x76, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, - 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, + 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, + 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x72, 0x6f, 0x6d, 0x50, + 0x6f, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, + 0x66, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x53, 0x61, 0x66, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xe2, 0x01, 0x0a, 0x12, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, + 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, + 0x67, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x53, 0x61, 0x66, 0x65, 0x12, 0x30, 0x0a, + 0x14, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x63, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x73, 0x22, + 0x4e, 0x0a, 0x1c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, + 0xdf, 0x01, 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x76, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x41, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, + 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x17, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, - 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x2d, - 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x64, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, - 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0xa6, 0x01, 0x0a, 0x18, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0d, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x0c, 0x62, 0x65, - 0x66, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x33, 0x0a, 0x0c, 0x61, 0x66, - 0x74, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x52, 0x0b, 0x61, 0x66, 0x74, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, - 0x1e, 0x0a, 0x0b, 0x77, 0x61, 0x73, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, 0x61, 0x73, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, - 0x4f, 0x0a, 0x1d, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, - 0x22, 0xe1, 0x01, 0x0a, 0x1e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, + 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, + 0x64, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, + 0xa6, 0x01, 0x0a, 0x18, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0d, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x0c, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x12, 0x33, 0x0a, 0x0c, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x0b, 0x61, 0x66, 0x74, + 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x77, 0x61, 0x73, 0x5f, + 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, + 0x61, 0x73, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0x4f, 0x0a, 0x1d, 0x43, 0x6c, 0x65, 0x61, + 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xe1, 0x01, 0x0a, 0x1e, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, + 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, + 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x50, 0x0a, + 0x1e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, + 0xe3, 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, + 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, - 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, + 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x50, 0x0a, 0x1e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xe3, 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x16, 0x72, 0x6f, - 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x03, 0x0a, - 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x12, 0x2f, 0x0a, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, - 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x12, 0x40, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, - 0x6f, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x0d, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0c, 0x73, 0x6e, 0x61, 0x70, 0x73, - 0x68, 0x6f, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x75, 0x72, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x5f, - 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, - 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x44, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x04, - 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x49, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x69, - 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x22, 0xa0, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x6c, 0x72, - 0x65, 0x61, 0x64, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x12, 0x73, 0x68, 0x61, 0x72, 0x64, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, - 0x78, 0x69, 0x73, 0x74, 0x73, 0x22, 0x41, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, - 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, - 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x9b, 0x01, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, - 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x5f, 0x69, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x49, - 0x66, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x16, - 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x79, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x17, 0x0a, 0x15, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x81, 0x03, 0x0a, 0x1d, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, - 0x6e, 0x63, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, + 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, + 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdd, 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, + 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x0d, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x52, 0x0c, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, + 0x0a, 0x11, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, 0x72, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x73, + 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x5f, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x44, 0x62, 0x4e, + 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, + 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0x49, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, - 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x22, + 0xa0, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x12, 0x30, 0x0a, 0x14, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x22, 0x41, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, + 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1a, + 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x15, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9b, 0x01, + 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x5f, 0x69, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x49, 0x66, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x72, 0x76, + 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, + 0x6c, 0x6c, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x72, 0x76, 0x56, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x79, + 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x3e, 0x0a, 0x0f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x52, 0x0e, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x12, 0x44, 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x70, 0x72, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, - 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x70, - 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x65, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x77, 0x61, 0x69, 0x74, - 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, - 0x6c, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x1e, 0x45, 0x6d, - 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, - 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x81, 0x03, 0x0a, 0x1d, 0x45, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x6e, 0x63, 0x79, + 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3e, + 0x0a, 0x0f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0e, + 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x44, + 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, + 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x70, 0x72, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x65, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x6d, + 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, + 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x11, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x6c, 0x6c, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x1e, 0x45, 0x6d, 0x65, 0x72, 0x67, + 0x65, 0x6e, 0x63, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, 0x10, 0x70, + 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0f, 0x70, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, 0x0a, + 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, + 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x19, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x75, 0x73, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x22, 0x47, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, + 0x63, 0x68, 0x41, 0x73, 0x44, 0x42, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, + 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x19, + 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x6c, 0x6f, + 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x47, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x42, 0x41, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x22, 0xa5, 0x01, 0x0a, 0x12, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x12, 0x55, 0x0a, 0x13, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x11, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x48, 0x6f, 0x6f, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5e, 0x0a, 0x13, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x47, 0x0a, 0x0b, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x68, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3c, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, + 0x41, 0x6c, 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x41, + 0x6c, 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x1a, 0x4b, 0x0a, 0x0b, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x63, + 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x54, 0x0a, 0x22, 0x46, 0x6f, 0x72, 0x63, 0x65, + 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xeb, 0x01, + 0x0a, 0x23, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, + 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, + 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x44, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x46, 0x0a, 0x13, + 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, + 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, + 0x6e, 0x66, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x30, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb6, 0x01, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x1a, 0x50, 0x0a, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, + 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x4c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, + 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x14, 0x47, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x30, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x46, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x22, 0x51, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x22, 0x5a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, + 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, 0x0a, 0x17, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, + 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, + 0x22, 0xb0, 0x02, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, - 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x22, 0x47, 0x0a, 0x19, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x41, 0x70, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x42, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, - 0x6e, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x47, 0x0a, 0x19, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x73, 0x44, 0x42, 0x41, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x22, 0xa5, 0x01, 0x0a, 0x12, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x48, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x12, 0x55, 0x0a, 0x13, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x68, - 0x6f, 0x6f, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x11, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5e, 0x0a, 0x13, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x0a, 0x68, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3c, 0x0a, 0x1e, 0x46, - 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x1f, 0x46, 0x69, - 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, - 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, - 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x1a, 0x4b, 0x0a, - 0x0b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x11, 0x47, - 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x44, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x63, 0x74, 0x6c, 0x2e, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x22, 0x28, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x46, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, - 0x6e, 0x66, 0x6f, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, - 0x66, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x30, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb6, 0x01, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x1a, 0x50, 0x0a, 0x0c, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, - 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x4c, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x30, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x46, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, - 0x51, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x22, 0x5a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, - 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, - 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, - 0xb0, 0x02, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, - 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, - 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, - 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, - 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, 0x65, - 0x77, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a, 0x10, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x7a, - 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x6e, - 0x6c, 0x79, 0x22, 0x50, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x22, 0xb8, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, - 0x75, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x72, - 0x65, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x72, - 0x65, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x6b, 0x69, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x6b, 0x69, 0x70, 0x22, - 0x59, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, - 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x11, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, - 0x32, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, - 0x6c, 0x6c, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x0a, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x20, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, - 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, - 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x59, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x72, 0x76, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, - 0x73, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x11, - 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x72, - 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf8, 0x02, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, - 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, - 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, - 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x74, 0x12, 0x2d, - 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x2f, 0x0a, - 0x14, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x3f, - 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x70, 0x70, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x52, 0x75, 0x6c, - 0x65, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x22, - 0x1f, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2a, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x4e, 0x0a, 0x15, - 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x52, 0x0a, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x2d, 0x0a, 0x15, - 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xc5, 0x01, 0x0a, 0x16, - 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, - 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0b, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x1a, 0x53, - 0x0a, 0x10, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, + 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, + 0x65, 0x77, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a, + 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, + 0x6e, 0x6c, 0x79, 0x22, 0x50, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0xb8, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x75, 0x75, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x21, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x06, + 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, + 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, + 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, + 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x6b, 0x69, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x6b, 0x69, 0x70, + 0x22, 0x59, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3a, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x13, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x72, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x11, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, + 0x22, 0x32, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, + 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x69, 0x0a, + 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x20, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, + 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x72, + 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x72, + 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0c, 0x73, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x56, 0x0a, + 0x11, 0x53, 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x72, - 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x4c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x22, 0x3d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x22, 0xe8, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, + 0x72, 0x76, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf8, 0x02, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, + 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x74, 0x12, + 0x2d, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x2f, + 0x0a, 0x14, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x41, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, + 0x3f, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x70, 0x70, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x70, 0x70, + 0x22, 0x1f, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x68, 0x72, 0x6f, 0x74, 0x74, + 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2a, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x4e, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0c, 0x73, 0x72, 0x76, 0x5f, 0x76, 0x5f, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x52, 0x0a, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x2d, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0xc5, 0x01, 0x0a, + 0x16, 0x47, 0x65, 0x74, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x73, 0x72, 0x76, 0x5f, 0x76, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x72, + 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0b, 0x73, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x1a, + 0x53, 0x0a, 0x10, 0x53, 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, + 0x72, 0x76, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x40, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x2c, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x46, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x04, 0x63, - 0x65, 0x6c, 0x6c, 0x22, 0x66, 0x0a, 0x0c, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x43, - 0x65, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x2f, 0x0a, 0x11, 0x47, - 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x4d, 0x0a, 0x11, - 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x2e, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, - 0xae, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x6e, - 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, - 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x4f, 0x6e, 0x6c, - 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x21, 0x0a, - 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4c, 0x6f, 0x67, 0x73, - 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x17, - 0x49, 0x6e, 0x69, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x22, 0x3d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x22, 0xe8, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x52, 0x0a, 0x1a, 0x70, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x17, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6c, 0x65, - 0x63, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x42, 0x0a, 0x18, 0x49, 0x6e, 0x69, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x4e, 0x0a, - 0x1c, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xdf, 0x01, - 0x0a, 0x1d, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x76, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x41, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x75, 0x6e, - 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, - 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, - 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0xff, 0x02, 0x0a, 0x19, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x76, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, - 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x42, 0x0a, 0x1e, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, - 0x75, 0x65, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x77, 0x69, - 0x74, 0x68, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, - 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, - 0x79, 0x57, 0x69, 0x74, 0x68, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, - 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x77, 0x0a, 0x1e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, + 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x40, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x2c, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x46, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x04, + 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x66, 0x0a, 0x0c, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x2f, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x4c, 0x0a, 0x1f, 0x4c, 0x6f, 0x6f, 0x6b, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x4d, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, + 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x2e, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x76, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x22, 0xc6, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, + 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x6f, 0x6e, + 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x4f, 0x6e, + 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4c, 0x6f, 0x67, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x12, 0x52, 0x0a, 0x1a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x17, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x15, + 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, + 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x22, 0x42, 0x0a, 0x18, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x50, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, + 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x4e, 0x0a, 0x1c, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x1d, 0x4c, 0x61, 0x75, 0x6e, 0x63, + 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x76, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, + 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, + 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, + 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xff, 0x02, 0x0a, 0x19, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, + 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x42, 0x0a, 0x1e, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x5f, 0x61, 0x66, 0x74, 0x65, + 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, + 0x65, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x57, 0x69, 0x74, 0x68, 0x4f, 0x77, + 0x6e, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x1e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x18, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x1b, - 0x0a, 0x19, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdd, 0x05, 0x0a, 0x14, - 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, - 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, - 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, - 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x69, 0x6d, - 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, 0x6c, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, 0x6c, 0x12, 0x26, 0x0a, 0x0f, - 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, 0x65, 0x72, - 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x6f, 0x72, - 0x65, 0x69, 0x67, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x64, 0x72, 0x6f, 0x70, 0x46, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x73, - 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, - 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, - 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6e, 0x6f, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x16, - 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, + 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x22, 0x4c, 0x0a, 0x1f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, + 0x56, 0x0a, 0x18, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x08, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x4d, 0x61, 0x74, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdd, 0x05, 0x0a, 0x14, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6b, - 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, - 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x65, 0x65, 0x70, - 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, - 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, - 0x79, 0x52, 0x75, 0x6e, 0x22, 0x5b, 0x0a, 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x43, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, - 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x73, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, - 0x70, 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, - 0x6f, 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, - 0x70, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, - 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x4d, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x6e, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x22, 0x19, 0x0a, 0x17, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x0a, 0x10, 0x4d, - 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x68, 0x6f, - 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, - 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, - 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x70, - 0x6f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, - 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, - 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4d, 0x6f, 0x75, 0x6e, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x11, - 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xbb, 0x06, 0x0a, 0x17, 0x4d, 0x6f, 0x76, 0x65, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, - 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, - 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x23, - 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, + 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, + 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, + 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x5f, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6c, 0x6c, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, + 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x15, - 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, - 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, + 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x5f, 0x6b, 0x65, - 0x79, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x72, 0x6f, 0x70, 0x46, 0x6f, + 0x79, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x72, 0x6f, 0x70, 0x46, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, + 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, - 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, - 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x12, + 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6e, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, - 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, - 0x6f, 0x70, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x43, 0x6f, 0x70, 0x79, 0x22, 0xd5, 0x01, 0x0a, 0x18, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x48, 0x0a, 0x07, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, - 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xe9, 0x01, - 0x0a, 0x19, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, - 0x12, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x52, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0x5e, 0x0a, 0x1a, 0x4d, 0x6f, 0x76, - 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, 0x52, - 0x75, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x4d, 0x0a, 0x11, 0x50, 0x69, 0x6e, - 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, - 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x50, 0x69, 0x6e, 0x67, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x89, - 0x02, 0x0a, 0x1b, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x12, 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, - 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x0d, 0x61, 0x76, 0x6f, 0x69, - 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0c, 0x61, 0x76, 0x6f, 0x69, 0x64, 0x50, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x44, 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xba, 0x01, 0x0a, 0x1c, 0x50, - 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, - 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0f, - 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, - 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x74, 0x0a, 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x1e, 0x0a, - 0x1c, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, - 0x1a, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x47, - 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, - 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x4f, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x75, 0x6c, 0x65, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, + 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, + 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0x5b, 0x0a, + 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, + 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x70, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x0a, 0x10, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x68, 0x6f, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, + 0x11, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x70, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x11, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x22, 0xbb, 0x06, 0x0a, 0x17, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, + 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, + 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, + 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, + 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6e, 0x5f, 0x64, 0x64, + 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, 0x6c, 0x12, 0x26, + 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x70, + 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, 0x66, 0x74, + 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x66, + 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x64, 0x72, 0x6f, 0x70, 0x46, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x4b, 0x65, + 0x79, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x6f, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6e, + 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x43, 0x6f, 0x70, 0x79, 0x22, 0xd5, + 0x01, 0x0a, 0x18, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x48, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, + 0x55, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, + 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x0a, 0x1a, 0x52, 0x65, 0x66, - 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, - 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, - 0x83, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2c, 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, - 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x50, - 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x36, 0x0a, - 0x17, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, - 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x4f, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa9, - 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, - 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, - 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x46, 0x0a, 0x1c, 0x52, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, - 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x81, 0x02, 0x0a, 0x19, 0x4d, 0x6f, 0x76, 0x65, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, + 0x70, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, + 0x65, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, + 0x75, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, + 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, + 0x75, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0x5e, 0x0a, 0x1a, 0x4d, 0x6f, + 0x76, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, + 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x4d, 0x0a, 0x11, 0x50, 0x69, + 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x50, 0x69, 0x6e, + 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xd7, 0x02, 0x0a, 0x1b, 0x50, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, - 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x22, 0x43, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, - 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, + 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, + 0x65, 0x77, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x0d, 0x61, 0x76, 0x6f, + 0x69, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0c, 0x61, 0x76, 0x6f, 0x69, 0x64, 0x50, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x44, 0x0a, 0x15, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x61, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x4c, 0x0a, 0x19, 0x74, + 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x17, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x67, 0x22, 0xba, 0x01, 0x0a, 0x1c, 0x50, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x40, 0x0a, 0x10, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0f, 0x70, + 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x26, + 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x5b, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x0a, 0x19, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x1c, 0x0a, 0x1a, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, - 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x16, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x74, 0x0a, 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, - 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, - 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x1e, 0x0a, 0x1c, + 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x47, + 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, 0x1a, + 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, + 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, + 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x4f, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x22, 0x7b, 0x0a, 0x16, 0x52, - 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x8f, 0x04, 0x0a, 0x14, 0x52, 0x65, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1a, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x23, - 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, - 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, - 0x63, 0x6f, 0x70, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x6b, 0x69, 0x70, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6e, - 0x5f, 0x64, 0x64, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, - 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x70, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, - 0x41, 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, - 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, - 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x82, 0x02, 0x0a, 0x18, 0x52, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x12, 0x2d, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x70, - 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x54, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, - 0x3e, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x12, 0x72, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, - 0xad, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, - 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, - 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, - 0x4d, 0x0a, 0x1b, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x0a, 0x1a, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x22, 0x83, + 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, + 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, + 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x73, 0x50, 0x61, + 0x72, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x17, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x22, 0x4f, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa9, 0x01, + 0x0a, 0x1b, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x69, + 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, + 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, + 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x46, 0x0a, 0x1c, 0x52, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, + 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xdd, - 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x75, 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x40, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x74, 0x72, - 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x61, 0x69, 0x74, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, + 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x22, 0x43, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, + 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x5b, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x0a, 0x19, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x43, 0x65, 0x6c, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x16, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, + 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, + 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, + 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x46, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, + 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x22, 0x7b, 0x0a, 0x16, 0x52, 0x65, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x8f, 0x04, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1a, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x23, 0x0a, + 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x28, 0x0a, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x63, + 0x6f, 0x70, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6e, 0x5f, + 0x64, 0x64, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x6e, 0x44, 0x64, 0x6c, + 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x63, + 0x6f, 0x70, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x41, + 0x66, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x66, 0x65, + 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, + 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x61, 0x75, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x82, 0x02, 0x0a, 0x18, 0x52, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x12, 0x2d, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x6f, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x54, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x3e, + 0x0a, 0x14, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, + 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x12, 0x72, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x54, 0x6f, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xad, + 0x01, 0x0a, 0x19, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x75, 0x74, 0x69, + 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x4d, + 0x0a, 0x1b, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xdd, 0x01, + 0x0a, 0x1c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x75, + 0x0a, 0x16, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x13, 0x72, 0x6f, 0x77, 0x73, 0x41, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x46, 0x0a, 0x18, 0x52, 0x6f, 0x77, 0x73, 0x41, 0x66, - 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x51, - 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x0a, 0x22, 0x53, - 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2b, 0x0a, - 0x11, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x55, 0x0a, 0x23, 0x53, 0x65, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x51, 0x0a, + 0x15, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x22, 0x18, 0x0a, 0x16, 0x52, 0x75, 0x6e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x0a, 0x22, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x35, - 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x4f, 0x0a, 0x1d, - 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x5e, 0x0a, - 0x1e, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x51, 0x0a, - 0x1f, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x11, + 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x55, 0x0a, 0x23, 0x53, 0x65, 0x74, + 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x75, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x22, 0x72, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x22, 0x5e, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x22, 0x51, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x22, 0x72, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, + 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x22, 0x49, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x22, 0x8e, 0x02, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x6e, 0x67, 0x22, 0x49, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x49, 0x73, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, - 0x8e, 0x02, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, - 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, - 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x22, 0x46, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x6a, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x57, - 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, - 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x72, 0x69, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, 0x72, 0x69, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x88, 0x01, 0x0a, 0x1a, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, + 0x6c, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, + 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x22, 0x46, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x6a, 0x0a, 0x12, 0x53, + 0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, + 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, + 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x57, 0x72, + 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x88, + 0x01, 0x0a, 0x1a, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, + 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x62, 0x0a, 0x1a, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x54, 0x0a, 0x1b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x38, 0x0a, 0x0c, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x62, 0x0a, 0x1a, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x6c, 0x6c, 0x22, 0x54, 0x0a, 0x1b, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x78, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0x54, 0x0a, 0x20, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0xaa, 0x03, 0x0a, 0x21, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x14, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x46, 0x69, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x54, 0x0a, 0x20, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0xaa, 0x03, 0x0a, 0x21, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, + 0x0a, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x4d, 0x61, 0x70, 0x1a, 0x5f, 0x0a, 0x18, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, - 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x74, 0x63, 0x74, - 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4d, 0x61, - 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4d, 0x61, - 0x70, 0x1a, 0x5f, 0x0a, 0x18, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x4e, 0x0a, 0x0e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x8b, 0x01, 0x0a, 0x1d, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, - 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x22, 0x20, 0x0a, 0x1e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x7c, 0x0a, 0x12, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x12, 0x2c, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x15, 0x0a, 0x13, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf0, 0x01, 0x0a, 0x15, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x4b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x3f, 0x0a, 0x16, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x5e, 0x0a, 0x18, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4e, 0x0a, 0x0e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8b, 0x01, 0x0a, 0x1d, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x42, 0x0a, 0x19, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, - 0x53, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x52, 0x0a, 0x16, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x52, 0x0a, 0x21, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x22, 0xc6, 0x01, 0x0a, 0x22, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, - 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x22, 0x20, 0x0a, 0x1e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7c, 0x0a, 0x12, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x50, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x52, 0x0a, 0x6f, 0x6c, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x5c, 0x0a, 0x15, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x65, 0x6c, - 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, - 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x5d, 0x0a, 0x16, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x65, 0x6c, 0x6c, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, - 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x64, 0x0a, 0x17, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x0a, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, - 0x65, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x35, 0x0a, 0x0b, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x63, 0x65, 0x6c, 0x6c, - 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x34, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, - 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0xfb, 0x01, 0x0a, - 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x62, 0x0a, 0x13, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x1a, - 0x69, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x58, 0x0a, 0x17, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x73, 0x22, 0xfc, 0x01, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x61, 0x0a, 0x10, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x63, - 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xd8, 0x01, 0x0a, 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, 0x65, 0x77, 0x73, 0x12, 0x26, 0x0a, - 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x6f, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x6f, 0x50, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x5f, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x88, - 0x02, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x67, 0x0a, 0x10, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x1a, 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, - 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, - 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6b, 0x0a, 0x14, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x31, 0x0a, 0x15, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x3c, 0x0a, 0x1e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8a, 0x02, 0x0a, 0x1f, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x68, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, - 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x38, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, - 0x98, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, + 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x2c, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf0, 0x01, 0x0a, 0x15, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x08, 0x6b, 0x65, 0x79, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x3f, 0x0a, + 0x16, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x5e, + 0x0a, 0x18, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x25, - 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x17, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x12, 0x60, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x76, 0x74, 0x63, - 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x42, + 0x0a, 0x19, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x6f, 0x70, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x22, 0x53, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, + 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x52, 0x0a, 0x16, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, + 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x52, 0x0a, 0x21, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x22, 0xc6, 0x01, 0x0a, 0x22, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, + 0x36, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x77, + 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x70, + 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x52, 0x0a, 0x6f, 0x6c, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, + 0x5c, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, + 0x63, 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x5d, 0x0a, + 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, + 0x65, 0x6c, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x64, 0x0a, 0x17, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x63, + 0x65, 0x6c, 0x6c, 0x73, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, + 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x22, 0x65, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x6c, 0x6c, + 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x5f, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x0a, 0x63, + 0x65, 0x6c, 0x6c, 0x73, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x34, 0x0a, 0x0f, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, + 0xfb, 0x01, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x62, + 0x0a, 0x13, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, + 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x11, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x1a, 0x69, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, + 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x58, 0x0a, + 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x67, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0xfc, 0x01, 0x0a, 0x18, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x61, + 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x1a, 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd8, 0x01, 0x0a, 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, 0x65, 0x77, 0x73, + 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x6f, 0x5f, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x4e, + 0x6f, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x22, 0x88, 0x02, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x67, + 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6b, 0x0a, 0x14, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, + 0x6e, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x73, 0x22, 0x31, 0x0a, 0x15, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x3c, 0x0a, 0x1e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x8a, 0x02, 0x0a, 0x1f, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x68, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, @@ -17397,269 +17537,315 @@ var file_vtctldata_proto_rawDesc = []byte{ 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x95, 0x06, 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, - 0x66, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x22, 0x38, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0xfa, 0x01, + 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x12, 0x60, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5f, 0x62, + 0x79, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, + 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x56, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x42, 0x79, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x1a, 0x63, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x42, 0x79, 0x53, 0x68, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x07, 0x0a, 0x12, 0x56, + 0x44, 0x69, 0x66, 0x66, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, + 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, + 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, + 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x55, 0x0a, 0x1e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x61, + 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x1b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, + 0x09, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x70, 0x5f, 0x6b, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x6f, 0x6e, 0x6c, 0x79, 0x50, 0x4b, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x52, 0x6f, 0x77, 0x73, 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x04, 0x77, 0x61, 0x69, 0x74, 0x12, 0x42, 0x0a, 0x14, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x77, 0x61, 0x69, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, + 0x6f, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, + 0x75, 0x74, 0x6f, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, + 0x6f, 0x73, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, + 0x73, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x3c, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x64, + 0x69, 0x66, 0x66, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x44, 0x69, 0x66, 0x66, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x0a, 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x55, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, + 0x22, 0x6b, 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, 0x66, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x72, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x67, 0x22, 0x15, 0x0a, + 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, + 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, + 0x75, 0x69, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x0a, 0x10, 0x56, 0x44, + 0x69, 0x66, 0x66, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, - 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x6c, 0x0a, 0x1b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x19, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x08, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x12, 0x55, 0x0a, 0x1e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x1b, 0x66, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x57, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x09, 0x6f, 0x6e, - 0x6c, 0x79, 0x5f, 0x70, 0x5f, 0x6b, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6f, - 0x6e, 0x6c, 0x79, 0x50, 0x4b, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, - 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x74, 0x72, - 0x61, 0x52, 0x6f, 0x77, 0x73, 0x54, 0x6f, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, - 0x69, 0x74, 0x12, 0x42, 0x0a, 0x14, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x12, 0x77, 0x61, 0x69, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, - 0x65, 0x74, 0x72, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x75, 0x74, 0x6f, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x22, - 0x29, 0x0a, 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, 0x22, 0x6b, 0x0a, 0x12, 0x56, 0x44, - 0x69, 0x66, 0x66, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x67, 0x22, 0x15, 0x0a, 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, - 0x0a, 0x12, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x15, 0x0a, - 0x13, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x69, 0x0a, 0x10, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, 0x68, 0x6f, - 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, - 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x72, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x67, 0x22, - 0xd7, 0x01, 0x0a, 0x11, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x31, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x44, 0x69, 0x66, - 0x66, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x73, 0x1a, 0x64, 0x0a, 0x14, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6b, 0x0a, 0x10, 0x56, 0x44, 0x69, - 0x66, 0x66, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x13, 0x0a, 0x11, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x15, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1b, 0x0a, - 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x65, - 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, - 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x16, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, - 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, - 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x72, 0x67, 0x22, 0xd7, 0x01, 0x0a, 0x11, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, + 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x64, 0x0a, 0x14, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x44, 0x69, 0x66, 0x66, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x6b, 0x0a, 0x10, 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, + 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x13, 0x0a, 0x11, + 0x56, 0x44, 0x69, 0x66, 0x66, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0xb2, 0x01, 0x0a, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x2c, 0x0a, 0x12, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, + 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x4f, 0x0a, 0x15, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x22, 0xe6, 0x07, - 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x10, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x33, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0xe8, 0x01, 0x0a, 0x0e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, - 0x6f, 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x72, 0x6f, 0x77, 0x73, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x09, 0x72, 0x6f, 0x77, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x72, - 0x6f, 0x77, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x02, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, - 0x70, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x02, 0x52, 0x0f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x61, 0x67, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x10, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, - 0x66, 0x6f, 0x1a, 0x5c, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, - 0x1a, 0x73, 0x0a, 0x13, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x46, 0x0a, 0x07, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x76, + 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x67, 0x0a, 0x15, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x22, 0xe6, 0x07, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, + 0x0a, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x6f, 0x0a, 0x11, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x76, 0x74, - 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7, 0x03, 0x0a, 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, - 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, - 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x4f, - 0x0a, 0x1b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x67, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, - 0x3c, 0x0a, 0x1a, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, - 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, - 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x07, 0x74, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, - 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, - 0x75, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, - 0x12, 0x3e, 0x0a, 0x1b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x73, - 0x22, 0xa7, 0x01, 0x0a, 0x1d, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x77, 0x69, - 0x74, 0x63, 0x68, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, - 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x15, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x5b, 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0d, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xd1, 0x01, - 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x12, 0x46, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x58, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0xe8, + 0x01, 0x0a, 0x0e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x72, 0x6f, 0x77, 0x73, 0x43, 0x6f, 0x70, 0x69, + 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x6f, 0x77, 0x73, 0x54, 0x6f, 0x74, 0x61, + 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x6f, 0x77, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0e, 0x72, 0x6f, 0x77, 0x73, + 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x12, 0x1f, 0x0a, + 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x29, + 0x0a, 0x10, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, + 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x10, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, + 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x1a, 0x5c, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x74, 0x63, 0x74, + 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0x73, 0x0a, 0x13, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, + 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, + 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x6f, 0x0a, 0x11, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xef, 0x03, 0x0a, + 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x54, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x1b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x6d, 0x61, 0x78, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x67, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x2a, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x3e, 0x0a, 0x1b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x22, 0xa7, + 0x01, 0x0a, 0x1d, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x72, 0x79, 0x52, 0x75, + 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x15, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x5b, + 0x0a, 0x0e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x56, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0d, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xd1, 0x01, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x0a, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, - 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x64, 0x2a, 0x4a, 0x0a, 0x15, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, - 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4d, 0x4f, 0x56, 0x45, 0x54, 0x41, - 0x42, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, - 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x02, 0x2a, 0x38, 0x0a, - 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x08, - 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x53, 0x43, 0x45, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x45, 0x53, 0x43, 0x45, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x42, 0x28, 0x5a, 0x26, 0x76, 0x69, 0x74, 0x65, 0x73, - 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, - 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x46, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x0a, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x06, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2a, + 0x4a, 0x0a, 0x15, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, + 0x4f, 0x4d, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4d, 0x4f, 0x56, 0x45, 0x54, 0x41, 0x42, 0x4c, + 0x45, 0x53, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x4c, 0x4f, + 0x4f, 0x4b, 0x55, 0x50, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x02, 0x2a, 0x38, 0x0a, 0x0d, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x08, 0x0a, 0x04, + 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x53, 0x43, 0x45, 0x4e, 0x44, + 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x45, 0x53, 0x43, 0x45, 0x4e, 0x44, + 0x49, 0x4e, 0x47, 0x10, 0x02, 0x42, 0x28, 0x5a, 0x26, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, + 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -17675,7 +17861,7 @@ func file_vtctldata_proto_rawDescGZIP() []byte { } var file_vtctldata_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_vtctldata_proto_msgTypes = make([]protoimpl.MessageInfo, 267) +var file_vtctldata_proto_msgTypes = make([]protoimpl.MessageInfo, 270) var file_vtctldata_proto_goTypes = []interface{}{ (MaterializationIntent)(0), // 0: vtctldata.MaterializationIntent (QueryOrdering)(0), // 1: vtctldata.QueryOrdering @@ -17738,117 +17924,117 @@ var file_vtctldata_proto_goTypes = []interface{}{ (*ExecuteHookResponse)(nil), // 58: vtctldata.ExecuteHookResponse (*FindAllShardsInKeyspaceRequest)(nil), // 59: vtctldata.FindAllShardsInKeyspaceRequest (*FindAllShardsInKeyspaceResponse)(nil), // 60: vtctldata.FindAllShardsInKeyspaceResponse - (*GetBackupsRequest)(nil), // 61: vtctldata.GetBackupsRequest - (*GetBackupsResponse)(nil), // 62: vtctldata.GetBackupsResponse - (*GetCellInfoRequest)(nil), // 63: vtctldata.GetCellInfoRequest - (*GetCellInfoResponse)(nil), // 64: vtctldata.GetCellInfoResponse - (*GetCellInfoNamesRequest)(nil), // 65: vtctldata.GetCellInfoNamesRequest - (*GetCellInfoNamesResponse)(nil), // 66: vtctldata.GetCellInfoNamesResponse - (*GetCellsAliasesRequest)(nil), // 67: vtctldata.GetCellsAliasesRequest - (*GetCellsAliasesResponse)(nil), // 68: vtctldata.GetCellsAliasesResponse - (*GetFullStatusRequest)(nil), // 69: vtctldata.GetFullStatusRequest - (*GetFullStatusResponse)(nil), // 70: vtctldata.GetFullStatusResponse - (*GetKeyspacesRequest)(nil), // 71: vtctldata.GetKeyspacesRequest - (*GetKeyspacesResponse)(nil), // 72: vtctldata.GetKeyspacesResponse - (*GetKeyspaceRequest)(nil), // 73: vtctldata.GetKeyspaceRequest - (*GetKeyspaceResponse)(nil), // 74: vtctldata.GetKeyspaceResponse - (*GetPermissionsRequest)(nil), // 75: vtctldata.GetPermissionsRequest - (*GetPermissionsResponse)(nil), // 76: vtctldata.GetPermissionsResponse - (*GetRoutingRulesRequest)(nil), // 77: vtctldata.GetRoutingRulesRequest - (*GetRoutingRulesResponse)(nil), // 78: vtctldata.GetRoutingRulesResponse - (*GetSchemaRequest)(nil), // 79: vtctldata.GetSchemaRequest - (*GetSchemaResponse)(nil), // 80: vtctldata.GetSchemaResponse - (*GetSchemaMigrationsRequest)(nil), // 81: vtctldata.GetSchemaMigrationsRequest - (*GetSchemaMigrationsResponse)(nil), // 82: vtctldata.GetSchemaMigrationsResponse - (*GetShardRequest)(nil), // 83: vtctldata.GetShardRequest - (*GetShardResponse)(nil), // 84: vtctldata.GetShardResponse - (*GetShardRoutingRulesRequest)(nil), // 85: vtctldata.GetShardRoutingRulesRequest - (*GetShardRoutingRulesResponse)(nil), // 86: vtctldata.GetShardRoutingRulesResponse - (*GetSrvKeyspaceNamesRequest)(nil), // 87: vtctldata.GetSrvKeyspaceNamesRequest - (*GetSrvKeyspaceNamesResponse)(nil), // 88: vtctldata.GetSrvKeyspaceNamesResponse - (*GetSrvKeyspacesRequest)(nil), // 89: vtctldata.GetSrvKeyspacesRequest - (*GetSrvKeyspacesResponse)(nil), // 90: vtctldata.GetSrvKeyspacesResponse - (*UpdateThrottlerConfigRequest)(nil), // 91: vtctldata.UpdateThrottlerConfigRequest - (*UpdateThrottlerConfigResponse)(nil), // 92: vtctldata.UpdateThrottlerConfigResponse - (*GetSrvVSchemaRequest)(nil), // 93: vtctldata.GetSrvVSchemaRequest - (*GetSrvVSchemaResponse)(nil), // 94: vtctldata.GetSrvVSchemaResponse - (*GetSrvVSchemasRequest)(nil), // 95: vtctldata.GetSrvVSchemasRequest - (*GetSrvVSchemasResponse)(nil), // 96: vtctldata.GetSrvVSchemasResponse - (*GetTabletRequest)(nil), // 97: vtctldata.GetTabletRequest - (*GetTabletResponse)(nil), // 98: vtctldata.GetTabletResponse - (*GetTabletsRequest)(nil), // 99: vtctldata.GetTabletsRequest - (*GetTabletsResponse)(nil), // 100: vtctldata.GetTabletsResponse - (*GetTopologyPathRequest)(nil), // 101: vtctldata.GetTopologyPathRequest - (*GetTopologyPathResponse)(nil), // 102: vtctldata.GetTopologyPathResponse - (*TopologyCell)(nil), // 103: vtctldata.TopologyCell - (*GetVSchemaRequest)(nil), // 104: vtctldata.GetVSchemaRequest - (*GetVersionRequest)(nil), // 105: vtctldata.GetVersionRequest - (*GetVersionResponse)(nil), // 106: vtctldata.GetVersionResponse - (*GetVSchemaResponse)(nil), // 107: vtctldata.GetVSchemaResponse - (*GetWorkflowsRequest)(nil), // 108: vtctldata.GetWorkflowsRequest - (*GetWorkflowsResponse)(nil), // 109: vtctldata.GetWorkflowsResponse - (*InitShardPrimaryRequest)(nil), // 110: vtctldata.InitShardPrimaryRequest - (*InitShardPrimaryResponse)(nil), // 111: vtctldata.InitShardPrimaryResponse - (*LaunchSchemaMigrationRequest)(nil), // 112: vtctldata.LaunchSchemaMigrationRequest - (*LaunchSchemaMigrationResponse)(nil), // 113: vtctldata.LaunchSchemaMigrationResponse - (*LookupVindexCreateRequest)(nil), // 114: vtctldata.LookupVindexCreateRequest - (*LookupVindexCreateResponse)(nil), // 115: vtctldata.LookupVindexCreateResponse - (*LookupVindexExternalizeRequest)(nil), // 116: vtctldata.LookupVindexExternalizeRequest - (*LookupVindexExternalizeResponse)(nil), // 117: vtctldata.LookupVindexExternalizeResponse - (*MaterializeCreateRequest)(nil), // 118: vtctldata.MaterializeCreateRequest - (*MaterializeCreateResponse)(nil), // 119: vtctldata.MaterializeCreateResponse - (*MigrateCreateRequest)(nil), // 120: vtctldata.MigrateCreateRequest - (*MigrateCompleteRequest)(nil), // 121: vtctldata.MigrateCompleteRequest - (*MigrateCompleteResponse)(nil), // 122: vtctldata.MigrateCompleteResponse - (*MountRegisterRequest)(nil), // 123: vtctldata.MountRegisterRequest - (*MountRegisterResponse)(nil), // 124: vtctldata.MountRegisterResponse - (*MountUnregisterRequest)(nil), // 125: vtctldata.MountUnregisterRequest - (*MountUnregisterResponse)(nil), // 126: vtctldata.MountUnregisterResponse - (*MountShowRequest)(nil), // 127: vtctldata.MountShowRequest - (*MountShowResponse)(nil), // 128: vtctldata.MountShowResponse - (*MountListRequest)(nil), // 129: vtctldata.MountListRequest - (*MountListResponse)(nil), // 130: vtctldata.MountListResponse - (*MoveTablesCreateRequest)(nil), // 131: vtctldata.MoveTablesCreateRequest - (*MoveTablesCreateResponse)(nil), // 132: vtctldata.MoveTablesCreateResponse - (*MoveTablesCompleteRequest)(nil), // 133: vtctldata.MoveTablesCompleteRequest - (*MoveTablesCompleteResponse)(nil), // 134: vtctldata.MoveTablesCompleteResponse - (*PingTabletRequest)(nil), // 135: vtctldata.PingTabletRequest - (*PingTabletResponse)(nil), // 136: vtctldata.PingTabletResponse - (*PlannedReparentShardRequest)(nil), // 137: vtctldata.PlannedReparentShardRequest - (*PlannedReparentShardResponse)(nil), // 138: vtctldata.PlannedReparentShardResponse - (*RebuildKeyspaceGraphRequest)(nil), // 139: vtctldata.RebuildKeyspaceGraphRequest - (*RebuildKeyspaceGraphResponse)(nil), // 140: vtctldata.RebuildKeyspaceGraphResponse - (*RebuildVSchemaGraphRequest)(nil), // 141: vtctldata.RebuildVSchemaGraphRequest - (*RebuildVSchemaGraphResponse)(nil), // 142: vtctldata.RebuildVSchemaGraphResponse - (*RefreshStateRequest)(nil), // 143: vtctldata.RefreshStateRequest - (*RefreshStateResponse)(nil), // 144: vtctldata.RefreshStateResponse - (*RefreshStateByShardRequest)(nil), // 145: vtctldata.RefreshStateByShardRequest - (*RefreshStateByShardResponse)(nil), // 146: vtctldata.RefreshStateByShardResponse - (*ReloadSchemaRequest)(nil), // 147: vtctldata.ReloadSchemaRequest - (*ReloadSchemaResponse)(nil), // 148: vtctldata.ReloadSchemaResponse - (*ReloadSchemaKeyspaceRequest)(nil), // 149: vtctldata.ReloadSchemaKeyspaceRequest - (*ReloadSchemaKeyspaceResponse)(nil), // 150: vtctldata.ReloadSchemaKeyspaceResponse - (*ReloadSchemaShardRequest)(nil), // 151: vtctldata.ReloadSchemaShardRequest - (*ReloadSchemaShardResponse)(nil), // 152: vtctldata.ReloadSchemaShardResponse - (*RemoveBackupRequest)(nil), // 153: vtctldata.RemoveBackupRequest - (*RemoveBackupResponse)(nil), // 154: vtctldata.RemoveBackupResponse - (*RemoveKeyspaceCellRequest)(nil), // 155: vtctldata.RemoveKeyspaceCellRequest - (*RemoveKeyspaceCellResponse)(nil), // 156: vtctldata.RemoveKeyspaceCellResponse - (*RemoveShardCellRequest)(nil), // 157: vtctldata.RemoveShardCellRequest - (*RemoveShardCellResponse)(nil), // 158: vtctldata.RemoveShardCellResponse - (*ReparentTabletRequest)(nil), // 159: vtctldata.ReparentTabletRequest - (*ReparentTabletResponse)(nil), // 160: vtctldata.ReparentTabletResponse - (*ReshardCreateRequest)(nil), // 161: vtctldata.ReshardCreateRequest - (*RestoreFromBackupRequest)(nil), // 162: vtctldata.RestoreFromBackupRequest - (*RestoreFromBackupResponse)(nil), // 163: vtctldata.RestoreFromBackupResponse - (*RetrySchemaMigrationRequest)(nil), // 164: vtctldata.RetrySchemaMigrationRequest - (*RetrySchemaMigrationResponse)(nil), // 165: vtctldata.RetrySchemaMigrationResponse - (*RunHealthCheckRequest)(nil), // 166: vtctldata.RunHealthCheckRequest - (*RunHealthCheckResponse)(nil), // 167: vtctldata.RunHealthCheckResponse - (*SetKeyspaceDurabilityPolicyRequest)(nil), // 168: vtctldata.SetKeyspaceDurabilityPolicyRequest - (*SetKeyspaceDurabilityPolicyResponse)(nil), // 169: vtctldata.SetKeyspaceDurabilityPolicyResponse - (*SetKeyspaceServedFromRequest)(nil), // 170: vtctldata.SetKeyspaceServedFromRequest - (*SetKeyspaceServedFromResponse)(nil), // 171: vtctldata.SetKeyspaceServedFromResponse + (*ForceCutOverSchemaMigrationRequest)(nil), // 61: vtctldata.ForceCutOverSchemaMigrationRequest + (*ForceCutOverSchemaMigrationResponse)(nil), // 62: vtctldata.ForceCutOverSchemaMigrationResponse + (*GetBackupsRequest)(nil), // 63: vtctldata.GetBackupsRequest + (*GetBackupsResponse)(nil), // 64: vtctldata.GetBackupsResponse + (*GetCellInfoRequest)(nil), // 65: vtctldata.GetCellInfoRequest + (*GetCellInfoResponse)(nil), // 66: vtctldata.GetCellInfoResponse + (*GetCellInfoNamesRequest)(nil), // 67: vtctldata.GetCellInfoNamesRequest + (*GetCellInfoNamesResponse)(nil), // 68: vtctldata.GetCellInfoNamesResponse + (*GetCellsAliasesRequest)(nil), // 69: vtctldata.GetCellsAliasesRequest + (*GetCellsAliasesResponse)(nil), // 70: vtctldata.GetCellsAliasesResponse + (*GetFullStatusRequest)(nil), // 71: vtctldata.GetFullStatusRequest + (*GetFullStatusResponse)(nil), // 72: vtctldata.GetFullStatusResponse + (*GetKeyspacesRequest)(nil), // 73: vtctldata.GetKeyspacesRequest + (*GetKeyspacesResponse)(nil), // 74: vtctldata.GetKeyspacesResponse + (*GetKeyspaceRequest)(nil), // 75: vtctldata.GetKeyspaceRequest + (*GetKeyspaceResponse)(nil), // 76: vtctldata.GetKeyspaceResponse + (*GetPermissionsRequest)(nil), // 77: vtctldata.GetPermissionsRequest + (*GetPermissionsResponse)(nil), // 78: vtctldata.GetPermissionsResponse + (*GetRoutingRulesRequest)(nil), // 79: vtctldata.GetRoutingRulesRequest + (*GetRoutingRulesResponse)(nil), // 80: vtctldata.GetRoutingRulesResponse + (*GetSchemaRequest)(nil), // 81: vtctldata.GetSchemaRequest + (*GetSchemaResponse)(nil), // 82: vtctldata.GetSchemaResponse + (*GetSchemaMigrationsRequest)(nil), // 83: vtctldata.GetSchemaMigrationsRequest + (*GetSchemaMigrationsResponse)(nil), // 84: vtctldata.GetSchemaMigrationsResponse + (*GetShardRequest)(nil), // 85: vtctldata.GetShardRequest + (*GetShardResponse)(nil), // 86: vtctldata.GetShardResponse + (*GetShardRoutingRulesRequest)(nil), // 87: vtctldata.GetShardRoutingRulesRequest + (*GetShardRoutingRulesResponse)(nil), // 88: vtctldata.GetShardRoutingRulesResponse + (*GetSrvKeyspaceNamesRequest)(nil), // 89: vtctldata.GetSrvKeyspaceNamesRequest + (*GetSrvKeyspaceNamesResponse)(nil), // 90: vtctldata.GetSrvKeyspaceNamesResponse + (*GetSrvKeyspacesRequest)(nil), // 91: vtctldata.GetSrvKeyspacesRequest + (*GetSrvKeyspacesResponse)(nil), // 92: vtctldata.GetSrvKeyspacesResponse + (*UpdateThrottlerConfigRequest)(nil), // 93: vtctldata.UpdateThrottlerConfigRequest + (*UpdateThrottlerConfigResponse)(nil), // 94: vtctldata.UpdateThrottlerConfigResponse + (*GetSrvVSchemaRequest)(nil), // 95: vtctldata.GetSrvVSchemaRequest + (*GetSrvVSchemaResponse)(nil), // 96: vtctldata.GetSrvVSchemaResponse + (*GetSrvVSchemasRequest)(nil), // 97: vtctldata.GetSrvVSchemasRequest + (*GetSrvVSchemasResponse)(nil), // 98: vtctldata.GetSrvVSchemasResponse + (*GetTabletRequest)(nil), // 99: vtctldata.GetTabletRequest + (*GetTabletResponse)(nil), // 100: vtctldata.GetTabletResponse + (*GetTabletsRequest)(nil), // 101: vtctldata.GetTabletsRequest + (*GetTabletsResponse)(nil), // 102: vtctldata.GetTabletsResponse + (*GetTopologyPathRequest)(nil), // 103: vtctldata.GetTopologyPathRequest + (*GetTopologyPathResponse)(nil), // 104: vtctldata.GetTopologyPathResponse + (*TopologyCell)(nil), // 105: vtctldata.TopologyCell + (*GetVSchemaRequest)(nil), // 106: vtctldata.GetVSchemaRequest + (*GetVersionRequest)(nil), // 107: vtctldata.GetVersionRequest + (*GetVersionResponse)(nil), // 108: vtctldata.GetVersionResponse + (*GetVSchemaResponse)(nil), // 109: vtctldata.GetVSchemaResponse + (*GetWorkflowsRequest)(nil), // 110: vtctldata.GetWorkflowsRequest + (*GetWorkflowsResponse)(nil), // 111: vtctldata.GetWorkflowsResponse + (*InitShardPrimaryRequest)(nil), // 112: vtctldata.InitShardPrimaryRequest + (*InitShardPrimaryResponse)(nil), // 113: vtctldata.InitShardPrimaryResponse + (*LaunchSchemaMigrationRequest)(nil), // 114: vtctldata.LaunchSchemaMigrationRequest + (*LaunchSchemaMigrationResponse)(nil), // 115: vtctldata.LaunchSchemaMigrationResponse + (*LookupVindexCreateRequest)(nil), // 116: vtctldata.LookupVindexCreateRequest + (*LookupVindexCreateResponse)(nil), // 117: vtctldata.LookupVindexCreateResponse + (*LookupVindexExternalizeRequest)(nil), // 118: vtctldata.LookupVindexExternalizeRequest + (*LookupVindexExternalizeResponse)(nil), // 119: vtctldata.LookupVindexExternalizeResponse + (*MaterializeCreateRequest)(nil), // 120: vtctldata.MaterializeCreateRequest + (*MaterializeCreateResponse)(nil), // 121: vtctldata.MaterializeCreateResponse + (*MigrateCreateRequest)(nil), // 122: vtctldata.MigrateCreateRequest + (*MigrateCompleteRequest)(nil), // 123: vtctldata.MigrateCompleteRequest + (*MigrateCompleteResponse)(nil), // 124: vtctldata.MigrateCompleteResponse + (*MountRegisterRequest)(nil), // 125: vtctldata.MountRegisterRequest + (*MountRegisterResponse)(nil), // 126: vtctldata.MountRegisterResponse + (*MountUnregisterRequest)(nil), // 127: vtctldata.MountUnregisterRequest + (*MountUnregisterResponse)(nil), // 128: vtctldata.MountUnregisterResponse + (*MountShowRequest)(nil), // 129: vtctldata.MountShowRequest + (*MountShowResponse)(nil), // 130: vtctldata.MountShowResponse + (*MountListRequest)(nil), // 131: vtctldata.MountListRequest + (*MountListResponse)(nil), // 132: vtctldata.MountListResponse + (*MoveTablesCreateRequest)(nil), // 133: vtctldata.MoveTablesCreateRequest + (*MoveTablesCreateResponse)(nil), // 134: vtctldata.MoveTablesCreateResponse + (*MoveTablesCompleteRequest)(nil), // 135: vtctldata.MoveTablesCompleteRequest + (*MoveTablesCompleteResponse)(nil), // 136: vtctldata.MoveTablesCompleteResponse + (*PingTabletRequest)(nil), // 137: vtctldata.PingTabletRequest + (*PingTabletResponse)(nil), // 138: vtctldata.PingTabletResponse + (*PlannedReparentShardRequest)(nil), // 139: vtctldata.PlannedReparentShardRequest + (*PlannedReparentShardResponse)(nil), // 140: vtctldata.PlannedReparentShardResponse + (*RebuildKeyspaceGraphRequest)(nil), // 141: vtctldata.RebuildKeyspaceGraphRequest + (*RebuildKeyspaceGraphResponse)(nil), // 142: vtctldata.RebuildKeyspaceGraphResponse + (*RebuildVSchemaGraphRequest)(nil), // 143: vtctldata.RebuildVSchemaGraphRequest + (*RebuildVSchemaGraphResponse)(nil), // 144: vtctldata.RebuildVSchemaGraphResponse + (*RefreshStateRequest)(nil), // 145: vtctldata.RefreshStateRequest + (*RefreshStateResponse)(nil), // 146: vtctldata.RefreshStateResponse + (*RefreshStateByShardRequest)(nil), // 147: vtctldata.RefreshStateByShardRequest + (*RefreshStateByShardResponse)(nil), // 148: vtctldata.RefreshStateByShardResponse + (*ReloadSchemaRequest)(nil), // 149: vtctldata.ReloadSchemaRequest + (*ReloadSchemaResponse)(nil), // 150: vtctldata.ReloadSchemaResponse + (*ReloadSchemaKeyspaceRequest)(nil), // 151: vtctldata.ReloadSchemaKeyspaceRequest + (*ReloadSchemaKeyspaceResponse)(nil), // 152: vtctldata.ReloadSchemaKeyspaceResponse + (*ReloadSchemaShardRequest)(nil), // 153: vtctldata.ReloadSchemaShardRequest + (*ReloadSchemaShardResponse)(nil), // 154: vtctldata.ReloadSchemaShardResponse + (*RemoveBackupRequest)(nil), // 155: vtctldata.RemoveBackupRequest + (*RemoveBackupResponse)(nil), // 156: vtctldata.RemoveBackupResponse + (*RemoveKeyspaceCellRequest)(nil), // 157: vtctldata.RemoveKeyspaceCellRequest + (*RemoveKeyspaceCellResponse)(nil), // 158: vtctldata.RemoveKeyspaceCellResponse + (*RemoveShardCellRequest)(nil), // 159: vtctldata.RemoveShardCellRequest + (*RemoveShardCellResponse)(nil), // 160: vtctldata.RemoveShardCellResponse + (*ReparentTabletRequest)(nil), // 161: vtctldata.ReparentTabletRequest + (*ReparentTabletResponse)(nil), // 162: vtctldata.ReparentTabletResponse + (*ReshardCreateRequest)(nil), // 163: vtctldata.ReshardCreateRequest + (*RestoreFromBackupRequest)(nil), // 164: vtctldata.RestoreFromBackupRequest + (*RestoreFromBackupResponse)(nil), // 165: vtctldata.RestoreFromBackupResponse + (*RetrySchemaMigrationRequest)(nil), // 166: vtctldata.RetrySchemaMigrationRequest + (*RetrySchemaMigrationResponse)(nil), // 167: vtctldata.RetrySchemaMigrationResponse + (*RunHealthCheckRequest)(nil), // 168: vtctldata.RunHealthCheckRequest + (*RunHealthCheckResponse)(nil), // 169: vtctldata.RunHealthCheckResponse + (*SetKeyspaceDurabilityPolicyRequest)(nil), // 170: vtctldata.SetKeyspaceDurabilityPolicyRequest + (*SetKeyspaceDurabilityPolicyResponse)(nil), // 171: vtctldata.SetKeyspaceDurabilityPolicyResponse (*SetKeyspaceShardingInfoRequest)(nil), // 172: vtctldata.SetKeyspaceShardingInfoRequest (*SetKeyspaceShardingInfoResponse)(nil), // 173: vtctldata.SetKeyspaceShardingInfoResponse (*SetShardIsPrimaryServingRequest)(nil), // 174: vtctldata.SetShardIsPrimaryServingRequest @@ -17921,281 +18107,287 @@ var file_vtctldata_proto_goTypes = []interface{}{ (*Workflow_Stream_Log)(nil), // 241: vtctldata.Workflow.Stream.Log (*Workflow_Stream_ThrottlerStatus)(nil), // 242: vtctldata.Workflow.Stream.ThrottlerStatus nil, // 243: vtctldata.ApplySchemaResponse.RowsAffectedByShardEntry - nil, // 244: vtctldata.CancelSchemaMigrationResponse.RowsAffectedByShardEntry - nil, // 245: vtctldata.CleanupSchemaMigrationResponse.RowsAffectedByShardEntry - nil, // 246: vtctldata.CompleteSchemaMigrationResponse.RowsAffectedByShardEntry - nil, // 247: vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry - nil, // 248: vtctldata.GetCellsAliasesResponse.AliasesEntry - nil, // 249: vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry - (*GetSrvKeyspaceNamesResponse_NameList)(nil), // 250: vtctldata.GetSrvKeyspaceNamesResponse.NameList - nil, // 251: vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry - nil, // 252: vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry - nil, // 253: vtctldata.LaunchSchemaMigrationResponse.RowsAffectedByShardEntry - (*MoveTablesCreateResponse_TabletInfo)(nil), // 254: vtctldata.MoveTablesCreateResponse.TabletInfo - nil, // 255: vtctldata.RetrySchemaMigrationResponse.RowsAffectedByShardEntry - nil, // 256: vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry - nil, // 257: vtctldata.ShardReplicationPositionsResponse.TabletMapEntry - nil, // 258: vtctldata.ValidateResponse.ResultsByKeyspaceEntry - nil, // 259: vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry - nil, // 260: vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry - nil, // 261: vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry - nil, // 262: vtctldata.ValidateVSchemaResponse.ResultsByShardEntry - nil, // 263: vtctldata.VDiffShowResponse.TabletResponsesEntry - (*WorkflowDeleteResponse_TabletInfo)(nil), // 264: vtctldata.WorkflowDeleteResponse.TabletInfo - (*WorkflowStatusResponse_TableCopyState)(nil), // 265: vtctldata.WorkflowStatusResponse.TableCopyState - (*WorkflowStatusResponse_ShardStreamState)(nil), // 266: vtctldata.WorkflowStatusResponse.ShardStreamState - (*WorkflowStatusResponse_ShardStreams)(nil), // 267: vtctldata.WorkflowStatusResponse.ShardStreams - nil, // 268: vtctldata.WorkflowStatusResponse.TableCopyStateEntry - nil, // 269: vtctldata.WorkflowStatusResponse.ShardStreamsEntry - (*WorkflowUpdateResponse_TabletInfo)(nil), // 270: vtctldata.WorkflowUpdateResponse.TabletInfo - (*logutil.Event)(nil), // 271: logutil.Event - (tabletmanagerdata.TabletSelectionPreference)(0), // 272: tabletmanagerdata.TabletSelectionPreference - (*topodata.Keyspace)(nil), // 273: topodata.Keyspace - (*vttime.Time)(nil), // 274: vttime.Time - (*topodata.TabletAlias)(nil), // 275: topodata.TabletAlias - (*vttime.Duration)(nil), // 276: vttime.Duration - (*topodata.Shard)(nil), // 277: topodata.Shard - (*topodata.CellInfo)(nil), // 278: topodata.CellInfo - (*vschema.RoutingRules)(nil), // 279: vschema.RoutingRules - (*vschema.ShardRoutingRules)(nil), // 280: vschema.ShardRoutingRules - (*vtrpc.CallerID)(nil), // 281: vtrpc.CallerID - (*vschema.Keyspace)(nil), // 282: vschema.Keyspace - (topodata.TabletType)(0), // 283: topodata.TabletType - (*topodata.Tablet)(nil), // 284: topodata.Tablet - (*topodata.Keyspace_ServedFrom)(nil), // 285: topodata.Keyspace.ServedFrom - (topodata.KeyspaceType)(0), // 286: topodata.KeyspaceType - (*query.QueryResult)(nil), // 287: query.QueryResult - (*tabletmanagerdata.ExecuteHookRequest)(nil), // 288: tabletmanagerdata.ExecuteHookRequest - (*tabletmanagerdata.ExecuteHookResponse)(nil), // 289: tabletmanagerdata.ExecuteHookResponse - (*mysqlctl.BackupInfo)(nil), // 290: mysqlctl.BackupInfo - (*replicationdata.FullStatus)(nil), // 291: replicationdata.FullStatus - (*tabletmanagerdata.Permissions)(nil), // 292: tabletmanagerdata.Permissions - (*tabletmanagerdata.SchemaDefinition)(nil), // 293: tabletmanagerdata.SchemaDefinition - (*topodata.ThrottledAppRule)(nil), // 294: topodata.ThrottledAppRule - (*vschema.SrvVSchema)(nil), // 295: vschema.SrvVSchema - (*topodata.ShardReplicationError)(nil), // 296: topodata.ShardReplicationError - (*topodata.KeyRange)(nil), // 297: topodata.KeyRange - (*topodata.CellsAlias)(nil), // 298: topodata.CellsAlias - (*tabletmanagerdata.UpdateVReplicationWorkflowRequest)(nil), // 299: tabletmanagerdata.UpdateVReplicationWorkflowRequest - (*topodata.Shard_TabletControl)(nil), // 300: topodata.Shard.TabletControl - (*binlogdata.BinlogSource)(nil), // 301: binlogdata.BinlogSource - (*topodata.SrvKeyspace)(nil), // 302: topodata.SrvKeyspace - (*replicationdata.Status)(nil), // 303: replicationdata.Status - (*tabletmanagerdata.VDiffResponse)(nil), // 304: tabletmanagerdata.VDiffResponse + nil, // 244: vtctldata.ApplyVSchemaResponse.UnknownVindexParamsEntry + (*ApplyVSchemaResponse_ParamList)(nil), // 245: vtctldata.ApplyVSchemaResponse.ParamList + nil, // 246: vtctldata.CancelSchemaMigrationResponse.RowsAffectedByShardEntry + nil, // 247: vtctldata.CleanupSchemaMigrationResponse.RowsAffectedByShardEntry + nil, // 248: vtctldata.CompleteSchemaMigrationResponse.RowsAffectedByShardEntry + nil, // 249: vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry + nil, // 250: vtctldata.ForceCutOverSchemaMigrationResponse.RowsAffectedByShardEntry + nil, // 251: vtctldata.GetCellsAliasesResponse.AliasesEntry + nil, // 252: vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry + (*GetSrvKeyspaceNamesResponse_NameList)(nil), // 253: vtctldata.GetSrvKeyspaceNamesResponse.NameList + nil, // 254: vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry + nil, // 255: vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry + nil, // 256: vtctldata.LaunchSchemaMigrationResponse.RowsAffectedByShardEntry + (*MoveTablesCreateResponse_TabletInfo)(nil), // 257: vtctldata.MoveTablesCreateResponse.TabletInfo + nil, // 258: vtctldata.RetrySchemaMigrationResponse.RowsAffectedByShardEntry + nil, // 259: vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry + nil, // 260: vtctldata.ShardReplicationPositionsResponse.TabletMapEntry + nil, // 261: vtctldata.ValidateResponse.ResultsByKeyspaceEntry + nil, // 262: vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry + nil, // 263: vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry + nil, // 264: vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry + nil, // 265: vtctldata.ValidateVSchemaResponse.ResultsByShardEntry + nil, // 266: vtctldata.VDiffShowResponse.TabletResponsesEntry + (*WorkflowDeleteResponse_TabletInfo)(nil), // 267: vtctldata.WorkflowDeleteResponse.TabletInfo + (*WorkflowStatusResponse_TableCopyState)(nil), // 268: vtctldata.WorkflowStatusResponse.TableCopyState + (*WorkflowStatusResponse_ShardStreamState)(nil), // 269: vtctldata.WorkflowStatusResponse.ShardStreamState + (*WorkflowStatusResponse_ShardStreams)(nil), // 270: vtctldata.WorkflowStatusResponse.ShardStreams + nil, // 271: vtctldata.WorkflowStatusResponse.TableCopyStateEntry + nil, // 272: vtctldata.WorkflowStatusResponse.ShardStreamsEntry + (*WorkflowUpdateResponse_TabletInfo)(nil), // 273: vtctldata.WorkflowUpdateResponse.TabletInfo + (*logutil.Event)(nil), // 274: logutil.Event + (tabletmanagerdata.TabletSelectionPreference)(0), // 275: tabletmanagerdata.TabletSelectionPreference + (*topodata.Keyspace)(nil), // 276: topodata.Keyspace + (*vttime.Time)(nil), // 277: vttime.Time + (*topodata.TabletAlias)(nil), // 278: topodata.TabletAlias + (*vttime.Duration)(nil), // 279: vttime.Duration + (*topodata.Shard)(nil), // 280: topodata.Shard + (*topodata.CellInfo)(nil), // 281: topodata.CellInfo + (*vschema.RoutingRules)(nil), // 282: vschema.RoutingRules + (*vschema.ShardRoutingRules)(nil), // 283: vschema.ShardRoutingRules + (*vtrpc.CallerID)(nil), // 284: vtrpc.CallerID + (*vschema.Keyspace)(nil), // 285: vschema.Keyspace + (topodata.TabletType)(0), // 286: topodata.TabletType + (*topodata.Tablet)(nil), // 287: topodata.Tablet + (topodata.KeyspaceType)(0), // 288: topodata.KeyspaceType + (*query.QueryResult)(nil), // 289: query.QueryResult + (*tabletmanagerdata.ExecuteHookRequest)(nil), // 290: tabletmanagerdata.ExecuteHookRequest + (*tabletmanagerdata.ExecuteHookResponse)(nil), // 291: tabletmanagerdata.ExecuteHookResponse + (*mysqlctl.BackupInfo)(nil), // 292: mysqlctl.BackupInfo + (*replicationdata.FullStatus)(nil), // 293: replicationdata.FullStatus + (*tabletmanagerdata.Permissions)(nil), // 294: tabletmanagerdata.Permissions + (*tabletmanagerdata.SchemaDefinition)(nil), // 295: tabletmanagerdata.SchemaDefinition + (*topodata.ThrottledAppRule)(nil), // 296: topodata.ThrottledAppRule + (*vschema.SrvVSchema)(nil), // 297: vschema.SrvVSchema + (*topodata.ShardReplicationError)(nil), // 298: topodata.ShardReplicationError + (*topodata.KeyRange)(nil), // 299: topodata.KeyRange + (*topodata.CellsAlias)(nil), // 300: topodata.CellsAlias + (*tabletmanagerdata.UpdateVReplicationWorkflowRequest)(nil), // 301: tabletmanagerdata.UpdateVReplicationWorkflowRequest + (*topodata.Shard_TabletControl)(nil), // 302: topodata.Shard.TabletControl + (*binlogdata.BinlogSource)(nil), // 303: binlogdata.BinlogSource + (*topodata.SrvKeyspace)(nil), // 304: topodata.SrvKeyspace + (*replicationdata.Status)(nil), // 305: replicationdata.Status + (*tabletmanagerdata.VDiffResponse)(nil), // 306: tabletmanagerdata.VDiffResponse } var file_vtctldata_proto_depIdxs = []int32{ - 271, // 0: vtctldata.ExecuteVtctlCommandResponse.event:type_name -> logutil.Event + 274, // 0: vtctldata.ExecuteVtctlCommandResponse.event:type_name -> logutil.Event 6, // 1: vtctldata.MaterializeSettings.table_settings:type_name -> vtctldata.TableMaterializeSettings 0, // 2: vtctldata.MaterializeSettings.materialization_intent:type_name -> vtctldata.MaterializationIntent - 272, // 3: vtctldata.MaterializeSettings.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 273, // 4: vtctldata.Keyspace.keyspace:type_name -> topodata.Keyspace + 275, // 3: vtctldata.MaterializeSettings.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 276, // 4: vtctldata.Keyspace.keyspace:type_name -> topodata.Keyspace 2, // 5: vtctldata.SchemaMigration.strategy:type_name -> vtctldata.SchemaMigration.Strategy - 274, // 6: vtctldata.SchemaMigration.added_at:type_name -> vttime.Time - 274, // 7: vtctldata.SchemaMigration.requested_at:type_name -> vttime.Time - 274, // 8: vtctldata.SchemaMigration.ready_at:type_name -> vttime.Time - 274, // 9: vtctldata.SchemaMigration.started_at:type_name -> vttime.Time - 274, // 10: vtctldata.SchemaMigration.liveness_timestamp:type_name -> vttime.Time - 274, // 11: vtctldata.SchemaMigration.completed_at:type_name -> vttime.Time - 274, // 12: vtctldata.SchemaMigration.cleaned_up_at:type_name -> vttime.Time + 277, // 6: vtctldata.SchemaMigration.added_at:type_name -> vttime.Time + 277, // 7: vtctldata.SchemaMigration.requested_at:type_name -> vttime.Time + 277, // 8: vtctldata.SchemaMigration.ready_at:type_name -> vttime.Time + 277, // 9: vtctldata.SchemaMigration.started_at:type_name -> vttime.Time + 277, // 10: vtctldata.SchemaMigration.liveness_timestamp:type_name -> vttime.Time + 277, // 11: vtctldata.SchemaMigration.completed_at:type_name -> vttime.Time + 277, // 12: vtctldata.SchemaMigration.cleaned_up_at:type_name -> vttime.Time 3, // 13: vtctldata.SchemaMigration.status:type_name -> vtctldata.SchemaMigration.Status - 275, // 14: vtctldata.SchemaMigration.tablet:type_name -> topodata.TabletAlias - 276, // 15: vtctldata.SchemaMigration.artifact_retention:type_name -> vttime.Duration - 274, // 16: vtctldata.SchemaMigration.last_throttled_at:type_name -> vttime.Time - 274, // 17: vtctldata.SchemaMigration.cancelled_at:type_name -> vttime.Time - 274, // 18: vtctldata.SchemaMigration.reviewed_at:type_name -> vttime.Time - 274, // 19: vtctldata.SchemaMigration.ready_to_complete_at:type_name -> vttime.Time - 277, // 20: vtctldata.Shard.shard:type_name -> topodata.Shard + 278, // 14: vtctldata.SchemaMigration.tablet:type_name -> topodata.TabletAlias + 279, // 15: vtctldata.SchemaMigration.artifact_retention:type_name -> vttime.Duration + 277, // 16: vtctldata.SchemaMigration.last_throttled_at:type_name -> vttime.Time + 277, // 17: vtctldata.SchemaMigration.cancelled_at:type_name -> vttime.Time + 277, // 18: vtctldata.SchemaMigration.reviewed_at:type_name -> vttime.Time + 277, // 19: vtctldata.SchemaMigration.ready_to_complete_at:type_name -> vttime.Time + 280, // 20: vtctldata.Shard.shard:type_name -> topodata.Shard 237, // 21: vtctldata.Workflow.source:type_name -> vtctldata.Workflow.ReplicationLocation 237, // 22: vtctldata.Workflow.target:type_name -> vtctldata.Workflow.ReplicationLocation 236, // 23: vtctldata.Workflow.shard_streams:type_name -> vtctldata.Workflow.ShardStreamsEntry - 278, // 24: vtctldata.AddCellInfoRequest.cell_info:type_name -> topodata.CellInfo - 279, // 25: vtctldata.ApplyRoutingRulesRequest.routing_rules:type_name -> vschema.RoutingRules - 280, // 26: vtctldata.ApplyShardRoutingRulesRequest.shard_routing_rules:type_name -> vschema.ShardRoutingRules - 276, // 27: vtctldata.ApplySchemaRequest.wait_replicas_timeout:type_name -> vttime.Duration - 281, // 28: vtctldata.ApplySchemaRequest.caller_id:type_name -> vtrpc.CallerID + 281, // 24: vtctldata.AddCellInfoRequest.cell_info:type_name -> topodata.CellInfo + 282, // 25: vtctldata.ApplyRoutingRulesRequest.routing_rules:type_name -> vschema.RoutingRules + 283, // 26: vtctldata.ApplyShardRoutingRulesRequest.shard_routing_rules:type_name -> vschema.ShardRoutingRules + 279, // 27: vtctldata.ApplySchemaRequest.wait_replicas_timeout:type_name -> vttime.Duration + 284, // 28: vtctldata.ApplySchemaRequest.caller_id:type_name -> vtrpc.CallerID 243, // 29: vtctldata.ApplySchemaResponse.rows_affected_by_shard:type_name -> vtctldata.ApplySchemaResponse.RowsAffectedByShardEntry - 282, // 30: vtctldata.ApplyVSchemaRequest.v_schema:type_name -> vschema.Keyspace - 282, // 31: vtctldata.ApplyVSchemaResponse.v_schema:type_name -> vschema.Keyspace - 275, // 32: vtctldata.BackupRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 33: vtctldata.BackupResponse.tablet_alias:type_name -> topodata.TabletAlias - 271, // 34: vtctldata.BackupResponse.event:type_name -> logutil.Event - 244, // 35: vtctldata.CancelSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CancelSchemaMigrationResponse.RowsAffectedByShardEntry - 275, // 36: vtctldata.ChangeTabletTypeRequest.tablet_alias:type_name -> topodata.TabletAlias - 283, // 37: vtctldata.ChangeTabletTypeRequest.db_type:type_name -> topodata.TabletType - 284, // 38: vtctldata.ChangeTabletTypeResponse.before_tablet:type_name -> topodata.Tablet - 284, // 39: vtctldata.ChangeTabletTypeResponse.after_tablet:type_name -> topodata.Tablet - 245, // 40: vtctldata.CleanupSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CleanupSchemaMigrationResponse.RowsAffectedByShardEntry - 246, // 41: vtctldata.CompleteSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CompleteSchemaMigrationResponse.RowsAffectedByShardEntry - 285, // 42: vtctldata.CreateKeyspaceRequest.served_froms:type_name -> topodata.Keyspace.ServedFrom - 286, // 43: vtctldata.CreateKeyspaceRequest.type:type_name -> topodata.KeyspaceType - 274, // 44: vtctldata.CreateKeyspaceRequest.snapshot_time:type_name -> vttime.Time + 285, // 30: vtctldata.ApplyVSchemaRequest.v_schema:type_name -> vschema.Keyspace + 285, // 31: vtctldata.ApplyVSchemaResponse.v_schema:type_name -> vschema.Keyspace + 244, // 32: vtctldata.ApplyVSchemaResponse.unknown_vindex_params:type_name -> vtctldata.ApplyVSchemaResponse.UnknownVindexParamsEntry + 278, // 33: vtctldata.BackupRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 34: vtctldata.BackupResponse.tablet_alias:type_name -> topodata.TabletAlias + 274, // 35: vtctldata.BackupResponse.event:type_name -> logutil.Event + 246, // 36: vtctldata.CancelSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CancelSchemaMigrationResponse.RowsAffectedByShardEntry + 278, // 37: vtctldata.ChangeTabletTypeRequest.tablet_alias:type_name -> topodata.TabletAlias + 286, // 38: vtctldata.ChangeTabletTypeRequest.db_type:type_name -> topodata.TabletType + 287, // 39: vtctldata.ChangeTabletTypeResponse.before_tablet:type_name -> topodata.Tablet + 287, // 40: vtctldata.ChangeTabletTypeResponse.after_tablet:type_name -> topodata.Tablet + 247, // 41: vtctldata.CleanupSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CleanupSchemaMigrationResponse.RowsAffectedByShardEntry + 248, // 42: vtctldata.CompleteSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.CompleteSchemaMigrationResponse.RowsAffectedByShardEntry + 288, // 43: vtctldata.CreateKeyspaceRequest.type:type_name -> topodata.KeyspaceType + 277, // 44: vtctldata.CreateKeyspaceRequest.snapshot_time:type_name -> vttime.Time 8, // 45: vtctldata.CreateKeyspaceResponse.keyspace:type_name -> vtctldata.Keyspace 8, // 46: vtctldata.CreateShardResponse.keyspace:type_name -> vtctldata.Keyspace 10, // 47: vtctldata.CreateShardResponse.shard:type_name -> vtctldata.Shard 10, // 48: vtctldata.DeleteShardsRequest.shards:type_name -> vtctldata.Shard - 275, // 49: vtctldata.DeleteTabletsRequest.tablet_aliases:type_name -> topodata.TabletAlias - 275, // 50: vtctldata.EmergencyReparentShardRequest.new_primary:type_name -> topodata.TabletAlias - 275, // 51: vtctldata.EmergencyReparentShardRequest.ignore_replicas:type_name -> topodata.TabletAlias - 276, // 52: vtctldata.EmergencyReparentShardRequest.wait_replicas_timeout:type_name -> vttime.Duration - 275, // 53: vtctldata.EmergencyReparentShardResponse.promoted_primary:type_name -> topodata.TabletAlias - 271, // 54: vtctldata.EmergencyReparentShardResponse.events:type_name -> logutil.Event - 275, // 55: vtctldata.ExecuteFetchAsAppRequest.tablet_alias:type_name -> topodata.TabletAlias - 287, // 56: vtctldata.ExecuteFetchAsAppResponse.result:type_name -> query.QueryResult - 275, // 57: vtctldata.ExecuteFetchAsDBARequest.tablet_alias:type_name -> topodata.TabletAlias - 287, // 58: vtctldata.ExecuteFetchAsDBAResponse.result:type_name -> query.QueryResult - 275, // 59: vtctldata.ExecuteHookRequest.tablet_alias:type_name -> topodata.TabletAlias - 288, // 60: vtctldata.ExecuteHookRequest.tablet_hook_request:type_name -> tabletmanagerdata.ExecuteHookRequest - 289, // 61: vtctldata.ExecuteHookResponse.hook_result:type_name -> tabletmanagerdata.ExecuteHookResponse - 247, // 62: vtctldata.FindAllShardsInKeyspaceResponse.shards:type_name -> vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry - 290, // 63: vtctldata.GetBackupsResponse.backups:type_name -> mysqlctl.BackupInfo - 278, // 64: vtctldata.GetCellInfoResponse.cell_info:type_name -> topodata.CellInfo - 248, // 65: vtctldata.GetCellsAliasesResponse.aliases:type_name -> vtctldata.GetCellsAliasesResponse.AliasesEntry - 275, // 66: vtctldata.GetFullStatusRequest.tablet_alias:type_name -> topodata.TabletAlias - 291, // 67: vtctldata.GetFullStatusResponse.status:type_name -> replicationdata.FullStatus - 8, // 68: vtctldata.GetKeyspacesResponse.keyspaces:type_name -> vtctldata.Keyspace - 8, // 69: vtctldata.GetKeyspaceResponse.keyspace:type_name -> vtctldata.Keyspace - 275, // 70: vtctldata.GetPermissionsRequest.tablet_alias:type_name -> topodata.TabletAlias - 292, // 71: vtctldata.GetPermissionsResponse.permissions:type_name -> tabletmanagerdata.Permissions - 279, // 72: vtctldata.GetRoutingRulesResponse.routing_rules:type_name -> vschema.RoutingRules - 275, // 73: vtctldata.GetSchemaRequest.tablet_alias:type_name -> topodata.TabletAlias - 293, // 74: vtctldata.GetSchemaResponse.schema:type_name -> tabletmanagerdata.SchemaDefinition - 3, // 75: vtctldata.GetSchemaMigrationsRequest.status:type_name -> vtctldata.SchemaMigration.Status - 276, // 76: vtctldata.GetSchemaMigrationsRequest.recent:type_name -> vttime.Duration - 1, // 77: vtctldata.GetSchemaMigrationsRequest.order:type_name -> vtctldata.QueryOrdering - 9, // 78: vtctldata.GetSchemaMigrationsResponse.migrations:type_name -> vtctldata.SchemaMigration - 10, // 79: vtctldata.GetShardResponse.shard:type_name -> vtctldata.Shard - 280, // 80: vtctldata.GetShardRoutingRulesResponse.shard_routing_rules:type_name -> vschema.ShardRoutingRules - 249, // 81: vtctldata.GetSrvKeyspaceNamesResponse.names:type_name -> vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry - 251, // 82: vtctldata.GetSrvKeyspacesResponse.srv_keyspaces:type_name -> vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry - 294, // 83: vtctldata.UpdateThrottlerConfigRequest.throttled_app:type_name -> topodata.ThrottledAppRule - 295, // 84: vtctldata.GetSrvVSchemaResponse.srv_v_schema:type_name -> vschema.SrvVSchema - 252, // 85: vtctldata.GetSrvVSchemasResponse.srv_v_schemas:type_name -> vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry - 275, // 86: vtctldata.GetTabletRequest.tablet_alias:type_name -> topodata.TabletAlias - 284, // 87: vtctldata.GetTabletResponse.tablet:type_name -> topodata.Tablet - 275, // 88: vtctldata.GetTabletsRequest.tablet_aliases:type_name -> topodata.TabletAlias - 283, // 89: vtctldata.GetTabletsRequest.tablet_type:type_name -> topodata.TabletType - 284, // 90: vtctldata.GetTabletsResponse.tablets:type_name -> topodata.Tablet - 103, // 91: vtctldata.GetTopologyPathResponse.cell:type_name -> vtctldata.TopologyCell - 275, // 92: vtctldata.GetVersionRequest.tablet_alias:type_name -> topodata.TabletAlias - 282, // 93: vtctldata.GetVSchemaResponse.v_schema:type_name -> vschema.Keyspace - 11, // 94: vtctldata.GetWorkflowsResponse.workflows:type_name -> vtctldata.Workflow - 275, // 95: vtctldata.InitShardPrimaryRequest.primary_elect_tablet_alias:type_name -> topodata.TabletAlias - 276, // 96: vtctldata.InitShardPrimaryRequest.wait_replicas_timeout:type_name -> vttime.Duration - 271, // 97: vtctldata.InitShardPrimaryResponse.events:type_name -> logutil.Event - 253, // 98: vtctldata.LaunchSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.LaunchSchemaMigrationResponse.RowsAffectedByShardEntry - 282, // 99: vtctldata.LookupVindexCreateRequest.vindex:type_name -> vschema.Keyspace - 283, // 100: vtctldata.LookupVindexCreateRequest.tablet_types:type_name -> topodata.TabletType - 272, // 101: vtctldata.LookupVindexCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 7, // 102: vtctldata.MaterializeCreateRequest.settings:type_name -> vtctldata.MaterializeSettings - 283, // 103: vtctldata.MigrateCreateRequest.tablet_types:type_name -> topodata.TabletType - 272, // 104: vtctldata.MigrateCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 283, // 105: vtctldata.MoveTablesCreateRequest.tablet_types:type_name -> topodata.TabletType - 272, // 106: vtctldata.MoveTablesCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 254, // 107: vtctldata.MoveTablesCreateResponse.details:type_name -> vtctldata.MoveTablesCreateResponse.TabletInfo - 275, // 108: vtctldata.PingTabletRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 109: vtctldata.PlannedReparentShardRequest.new_primary:type_name -> topodata.TabletAlias - 275, // 110: vtctldata.PlannedReparentShardRequest.avoid_primary:type_name -> topodata.TabletAlias - 276, // 111: vtctldata.PlannedReparentShardRequest.wait_replicas_timeout:type_name -> vttime.Duration - 275, // 112: vtctldata.PlannedReparentShardResponse.promoted_primary:type_name -> topodata.TabletAlias - 271, // 113: vtctldata.PlannedReparentShardResponse.events:type_name -> logutil.Event - 275, // 114: vtctldata.RefreshStateRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 115: vtctldata.ReloadSchemaRequest.tablet_alias:type_name -> topodata.TabletAlias - 271, // 116: vtctldata.ReloadSchemaKeyspaceResponse.events:type_name -> logutil.Event - 271, // 117: vtctldata.ReloadSchemaShardResponse.events:type_name -> logutil.Event - 275, // 118: vtctldata.ReparentTabletRequest.tablet:type_name -> topodata.TabletAlias - 275, // 119: vtctldata.ReparentTabletResponse.primary:type_name -> topodata.TabletAlias - 283, // 120: vtctldata.ReshardCreateRequest.tablet_types:type_name -> topodata.TabletType - 272, // 121: vtctldata.ReshardCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 275, // 122: vtctldata.RestoreFromBackupRequest.tablet_alias:type_name -> topodata.TabletAlias - 274, // 123: vtctldata.RestoreFromBackupRequest.backup_time:type_name -> vttime.Time - 274, // 124: vtctldata.RestoreFromBackupRequest.restore_to_timestamp:type_name -> vttime.Time - 275, // 125: vtctldata.RestoreFromBackupResponse.tablet_alias:type_name -> topodata.TabletAlias - 271, // 126: vtctldata.RestoreFromBackupResponse.event:type_name -> logutil.Event - 255, // 127: vtctldata.RetrySchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.RetrySchemaMigrationResponse.RowsAffectedByShardEntry - 275, // 128: vtctldata.RunHealthCheckRequest.tablet_alias:type_name -> topodata.TabletAlias - 273, // 129: vtctldata.SetKeyspaceDurabilityPolicyResponse.keyspace:type_name -> topodata.Keyspace - 283, // 130: vtctldata.SetKeyspaceServedFromRequest.tablet_type:type_name -> topodata.TabletType - 273, // 131: vtctldata.SetKeyspaceServedFromResponse.keyspace:type_name -> topodata.Keyspace - 273, // 132: vtctldata.SetKeyspaceShardingInfoResponse.keyspace:type_name -> topodata.Keyspace - 277, // 133: vtctldata.SetShardIsPrimaryServingResponse.shard:type_name -> topodata.Shard - 283, // 134: vtctldata.SetShardTabletControlRequest.tablet_type:type_name -> topodata.TabletType - 277, // 135: vtctldata.SetShardTabletControlResponse.shard:type_name -> topodata.Shard - 275, // 136: vtctldata.SetWritableRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 137: vtctldata.ShardReplicationAddRequest.tablet_alias:type_name -> topodata.TabletAlias - 296, // 138: vtctldata.ShardReplicationFixResponse.error:type_name -> topodata.ShardReplicationError - 256, // 139: vtctldata.ShardReplicationPositionsResponse.replication_statuses:type_name -> vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry - 257, // 140: vtctldata.ShardReplicationPositionsResponse.tablet_map:type_name -> vtctldata.ShardReplicationPositionsResponse.TabletMapEntry - 275, // 141: vtctldata.ShardReplicationRemoveRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 142: vtctldata.SleepTabletRequest.tablet_alias:type_name -> topodata.TabletAlias - 276, // 143: vtctldata.SleepTabletRequest.duration:type_name -> vttime.Duration - 297, // 144: vtctldata.SourceShardAddRequest.key_range:type_name -> topodata.KeyRange - 277, // 145: vtctldata.SourceShardAddResponse.shard:type_name -> topodata.Shard - 277, // 146: vtctldata.SourceShardDeleteResponse.shard:type_name -> topodata.Shard - 275, // 147: vtctldata.StartReplicationRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 148: vtctldata.StopReplicationRequest.tablet_alias:type_name -> topodata.TabletAlias - 275, // 149: vtctldata.TabletExternallyReparentedRequest.tablet:type_name -> topodata.TabletAlias - 275, // 150: vtctldata.TabletExternallyReparentedResponse.new_primary:type_name -> topodata.TabletAlias - 275, // 151: vtctldata.TabletExternallyReparentedResponse.old_primary:type_name -> topodata.TabletAlias - 278, // 152: vtctldata.UpdateCellInfoRequest.cell_info:type_name -> topodata.CellInfo - 278, // 153: vtctldata.UpdateCellInfoResponse.cell_info:type_name -> topodata.CellInfo - 298, // 154: vtctldata.UpdateCellsAliasRequest.cells_alias:type_name -> topodata.CellsAlias - 298, // 155: vtctldata.UpdateCellsAliasResponse.cells_alias:type_name -> topodata.CellsAlias - 258, // 156: vtctldata.ValidateResponse.results_by_keyspace:type_name -> vtctldata.ValidateResponse.ResultsByKeyspaceEntry - 259, // 157: vtctldata.ValidateKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry - 260, // 158: vtctldata.ValidateSchemaKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry - 261, // 159: vtctldata.ValidateVersionKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry - 262, // 160: vtctldata.ValidateVSchemaResponse.results_by_shard:type_name -> vtctldata.ValidateVSchemaResponse.ResultsByShardEntry - 283, // 161: vtctldata.VDiffCreateRequest.tablet_types:type_name -> topodata.TabletType - 272, // 162: vtctldata.VDiffCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference - 276, // 163: vtctldata.VDiffCreateRequest.filtered_replication_wait_time:type_name -> vttime.Duration - 276, // 164: vtctldata.VDiffCreateRequest.wait_update_interval:type_name -> vttime.Duration - 263, // 165: vtctldata.VDiffShowResponse.tablet_responses:type_name -> vtctldata.VDiffShowResponse.TabletResponsesEntry - 264, // 166: vtctldata.WorkflowDeleteResponse.details:type_name -> vtctldata.WorkflowDeleteResponse.TabletInfo - 268, // 167: vtctldata.WorkflowStatusResponse.table_copy_state:type_name -> vtctldata.WorkflowStatusResponse.TableCopyStateEntry - 269, // 168: vtctldata.WorkflowStatusResponse.shard_streams:type_name -> vtctldata.WorkflowStatusResponse.ShardStreamsEntry - 283, // 169: vtctldata.WorkflowSwitchTrafficRequest.tablet_types:type_name -> topodata.TabletType - 276, // 170: vtctldata.WorkflowSwitchTrafficRequest.max_replication_lag_allowed:type_name -> vttime.Duration - 276, // 171: vtctldata.WorkflowSwitchTrafficRequest.timeout:type_name -> vttime.Duration - 299, // 172: vtctldata.WorkflowUpdateRequest.tablet_request:type_name -> tabletmanagerdata.UpdateVReplicationWorkflowRequest - 270, // 173: vtctldata.WorkflowUpdateResponse.details:type_name -> vtctldata.WorkflowUpdateResponse.TabletInfo - 238, // 174: vtctldata.Workflow.ShardStreamsEntry.value:type_name -> vtctldata.Workflow.ShardStream - 239, // 175: vtctldata.Workflow.ShardStream.streams:type_name -> vtctldata.Workflow.Stream - 300, // 176: vtctldata.Workflow.ShardStream.tablet_controls:type_name -> topodata.Shard.TabletControl - 275, // 177: vtctldata.Workflow.Stream.tablet:type_name -> topodata.TabletAlias - 301, // 178: vtctldata.Workflow.Stream.binlog_source:type_name -> binlogdata.BinlogSource - 274, // 179: vtctldata.Workflow.Stream.transaction_timestamp:type_name -> vttime.Time - 274, // 180: vtctldata.Workflow.Stream.time_updated:type_name -> vttime.Time - 240, // 181: vtctldata.Workflow.Stream.copy_states:type_name -> vtctldata.Workflow.Stream.CopyState - 241, // 182: vtctldata.Workflow.Stream.logs:type_name -> vtctldata.Workflow.Stream.Log - 242, // 183: vtctldata.Workflow.Stream.throttler_status:type_name -> vtctldata.Workflow.Stream.ThrottlerStatus - 274, // 184: vtctldata.Workflow.Stream.Log.created_at:type_name -> vttime.Time - 274, // 185: vtctldata.Workflow.Stream.Log.updated_at:type_name -> vttime.Time - 274, // 186: vtctldata.Workflow.Stream.ThrottlerStatus.time_throttled:type_name -> vttime.Time - 10, // 187: vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry.value:type_name -> vtctldata.Shard - 298, // 188: vtctldata.GetCellsAliasesResponse.AliasesEntry.value:type_name -> topodata.CellsAlias - 250, // 189: vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry.value:type_name -> vtctldata.GetSrvKeyspaceNamesResponse.NameList - 302, // 190: vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry.value:type_name -> topodata.SrvKeyspace - 295, // 191: vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry.value:type_name -> vschema.SrvVSchema - 275, // 192: vtctldata.MoveTablesCreateResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias - 303, // 193: vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry.value:type_name -> replicationdata.Status - 284, // 194: vtctldata.ShardReplicationPositionsResponse.TabletMapEntry.value:type_name -> topodata.Tablet - 207, // 195: vtctldata.ValidateResponse.ResultsByKeyspaceEntry.value:type_name -> vtctldata.ValidateKeyspaceResponse - 211, // 196: vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse - 211, // 197: vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse - 211, // 198: vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse - 211, // 199: vtctldata.ValidateVSchemaResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse - 304, // 200: vtctldata.VDiffShowResponse.TabletResponsesEntry.value:type_name -> tabletmanagerdata.VDiffResponse - 275, // 201: vtctldata.WorkflowDeleteResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias - 275, // 202: vtctldata.WorkflowStatusResponse.ShardStreamState.tablet:type_name -> topodata.TabletAlias - 266, // 203: vtctldata.WorkflowStatusResponse.ShardStreams.streams:type_name -> vtctldata.WorkflowStatusResponse.ShardStreamState - 265, // 204: vtctldata.WorkflowStatusResponse.TableCopyStateEntry.value:type_name -> vtctldata.WorkflowStatusResponse.TableCopyState - 267, // 205: vtctldata.WorkflowStatusResponse.ShardStreamsEntry.value:type_name -> vtctldata.WorkflowStatusResponse.ShardStreams - 275, // 206: vtctldata.WorkflowUpdateResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias - 207, // [207:207] is the sub-list for method output_type - 207, // [207:207] is the sub-list for method input_type - 207, // [207:207] is the sub-list for extension type_name - 207, // [207:207] is the sub-list for extension extendee - 0, // [0:207] is the sub-list for field type_name + 278, // 49: vtctldata.DeleteTabletsRequest.tablet_aliases:type_name -> topodata.TabletAlias + 278, // 50: vtctldata.EmergencyReparentShardRequest.new_primary:type_name -> topodata.TabletAlias + 278, // 51: vtctldata.EmergencyReparentShardRequest.ignore_replicas:type_name -> topodata.TabletAlias + 279, // 52: vtctldata.EmergencyReparentShardRequest.wait_replicas_timeout:type_name -> vttime.Duration + 278, // 53: vtctldata.EmergencyReparentShardResponse.promoted_primary:type_name -> topodata.TabletAlias + 274, // 54: vtctldata.EmergencyReparentShardResponse.events:type_name -> logutil.Event + 278, // 55: vtctldata.ExecuteFetchAsAppRequest.tablet_alias:type_name -> topodata.TabletAlias + 289, // 56: vtctldata.ExecuteFetchAsAppResponse.result:type_name -> query.QueryResult + 278, // 57: vtctldata.ExecuteFetchAsDBARequest.tablet_alias:type_name -> topodata.TabletAlias + 289, // 58: vtctldata.ExecuteFetchAsDBAResponse.result:type_name -> query.QueryResult + 278, // 59: vtctldata.ExecuteHookRequest.tablet_alias:type_name -> topodata.TabletAlias + 290, // 60: vtctldata.ExecuteHookRequest.tablet_hook_request:type_name -> tabletmanagerdata.ExecuteHookRequest + 291, // 61: vtctldata.ExecuteHookResponse.hook_result:type_name -> tabletmanagerdata.ExecuteHookResponse + 249, // 62: vtctldata.FindAllShardsInKeyspaceResponse.shards:type_name -> vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry + 250, // 63: vtctldata.ForceCutOverSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.ForceCutOverSchemaMigrationResponse.RowsAffectedByShardEntry + 292, // 64: vtctldata.GetBackupsResponse.backups:type_name -> mysqlctl.BackupInfo + 281, // 65: vtctldata.GetCellInfoResponse.cell_info:type_name -> topodata.CellInfo + 251, // 66: vtctldata.GetCellsAliasesResponse.aliases:type_name -> vtctldata.GetCellsAliasesResponse.AliasesEntry + 278, // 67: vtctldata.GetFullStatusRequest.tablet_alias:type_name -> topodata.TabletAlias + 293, // 68: vtctldata.GetFullStatusResponse.status:type_name -> replicationdata.FullStatus + 8, // 69: vtctldata.GetKeyspacesResponse.keyspaces:type_name -> vtctldata.Keyspace + 8, // 70: vtctldata.GetKeyspaceResponse.keyspace:type_name -> vtctldata.Keyspace + 278, // 71: vtctldata.GetPermissionsRequest.tablet_alias:type_name -> topodata.TabletAlias + 294, // 72: vtctldata.GetPermissionsResponse.permissions:type_name -> tabletmanagerdata.Permissions + 282, // 73: vtctldata.GetRoutingRulesResponse.routing_rules:type_name -> vschema.RoutingRules + 278, // 74: vtctldata.GetSchemaRequest.tablet_alias:type_name -> topodata.TabletAlias + 295, // 75: vtctldata.GetSchemaResponse.schema:type_name -> tabletmanagerdata.SchemaDefinition + 3, // 76: vtctldata.GetSchemaMigrationsRequest.status:type_name -> vtctldata.SchemaMigration.Status + 279, // 77: vtctldata.GetSchemaMigrationsRequest.recent:type_name -> vttime.Duration + 1, // 78: vtctldata.GetSchemaMigrationsRequest.order:type_name -> vtctldata.QueryOrdering + 9, // 79: vtctldata.GetSchemaMigrationsResponse.migrations:type_name -> vtctldata.SchemaMigration + 10, // 80: vtctldata.GetShardResponse.shard:type_name -> vtctldata.Shard + 283, // 81: vtctldata.GetShardRoutingRulesResponse.shard_routing_rules:type_name -> vschema.ShardRoutingRules + 252, // 82: vtctldata.GetSrvKeyspaceNamesResponse.names:type_name -> vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry + 254, // 83: vtctldata.GetSrvKeyspacesResponse.srv_keyspaces:type_name -> vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry + 296, // 84: vtctldata.UpdateThrottlerConfigRequest.throttled_app:type_name -> topodata.ThrottledAppRule + 297, // 85: vtctldata.GetSrvVSchemaResponse.srv_v_schema:type_name -> vschema.SrvVSchema + 255, // 86: vtctldata.GetSrvVSchemasResponse.srv_v_schemas:type_name -> vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry + 278, // 87: vtctldata.GetTabletRequest.tablet_alias:type_name -> topodata.TabletAlias + 287, // 88: vtctldata.GetTabletResponse.tablet:type_name -> topodata.Tablet + 278, // 89: vtctldata.GetTabletsRequest.tablet_aliases:type_name -> topodata.TabletAlias + 286, // 90: vtctldata.GetTabletsRequest.tablet_type:type_name -> topodata.TabletType + 287, // 91: vtctldata.GetTabletsResponse.tablets:type_name -> topodata.Tablet + 105, // 92: vtctldata.GetTopologyPathResponse.cell:type_name -> vtctldata.TopologyCell + 278, // 93: vtctldata.GetVersionRequest.tablet_alias:type_name -> topodata.TabletAlias + 285, // 94: vtctldata.GetVSchemaResponse.v_schema:type_name -> vschema.Keyspace + 11, // 95: vtctldata.GetWorkflowsResponse.workflows:type_name -> vtctldata.Workflow + 278, // 96: vtctldata.InitShardPrimaryRequest.primary_elect_tablet_alias:type_name -> topodata.TabletAlias + 279, // 97: vtctldata.InitShardPrimaryRequest.wait_replicas_timeout:type_name -> vttime.Duration + 274, // 98: vtctldata.InitShardPrimaryResponse.events:type_name -> logutil.Event + 256, // 99: vtctldata.LaunchSchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.LaunchSchemaMigrationResponse.RowsAffectedByShardEntry + 285, // 100: vtctldata.LookupVindexCreateRequest.vindex:type_name -> vschema.Keyspace + 286, // 101: vtctldata.LookupVindexCreateRequest.tablet_types:type_name -> topodata.TabletType + 275, // 102: vtctldata.LookupVindexCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 7, // 103: vtctldata.MaterializeCreateRequest.settings:type_name -> vtctldata.MaterializeSettings + 286, // 104: vtctldata.MigrateCreateRequest.tablet_types:type_name -> topodata.TabletType + 275, // 105: vtctldata.MigrateCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 286, // 106: vtctldata.MoveTablesCreateRequest.tablet_types:type_name -> topodata.TabletType + 275, // 107: vtctldata.MoveTablesCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 257, // 108: vtctldata.MoveTablesCreateResponse.details:type_name -> vtctldata.MoveTablesCreateResponse.TabletInfo + 278, // 109: vtctldata.PingTabletRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 110: vtctldata.PlannedReparentShardRequest.new_primary:type_name -> topodata.TabletAlias + 278, // 111: vtctldata.PlannedReparentShardRequest.avoid_primary:type_name -> topodata.TabletAlias + 279, // 112: vtctldata.PlannedReparentShardRequest.wait_replicas_timeout:type_name -> vttime.Duration + 279, // 113: vtctldata.PlannedReparentShardRequest.tolerable_replication_lag:type_name -> vttime.Duration + 278, // 114: vtctldata.PlannedReparentShardResponse.promoted_primary:type_name -> topodata.TabletAlias + 274, // 115: vtctldata.PlannedReparentShardResponse.events:type_name -> logutil.Event + 278, // 116: vtctldata.RefreshStateRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 117: vtctldata.ReloadSchemaRequest.tablet_alias:type_name -> topodata.TabletAlias + 274, // 118: vtctldata.ReloadSchemaKeyspaceResponse.events:type_name -> logutil.Event + 274, // 119: vtctldata.ReloadSchemaShardResponse.events:type_name -> logutil.Event + 278, // 120: vtctldata.ReparentTabletRequest.tablet:type_name -> topodata.TabletAlias + 278, // 121: vtctldata.ReparentTabletResponse.primary:type_name -> topodata.TabletAlias + 286, // 122: vtctldata.ReshardCreateRequest.tablet_types:type_name -> topodata.TabletType + 275, // 123: vtctldata.ReshardCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 278, // 124: vtctldata.RestoreFromBackupRequest.tablet_alias:type_name -> topodata.TabletAlias + 277, // 125: vtctldata.RestoreFromBackupRequest.backup_time:type_name -> vttime.Time + 277, // 126: vtctldata.RestoreFromBackupRequest.restore_to_timestamp:type_name -> vttime.Time + 278, // 127: vtctldata.RestoreFromBackupResponse.tablet_alias:type_name -> topodata.TabletAlias + 274, // 128: vtctldata.RestoreFromBackupResponse.event:type_name -> logutil.Event + 258, // 129: vtctldata.RetrySchemaMigrationResponse.rows_affected_by_shard:type_name -> vtctldata.RetrySchemaMigrationResponse.RowsAffectedByShardEntry + 278, // 130: vtctldata.RunHealthCheckRequest.tablet_alias:type_name -> topodata.TabletAlias + 276, // 131: vtctldata.SetKeyspaceDurabilityPolicyResponse.keyspace:type_name -> topodata.Keyspace + 276, // 132: vtctldata.SetKeyspaceShardingInfoResponse.keyspace:type_name -> topodata.Keyspace + 280, // 133: vtctldata.SetShardIsPrimaryServingResponse.shard:type_name -> topodata.Shard + 286, // 134: vtctldata.SetShardTabletControlRequest.tablet_type:type_name -> topodata.TabletType + 280, // 135: vtctldata.SetShardTabletControlResponse.shard:type_name -> topodata.Shard + 278, // 136: vtctldata.SetWritableRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 137: vtctldata.ShardReplicationAddRequest.tablet_alias:type_name -> topodata.TabletAlias + 298, // 138: vtctldata.ShardReplicationFixResponse.error:type_name -> topodata.ShardReplicationError + 259, // 139: vtctldata.ShardReplicationPositionsResponse.replication_statuses:type_name -> vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry + 260, // 140: vtctldata.ShardReplicationPositionsResponse.tablet_map:type_name -> vtctldata.ShardReplicationPositionsResponse.TabletMapEntry + 278, // 141: vtctldata.ShardReplicationRemoveRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 142: vtctldata.SleepTabletRequest.tablet_alias:type_name -> topodata.TabletAlias + 279, // 143: vtctldata.SleepTabletRequest.duration:type_name -> vttime.Duration + 299, // 144: vtctldata.SourceShardAddRequest.key_range:type_name -> topodata.KeyRange + 280, // 145: vtctldata.SourceShardAddResponse.shard:type_name -> topodata.Shard + 280, // 146: vtctldata.SourceShardDeleteResponse.shard:type_name -> topodata.Shard + 278, // 147: vtctldata.StartReplicationRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 148: vtctldata.StopReplicationRequest.tablet_alias:type_name -> topodata.TabletAlias + 278, // 149: vtctldata.TabletExternallyReparentedRequest.tablet:type_name -> topodata.TabletAlias + 278, // 150: vtctldata.TabletExternallyReparentedResponse.new_primary:type_name -> topodata.TabletAlias + 278, // 151: vtctldata.TabletExternallyReparentedResponse.old_primary:type_name -> topodata.TabletAlias + 281, // 152: vtctldata.UpdateCellInfoRequest.cell_info:type_name -> topodata.CellInfo + 281, // 153: vtctldata.UpdateCellInfoResponse.cell_info:type_name -> topodata.CellInfo + 300, // 154: vtctldata.UpdateCellsAliasRequest.cells_alias:type_name -> topodata.CellsAlias + 300, // 155: vtctldata.UpdateCellsAliasResponse.cells_alias:type_name -> topodata.CellsAlias + 261, // 156: vtctldata.ValidateResponse.results_by_keyspace:type_name -> vtctldata.ValidateResponse.ResultsByKeyspaceEntry + 262, // 157: vtctldata.ValidateKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry + 263, // 158: vtctldata.ValidateSchemaKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry + 264, // 159: vtctldata.ValidateVersionKeyspaceResponse.results_by_shard:type_name -> vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry + 265, // 160: vtctldata.ValidateVSchemaResponse.results_by_shard:type_name -> vtctldata.ValidateVSchemaResponse.ResultsByShardEntry + 286, // 161: vtctldata.VDiffCreateRequest.tablet_types:type_name -> topodata.TabletType + 275, // 162: vtctldata.VDiffCreateRequest.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 279, // 163: vtctldata.VDiffCreateRequest.filtered_replication_wait_time:type_name -> vttime.Duration + 279, // 164: vtctldata.VDiffCreateRequest.wait_update_interval:type_name -> vttime.Duration + 279, // 165: vtctldata.VDiffCreateRequest.max_diff_duration:type_name -> vttime.Duration + 266, // 166: vtctldata.VDiffShowResponse.tablet_responses:type_name -> vtctldata.VDiffShowResponse.TabletResponsesEntry + 267, // 167: vtctldata.WorkflowDeleteResponse.details:type_name -> vtctldata.WorkflowDeleteResponse.TabletInfo + 271, // 168: vtctldata.WorkflowStatusResponse.table_copy_state:type_name -> vtctldata.WorkflowStatusResponse.TableCopyStateEntry + 272, // 169: vtctldata.WorkflowStatusResponse.shard_streams:type_name -> vtctldata.WorkflowStatusResponse.ShardStreamsEntry + 286, // 170: vtctldata.WorkflowSwitchTrafficRequest.tablet_types:type_name -> topodata.TabletType + 279, // 171: vtctldata.WorkflowSwitchTrafficRequest.max_replication_lag_allowed:type_name -> vttime.Duration + 279, // 172: vtctldata.WorkflowSwitchTrafficRequest.timeout:type_name -> vttime.Duration + 301, // 173: vtctldata.WorkflowUpdateRequest.tablet_request:type_name -> tabletmanagerdata.UpdateVReplicationWorkflowRequest + 273, // 174: vtctldata.WorkflowUpdateResponse.details:type_name -> vtctldata.WorkflowUpdateResponse.TabletInfo + 238, // 175: vtctldata.Workflow.ShardStreamsEntry.value:type_name -> vtctldata.Workflow.ShardStream + 239, // 176: vtctldata.Workflow.ShardStream.streams:type_name -> vtctldata.Workflow.Stream + 302, // 177: vtctldata.Workflow.ShardStream.tablet_controls:type_name -> topodata.Shard.TabletControl + 278, // 178: vtctldata.Workflow.Stream.tablet:type_name -> topodata.TabletAlias + 303, // 179: vtctldata.Workflow.Stream.binlog_source:type_name -> binlogdata.BinlogSource + 277, // 180: vtctldata.Workflow.Stream.transaction_timestamp:type_name -> vttime.Time + 277, // 181: vtctldata.Workflow.Stream.time_updated:type_name -> vttime.Time + 240, // 182: vtctldata.Workflow.Stream.copy_states:type_name -> vtctldata.Workflow.Stream.CopyState + 241, // 183: vtctldata.Workflow.Stream.logs:type_name -> vtctldata.Workflow.Stream.Log + 242, // 184: vtctldata.Workflow.Stream.throttler_status:type_name -> vtctldata.Workflow.Stream.ThrottlerStatus + 286, // 185: vtctldata.Workflow.Stream.tablet_types:type_name -> topodata.TabletType + 275, // 186: vtctldata.Workflow.Stream.tablet_selection_preference:type_name -> tabletmanagerdata.TabletSelectionPreference + 277, // 187: vtctldata.Workflow.Stream.Log.created_at:type_name -> vttime.Time + 277, // 188: vtctldata.Workflow.Stream.Log.updated_at:type_name -> vttime.Time + 277, // 189: vtctldata.Workflow.Stream.ThrottlerStatus.time_throttled:type_name -> vttime.Time + 245, // 190: vtctldata.ApplyVSchemaResponse.UnknownVindexParamsEntry.value:type_name -> vtctldata.ApplyVSchemaResponse.ParamList + 10, // 191: vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry.value:type_name -> vtctldata.Shard + 300, // 192: vtctldata.GetCellsAliasesResponse.AliasesEntry.value:type_name -> topodata.CellsAlias + 253, // 193: vtctldata.GetSrvKeyspaceNamesResponse.NamesEntry.value:type_name -> vtctldata.GetSrvKeyspaceNamesResponse.NameList + 304, // 194: vtctldata.GetSrvKeyspacesResponse.SrvKeyspacesEntry.value:type_name -> topodata.SrvKeyspace + 297, // 195: vtctldata.GetSrvVSchemasResponse.SrvVSchemasEntry.value:type_name -> vschema.SrvVSchema + 278, // 196: vtctldata.MoveTablesCreateResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias + 305, // 197: vtctldata.ShardReplicationPositionsResponse.ReplicationStatusesEntry.value:type_name -> replicationdata.Status + 287, // 198: vtctldata.ShardReplicationPositionsResponse.TabletMapEntry.value:type_name -> topodata.Tablet + 207, // 199: vtctldata.ValidateResponse.ResultsByKeyspaceEntry.value:type_name -> vtctldata.ValidateKeyspaceResponse + 211, // 200: vtctldata.ValidateKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse + 211, // 201: vtctldata.ValidateSchemaKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse + 211, // 202: vtctldata.ValidateVersionKeyspaceResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse + 211, // 203: vtctldata.ValidateVSchemaResponse.ResultsByShardEntry.value:type_name -> vtctldata.ValidateShardResponse + 306, // 204: vtctldata.VDiffShowResponse.TabletResponsesEntry.value:type_name -> tabletmanagerdata.VDiffResponse + 278, // 205: vtctldata.WorkflowDeleteResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias + 278, // 206: vtctldata.WorkflowStatusResponse.ShardStreamState.tablet:type_name -> topodata.TabletAlias + 269, // 207: vtctldata.WorkflowStatusResponse.ShardStreams.streams:type_name -> vtctldata.WorkflowStatusResponse.ShardStreamState + 268, // 208: vtctldata.WorkflowStatusResponse.TableCopyStateEntry.value:type_name -> vtctldata.WorkflowStatusResponse.TableCopyState + 270, // 209: vtctldata.WorkflowStatusResponse.ShardStreamsEntry.value:type_name -> vtctldata.WorkflowStatusResponse.ShardStreams + 278, // 210: vtctldata.WorkflowUpdateResponse.TabletInfo.tablet:type_name -> topodata.TabletAlias + 211, // [211:211] is the sub-list for method output_type + 211, // [211:211] is the sub-list for method input_type + 211, // [211:211] is the sub-list for extension type_name + 211, // [211:211] is the sub-list for extension extendee + 0, // [0:211] is the sub-list for field type_name } func init() { file_vtctldata_proto_init() } @@ -18889,7 +19081,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBackupsRequest); i { + switch v := v.(*ForceCutOverSchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -18901,7 +19093,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBackupsResponse); i { + switch v := v.(*ForceCutOverSchemaMigrationResponse); i { case 0: return &v.state case 1: @@ -18913,7 +19105,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellInfoRequest); i { + switch v := v.(*GetBackupsRequest); i { case 0: return &v.state case 1: @@ -18925,7 +19117,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellInfoResponse); i { + switch v := v.(*GetBackupsResponse); i { case 0: return &v.state case 1: @@ -18937,7 +19129,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellInfoNamesRequest); i { + switch v := v.(*GetCellInfoRequest); i { case 0: return &v.state case 1: @@ -18949,7 +19141,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellInfoNamesResponse); i { + switch v := v.(*GetCellInfoResponse); i { case 0: return &v.state case 1: @@ -18961,7 +19153,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellsAliasesRequest); i { + switch v := v.(*GetCellInfoNamesRequest); i { case 0: return &v.state case 1: @@ -18973,7 +19165,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCellsAliasesResponse); i { + switch v := v.(*GetCellInfoNamesResponse); i { case 0: return &v.state case 1: @@ -18985,7 +19177,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFullStatusRequest); i { + switch v := v.(*GetCellsAliasesRequest); i { case 0: return &v.state case 1: @@ -18997,7 +19189,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFullStatusResponse); i { + switch v := v.(*GetCellsAliasesResponse); i { case 0: return &v.state case 1: @@ -19009,7 +19201,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetKeyspacesRequest); i { + switch v := v.(*GetFullStatusRequest); i { case 0: return &v.state case 1: @@ -19021,7 +19213,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetKeyspacesResponse); i { + switch v := v.(*GetFullStatusResponse); i { case 0: return &v.state case 1: @@ -19033,7 +19225,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetKeyspaceRequest); i { + switch v := v.(*GetKeyspacesRequest); i { case 0: return &v.state case 1: @@ -19045,7 +19237,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetKeyspaceResponse); i { + switch v := v.(*GetKeyspacesResponse); i { case 0: return &v.state case 1: @@ -19057,7 +19249,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPermissionsRequest); i { + switch v := v.(*GetKeyspaceRequest); i { case 0: return &v.state case 1: @@ -19069,7 +19261,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPermissionsResponse); i { + switch v := v.(*GetKeyspaceResponse); i { case 0: return &v.state case 1: @@ -19081,7 +19273,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoutingRulesRequest); i { + switch v := v.(*GetPermissionsRequest); i { case 0: return &v.state case 1: @@ -19093,7 +19285,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRoutingRulesResponse); i { + switch v := v.(*GetPermissionsResponse); i { case 0: return &v.state case 1: @@ -19105,7 +19297,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSchemaRequest); i { + switch v := v.(*GetRoutingRulesRequest); i { case 0: return &v.state case 1: @@ -19117,7 +19309,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSchemaResponse); i { + switch v := v.(*GetRoutingRulesResponse); i { case 0: return &v.state case 1: @@ -19129,7 +19321,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSchemaMigrationsRequest); i { + switch v := v.(*GetSchemaRequest); i { case 0: return &v.state case 1: @@ -19141,7 +19333,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSchemaMigrationsResponse); i { + switch v := v.(*GetSchemaResponse); i { case 0: return &v.state case 1: @@ -19153,7 +19345,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetShardRequest); i { + switch v := v.(*GetSchemaMigrationsRequest); i { case 0: return &v.state case 1: @@ -19165,7 +19357,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetShardResponse); i { + switch v := v.(*GetSchemaMigrationsResponse); i { case 0: return &v.state case 1: @@ -19177,7 +19369,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetShardRoutingRulesRequest); i { + switch v := v.(*GetShardRequest); i { case 0: return &v.state case 1: @@ -19189,7 +19381,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetShardRoutingRulesResponse); i { + switch v := v.(*GetShardResponse); i { case 0: return &v.state case 1: @@ -19201,7 +19393,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvKeyspaceNamesRequest); i { + switch v := v.(*GetShardRoutingRulesRequest); i { case 0: return &v.state case 1: @@ -19213,7 +19405,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvKeyspaceNamesResponse); i { + switch v := v.(*GetShardRoutingRulesResponse); i { case 0: return &v.state case 1: @@ -19225,7 +19417,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvKeyspacesRequest); i { + switch v := v.(*GetSrvKeyspaceNamesRequest); i { case 0: return &v.state case 1: @@ -19237,7 +19429,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvKeyspacesResponse); i { + switch v := v.(*GetSrvKeyspaceNamesResponse); i { case 0: return &v.state case 1: @@ -19249,7 +19441,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateThrottlerConfigRequest); i { + switch v := v.(*GetSrvKeyspacesRequest); i { case 0: return &v.state case 1: @@ -19261,7 +19453,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateThrottlerConfigResponse); i { + switch v := v.(*GetSrvKeyspacesResponse); i { case 0: return &v.state case 1: @@ -19273,7 +19465,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvVSchemaRequest); i { + switch v := v.(*UpdateThrottlerConfigRequest); i { case 0: return &v.state case 1: @@ -19285,7 +19477,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvVSchemaResponse); i { + switch v := v.(*UpdateThrottlerConfigResponse); i { case 0: return &v.state case 1: @@ -19297,7 +19489,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvVSchemasRequest); i { + switch v := v.(*GetSrvVSchemaRequest); i { case 0: return &v.state case 1: @@ -19309,7 +19501,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSrvVSchemasResponse); i { + switch v := v.(*GetSrvVSchemaResponse); i { case 0: return &v.state case 1: @@ -19321,7 +19513,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTabletRequest); i { + switch v := v.(*GetSrvVSchemasRequest); i { case 0: return &v.state case 1: @@ -19333,7 +19525,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTabletResponse); i { + switch v := v.(*GetSrvVSchemasResponse); i { case 0: return &v.state case 1: @@ -19345,7 +19537,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTabletsRequest); i { + switch v := v.(*GetTabletRequest); i { case 0: return &v.state case 1: @@ -19357,7 +19549,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTabletsResponse); i { + switch v := v.(*GetTabletResponse); i { case 0: return &v.state case 1: @@ -19369,7 +19561,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTopologyPathRequest); i { + switch v := v.(*GetTabletsRequest); i { case 0: return &v.state case 1: @@ -19381,7 +19573,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTopologyPathResponse); i { + switch v := v.(*GetTabletsResponse); i { case 0: return &v.state case 1: @@ -19393,7 +19585,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TopologyCell); i { + switch v := v.(*GetTopologyPathRequest); i { case 0: return &v.state case 1: @@ -19405,7 +19597,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVSchemaRequest); i { + switch v := v.(*GetTopologyPathResponse); i { case 0: return &v.state case 1: @@ -19417,7 +19609,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVersionRequest); i { + switch v := v.(*TopologyCell); i { case 0: return &v.state case 1: @@ -19429,7 +19621,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVersionResponse); i { + switch v := v.(*GetVSchemaRequest); i { case 0: return &v.state case 1: @@ -19441,7 +19633,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVSchemaResponse); i { + switch v := v.(*GetVersionRequest); i { case 0: return &v.state case 1: @@ -19453,7 +19645,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkflowsRequest); i { + switch v := v.(*GetVersionResponse); i { case 0: return &v.state case 1: @@ -19465,7 +19657,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetWorkflowsResponse); i { + switch v := v.(*GetVSchemaResponse); i { case 0: return &v.state case 1: @@ -19477,7 +19669,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InitShardPrimaryRequest); i { + switch v := v.(*GetWorkflowsRequest); i { case 0: return &v.state case 1: @@ -19489,7 +19681,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InitShardPrimaryResponse); i { + switch v := v.(*GetWorkflowsResponse); i { case 0: return &v.state case 1: @@ -19501,7 +19693,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LaunchSchemaMigrationRequest); i { + switch v := v.(*InitShardPrimaryRequest); i { case 0: return &v.state case 1: @@ -19513,7 +19705,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LaunchSchemaMigrationResponse); i { + switch v := v.(*InitShardPrimaryResponse); i { case 0: return &v.state case 1: @@ -19525,7 +19717,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LookupVindexCreateRequest); i { + switch v := v.(*LaunchSchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -19537,7 +19729,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LookupVindexCreateResponse); i { + switch v := v.(*LaunchSchemaMigrationResponse); i { case 0: return &v.state case 1: @@ -19549,7 +19741,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LookupVindexExternalizeRequest); i { + switch v := v.(*LookupVindexCreateRequest); i { case 0: return &v.state case 1: @@ -19561,7 +19753,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LookupVindexExternalizeResponse); i { + switch v := v.(*LookupVindexCreateResponse); i { case 0: return &v.state case 1: @@ -19573,7 +19765,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MaterializeCreateRequest); i { + switch v := v.(*LookupVindexExternalizeRequest); i { case 0: return &v.state case 1: @@ -19585,7 +19777,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MaterializeCreateResponse); i { + switch v := v.(*LookupVindexExternalizeResponse); i { case 0: return &v.state case 1: @@ -19597,7 +19789,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MigrateCreateRequest); i { + switch v := v.(*MaterializeCreateRequest); i { case 0: return &v.state case 1: @@ -19609,7 +19801,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MigrateCompleteRequest); i { + switch v := v.(*MaterializeCreateResponse); i { case 0: return &v.state case 1: @@ -19621,7 +19813,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MigrateCompleteResponse); i { + switch v := v.(*MigrateCreateRequest); i { case 0: return &v.state case 1: @@ -19633,7 +19825,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountRegisterRequest); i { + switch v := v.(*MigrateCompleteRequest); i { case 0: return &v.state case 1: @@ -19645,7 +19837,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountRegisterResponse); i { + switch v := v.(*MigrateCompleteResponse); i { case 0: return &v.state case 1: @@ -19657,7 +19849,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountUnregisterRequest); i { + switch v := v.(*MountRegisterRequest); i { case 0: return &v.state case 1: @@ -19669,7 +19861,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountUnregisterResponse); i { + switch v := v.(*MountRegisterResponse); i { case 0: return &v.state case 1: @@ -19681,7 +19873,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountShowRequest); i { + switch v := v.(*MountUnregisterRequest); i { case 0: return &v.state case 1: @@ -19693,7 +19885,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountShowResponse); i { + switch v := v.(*MountUnregisterResponse); i { case 0: return &v.state case 1: @@ -19705,7 +19897,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountListRequest); i { + switch v := v.(*MountShowRequest); i { case 0: return &v.state case 1: @@ -19717,7 +19909,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MountListResponse); i { + switch v := v.(*MountShowResponse); i { case 0: return &v.state case 1: @@ -19729,7 +19921,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[127].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MoveTablesCreateRequest); i { + switch v := v.(*MountListRequest); i { case 0: return &v.state case 1: @@ -19741,7 +19933,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[128].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MoveTablesCreateResponse); i { + switch v := v.(*MountListResponse); i { case 0: return &v.state case 1: @@ -19753,7 +19945,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[129].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MoveTablesCompleteRequest); i { + switch v := v.(*MoveTablesCreateRequest); i { case 0: return &v.state case 1: @@ -19765,7 +19957,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[130].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MoveTablesCompleteResponse); i { + switch v := v.(*MoveTablesCreateResponse); i { case 0: return &v.state case 1: @@ -19777,7 +19969,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[131].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingTabletRequest); i { + switch v := v.(*MoveTablesCompleteRequest); i { case 0: return &v.state case 1: @@ -19789,7 +19981,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[132].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingTabletResponse); i { + switch v := v.(*MoveTablesCompleteResponse); i { case 0: return &v.state case 1: @@ -19801,7 +19993,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[133].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlannedReparentShardRequest); i { + switch v := v.(*PingTabletRequest); i { case 0: return &v.state case 1: @@ -19813,7 +20005,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[134].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlannedReparentShardResponse); i { + switch v := v.(*PingTabletResponse); i { case 0: return &v.state case 1: @@ -19825,7 +20017,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[135].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RebuildKeyspaceGraphRequest); i { + switch v := v.(*PlannedReparentShardRequest); i { case 0: return &v.state case 1: @@ -19837,7 +20029,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[136].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RebuildKeyspaceGraphResponse); i { + switch v := v.(*PlannedReparentShardResponse); i { case 0: return &v.state case 1: @@ -19849,7 +20041,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RebuildVSchemaGraphRequest); i { + switch v := v.(*RebuildKeyspaceGraphRequest); i { case 0: return &v.state case 1: @@ -19861,7 +20053,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[138].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RebuildVSchemaGraphResponse); i { + switch v := v.(*RebuildKeyspaceGraphResponse); i { case 0: return &v.state case 1: @@ -19873,7 +20065,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[139].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RefreshStateRequest); i { + switch v := v.(*RebuildVSchemaGraphRequest); i { case 0: return &v.state case 1: @@ -19885,7 +20077,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[140].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RefreshStateResponse); i { + switch v := v.(*RebuildVSchemaGraphResponse); i { case 0: return &v.state case 1: @@ -19897,7 +20089,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[141].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RefreshStateByShardRequest); i { + switch v := v.(*RefreshStateRequest); i { case 0: return &v.state case 1: @@ -19909,7 +20101,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[142].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RefreshStateByShardResponse); i { + switch v := v.(*RefreshStateResponse); i { case 0: return &v.state case 1: @@ -19921,7 +20113,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[143].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaRequest); i { + switch v := v.(*RefreshStateByShardRequest); i { case 0: return &v.state case 1: @@ -19933,7 +20125,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[144].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaResponse); i { + switch v := v.(*RefreshStateByShardResponse); i { case 0: return &v.state case 1: @@ -19945,7 +20137,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[145].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaKeyspaceRequest); i { + switch v := v.(*ReloadSchemaRequest); i { case 0: return &v.state case 1: @@ -19957,7 +20149,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[146].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaKeyspaceResponse); i { + switch v := v.(*ReloadSchemaResponse); i { case 0: return &v.state case 1: @@ -19969,7 +20161,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[147].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaShardRequest); i { + switch v := v.(*ReloadSchemaKeyspaceRequest); i { case 0: return &v.state case 1: @@ -19981,7 +20173,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[148].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReloadSchemaShardResponse); i { + switch v := v.(*ReloadSchemaKeyspaceResponse); i { case 0: return &v.state case 1: @@ -19993,7 +20185,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[149].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveBackupRequest); i { + switch v := v.(*ReloadSchemaShardRequest); i { case 0: return &v.state case 1: @@ -20005,7 +20197,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[150].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveBackupResponse); i { + switch v := v.(*ReloadSchemaShardResponse); i { case 0: return &v.state case 1: @@ -20017,7 +20209,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[151].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveKeyspaceCellRequest); i { + switch v := v.(*RemoveBackupRequest); i { case 0: return &v.state case 1: @@ -20029,7 +20221,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[152].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveKeyspaceCellResponse); i { + switch v := v.(*RemoveBackupResponse); i { case 0: return &v.state case 1: @@ -20041,7 +20233,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[153].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveShardCellRequest); i { + switch v := v.(*RemoveKeyspaceCellRequest); i { case 0: return &v.state case 1: @@ -20053,7 +20245,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[154].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveShardCellResponse); i { + switch v := v.(*RemoveKeyspaceCellResponse); i { case 0: return &v.state case 1: @@ -20065,7 +20257,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[155].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReparentTabletRequest); i { + switch v := v.(*RemoveShardCellRequest); i { case 0: return &v.state case 1: @@ -20077,7 +20269,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[156].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReparentTabletResponse); i { + switch v := v.(*RemoveShardCellResponse); i { case 0: return &v.state case 1: @@ -20089,7 +20281,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[157].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReshardCreateRequest); i { + switch v := v.(*ReparentTabletRequest); i { case 0: return &v.state case 1: @@ -20101,7 +20293,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[158].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreFromBackupRequest); i { + switch v := v.(*ReparentTabletResponse); i { case 0: return &v.state case 1: @@ -20113,7 +20305,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[159].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreFromBackupResponse); i { + switch v := v.(*ReshardCreateRequest); i { case 0: return &v.state case 1: @@ -20125,7 +20317,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RetrySchemaMigrationRequest); i { + switch v := v.(*RestoreFromBackupRequest); i { case 0: return &v.state case 1: @@ -20137,7 +20329,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[161].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RetrySchemaMigrationResponse); i { + switch v := v.(*RestoreFromBackupResponse); i { case 0: return &v.state case 1: @@ -20149,7 +20341,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[162].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RunHealthCheckRequest); i { + switch v := v.(*RetrySchemaMigrationRequest); i { case 0: return &v.state case 1: @@ -20161,7 +20353,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[163].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RunHealthCheckResponse); i { + switch v := v.(*RetrySchemaMigrationResponse); i { case 0: return &v.state case 1: @@ -20173,7 +20365,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[164].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetKeyspaceDurabilityPolicyRequest); i { + switch v := v.(*RunHealthCheckRequest); i { case 0: return &v.state case 1: @@ -20185,7 +20377,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[165].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetKeyspaceDurabilityPolicyResponse); i { + switch v := v.(*RunHealthCheckResponse); i { case 0: return &v.state case 1: @@ -20197,7 +20389,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[166].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetKeyspaceServedFromRequest); i { + switch v := v.(*SetKeyspaceDurabilityPolicyRequest); i { case 0: return &v.state case 1: @@ -20209,7 +20401,7 @@ func file_vtctldata_proto_init() { } } file_vtctldata_proto_msgTypes[167].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetKeyspaceServedFromResponse); i { + switch v := v.(*SetKeyspaceDurabilityPolicyResponse); i { case 0: return &v.state case 1: @@ -21060,7 +21252,19 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[246].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[241].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyVSchemaResponse_ParamList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vtctldata_proto_msgTypes[249].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSrvKeyspaceNamesResponse_NameList); i { case 0: return &v.state @@ -21072,7 +21276,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[250].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[253].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MoveTablesCreateResponse_TabletInfo); i { case 0: return &v.state @@ -21084,7 +21288,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[260].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[263].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowDeleteResponse_TabletInfo); i { case 0: return &v.state @@ -21096,7 +21300,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[261].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[264].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowStatusResponse_TableCopyState); i { case 0: return &v.state @@ -21108,7 +21312,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[262].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[265].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowStatusResponse_ShardStreamState); i { case 0: return &v.state @@ -21120,7 +21324,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[263].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[266].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowStatusResponse_ShardStreams); i { case 0: return &v.state @@ -21132,7 +21336,7 @@ func file_vtctldata_proto_init() { return nil } } - file_vtctldata_proto_msgTypes[266].Exporter = func(v interface{}, i int) interface{} { + file_vtctldata_proto_msgTypes[269].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowUpdateResponse_TabletInfo); i { case 0: return &v.state @@ -21151,7 +21355,7 @@ func file_vtctldata_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_vtctldata_proto_rawDesc, NumEnums: 4, - NumMessages: 267, + NumMessages: 270, NumExtensions: 0, NumServices: 0, }, diff --git a/go/vt/proto/vtctldata/vtctldata_vtproto.pb.go b/go/vt/proto/vtctldata/vtctldata_vtproto.pb.go index b453eb2a0b2..d62ee7c9cdb 100644 --- a/go/vt/proto/vtctldata/vtctldata_vtproto.pb.go +++ b/go/vt/proto/vtctldata/vtctldata_vtproto.pb.go @@ -305,8 +305,9 @@ func (m *Workflow_Stream_CopyState) CloneVT() *Workflow_Stream_CopyState { return (*Workflow_Stream_CopyState)(nil) } r := &Workflow_Stream_CopyState{ - Table: m.Table, - LastPk: m.LastPk, + Table: m.Table, + LastPk: m.LastPk, + StreamId: m.StreamId, } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -368,20 +369,21 @@ func (m *Workflow_Stream) CloneVT() *Workflow_Stream { return (*Workflow_Stream)(nil) } r := &Workflow_Stream{ - Id: m.Id, - Shard: m.Shard, - Tablet: m.Tablet.CloneVT(), - BinlogSource: m.BinlogSource.CloneVT(), - Position: m.Position, - StopPosition: m.StopPosition, - State: m.State, - DbName: m.DbName, - TransactionTimestamp: m.TransactionTimestamp.CloneVT(), - TimeUpdated: m.TimeUpdated.CloneVT(), - Message: m.Message, - LogFetchError: m.LogFetchError, - RowsCopied: m.RowsCopied, - ThrottlerStatus: m.ThrottlerStatus.CloneVT(), + Id: m.Id, + Shard: m.Shard, + Tablet: m.Tablet.CloneVT(), + BinlogSource: m.BinlogSource.CloneVT(), + Position: m.Position, + StopPosition: m.StopPosition, + State: m.State, + DbName: m.DbName, + TransactionTimestamp: m.TransactionTimestamp.CloneVT(), + TimeUpdated: m.TimeUpdated.CloneVT(), + Message: m.Message, + LogFetchError: m.LogFetchError, + RowsCopied: m.RowsCopied, + ThrottlerStatus: m.ThrottlerStatus.CloneVT(), + TabletSelectionPreference: m.TabletSelectionPreference, } if rhs := m.CopyStates; rhs != nil { tmpContainer := make([]*Workflow_Stream_CopyState, len(rhs)) @@ -402,6 +404,16 @@ func (m *Workflow_Stream) CloneVT() *Workflow_Stream { copy(tmpContainer, rhs) r.Tags = tmpContainer } + if rhs := m.TabletTypes; rhs != nil { + tmpContainer := make([]topodata.TabletType, len(rhs)) + copy(tmpContainer, rhs) + r.TabletTypes = tmpContainer + } + if rhs := m.Cells; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Cells = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -670,6 +682,7 @@ func (m *ApplyVSchemaRequest) CloneVT() *ApplyVSchemaRequest { DryRun: m.DryRun, VSchema: m.VSchema.CloneVT(), Sql: m.Sql, + Strict: m.Strict, } if rhs := m.Cells; rhs != nil { tmpContainer := make([]string, len(rhs)) @@ -687,6 +700,27 @@ func (m *ApplyVSchemaRequest) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *ApplyVSchemaResponse_ParamList) CloneVT() *ApplyVSchemaResponse_ParamList { + if m == nil { + return (*ApplyVSchemaResponse_ParamList)(nil) + } + r := &ApplyVSchemaResponse_ParamList{} + if rhs := m.Params; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Params = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *ApplyVSchemaResponse_ParamList) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *ApplyVSchemaResponse) CloneVT() *ApplyVSchemaResponse { if m == nil { return (*ApplyVSchemaResponse)(nil) @@ -694,6 +728,13 @@ func (m *ApplyVSchemaResponse) CloneVT() *ApplyVSchemaResponse { r := &ApplyVSchemaResponse{ VSchema: m.VSchema.CloneVT(), } + if rhs := m.UnknownVindexParams; rhs != nil { + tmpContainer := make(map[string]*ApplyVSchemaResponse_ParamList, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.UnknownVindexParams = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -951,13 +992,6 @@ func (m *CreateKeyspaceRequest) CloneVT() *CreateKeyspaceRequest { DurabilityPolicy: m.DurabilityPolicy, SidecarDbName: m.SidecarDbName, } - if rhs := m.ServedFroms; rhs != nil { - tmpContainer := make([]*topodata.Keyspace_ServedFrom, len(rhs)) - for k, v := range rhs { - tmpContainer[k] = v.CloneVT() - } - r.ServedFroms = tmpContainer - } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -1465,6 +1499,48 @@ func (m *FindAllShardsInKeyspaceResponse) CloneMessageVT() proto.Message { return m.CloneVT() } +func (m *ForceCutOverSchemaMigrationRequest) CloneVT() *ForceCutOverSchemaMigrationRequest { + if m == nil { + return (*ForceCutOverSchemaMigrationRequest)(nil) + } + r := &ForceCutOverSchemaMigrationRequest{ + Keyspace: m.Keyspace, + Uuid: m.Uuid, + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *ForceCutOverSchemaMigrationRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *ForceCutOverSchemaMigrationResponse) CloneVT() *ForceCutOverSchemaMigrationResponse { + if m == nil { + return (*ForceCutOverSchemaMigrationResponse)(nil) + } + r := &ForceCutOverSchemaMigrationResponse{} + if rhs := m.RowsAffectedByShard; rhs != nil { + tmpContainer := make(map[string]uint64, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v + } + r.RowsAffectedByShard = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *ForceCutOverSchemaMigrationResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + func (m *GetBackupsRequest) CloneVT() *GetBackupsRequest { if m == nil { return (*GetBackupsRequest)(nil) @@ -2441,6 +2517,11 @@ func (m *GetWorkflowsRequest) CloneVT() *GetWorkflowsRequest { Workflow: m.Workflow, IncludeLogs: m.IncludeLogs, } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -3037,6 +3118,11 @@ func (m *MoveTablesCompleteRequest) CloneVT() *MoveTablesCompleteRequest { RenameTables: m.RenameTables, DryRun: m.DryRun, } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -3110,11 +3196,12 @@ func (m *PlannedReparentShardRequest) CloneVT() *PlannedReparentShardRequest { return (*PlannedReparentShardRequest)(nil) } r := &PlannedReparentShardRequest{ - Keyspace: m.Keyspace, - Shard: m.Shard, - NewPrimary: m.NewPrimary.CloneVT(), - AvoidPrimary: m.AvoidPrimary.CloneVT(), - WaitReplicasTimeout: m.WaitReplicasTimeout.CloneVT(), + Keyspace: m.Keyspace, + Shard: m.Shard, + NewPrimary: m.NewPrimary.CloneVT(), + AvoidPrimary: m.AvoidPrimary.CloneVT(), + WaitReplicasTimeout: m.WaitReplicasTimeout.CloneVT(), + TolerableReplicationLag: m.TolerableReplicationLag.CloneVT(), } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) @@ -3781,50 +3868,6 @@ func (m *SetKeyspaceDurabilityPolicyResponse) CloneMessageVT() proto.Message { return m.CloneVT() } -func (m *SetKeyspaceServedFromRequest) CloneVT() *SetKeyspaceServedFromRequest { - if m == nil { - return (*SetKeyspaceServedFromRequest)(nil) - } - r := &SetKeyspaceServedFromRequest{ - Keyspace: m.Keyspace, - TabletType: m.TabletType, - Remove: m.Remove, - SourceKeyspace: m.SourceKeyspace, - } - if rhs := m.Cells; rhs != nil { - tmpContainer := make([]string, len(rhs)) - copy(tmpContainer, rhs) - r.Cells = tmpContainer - } - if len(m.unknownFields) > 0 { - r.unknownFields = make([]byte, len(m.unknownFields)) - copy(r.unknownFields, m.unknownFields) - } - return r -} - -func (m *SetKeyspaceServedFromRequest) CloneMessageVT() proto.Message { - return m.CloneVT() -} - -func (m *SetKeyspaceServedFromResponse) CloneVT() *SetKeyspaceServedFromResponse { - if m == nil { - return (*SetKeyspaceServedFromResponse)(nil) - } - r := &SetKeyspaceServedFromResponse{ - Keyspace: m.Keyspace.CloneVT(), - } - if len(m.unknownFields) > 0 { - r.unknownFields = make([]byte, len(m.unknownFields)) - copy(r.unknownFields, m.unknownFields) - } - return r -} - -func (m *SetKeyspaceServedFromResponse) CloneMessageVT() proto.Message { - return m.CloneVT() -} - func (m *SetKeyspaceShardingInfoRequest) CloneVT() *SetKeyspaceShardingInfoRequest { if m == nil { return (*SetKeyspaceShardingInfoRequest)(nil) @@ -4796,6 +4839,8 @@ func (m *VDiffCreateRequest) CloneVT() *VDiffCreateRequest { WaitUpdateInterval: m.WaitUpdateInterval.CloneVT(), AutoRetry: m.AutoRetry, Verbose: m.Verbose, + MaxReportSampleRows: m.MaxReportSampleRows, + MaxDiffDuration: m.MaxDiffDuration.CloneVT(), } if rhs := m.SourceCells; rhs != nil { tmpContainer := make([]string, len(rhs)) @@ -5007,6 +5052,11 @@ func (m *WorkflowDeleteRequest) CloneVT() *WorkflowDeleteRequest { KeepData: m.KeepData, KeepRoutingRules: m.KeepRoutingRules, } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -5070,6 +5120,11 @@ func (m *WorkflowStatusRequest) CloneVT() *WorkflowStatusRequest { Keyspace: m.Keyspace, Workflow: m.Workflow, } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -5206,6 +5261,11 @@ func (m *WorkflowSwitchTrafficRequest) CloneVT() *WorkflowSwitchTrafficRequest { copy(tmpContainer, rhs) r.TabletTypes = tmpContainer } + if rhs := m.Shards; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Shards = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -6388,6 +6448,11 @@ func (m *Workflow_Stream_CopyState) MarshalToSizedBufferVT(dAtA []byte) (int, er i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.StreamId != 0 { + i = encodeVarint(dAtA, i, uint64(m.StreamId)) + i-- + dAtA[i] = 0x18 + } if len(m.LastPk) > 0 { i -= len(m.LastPk) copy(dAtA[i:], m.LastPk) @@ -6574,6 +6639,47 @@ func (m *Workflow_Stream) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Cells) > 0 { + for iNdEx := len(m.Cells) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Cells[iNdEx]) + copy(dAtA[i:], m.Cells[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Cells[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } + } + if m.TabletSelectionPreference != 0 { + i = encodeVarint(dAtA, i, uint64(m.TabletSelectionPreference)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if len(m.TabletTypes) > 0 { + var pksize2 int + for _, num := range m.TabletTypes { + pksize2 += sov(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num1 := range m.TabletTypes { + num := uint64(num1) + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = encodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } if m.ThrottlerStatus != nil { size, err := m.ThrottlerStatus.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { @@ -7380,6 +7486,16 @@ func (m *ApplyVSchemaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.Strict { + i-- + if m.Strict { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if len(m.Sql) > 0 { i -= len(m.Sql) copy(dAtA[i:], m.Sql) @@ -7436,6 +7552,48 @@ func (m *ApplyVSchemaRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ApplyVSchemaResponse_ParamList) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ApplyVSchemaResponse_ParamList) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ApplyVSchemaResponse_ParamList) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Params) > 0 { + for iNdEx := len(m.Params) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Params[iNdEx]) + copy(dAtA[i:], m.Params[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Params[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *ApplyVSchemaResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -7466,6 +7624,28 @@ func (m *ApplyVSchemaResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.UnknownVindexParams) > 0 { + for k := range m.UnknownVindexParams { + v := m.UnknownVindexParams[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } if m.VSchema != nil { size, err := m.VSchema.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { @@ -8178,18 +8358,6 @@ func (m *CreateKeyspaceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i-- dAtA[i] = 0x38 } - if len(m.ServedFroms) > 0 { - for iNdEx := len(m.ServedFroms) - 1; iNdEx >= 0; iNdEx-- { - size, err := m.ServedFroms[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0x32 - } - } if m.AllowEmptyVSchema { i-- if m.AllowEmptyVSchema { @@ -9496,6 +9664,103 @@ func (m *FindAllShardsInKeyspaceResponse) MarshalToSizedBufferVT(dAtA []byte) (i return len(dAtA) - i, nil } +func (m *ForceCutOverSchemaMigrationRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ForceCutOverSchemaMigrationRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ForceCutOverSchemaMigrationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Uuid) > 0 { + i -= len(m.Uuid) + copy(dAtA[i:], m.Uuid) + i = encodeVarint(dAtA, i, uint64(len(m.Uuid))) + i-- + dAtA[i] = 0x12 + } + if len(m.Keyspace) > 0 { + i -= len(m.Keyspace) + copy(dAtA[i:], m.Keyspace) + i = encodeVarint(dAtA, i, uint64(len(m.Keyspace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ForceCutOverSchemaMigrationResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ForceCutOverSchemaMigrationResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ForceCutOverSchemaMigrationResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.RowsAffectedByShard) > 0 { + for k := range m.RowsAffectedByShard { + v := m.RowsAffectedByShard[k] + baseI := i + i = encodeVarint(dAtA, i, uint64(v)) + i-- + dAtA[i] = 0x10 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *GetBackupsRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -11827,6 +12092,15 @@ func (m *GetWorkflowsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } if m.IncludeLogs { i-- if m.IncludeLogs { @@ -13467,6 +13741,15 @@ func (m *MoveTablesCompleteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, er i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } if m.DryRun { i-- if m.DryRun { @@ -13679,6 +13962,16 @@ func (m *PlannedReparentShardRequest) MarshalToSizedBufferVT(dAtA []byte) (int, i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.TolerableReplicationLag != nil { + size, err := m.TolerableReplicationLag.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } if m.WaitReplicasTimeout != nil { size, err := m.WaitReplicasTimeout.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { @@ -15392,120 +15685,6 @@ func (m *SetKeyspaceDurabilityPolicyResponse) MarshalToSizedBufferVT(dAtA []byte return len(dAtA) - i, nil } -func (m *SetKeyspaceServedFromRequest) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SetKeyspaceServedFromRequest) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *SetKeyspaceServedFromRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if len(m.SourceKeyspace) > 0 { - i -= len(m.SourceKeyspace) - copy(dAtA[i:], m.SourceKeyspace) - i = encodeVarint(dAtA, i, uint64(len(m.SourceKeyspace))) - i-- - dAtA[i] = 0x2a - } - if m.Remove { - i-- - if m.Remove { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x20 - } - if len(m.Cells) > 0 { - for iNdEx := len(m.Cells) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Cells[iNdEx]) - copy(dAtA[i:], m.Cells[iNdEx]) - i = encodeVarint(dAtA, i, uint64(len(m.Cells[iNdEx]))) - i-- - dAtA[i] = 0x1a - } - } - if m.TabletType != 0 { - i = encodeVarint(dAtA, i, uint64(m.TabletType)) - i-- - dAtA[i] = 0x10 - } - if len(m.Keyspace) > 0 { - i -= len(m.Keyspace) - copy(dAtA[i:], m.Keyspace) - i = encodeVarint(dAtA, i, uint64(len(m.Keyspace))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *SetKeyspaceServedFromResponse) MarshalVT() (dAtA []byte, err error) { - if m == nil { - return nil, nil - } - size := m.SizeVT() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBufferVT(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *SetKeyspaceServedFromResponse) MarshalToVT(dAtA []byte) (int, error) { - size := m.SizeVT() - return m.MarshalToSizedBufferVT(dAtA[:size]) -} - -func (m *SetKeyspaceServedFromResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { - if m == nil { - return 0, nil - } - i := len(dAtA) - _ = i - var l int - _ = l - if m.unknownFields != nil { - i -= len(m.unknownFields) - copy(dAtA[i:], m.unknownFields) - } - if m.Keyspace != nil { - size, err := m.Keyspace.MarshalToSizedBufferVT(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarint(dAtA, i, uint64(size)) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - func (m *SetKeyspaceShardingInfoRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil @@ -17908,6 +18087,25 @@ func (m *VDiffCreateRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.MaxDiffDuration != nil { + size, err := m.MaxDiffDuration.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } + if m.MaxReportSampleRows != 0 { + i = encodeVarint(dAtA, i, uint64(m.MaxReportSampleRows)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if m.Verbose { i-- if m.Verbose { @@ -18521,6 +18719,15 @@ func (m *WorkflowDeleteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } if m.KeepRoutingRules { i-- if m.KeepRoutingRules { @@ -18693,6 +18900,15 @@ func (m *WorkflowStatusRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } if len(m.Workflow) > 0 { i -= len(m.Workflow) copy(dAtA[i:], m.Workflow) @@ -19010,6 +19226,15 @@ func (m *WorkflowSwitchTrafficRequest) MarshalToSizedBufferVT(dAtA []byte) (int, i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Shards) > 0 { + for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Shards[iNdEx]) + copy(dAtA[i:], m.Shards[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.Shards[iNdEx]))) + i-- + dAtA[i] = 0x5a + } + } if m.InitializeTargetSequences { i-- if m.InitializeTargetSequences { @@ -19775,6 +20000,9 @@ func (m *Workflow_Stream_CopyState) SizeVT() (n int) { if l > 0 { n += 1 + l + sov(uint64(l)) } + if m.StreamId != 0 { + n += 1 + sov(uint64(m.StreamId)) + } n += len(m.unknownFields) return n } @@ -19914,6 +20142,22 @@ func (m *Workflow_Stream) SizeVT() (n int) { l = m.ThrottlerStatus.SizeVT() n += 2 + l + sov(uint64(l)) } + if len(m.TabletTypes) > 0 { + l = 0 + for _, e := range m.TabletTypes { + l += sov(uint64(e)) + } + n += 2 + sov(uint64(l)) + l + } + if m.TabletSelectionPreference != 0 { + n += 2 + sov(uint64(m.TabletSelectionPreference)) + } + if len(m.Cells) > 0 { + for _, s := range m.Cells { + l = len(s) + n += 2 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -20193,6 +20437,25 @@ func (m *ApplyVSchemaRequest) SizeVT() (n int) { if l > 0 { n += 1 + l + sov(uint64(l)) } + if m.Strict { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *ApplyVSchemaResponse_ParamList) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Params) > 0 { + for _, s := range m.Params { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -20207,6 +20470,19 @@ func (m *ApplyVSchemaResponse) SizeVT() (n int) { l = m.VSchema.SizeVT() n += 1 + l + sov(uint64(l)) } + if len(m.UnknownVindexParams) > 0 { + for k, v := range m.UnknownVindexParams { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + sov(uint64(l)) + mapEntrySize := 1 + len(k) + sov(uint64(len(k))) + l + n += mapEntrySize + 1 + sov(uint64(mapEntrySize)) + } + } n += len(m.unknownFields) return n } @@ -20460,12 +20736,6 @@ func (m *CreateKeyspaceRequest) SizeVT() (n int) { if m.AllowEmptyVSchema { n += 2 } - if len(m.ServedFroms) > 0 { - for _, e := range m.ServedFroms { - l = e.SizeVT() - n += 1 + l + sov(uint64(l)) - } - } if m.Type != 0 { n += 1 + sov(uint64(m.Type)) } @@ -20931,6 +21201,42 @@ func (m *FindAllShardsInKeyspaceResponse) SizeVT() (n int) { return n } +func (m *ForceCutOverSchemaMigrationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Keyspace) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Uuid) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ForceCutOverSchemaMigrationResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.RowsAffectedByShard) > 0 { + for k, v := range m.RowsAffectedByShard { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sov(uint64(len(k))) + 1 + sov(uint64(v)) + n += mapEntrySize + 1 + sov(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + func (m *GetBackupsRequest) SizeVT() (n int) { if m == nil { return 0 @@ -21787,6 +22093,12 @@ func (m *GetWorkflowsRequest) SizeVT() (n int) { if m.IncludeLogs { n += 2 } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -22406,6 +22718,12 @@ func (m *MoveTablesCompleteRequest) SizeVT() (n int) { if m.DryRun { n += 2 } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -22480,6 +22798,10 @@ func (m *PlannedReparentShardRequest) SizeVT() (n int) { l = m.WaitReplicasTimeout.SizeVT() n += 1 + l + sov(uint64(l)) } + if m.TolerableReplicationLag != nil { + l = m.TolerableReplicationLag.SizeVT() + n += 1 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -23093,50 +23415,6 @@ func (m *SetKeyspaceDurabilityPolicyResponse) SizeVT() (n int) { return n } -func (m *SetKeyspaceServedFromRequest) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Keyspace) - if l > 0 { - n += 1 + l + sov(uint64(l)) - } - if m.TabletType != 0 { - n += 1 + sov(uint64(m.TabletType)) - } - if len(m.Cells) > 0 { - for _, s := range m.Cells { - l = len(s) - n += 1 + l + sov(uint64(l)) - } - } - if m.Remove { - n += 2 - } - l = len(m.SourceKeyspace) - if l > 0 { - n += 1 + l + sov(uint64(l)) - } - n += len(m.unknownFields) - return n -} - -func (m *SetKeyspaceServedFromResponse) SizeVT() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Keyspace != nil { - l = m.Keyspace.SizeVT() - n += 1 + l + sov(uint64(l)) - } - n += len(m.unknownFields) - return n -} - func (m *SetKeyspaceShardingInfoRequest) SizeVT() (n int) { if m == nil { return 0 @@ -24109,6 +24387,13 @@ func (m *VDiffCreateRequest) SizeVT() (n int) { if m.Verbose { n += 3 } + if m.MaxReportSampleRows != 0 { + n += 2 + sov(uint64(m.MaxReportSampleRows)) + } + if m.MaxDiffDuration != nil { + l = m.MaxDiffDuration.SizeVT() + n += 2 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -24288,6 +24573,12 @@ func (m *WorkflowDeleteRequest) SizeVT() (n int) { if m.KeepRoutingRules { n += 2 } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -24343,6 +24634,12 @@ func (m *WorkflowStatusRequest) SizeVT() (n int) { if l > 0 { n += 1 + l + sov(uint64(l)) } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -24511,6 +24808,12 @@ func (m *WorkflowSwitchTrafficRequest) SizeVT() (n int) { if m.InitializeTargetSequences { n += 2 } + if len(m.Shards) > 0 { + for _, s := range m.Shards { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -27635,6 +27938,25 @@ func (m *Workflow_Stream_CopyState) UnmarshalVT(dAtA []byte) error { } m.LastPk = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StreamId", wireType) + } + m.StreamId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StreamId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -28397,16 +28719,116 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.TimeUpdated == nil { - m.TimeUpdated = &vttime.Time{} - } - if err := m.TimeUpdated.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if m.TimeUpdated == nil { + m.TimeUpdated = &vttime.Time{} + } + if err := m.TimeUpdated.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CopyStates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CopyStates = append(m.CopyStates, &Workflow_Stream_CopyState{}) + if err := m.CopyStates[len(m.CopyStates)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, &Workflow_Stream_Log{}) + if err := m.Logs[len(m.Logs)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 11: + case 14: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field LogFetchError", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -28434,13 +28856,13 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Message = string(dAtA[iNdEx:postIndex]) + m.LogFetchError = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 12: + case 15: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CopyStates", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -28450,31 +28872,29 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLength } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.CopyStates = append(m.CopyStates, &Workflow_Stream_CopyState{}) - if err := m.CopyStates[len(m.CopyStates)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Tags = append(m.Tags, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 13: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RowsCopied", wireType) } - var msglen int + m.RowsCopied = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -28484,31 +28904,16 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + m.RowsCopied |= int64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Logs = append(m.Logs, &Workflow_Stream_Log{}) - if err := m.Logs[len(m.Logs)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 14: + case 17: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LogFetchError", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ThrottlerStatus", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -28518,61 +28923,102 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLength } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - m.LogFetchError = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 15: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + if m.ThrottlerStatus == nil { + m.ThrottlerStatus = &Workflow_Stream_ThrottlerStatus{} } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow + if err := m.ThrottlerStatus.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 18: + if wireType == 0 { + var v topodata.TabletType + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= topodata.TabletType(b&0x7F) << shift + if b < 0x80 { + break + } } - if iNdEx >= l { + m.TabletTypes = append(m.TabletTypes, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break + var elementCount int + if elementCount != 0 && len(m.TabletTypes) == 0 { + m.TabletTypes = make([]topodata.TabletType, 0, elementCount) } + for iNdEx < postIndex { + var v topodata.TabletType + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= topodata.TabletType(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.TabletTypes = append(m.TabletTypes, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field TabletTypes", wireType) } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tags = append(m.Tags, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 16: + case 19: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RowsCopied", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TabletSelectionPreference", wireType) } - m.RowsCopied = 0 + m.TabletSelectionPreference = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -28582,16 +29028,16 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RowsCopied |= int64(b&0x7F) << shift + m.TabletSelectionPreference |= tabletmanagerdata.TabletSelectionPreference(b&0x7F) << shift if b < 0x80 { break } } - case 17: + case 20: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ThrottlerStatus", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -28601,27 +29047,23 @@ func (m *Workflow_Stream) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLength } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } - if m.ThrottlerStatus == nil { - m.ThrottlerStatus = &Workflow_Stream_ThrottlerStatus{} - } - if err := m.ThrottlerStatus.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -30324,13 +30766,85 @@ func (m *ApplyVSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Keyspace = string(dAtA[iNdEx:postIndex]) + m.Keyspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SkipRebuild", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SkipRebuild = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DryRun", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DryRun = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field SkipRebuild", wireType) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VSchema", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -30340,35 +30854,31 @@ func (m *ApplyVSchemaRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.SkipRebuild = bool(v != 0) - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DryRun", wireType) + if msglen < 0 { + return ErrInvalidLength } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength } - m.DryRun = bool(v != 0) - case 4: + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.VSchema == nil { + m.VSchema = &vschema.Keyspace{} + } + if err := m.VSchema.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Sql", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -30396,13 +30906,13 @@ func (m *ApplyVSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) + m.Sql = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VSchema", wireType) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Strict", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -30412,31 +30922,66 @@ func (m *ApplyVSchemaRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLength + m.Strict = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err } - postIndex := iNdEx + msglen - if postIndex < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLength } - if postIndex > l { + if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - if m.VSchema == nil { - m.VSchema = &vschema.Keyspace{} + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ApplyVSchemaResponse_ParamList) UnmarshalVT(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 ErrIntOverflow } - if err := m.VSchema.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err + if iNdEx >= l { + return io.ErrUnexpectedEOF } - iNdEx = postIndex - case 6: + 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: ApplyVSchemaResponse_ParamList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApplyVSchemaResponse_ParamList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Sql", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -30464,7 +31009,7 @@ func (m *ApplyVSchemaRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Sql = string(dAtA[iNdEx:postIndex]) + m.Params = append(m.Params, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -30553,6 +31098,135 @@ func (m *ApplyVSchemaResponse) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnknownVindexParams", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UnknownVindexParams == nil { + m.UnknownVindexParams = make(map[string]*ApplyVSchemaResponse_ParamList) + } + var mapkey string + var mapvalue *ApplyVSchemaResponse_ParamList + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrIntOverflow + } + 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 ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &ApplyVSchemaResponse_ParamList{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.UnknownVindexParams[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -30674,7 +31348,7 @@ func (m *BackupRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint64(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -31067,7 +31741,7 @@ func (m *BackupShardRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint64(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -32353,40 +33027,6 @@ func (m *CreateKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } } m.AllowEmptyVSchema = bool(v != 0) - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServedFroms", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServedFroms = append(m.ServedFroms, &topodata.Keyspace_ServedFrom{}) - if err := m.ServedFroms[len(m.ServedFroms)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) @@ -35322,6 +35962,285 @@ func (m *FindAllShardsInKeyspaceResponse) UnmarshalVT(dAtA []byte) error { } return nil } +func (m *ForceCutOverSchemaMigrationRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: ForceCutOverSchemaMigrationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ForceCutOverSchemaMigrationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Keyspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ForceCutOverSchemaMigrationResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: ForceCutOverSchemaMigrationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ForceCutOverSchemaMigrationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RowsAffectedByShard", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RowsAffectedByShard == nil { + m.RowsAffectedByShard = make(map[string]uint64) + } + var mapkey string + var mapvalue uint64 + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrIntOverflow + } + 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 ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + } else { + iNdEx = entryPreIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.RowsAffectedByShard[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *GetBackupsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -40617,6 +41536,38 @@ func (m *GetWorkflowsRequest) UnmarshalVT(dAtA []byte) error { } } m.IncludeLogs = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -44606,6 +45557,38 @@ func (m *MoveTablesCompleteRequest) UnmarshalVT(dAtA []byte) error { } } m.DryRun = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -45082,6 +46065,42 @@ func (m *PlannedReparentShardRequest) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TolerableReplicationLag", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TolerableReplicationLag == nil { + m.TolerableReplicationLag = &vttime.Duration{} + } + if err := m.TolerableReplicationLag.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -46262,7 +47281,7 @@ func (m *ReloadSchemaKeyspaceRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint32(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -46533,7 +47552,7 @@ func (m *ReloadSchemaShardRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Concurrency |= uint32(b&0x7F) << shift + m.Concurrency |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -48700,173 +49719,7 @@ func (m *RunHealthCheckRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *RunHealthCheckResponse) UnmarshalVT(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 ErrIntOverflow - } - 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: RunHealthCheckResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RunHealthCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SetKeyspaceDurabilityPolicyRequest) UnmarshalVT(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 ErrIntOverflow - } - 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: SetKeyspaceDurabilityPolicyRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Keyspace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DurabilityPolicy", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.DurabilityPolicy = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *SetKeyspaceDurabilityPolicyResponse) UnmarshalVT(dAtA []byte) error { +func (m *RunHealthCheckResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -48889,48 +49742,12 @@ func (m *SetKeyspaceDurabilityPolicyResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyResponse: wiretype end group for non-group") + return fmt.Errorf("proto: RunHealthCheckResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RunHealthCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Keyspace == nil { - m.Keyspace = &topodata.Keyspace{} - } - if err := m.Keyspace.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -48953,7 +49770,7 @@ func (m *SetKeyspaceDurabilityPolicyResponse) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *SetKeyspaceServedFromRequest) UnmarshalVT(dAtA []byte) error { +func (m *SetKeyspaceDurabilityPolicyRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -48976,10 +49793,10 @@ func (m *SetKeyspaceServedFromRequest) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SetKeyspaceServedFromRequest: wiretype end group for non-group") + return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SetKeyspaceServedFromRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -49015,79 +49832,8 @@ func (m *SetKeyspaceServedFromRequest) UnmarshalVT(dAtA []byte) error { m.Keyspace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TabletType", wireType) - } - m.TabletType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TabletType |= topodata.TabletType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cells", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Cells = append(m.Cells, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Remove", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Remove = bool(v != 0) - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SourceKeyspace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DurabilityPolicy", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -49115,7 +49861,7 @@ func (m *SetKeyspaceServedFromRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.SourceKeyspace = string(dAtA[iNdEx:postIndex]) + m.DurabilityPolicy = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -49139,7 +49885,7 @@ func (m *SetKeyspaceServedFromRequest) UnmarshalVT(dAtA []byte) error { } return nil } -func (m *SetKeyspaceServedFromResponse) UnmarshalVT(dAtA []byte) error { +func (m *SetKeyspaceDurabilityPolicyResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -49162,10 +49908,10 @@ func (m *SetKeyspaceServedFromResponse) UnmarshalVT(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SetKeyspaceServedFromResponse: wiretype end group for non-group") + return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SetKeyspaceServedFromResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SetKeyspaceDurabilityPolicyResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -55389,36 +56135,151 @@ func (m *VDiffCreateRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.FilteredReplicationWaitTime == nil { - m.FilteredReplicationWaitTime = &vttime.Duration{} + if m.FilteredReplicationWaitTime == nil { + m.FilteredReplicationWaitTime = &vttime.Duration{} + } + if err := m.FilteredReplicationWaitTime.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DebugQuery", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DebugQuery = bool(v != 0) + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OnlyPKs", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OnlyPKs = bool(v != 0) + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateTableStats", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UpdateTableStats = bool(v != 0) + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxExtraRowsToCompare", wireType) + } + m.MaxExtraRowsToCompare = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxExtraRowsToCompare |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Wait", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Wait = bool(v != 0) + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WaitUpdateInterval", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.WaitUpdateInterval == nil { + m.WaitUpdateInterval = &vttime.Duration{} } - if err := m.FilteredReplicationWaitTime.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.WaitUpdateInterval.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 11: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DebugQuery", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.DebugQuery = bool(v != 0) - case 12: + case 17: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field OnlyPKs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AutoRetry", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -55435,10 +56296,10 @@ func (m *VDiffCreateRequest) UnmarshalVT(dAtA []byte) error { break } } - m.OnlyPKs = bool(v != 0) - case 13: + m.AutoRetry = bool(v != 0) + case 18: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field UpdateTableStats", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Verbose", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -55455,31 +56316,12 @@ func (m *VDiffCreateRequest) UnmarshalVT(dAtA []byte) error { break } } - m.UpdateTableStats = bool(v != 0) - case 14: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MaxExtraRowsToCompare", wireType) - } - m.MaxExtraRowsToCompare = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MaxExtraRowsToCompare |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 15: + m.Verbose = bool(v != 0) + case 19: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Wait", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxReportSampleRows", wireType) } - var v int + m.MaxReportSampleRows = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -55489,15 +56331,14 @@ func (m *VDiffCreateRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + m.MaxReportSampleRows |= int64(b&0x7F) << shift if b < 0x80 { break } } - m.Wait = bool(v != 0) - case 16: + case 20: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WaitUpdateInterval", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxDiffDuration", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -55524,53 +56365,13 @@ func (m *VDiffCreateRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.WaitUpdateInterval == nil { - m.WaitUpdateInterval = &vttime.Duration{} + if m.MaxDiffDuration == nil { + m.MaxDiffDuration = &vttime.Duration{} } - if err := m.WaitUpdateInterval.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + if err := m.MaxDiffDuration.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 17: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AutoRetry", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.AutoRetry = bool(v != 0) - case 18: - 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 ErrIntOverflow - } - 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 := skip(dAtA[iNdEx:]) @@ -56490,145 +57291,177 @@ func (m *VDiffStopRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.TargetKeyspace = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Uuid = string(dAtA[iNdEx:postIndex]) + m.TargetKeyspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VDiffStopResponse) UnmarshalVT(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 ErrIntOverflow + } + 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: VDiffStopResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VDiffStopResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WorkflowDeleteRequest) UnmarshalVT(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 ErrIntOverflow + } + 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: WorkflowDeleteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkflowDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Keyspace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *VDiffStopResponse) UnmarshalVT(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 ErrIntOverflow - } - 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: VDiffStopResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: VDiffStopResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skip(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLength - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *WorkflowDeleteRequest) UnmarshalVT(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 ErrIntOverflow - } - 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: WorkflowDeleteRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: WorkflowDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Keyspace", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Workflow", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -56656,13 +57489,13 @@ func (m *WorkflowDeleteRequest) UnmarshalVT(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Keyspace = string(dAtA[iNdEx:postIndex]) + m.Workflow = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Workflow", wireType) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KeepData", wireType) } - var stringLen uint64 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -56672,27 +57505,15 @@ func (m *WorkflowDeleteRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Workflow = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: + m.KeepData = bool(v != 0) + case 4: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KeepData", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field KeepRoutingRules", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -56709,12 +57530,12 @@ func (m *WorkflowDeleteRequest) UnmarshalVT(dAtA []byte) error { break } } - m.KeepData = bool(v != 0) - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KeepRoutingRules", wireType) + m.KeepRoutingRules = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflow @@ -56724,12 +57545,24 @@ func (m *WorkflowDeleteRequest) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.KeepRoutingRules = bool(v != 0) + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -57069,6 +57902,38 @@ func (m *WorkflowStatusRequest) UnmarshalVT(dAtA []byte) error { } m.Workflow = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -58245,6 +59110,38 @@ func (m *WorkflowSwitchTrafficRequest) UnmarshalVT(dAtA []byte) error { } } m.InitializeTargetSequences = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shards", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + 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 ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Shards = append(m.Shards, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/go/vt/proto/vtctlservice/vtctlservice.pb.go b/go/vt/proto/vtctlservice/vtctlservice.pb.go index 41231828a3d..96b3eeadd26 100644 --- a/go/vt/proto/vtctlservice/vtctlservice.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtctlservice.proto @@ -51,7 +51,7 @@ var file_vtctlservice_proto_rawDesc = []byte{ 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x32, 0x93, 0x50, 0x0a, 0x06, 0x56, 0x74, 0x63, 0x74, 0x6c, + 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x32, 0x93, 0x51, 0x0a, 0x06, 0x56, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x12, 0x4e, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, @@ -197,6 +197,14 @@ var file_vtctlservice_proto_rawDesc = []byte{ 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x6c, 0x6c, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x49, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x1b, 0x46, 0x6f, 0x72, 0x63, + 0x65, 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x75, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x74, 0x63, 0x74, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, @@ -726,198 +734,200 @@ var file_vtctlservice_proto_goTypes = []interface{}{ (*vtctldata.ExecuteFetchAsDBARequest)(nil), // 23: vtctldata.ExecuteFetchAsDBARequest (*vtctldata.ExecuteHookRequest)(nil), // 24: vtctldata.ExecuteHookRequest (*vtctldata.FindAllShardsInKeyspaceRequest)(nil), // 25: vtctldata.FindAllShardsInKeyspaceRequest - (*vtctldata.GetBackupsRequest)(nil), // 26: vtctldata.GetBackupsRequest - (*vtctldata.GetCellInfoRequest)(nil), // 27: vtctldata.GetCellInfoRequest - (*vtctldata.GetCellInfoNamesRequest)(nil), // 28: vtctldata.GetCellInfoNamesRequest - (*vtctldata.GetCellsAliasesRequest)(nil), // 29: vtctldata.GetCellsAliasesRequest - (*vtctldata.GetFullStatusRequest)(nil), // 30: vtctldata.GetFullStatusRequest - (*vtctldata.GetKeyspaceRequest)(nil), // 31: vtctldata.GetKeyspaceRequest - (*vtctldata.GetKeyspacesRequest)(nil), // 32: vtctldata.GetKeyspacesRequest - (*vtctldata.GetPermissionsRequest)(nil), // 33: vtctldata.GetPermissionsRequest - (*vtctldata.GetRoutingRulesRequest)(nil), // 34: vtctldata.GetRoutingRulesRequest - (*vtctldata.GetSchemaRequest)(nil), // 35: vtctldata.GetSchemaRequest - (*vtctldata.GetSchemaMigrationsRequest)(nil), // 36: vtctldata.GetSchemaMigrationsRequest - (*vtctldata.GetShardRequest)(nil), // 37: vtctldata.GetShardRequest - (*vtctldata.GetShardRoutingRulesRequest)(nil), // 38: vtctldata.GetShardRoutingRulesRequest - (*vtctldata.GetSrvKeyspaceNamesRequest)(nil), // 39: vtctldata.GetSrvKeyspaceNamesRequest - (*vtctldata.GetSrvKeyspacesRequest)(nil), // 40: vtctldata.GetSrvKeyspacesRequest - (*vtctldata.UpdateThrottlerConfigRequest)(nil), // 41: vtctldata.UpdateThrottlerConfigRequest - (*vtctldata.GetSrvVSchemaRequest)(nil), // 42: vtctldata.GetSrvVSchemaRequest - (*vtctldata.GetSrvVSchemasRequest)(nil), // 43: vtctldata.GetSrvVSchemasRequest - (*vtctldata.GetTabletRequest)(nil), // 44: vtctldata.GetTabletRequest - (*vtctldata.GetTabletsRequest)(nil), // 45: vtctldata.GetTabletsRequest - (*vtctldata.GetTopologyPathRequest)(nil), // 46: vtctldata.GetTopologyPathRequest - (*vtctldata.GetVersionRequest)(nil), // 47: vtctldata.GetVersionRequest - (*vtctldata.GetVSchemaRequest)(nil), // 48: vtctldata.GetVSchemaRequest - (*vtctldata.GetWorkflowsRequest)(nil), // 49: vtctldata.GetWorkflowsRequest - (*vtctldata.InitShardPrimaryRequest)(nil), // 50: vtctldata.InitShardPrimaryRequest - (*vtctldata.LaunchSchemaMigrationRequest)(nil), // 51: vtctldata.LaunchSchemaMigrationRequest - (*vtctldata.LookupVindexCreateRequest)(nil), // 52: vtctldata.LookupVindexCreateRequest - (*vtctldata.LookupVindexExternalizeRequest)(nil), // 53: vtctldata.LookupVindexExternalizeRequest - (*vtctldata.MaterializeCreateRequest)(nil), // 54: vtctldata.MaterializeCreateRequest - (*vtctldata.MigrateCreateRequest)(nil), // 55: vtctldata.MigrateCreateRequest - (*vtctldata.MountRegisterRequest)(nil), // 56: vtctldata.MountRegisterRequest - (*vtctldata.MountUnregisterRequest)(nil), // 57: vtctldata.MountUnregisterRequest - (*vtctldata.MountShowRequest)(nil), // 58: vtctldata.MountShowRequest - (*vtctldata.MountListRequest)(nil), // 59: vtctldata.MountListRequest - (*vtctldata.MoveTablesCreateRequest)(nil), // 60: vtctldata.MoveTablesCreateRequest - (*vtctldata.MoveTablesCompleteRequest)(nil), // 61: vtctldata.MoveTablesCompleteRequest - (*vtctldata.PingTabletRequest)(nil), // 62: vtctldata.PingTabletRequest - (*vtctldata.PlannedReparentShardRequest)(nil), // 63: vtctldata.PlannedReparentShardRequest - (*vtctldata.RebuildKeyspaceGraphRequest)(nil), // 64: vtctldata.RebuildKeyspaceGraphRequest - (*vtctldata.RebuildVSchemaGraphRequest)(nil), // 65: vtctldata.RebuildVSchemaGraphRequest - (*vtctldata.RefreshStateRequest)(nil), // 66: vtctldata.RefreshStateRequest - (*vtctldata.RefreshStateByShardRequest)(nil), // 67: vtctldata.RefreshStateByShardRequest - (*vtctldata.ReloadSchemaRequest)(nil), // 68: vtctldata.ReloadSchemaRequest - (*vtctldata.ReloadSchemaKeyspaceRequest)(nil), // 69: vtctldata.ReloadSchemaKeyspaceRequest - (*vtctldata.ReloadSchemaShardRequest)(nil), // 70: vtctldata.ReloadSchemaShardRequest - (*vtctldata.RemoveBackupRequest)(nil), // 71: vtctldata.RemoveBackupRequest - (*vtctldata.RemoveKeyspaceCellRequest)(nil), // 72: vtctldata.RemoveKeyspaceCellRequest - (*vtctldata.RemoveShardCellRequest)(nil), // 73: vtctldata.RemoveShardCellRequest - (*vtctldata.ReparentTabletRequest)(nil), // 74: vtctldata.ReparentTabletRequest - (*vtctldata.ReshardCreateRequest)(nil), // 75: vtctldata.ReshardCreateRequest - (*vtctldata.RestoreFromBackupRequest)(nil), // 76: vtctldata.RestoreFromBackupRequest - (*vtctldata.RetrySchemaMigrationRequest)(nil), // 77: vtctldata.RetrySchemaMigrationRequest - (*vtctldata.RunHealthCheckRequest)(nil), // 78: vtctldata.RunHealthCheckRequest - (*vtctldata.SetKeyspaceDurabilityPolicyRequest)(nil), // 79: vtctldata.SetKeyspaceDurabilityPolicyRequest - (*vtctldata.SetShardIsPrimaryServingRequest)(nil), // 80: vtctldata.SetShardIsPrimaryServingRequest - (*vtctldata.SetShardTabletControlRequest)(nil), // 81: vtctldata.SetShardTabletControlRequest - (*vtctldata.SetWritableRequest)(nil), // 82: vtctldata.SetWritableRequest - (*vtctldata.ShardReplicationAddRequest)(nil), // 83: vtctldata.ShardReplicationAddRequest - (*vtctldata.ShardReplicationFixRequest)(nil), // 84: vtctldata.ShardReplicationFixRequest - (*vtctldata.ShardReplicationPositionsRequest)(nil), // 85: vtctldata.ShardReplicationPositionsRequest - (*vtctldata.ShardReplicationRemoveRequest)(nil), // 86: vtctldata.ShardReplicationRemoveRequest - (*vtctldata.SleepTabletRequest)(nil), // 87: vtctldata.SleepTabletRequest - (*vtctldata.SourceShardAddRequest)(nil), // 88: vtctldata.SourceShardAddRequest - (*vtctldata.SourceShardDeleteRequest)(nil), // 89: vtctldata.SourceShardDeleteRequest - (*vtctldata.StartReplicationRequest)(nil), // 90: vtctldata.StartReplicationRequest - (*vtctldata.StopReplicationRequest)(nil), // 91: vtctldata.StopReplicationRequest - (*vtctldata.TabletExternallyReparentedRequest)(nil), // 92: vtctldata.TabletExternallyReparentedRequest - (*vtctldata.UpdateCellInfoRequest)(nil), // 93: vtctldata.UpdateCellInfoRequest - (*vtctldata.UpdateCellsAliasRequest)(nil), // 94: vtctldata.UpdateCellsAliasRequest - (*vtctldata.ValidateRequest)(nil), // 95: vtctldata.ValidateRequest - (*vtctldata.ValidateKeyspaceRequest)(nil), // 96: vtctldata.ValidateKeyspaceRequest - (*vtctldata.ValidateSchemaKeyspaceRequest)(nil), // 97: vtctldata.ValidateSchemaKeyspaceRequest - (*vtctldata.ValidateShardRequest)(nil), // 98: vtctldata.ValidateShardRequest - (*vtctldata.ValidateVersionKeyspaceRequest)(nil), // 99: vtctldata.ValidateVersionKeyspaceRequest - (*vtctldata.ValidateVersionShardRequest)(nil), // 100: vtctldata.ValidateVersionShardRequest - (*vtctldata.ValidateVSchemaRequest)(nil), // 101: vtctldata.ValidateVSchemaRequest - (*vtctldata.VDiffCreateRequest)(nil), // 102: vtctldata.VDiffCreateRequest - (*vtctldata.VDiffDeleteRequest)(nil), // 103: vtctldata.VDiffDeleteRequest - (*vtctldata.VDiffResumeRequest)(nil), // 104: vtctldata.VDiffResumeRequest - (*vtctldata.VDiffShowRequest)(nil), // 105: vtctldata.VDiffShowRequest - (*vtctldata.VDiffStopRequest)(nil), // 106: vtctldata.VDiffStopRequest - (*vtctldata.WorkflowDeleteRequest)(nil), // 107: vtctldata.WorkflowDeleteRequest - (*vtctldata.WorkflowStatusRequest)(nil), // 108: vtctldata.WorkflowStatusRequest - (*vtctldata.WorkflowSwitchTrafficRequest)(nil), // 109: vtctldata.WorkflowSwitchTrafficRequest - (*vtctldata.WorkflowUpdateRequest)(nil), // 110: vtctldata.WorkflowUpdateRequest - (*vtctldata.ExecuteVtctlCommandResponse)(nil), // 111: vtctldata.ExecuteVtctlCommandResponse - (*vtctldata.AddCellInfoResponse)(nil), // 112: vtctldata.AddCellInfoResponse - (*vtctldata.AddCellsAliasResponse)(nil), // 113: vtctldata.AddCellsAliasResponse - (*vtctldata.ApplyRoutingRulesResponse)(nil), // 114: vtctldata.ApplyRoutingRulesResponse - (*vtctldata.ApplySchemaResponse)(nil), // 115: vtctldata.ApplySchemaResponse - (*vtctldata.ApplyShardRoutingRulesResponse)(nil), // 116: vtctldata.ApplyShardRoutingRulesResponse - (*vtctldata.ApplyVSchemaResponse)(nil), // 117: vtctldata.ApplyVSchemaResponse - (*vtctldata.BackupResponse)(nil), // 118: vtctldata.BackupResponse - (*vtctldata.CancelSchemaMigrationResponse)(nil), // 119: vtctldata.CancelSchemaMigrationResponse - (*vtctldata.ChangeTabletTypeResponse)(nil), // 120: vtctldata.ChangeTabletTypeResponse - (*vtctldata.CleanupSchemaMigrationResponse)(nil), // 121: vtctldata.CleanupSchemaMigrationResponse - (*vtctldata.CompleteSchemaMigrationResponse)(nil), // 122: vtctldata.CompleteSchemaMigrationResponse - (*vtctldata.CreateKeyspaceResponse)(nil), // 123: vtctldata.CreateKeyspaceResponse - (*vtctldata.CreateShardResponse)(nil), // 124: vtctldata.CreateShardResponse - (*vtctldata.DeleteCellInfoResponse)(nil), // 125: vtctldata.DeleteCellInfoResponse - (*vtctldata.DeleteCellsAliasResponse)(nil), // 126: vtctldata.DeleteCellsAliasResponse - (*vtctldata.DeleteKeyspaceResponse)(nil), // 127: vtctldata.DeleteKeyspaceResponse - (*vtctldata.DeleteShardsResponse)(nil), // 128: vtctldata.DeleteShardsResponse - (*vtctldata.DeleteSrvVSchemaResponse)(nil), // 129: vtctldata.DeleteSrvVSchemaResponse - (*vtctldata.DeleteTabletsResponse)(nil), // 130: vtctldata.DeleteTabletsResponse - (*vtctldata.EmergencyReparentShardResponse)(nil), // 131: vtctldata.EmergencyReparentShardResponse - (*vtctldata.ExecuteFetchAsAppResponse)(nil), // 132: vtctldata.ExecuteFetchAsAppResponse - (*vtctldata.ExecuteFetchAsDBAResponse)(nil), // 133: vtctldata.ExecuteFetchAsDBAResponse - (*vtctldata.ExecuteHookResponse)(nil), // 134: vtctldata.ExecuteHookResponse - (*vtctldata.FindAllShardsInKeyspaceResponse)(nil), // 135: vtctldata.FindAllShardsInKeyspaceResponse - (*vtctldata.GetBackupsResponse)(nil), // 136: vtctldata.GetBackupsResponse - (*vtctldata.GetCellInfoResponse)(nil), // 137: vtctldata.GetCellInfoResponse - (*vtctldata.GetCellInfoNamesResponse)(nil), // 138: vtctldata.GetCellInfoNamesResponse - (*vtctldata.GetCellsAliasesResponse)(nil), // 139: vtctldata.GetCellsAliasesResponse - (*vtctldata.GetFullStatusResponse)(nil), // 140: vtctldata.GetFullStatusResponse - (*vtctldata.GetKeyspaceResponse)(nil), // 141: vtctldata.GetKeyspaceResponse - (*vtctldata.GetKeyspacesResponse)(nil), // 142: vtctldata.GetKeyspacesResponse - (*vtctldata.GetPermissionsResponse)(nil), // 143: vtctldata.GetPermissionsResponse - (*vtctldata.GetRoutingRulesResponse)(nil), // 144: vtctldata.GetRoutingRulesResponse - (*vtctldata.GetSchemaResponse)(nil), // 145: vtctldata.GetSchemaResponse - (*vtctldata.GetSchemaMigrationsResponse)(nil), // 146: vtctldata.GetSchemaMigrationsResponse - (*vtctldata.GetShardResponse)(nil), // 147: vtctldata.GetShardResponse - (*vtctldata.GetShardRoutingRulesResponse)(nil), // 148: vtctldata.GetShardRoutingRulesResponse - (*vtctldata.GetSrvKeyspaceNamesResponse)(nil), // 149: vtctldata.GetSrvKeyspaceNamesResponse - (*vtctldata.GetSrvKeyspacesResponse)(nil), // 150: vtctldata.GetSrvKeyspacesResponse - (*vtctldata.UpdateThrottlerConfigResponse)(nil), // 151: vtctldata.UpdateThrottlerConfigResponse - (*vtctldata.GetSrvVSchemaResponse)(nil), // 152: vtctldata.GetSrvVSchemaResponse - (*vtctldata.GetSrvVSchemasResponse)(nil), // 153: vtctldata.GetSrvVSchemasResponse - (*vtctldata.GetTabletResponse)(nil), // 154: vtctldata.GetTabletResponse - (*vtctldata.GetTabletsResponse)(nil), // 155: vtctldata.GetTabletsResponse - (*vtctldata.GetTopologyPathResponse)(nil), // 156: vtctldata.GetTopologyPathResponse - (*vtctldata.GetVersionResponse)(nil), // 157: vtctldata.GetVersionResponse - (*vtctldata.GetVSchemaResponse)(nil), // 158: vtctldata.GetVSchemaResponse - (*vtctldata.GetWorkflowsResponse)(nil), // 159: vtctldata.GetWorkflowsResponse - (*vtctldata.InitShardPrimaryResponse)(nil), // 160: vtctldata.InitShardPrimaryResponse - (*vtctldata.LaunchSchemaMigrationResponse)(nil), // 161: vtctldata.LaunchSchemaMigrationResponse - (*vtctldata.LookupVindexCreateResponse)(nil), // 162: vtctldata.LookupVindexCreateResponse - (*vtctldata.LookupVindexExternalizeResponse)(nil), // 163: vtctldata.LookupVindexExternalizeResponse - (*vtctldata.MaterializeCreateResponse)(nil), // 164: vtctldata.MaterializeCreateResponse - (*vtctldata.WorkflowStatusResponse)(nil), // 165: vtctldata.WorkflowStatusResponse - (*vtctldata.MountRegisterResponse)(nil), // 166: vtctldata.MountRegisterResponse - (*vtctldata.MountUnregisterResponse)(nil), // 167: vtctldata.MountUnregisterResponse - (*vtctldata.MountShowResponse)(nil), // 168: vtctldata.MountShowResponse - (*vtctldata.MountListResponse)(nil), // 169: vtctldata.MountListResponse - (*vtctldata.MoveTablesCompleteResponse)(nil), // 170: vtctldata.MoveTablesCompleteResponse - (*vtctldata.PingTabletResponse)(nil), // 171: vtctldata.PingTabletResponse - (*vtctldata.PlannedReparentShardResponse)(nil), // 172: vtctldata.PlannedReparentShardResponse - (*vtctldata.RebuildKeyspaceGraphResponse)(nil), // 173: vtctldata.RebuildKeyspaceGraphResponse - (*vtctldata.RebuildVSchemaGraphResponse)(nil), // 174: vtctldata.RebuildVSchemaGraphResponse - (*vtctldata.RefreshStateResponse)(nil), // 175: vtctldata.RefreshStateResponse - (*vtctldata.RefreshStateByShardResponse)(nil), // 176: vtctldata.RefreshStateByShardResponse - (*vtctldata.ReloadSchemaResponse)(nil), // 177: vtctldata.ReloadSchemaResponse - (*vtctldata.ReloadSchemaKeyspaceResponse)(nil), // 178: vtctldata.ReloadSchemaKeyspaceResponse - (*vtctldata.ReloadSchemaShardResponse)(nil), // 179: vtctldata.ReloadSchemaShardResponse - (*vtctldata.RemoveBackupResponse)(nil), // 180: vtctldata.RemoveBackupResponse - (*vtctldata.RemoveKeyspaceCellResponse)(nil), // 181: vtctldata.RemoveKeyspaceCellResponse - (*vtctldata.RemoveShardCellResponse)(nil), // 182: vtctldata.RemoveShardCellResponse - (*vtctldata.ReparentTabletResponse)(nil), // 183: vtctldata.ReparentTabletResponse - (*vtctldata.RestoreFromBackupResponse)(nil), // 184: vtctldata.RestoreFromBackupResponse - (*vtctldata.RetrySchemaMigrationResponse)(nil), // 185: vtctldata.RetrySchemaMigrationResponse - (*vtctldata.RunHealthCheckResponse)(nil), // 186: vtctldata.RunHealthCheckResponse - (*vtctldata.SetKeyspaceDurabilityPolicyResponse)(nil), // 187: vtctldata.SetKeyspaceDurabilityPolicyResponse - (*vtctldata.SetShardIsPrimaryServingResponse)(nil), // 188: vtctldata.SetShardIsPrimaryServingResponse - (*vtctldata.SetShardTabletControlResponse)(nil), // 189: vtctldata.SetShardTabletControlResponse - (*vtctldata.SetWritableResponse)(nil), // 190: vtctldata.SetWritableResponse - (*vtctldata.ShardReplicationAddResponse)(nil), // 191: vtctldata.ShardReplicationAddResponse - (*vtctldata.ShardReplicationFixResponse)(nil), // 192: vtctldata.ShardReplicationFixResponse - (*vtctldata.ShardReplicationPositionsResponse)(nil), // 193: vtctldata.ShardReplicationPositionsResponse - (*vtctldata.ShardReplicationRemoveResponse)(nil), // 194: vtctldata.ShardReplicationRemoveResponse - (*vtctldata.SleepTabletResponse)(nil), // 195: vtctldata.SleepTabletResponse - (*vtctldata.SourceShardAddResponse)(nil), // 196: vtctldata.SourceShardAddResponse - (*vtctldata.SourceShardDeleteResponse)(nil), // 197: vtctldata.SourceShardDeleteResponse - (*vtctldata.StartReplicationResponse)(nil), // 198: vtctldata.StartReplicationResponse - (*vtctldata.StopReplicationResponse)(nil), // 199: vtctldata.StopReplicationResponse - (*vtctldata.TabletExternallyReparentedResponse)(nil), // 200: vtctldata.TabletExternallyReparentedResponse - (*vtctldata.UpdateCellInfoResponse)(nil), // 201: vtctldata.UpdateCellInfoResponse - (*vtctldata.UpdateCellsAliasResponse)(nil), // 202: vtctldata.UpdateCellsAliasResponse - (*vtctldata.ValidateResponse)(nil), // 203: vtctldata.ValidateResponse - (*vtctldata.ValidateKeyspaceResponse)(nil), // 204: vtctldata.ValidateKeyspaceResponse - (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 205: vtctldata.ValidateSchemaKeyspaceResponse - (*vtctldata.ValidateShardResponse)(nil), // 206: vtctldata.ValidateShardResponse - (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 207: vtctldata.ValidateVersionKeyspaceResponse - (*vtctldata.ValidateVersionShardResponse)(nil), // 208: vtctldata.ValidateVersionShardResponse - (*vtctldata.ValidateVSchemaResponse)(nil), // 209: vtctldata.ValidateVSchemaResponse - (*vtctldata.VDiffCreateResponse)(nil), // 210: vtctldata.VDiffCreateResponse - (*vtctldata.VDiffDeleteResponse)(nil), // 211: vtctldata.VDiffDeleteResponse - (*vtctldata.VDiffResumeResponse)(nil), // 212: vtctldata.VDiffResumeResponse - (*vtctldata.VDiffShowResponse)(nil), // 213: vtctldata.VDiffShowResponse - (*vtctldata.VDiffStopResponse)(nil), // 214: vtctldata.VDiffStopResponse - (*vtctldata.WorkflowDeleteResponse)(nil), // 215: vtctldata.WorkflowDeleteResponse - (*vtctldata.WorkflowSwitchTrafficResponse)(nil), // 216: vtctldata.WorkflowSwitchTrafficResponse - (*vtctldata.WorkflowUpdateResponse)(nil), // 217: vtctldata.WorkflowUpdateResponse + (*vtctldata.ForceCutOverSchemaMigrationRequest)(nil), // 26: vtctldata.ForceCutOverSchemaMigrationRequest + (*vtctldata.GetBackupsRequest)(nil), // 27: vtctldata.GetBackupsRequest + (*vtctldata.GetCellInfoRequest)(nil), // 28: vtctldata.GetCellInfoRequest + (*vtctldata.GetCellInfoNamesRequest)(nil), // 29: vtctldata.GetCellInfoNamesRequest + (*vtctldata.GetCellsAliasesRequest)(nil), // 30: vtctldata.GetCellsAliasesRequest + (*vtctldata.GetFullStatusRequest)(nil), // 31: vtctldata.GetFullStatusRequest + (*vtctldata.GetKeyspaceRequest)(nil), // 32: vtctldata.GetKeyspaceRequest + (*vtctldata.GetKeyspacesRequest)(nil), // 33: vtctldata.GetKeyspacesRequest + (*vtctldata.GetPermissionsRequest)(nil), // 34: vtctldata.GetPermissionsRequest + (*vtctldata.GetRoutingRulesRequest)(nil), // 35: vtctldata.GetRoutingRulesRequest + (*vtctldata.GetSchemaRequest)(nil), // 36: vtctldata.GetSchemaRequest + (*vtctldata.GetSchemaMigrationsRequest)(nil), // 37: vtctldata.GetSchemaMigrationsRequest + (*vtctldata.GetShardRequest)(nil), // 38: vtctldata.GetShardRequest + (*vtctldata.GetShardRoutingRulesRequest)(nil), // 39: vtctldata.GetShardRoutingRulesRequest + (*vtctldata.GetSrvKeyspaceNamesRequest)(nil), // 40: vtctldata.GetSrvKeyspaceNamesRequest + (*vtctldata.GetSrvKeyspacesRequest)(nil), // 41: vtctldata.GetSrvKeyspacesRequest + (*vtctldata.UpdateThrottlerConfigRequest)(nil), // 42: vtctldata.UpdateThrottlerConfigRequest + (*vtctldata.GetSrvVSchemaRequest)(nil), // 43: vtctldata.GetSrvVSchemaRequest + (*vtctldata.GetSrvVSchemasRequest)(nil), // 44: vtctldata.GetSrvVSchemasRequest + (*vtctldata.GetTabletRequest)(nil), // 45: vtctldata.GetTabletRequest + (*vtctldata.GetTabletsRequest)(nil), // 46: vtctldata.GetTabletsRequest + (*vtctldata.GetTopologyPathRequest)(nil), // 47: vtctldata.GetTopologyPathRequest + (*vtctldata.GetVersionRequest)(nil), // 48: vtctldata.GetVersionRequest + (*vtctldata.GetVSchemaRequest)(nil), // 49: vtctldata.GetVSchemaRequest + (*vtctldata.GetWorkflowsRequest)(nil), // 50: vtctldata.GetWorkflowsRequest + (*vtctldata.InitShardPrimaryRequest)(nil), // 51: vtctldata.InitShardPrimaryRequest + (*vtctldata.LaunchSchemaMigrationRequest)(nil), // 52: vtctldata.LaunchSchemaMigrationRequest + (*vtctldata.LookupVindexCreateRequest)(nil), // 53: vtctldata.LookupVindexCreateRequest + (*vtctldata.LookupVindexExternalizeRequest)(nil), // 54: vtctldata.LookupVindexExternalizeRequest + (*vtctldata.MaterializeCreateRequest)(nil), // 55: vtctldata.MaterializeCreateRequest + (*vtctldata.MigrateCreateRequest)(nil), // 56: vtctldata.MigrateCreateRequest + (*vtctldata.MountRegisterRequest)(nil), // 57: vtctldata.MountRegisterRequest + (*vtctldata.MountUnregisterRequest)(nil), // 58: vtctldata.MountUnregisterRequest + (*vtctldata.MountShowRequest)(nil), // 59: vtctldata.MountShowRequest + (*vtctldata.MountListRequest)(nil), // 60: vtctldata.MountListRequest + (*vtctldata.MoveTablesCreateRequest)(nil), // 61: vtctldata.MoveTablesCreateRequest + (*vtctldata.MoveTablesCompleteRequest)(nil), // 62: vtctldata.MoveTablesCompleteRequest + (*vtctldata.PingTabletRequest)(nil), // 63: vtctldata.PingTabletRequest + (*vtctldata.PlannedReparentShardRequest)(nil), // 64: vtctldata.PlannedReparentShardRequest + (*vtctldata.RebuildKeyspaceGraphRequest)(nil), // 65: vtctldata.RebuildKeyspaceGraphRequest + (*vtctldata.RebuildVSchemaGraphRequest)(nil), // 66: vtctldata.RebuildVSchemaGraphRequest + (*vtctldata.RefreshStateRequest)(nil), // 67: vtctldata.RefreshStateRequest + (*vtctldata.RefreshStateByShardRequest)(nil), // 68: vtctldata.RefreshStateByShardRequest + (*vtctldata.ReloadSchemaRequest)(nil), // 69: vtctldata.ReloadSchemaRequest + (*vtctldata.ReloadSchemaKeyspaceRequest)(nil), // 70: vtctldata.ReloadSchemaKeyspaceRequest + (*vtctldata.ReloadSchemaShardRequest)(nil), // 71: vtctldata.ReloadSchemaShardRequest + (*vtctldata.RemoveBackupRequest)(nil), // 72: vtctldata.RemoveBackupRequest + (*vtctldata.RemoveKeyspaceCellRequest)(nil), // 73: vtctldata.RemoveKeyspaceCellRequest + (*vtctldata.RemoveShardCellRequest)(nil), // 74: vtctldata.RemoveShardCellRequest + (*vtctldata.ReparentTabletRequest)(nil), // 75: vtctldata.ReparentTabletRequest + (*vtctldata.ReshardCreateRequest)(nil), // 76: vtctldata.ReshardCreateRequest + (*vtctldata.RestoreFromBackupRequest)(nil), // 77: vtctldata.RestoreFromBackupRequest + (*vtctldata.RetrySchemaMigrationRequest)(nil), // 78: vtctldata.RetrySchemaMigrationRequest + (*vtctldata.RunHealthCheckRequest)(nil), // 79: vtctldata.RunHealthCheckRequest + (*vtctldata.SetKeyspaceDurabilityPolicyRequest)(nil), // 80: vtctldata.SetKeyspaceDurabilityPolicyRequest + (*vtctldata.SetShardIsPrimaryServingRequest)(nil), // 81: vtctldata.SetShardIsPrimaryServingRequest + (*vtctldata.SetShardTabletControlRequest)(nil), // 82: vtctldata.SetShardTabletControlRequest + (*vtctldata.SetWritableRequest)(nil), // 83: vtctldata.SetWritableRequest + (*vtctldata.ShardReplicationAddRequest)(nil), // 84: vtctldata.ShardReplicationAddRequest + (*vtctldata.ShardReplicationFixRequest)(nil), // 85: vtctldata.ShardReplicationFixRequest + (*vtctldata.ShardReplicationPositionsRequest)(nil), // 86: vtctldata.ShardReplicationPositionsRequest + (*vtctldata.ShardReplicationRemoveRequest)(nil), // 87: vtctldata.ShardReplicationRemoveRequest + (*vtctldata.SleepTabletRequest)(nil), // 88: vtctldata.SleepTabletRequest + (*vtctldata.SourceShardAddRequest)(nil), // 89: vtctldata.SourceShardAddRequest + (*vtctldata.SourceShardDeleteRequest)(nil), // 90: vtctldata.SourceShardDeleteRequest + (*vtctldata.StartReplicationRequest)(nil), // 91: vtctldata.StartReplicationRequest + (*vtctldata.StopReplicationRequest)(nil), // 92: vtctldata.StopReplicationRequest + (*vtctldata.TabletExternallyReparentedRequest)(nil), // 93: vtctldata.TabletExternallyReparentedRequest + (*vtctldata.UpdateCellInfoRequest)(nil), // 94: vtctldata.UpdateCellInfoRequest + (*vtctldata.UpdateCellsAliasRequest)(nil), // 95: vtctldata.UpdateCellsAliasRequest + (*vtctldata.ValidateRequest)(nil), // 96: vtctldata.ValidateRequest + (*vtctldata.ValidateKeyspaceRequest)(nil), // 97: vtctldata.ValidateKeyspaceRequest + (*vtctldata.ValidateSchemaKeyspaceRequest)(nil), // 98: vtctldata.ValidateSchemaKeyspaceRequest + (*vtctldata.ValidateShardRequest)(nil), // 99: vtctldata.ValidateShardRequest + (*vtctldata.ValidateVersionKeyspaceRequest)(nil), // 100: vtctldata.ValidateVersionKeyspaceRequest + (*vtctldata.ValidateVersionShardRequest)(nil), // 101: vtctldata.ValidateVersionShardRequest + (*vtctldata.ValidateVSchemaRequest)(nil), // 102: vtctldata.ValidateVSchemaRequest + (*vtctldata.VDiffCreateRequest)(nil), // 103: vtctldata.VDiffCreateRequest + (*vtctldata.VDiffDeleteRequest)(nil), // 104: vtctldata.VDiffDeleteRequest + (*vtctldata.VDiffResumeRequest)(nil), // 105: vtctldata.VDiffResumeRequest + (*vtctldata.VDiffShowRequest)(nil), // 106: vtctldata.VDiffShowRequest + (*vtctldata.VDiffStopRequest)(nil), // 107: vtctldata.VDiffStopRequest + (*vtctldata.WorkflowDeleteRequest)(nil), // 108: vtctldata.WorkflowDeleteRequest + (*vtctldata.WorkflowStatusRequest)(nil), // 109: vtctldata.WorkflowStatusRequest + (*vtctldata.WorkflowSwitchTrafficRequest)(nil), // 110: vtctldata.WorkflowSwitchTrafficRequest + (*vtctldata.WorkflowUpdateRequest)(nil), // 111: vtctldata.WorkflowUpdateRequest + (*vtctldata.ExecuteVtctlCommandResponse)(nil), // 112: vtctldata.ExecuteVtctlCommandResponse + (*vtctldata.AddCellInfoResponse)(nil), // 113: vtctldata.AddCellInfoResponse + (*vtctldata.AddCellsAliasResponse)(nil), // 114: vtctldata.AddCellsAliasResponse + (*vtctldata.ApplyRoutingRulesResponse)(nil), // 115: vtctldata.ApplyRoutingRulesResponse + (*vtctldata.ApplySchemaResponse)(nil), // 116: vtctldata.ApplySchemaResponse + (*vtctldata.ApplyShardRoutingRulesResponse)(nil), // 117: vtctldata.ApplyShardRoutingRulesResponse + (*vtctldata.ApplyVSchemaResponse)(nil), // 118: vtctldata.ApplyVSchemaResponse + (*vtctldata.BackupResponse)(nil), // 119: vtctldata.BackupResponse + (*vtctldata.CancelSchemaMigrationResponse)(nil), // 120: vtctldata.CancelSchemaMigrationResponse + (*vtctldata.ChangeTabletTypeResponse)(nil), // 121: vtctldata.ChangeTabletTypeResponse + (*vtctldata.CleanupSchemaMigrationResponse)(nil), // 122: vtctldata.CleanupSchemaMigrationResponse + (*vtctldata.CompleteSchemaMigrationResponse)(nil), // 123: vtctldata.CompleteSchemaMigrationResponse + (*vtctldata.CreateKeyspaceResponse)(nil), // 124: vtctldata.CreateKeyspaceResponse + (*vtctldata.CreateShardResponse)(nil), // 125: vtctldata.CreateShardResponse + (*vtctldata.DeleteCellInfoResponse)(nil), // 126: vtctldata.DeleteCellInfoResponse + (*vtctldata.DeleteCellsAliasResponse)(nil), // 127: vtctldata.DeleteCellsAliasResponse + (*vtctldata.DeleteKeyspaceResponse)(nil), // 128: vtctldata.DeleteKeyspaceResponse + (*vtctldata.DeleteShardsResponse)(nil), // 129: vtctldata.DeleteShardsResponse + (*vtctldata.DeleteSrvVSchemaResponse)(nil), // 130: vtctldata.DeleteSrvVSchemaResponse + (*vtctldata.DeleteTabletsResponse)(nil), // 131: vtctldata.DeleteTabletsResponse + (*vtctldata.EmergencyReparentShardResponse)(nil), // 132: vtctldata.EmergencyReparentShardResponse + (*vtctldata.ExecuteFetchAsAppResponse)(nil), // 133: vtctldata.ExecuteFetchAsAppResponse + (*vtctldata.ExecuteFetchAsDBAResponse)(nil), // 134: vtctldata.ExecuteFetchAsDBAResponse + (*vtctldata.ExecuteHookResponse)(nil), // 135: vtctldata.ExecuteHookResponse + (*vtctldata.FindAllShardsInKeyspaceResponse)(nil), // 136: vtctldata.FindAllShardsInKeyspaceResponse + (*vtctldata.ForceCutOverSchemaMigrationResponse)(nil), // 137: vtctldata.ForceCutOverSchemaMigrationResponse + (*vtctldata.GetBackupsResponse)(nil), // 138: vtctldata.GetBackupsResponse + (*vtctldata.GetCellInfoResponse)(nil), // 139: vtctldata.GetCellInfoResponse + (*vtctldata.GetCellInfoNamesResponse)(nil), // 140: vtctldata.GetCellInfoNamesResponse + (*vtctldata.GetCellsAliasesResponse)(nil), // 141: vtctldata.GetCellsAliasesResponse + (*vtctldata.GetFullStatusResponse)(nil), // 142: vtctldata.GetFullStatusResponse + (*vtctldata.GetKeyspaceResponse)(nil), // 143: vtctldata.GetKeyspaceResponse + (*vtctldata.GetKeyspacesResponse)(nil), // 144: vtctldata.GetKeyspacesResponse + (*vtctldata.GetPermissionsResponse)(nil), // 145: vtctldata.GetPermissionsResponse + (*vtctldata.GetRoutingRulesResponse)(nil), // 146: vtctldata.GetRoutingRulesResponse + (*vtctldata.GetSchemaResponse)(nil), // 147: vtctldata.GetSchemaResponse + (*vtctldata.GetSchemaMigrationsResponse)(nil), // 148: vtctldata.GetSchemaMigrationsResponse + (*vtctldata.GetShardResponse)(nil), // 149: vtctldata.GetShardResponse + (*vtctldata.GetShardRoutingRulesResponse)(nil), // 150: vtctldata.GetShardRoutingRulesResponse + (*vtctldata.GetSrvKeyspaceNamesResponse)(nil), // 151: vtctldata.GetSrvKeyspaceNamesResponse + (*vtctldata.GetSrvKeyspacesResponse)(nil), // 152: vtctldata.GetSrvKeyspacesResponse + (*vtctldata.UpdateThrottlerConfigResponse)(nil), // 153: vtctldata.UpdateThrottlerConfigResponse + (*vtctldata.GetSrvVSchemaResponse)(nil), // 154: vtctldata.GetSrvVSchemaResponse + (*vtctldata.GetSrvVSchemasResponse)(nil), // 155: vtctldata.GetSrvVSchemasResponse + (*vtctldata.GetTabletResponse)(nil), // 156: vtctldata.GetTabletResponse + (*vtctldata.GetTabletsResponse)(nil), // 157: vtctldata.GetTabletsResponse + (*vtctldata.GetTopologyPathResponse)(nil), // 158: vtctldata.GetTopologyPathResponse + (*vtctldata.GetVersionResponse)(nil), // 159: vtctldata.GetVersionResponse + (*vtctldata.GetVSchemaResponse)(nil), // 160: vtctldata.GetVSchemaResponse + (*vtctldata.GetWorkflowsResponse)(nil), // 161: vtctldata.GetWorkflowsResponse + (*vtctldata.InitShardPrimaryResponse)(nil), // 162: vtctldata.InitShardPrimaryResponse + (*vtctldata.LaunchSchemaMigrationResponse)(nil), // 163: vtctldata.LaunchSchemaMigrationResponse + (*vtctldata.LookupVindexCreateResponse)(nil), // 164: vtctldata.LookupVindexCreateResponse + (*vtctldata.LookupVindexExternalizeResponse)(nil), // 165: vtctldata.LookupVindexExternalizeResponse + (*vtctldata.MaterializeCreateResponse)(nil), // 166: vtctldata.MaterializeCreateResponse + (*vtctldata.WorkflowStatusResponse)(nil), // 167: vtctldata.WorkflowStatusResponse + (*vtctldata.MountRegisterResponse)(nil), // 168: vtctldata.MountRegisterResponse + (*vtctldata.MountUnregisterResponse)(nil), // 169: vtctldata.MountUnregisterResponse + (*vtctldata.MountShowResponse)(nil), // 170: vtctldata.MountShowResponse + (*vtctldata.MountListResponse)(nil), // 171: vtctldata.MountListResponse + (*vtctldata.MoveTablesCompleteResponse)(nil), // 172: vtctldata.MoveTablesCompleteResponse + (*vtctldata.PingTabletResponse)(nil), // 173: vtctldata.PingTabletResponse + (*vtctldata.PlannedReparentShardResponse)(nil), // 174: vtctldata.PlannedReparentShardResponse + (*vtctldata.RebuildKeyspaceGraphResponse)(nil), // 175: vtctldata.RebuildKeyspaceGraphResponse + (*vtctldata.RebuildVSchemaGraphResponse)(nil), // 176: vtctldata.RebuildVSchemaGraphResponse + (*vtctldata.RefreshStateResponse)(nil), // 177: vtctldata.RefreshStateResponse + (*vtctldata.RefreshStateByShardResponse)(nil), // 178: vtctldata.RefreshStateByShardResponse + (*vtctldata.ReloadSchemaResponse)(nil), // 179: vtctldata.ReloadSchemaResponse + (*vtctldata.ReloadSchemaKeyspaceResponse)(nil), // 180: vtctldata.ReloadSchemaKeyspaceResponse + (*vtctldata.ReloadSchemaShardResponse)(nil), // 181: vtctldata.ReloadSchemaShardResponse + (*vtctldata.RemoveBackupResponse)(nil), // 182: vtctldata.RemoveBackupResponse + (*vtctldata.RemoveKeyspaceCellResponse)(nil), // 183: vtctldata.RemoveKeyspaceCellResponse + (*vtctldata.RemoveShardCellResponse)(nil), // 184: vtctldata.RemoveShardCellResponse + (*vtctldata.ReparentTabletResponse)(nil), // 185: vtctldata.ReparentTabletResponse + (*vtctldata.RestoreFromBackupResponse)(nil), // 186: vtctldata.RestoreFromBackupResponse + (*vtctldata.RetrySchemaMigrationResponse)(nil), // 187: vtctldata.RetrySchemaMigrationResponse + (*vtctldata.RunHealthCheckResponse)(nil), // 188: vtctldata.RunHealthCheckResponse + (*vtctldata.SetKeyspaceDurabilityPolicyResponse)(nil), // 189: vtctldata.SetKeyspaceDurabilityPolicyResponse + (*vtctldata.SetShardIsPrimaryServingResponse)(nil), // 190: vtctldata.SetShardIsPrimaryServingResponse + (*vtctldata.SetShardTabletControlResponse)(nil), // 191: vtctldata.SetShardTabletControlResponse + (*vtctldata.SetWritableResponse)(nil), // 192: vtctldata.SetWritableResponse + (*vtctldata.ShardReplicationAddResponse)(nil), // 193: vtctldata.ShardReplicationAddResponse + (*vtctldata.ShardReplicationFixResponse)(nil), // 194: vtctldata.ShardReplicationFixResponse + (*vtctldata.ShardReplicationPositionsResponse)(nil), // 195: vtctldata.ShardReplicationPositionsResponse + (*vtctldata.ShardReplicationRemoveResponse)(nil), // 196: vtctldata.ShardReplicationRemoveResponse + (*vtctldata.SleepTabletResponse)(nil), // 197: vtctldata.SleepTabletResponse + (*vtctldata.SourceShardAddResponse)(nil), // 198: vtctldata.SourceShardAddResponse + (*vtctldata.SourceShardDeleteResponse)(nil), // 199: vtctldata.SourceShardDeleteResponse + (*vtctldata.StartReplicationResponse)(nil), // 200: vtctldata.StartReplicationResponse + (*vtctldata.StopReplicationResponse)(nil), // 201: vtctldata.StopReplicationResponse + (*vtctldata.TabletExternallyReparentedResponse)(nil), // 202: vtctldata.TabletExternallyReparentedResponse + (*vtctldata.UpdateCellInfoResponse)(nil), // 203: vtctldata.UpdateCellInfoResponse + (*vtctldata.UpdateCellsAliasResponse)(nil), // 204: vtctldata.UpdateCellsAliasResponse + (*vtctldata.ValidateResponse)(nil), // 205: vtctldata.ValidateResponse + (*vtctldata.ValidateKeyspaceResponse)(nil), // 206: vtctldata.ValidateKeyspaceResponse + (*vtctldata.ValidateSchemaKeyspaceResponse)(nil), // 207: vtctldata.ValidateSchemaKeyspaceResponse + (*vtctldata.ValidateShardResponse)(nil), // 208: vtctldata.ValidateShardResponse + (*vtctldata.ValidateVersionKeyspaceResponse)(nil), // 209: vtctldata.ValidateVersionKeyspaceResponse + (*vtctldata.ValidateVersionShardResponse)(nil), // 210: vtctldata.ValidateVersionShardResponse + (*vtctldata.ValidateVSchemaResponse)(nil), // 211: vtctldata.ValidateVSchemaResponse + (*vtctldata.VDiffCreateResponse)(nil), // 212: vtctldata.VDiffCreateResponse + (*vtctldata.VDiffDeleteResponse)(nil), // 213: vtctldata.VDiffDeleteResponse + (*vtctldata.VDiffResumeResponse)(nil), // 214: vtctldata.VDiffResumeResponse + (*vtctldata.VDiffShowResponse)(nil), // 215: vtctldata.VDiffShowResponse + (*vtctldata.VDiffStopResponse)(nil), // 216: vtctldata.VDiffStopResponse + (*vtctldata.WorkflowDeleteResponse)(nil), // 217: vtctldata.WorkflowDeleteResponse + (*vtctldata.WorkflowSwitchTrafficResponse)(nil), // 218: vtctldata.WorkflowSwitchTrafficResponse + (*vtctldata.WorkflowUpdateResponse)(nil), // 219: vtctldata.WorkflowUpdateResponse } var file_vtctlservice_proto_depIdxs = []int32{ 0, // 0: vtctlservice.Vtctl.ExecuteVtctlCommand:input_type -> vtctldata.ExecuteVtctlCommandRequest @@ -946,204 +956,206 @@ var file_vtctlservice_proto_depIdxs = []int32{ 23, // 23: vtctlservice.Vtctld.ExecuteFetchAsDBA:input_type -> vtctldata.ExecuteFetchAsDBARequest 24, // 24: vtctlservice.Vtctld.ExecuteHook:input_type -> vtctldata.ExecuteHookRequest 25, // 25: vtctlservice.Vtctld.FindAllShardsInKeyspace:input_type -> vtctldata.FindAllShardsInKeyspaceRequest - 26, // 26: vtctlservice.Vtctld.GetBackups:input_type -> vtctldata.GetBackupsRequest - 27, // 27: vtctlservice.Vtctld.GetCellInfo:input_type -> vtctldata.GetCellInfoRequest - 28, // 28: vtctlservice.Vtctld.GetCellInfoNames:input_type -> vtctldata.GetCellInfoNamesRequest - 29, // 29: vtctlservice.Vtctld.GetCellsAliases:input_type -> vtctldata.GetCellsAliasesRequest - 30, // 30: vtctlservice.Vtctld.GetFullStatus:input_type -> vtctldata.GetFullStatusRequest - 31, // 31: vtctlservice.Vtctld.GetKeyspace:input_type -> vtctldata.GetKeyspaceRequest - 32, // 32: vtctlservice.Vtctld.GetKeyspaces:input_type -> vtctldata.GetKeyspacesRequest - 33, // 33: vtctlservice.Vtctld.GetPermissions:input_type -> vtctldata.GetPermissionsRequest - 34, // 34: vtctlservice.Vtctld.GetRoutingRules:input_type -> vtctldata.GetRoutingRulesRequest - 35, // 35: vtctlservice.Vtctld.GetSchema:input_type -> vtctldata.GetSchemaRequest - 36, // 36: vtctlservice.Vtctld.GetSchemaMigrations:input_type -> vtctldata.GetSchemaMigrationsRequest - 37, // 37: vtctlservice.Vtctld.GetShard:input_type -> vtctldata.GetShardRequest - 38, // 38: vtctlservice.Vtctld.GetShardRoutingRules:input_type -> vtctldata.GetShardRoutingRulesRequest - 39, // 39: vtctlservice.Vtctld.GetSrvKeyspaceNames:input_type -> vtctldata.GetSrvKeyspaceNamesRequest - 40, // 40: vtctlservice.Vtctld.GetSrvKeyspaces:input_type -> vtctldata.GetSrvKeyspacesRequest - 41, // 41: vtctlservice.Vtctld.UpdateThrottlerConfig:input_type -> vtctldata.UpdateThrottlerConfigRequest - 42, // 42: vtctlservice.Vtctld.GetSrvVSchema:input_type -> vtctldata.GetSrvVSchemaRequest - 43, // 43: vtctlservice.Vtctld.GetSrvVSchemas:input_type -> vtctldata.GetSrvVSchemasRequest - 44, // 44: vtctlservice.Vtctld.GetTablet:input_type -> vtctldata.GetTabletRequest - 45, // 45: vtctlservice.Vtctld.GetTablets:input_type -> vtctldata.GetTabletsRequest - 46, // 46: vtctlservice.Vtctld.GetTopologyPath:input_type -> vtctldata.GetTopologyPathRequest - 47, // 47: vtctlservice.Vtctld.GetVersion:input_type -> vtctldata.GetVersionRequest - 48, // 48: vtctlservice.Vtctld.GetVSchema:input_type -> vtctldata.GetVSchemaRequest - 49, // 49: vtctlservice.Vtctld.GetWorkflows:input_type -> vtctldata.GetWorkflowsRequest - 50, // 50: vtctlservice.Vtctld.InitShardPrimary:input_type -> vtctldata.InitShardPrimaryRequest - 51, // 51: vtctlservice.Vtctld.LaunchSchemaMigration:input_type -> vtctldata.LaunchSchemaMigrationRequest - 52, // 52: vtctlservice.Vtctld.LookupVindexCreate:input_type -> vtctldata.LookupVindexCreateRequest - 53, // 53: vtctlservice.Vtctld.LookupVindexExternalize:input_type -> vtctldata.LookupVindexExternalizeRequest - 54, // 54: vtctlservice.Vtctld.MaterializeCreate:input_type -> vtctldata.MaterializeCreateRequest - 55, // 55: vtctlservice.Vtctld.MigrateCreate:input_type -> vtctldata.MigrateCreateRequest - 56, // 56: vtctlservice.Vtctld.MountRegister:input_type -> vtctldata.MountRegisterRequest - 57, // 57: vtctlservice.Vtctld.MountUnregister:input_type -> vtctldata.MountUnregisterRequest - 58, // 58: vtctlservice.Vtctld.MountShow:input_type -> vtctldata.MountShowRequest - 59, // 59: vtctlservice.Vtctld.MountList:input_type -> vtctldata.MountListRequest - 60, // 60: vtctlservice.Vtctld.MoveTablesCreate:input_type -> vtctldata.MoveTablesCreateRequest - 61, // 61: vtctlservice.Vtctld.MoveTablesComplete:input_type -> vtctldata.MoveTablesCompleteRequest - 62, // 62: vtctlservice.Vtctld.PingTablet:input_type -> vtctldata.PingTabletRequest - 63, // 63: vtctlservice.Vtctld.PlannedReparentShard:input_type -> vtctldata.PlannedReparentShardRequest - 64, // 64: vtctlservice.Vtctld.RebuildKeyspaceGraph:input_type -> vtctldata.RebuildKeyspaceGraphRequest - 65, // 65: vtctlservice.Vtctld.RebuildVSchemaGraph:input_type -> vtctldata.RebuildVSchemaGraphRequest - 66, // 66: vtctlservice.Vtctld.RefreshState:input_type -> vtctldata.RefreshStateRequest - 67, // 67: vtctlservice.Vtctld.RefreshStateByShard:input_type -> vtctldata.RefreshStateByShardRequest - 68, // 68: vtctlservice.Vtctld.ReloadSchema:input_type -> vtctldata.ReloadSchemaRequest - 69, // 69: vtctlservice.Vtctld.ReloadSchemaKeyspace:input_type -> vtctldata.ReloadSchemaKeyspaceRequest - 70, // 70: vtctlservice.Vtctld.ReloadSchemaShard:input_type -> vtctldata.ReloadSchemaShardRequest - 71, // 71: vtctlservice.Vtctld.RemoveBackup:input_type -> vtctldata.RemoveBackupRequest - 72, // 72: vtctlservice.Vtctld.RemoveKeyspaceCell:input_type -> vtctldata.RemoveKeyspaceCellRequest - 73, // 73: vtctlservice.Vtctld.RemoveShardCell:input_type -> vtctldata.RemoveShardCellRequest - 74, // 74: vtctlservice.Vtctld.ReparentTablet:input_type -> vtctldata.ReparentTabletRequest - 75, // 75: vtctlservice.Vtctld.ReshardCreate:input_type -> vtctldata.ReshardCreateRequest - 76, // 76: vtctlservice.Vtctld.RestoreFromBackup:input_type -> vtctldata.RestoreFromBackupRequest - 77, // 77: vtctlservice.Vtctld.RetrySchemaMigration:input_type -> vtctldata.RetrySchemaMigrationRequest - 78, // 78: vtctlservice.Vtctld.RunHealthCheck:input_type -> vtctldata.RunHealthCheckRequest - 79, // 79: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:input_type -> vtctldata.SetKeyspaceDurabilityPolicyRequest - 80, // 80: vtctlservice.Vtctld.SetShardIsPrimaryServing:input_type -> vtctldata.SetShardIsPrimaryServingRequest - 81, // 81: vtctlservice.Vtctld.SetShardTabletControl:input_type -> vtctldata.SetShardTabletControlRequest - 82, // 82: vtctlservice.Vtctld.SetWritable:input_type -> vtctldata.SetWritableRequest - 83, // 83: vtctlservice.Vtctld.ShardReplicationAdd:input_type -> vtctldata.ShardReplicationAddRequest - 84, // 84: vtctlservice.Vtctld.ShardReplicationFix:input_type -> vtctldata.ShardReplicationFixRequest - 85, // 85: vtctlservice.Vtctld.ShardReplicationPositions:input_type -> vtctldata.ShardReplicationPositionsRequest - 86, // 86: vtctlservice.Vtctld.ShardReplicationRemove:input_type -> vtctldata.ShardReplicationRemoveRequest - 87, // 87: vtctlservice.Vtctld.SleepTablet:input_type -> vtctldata.SleepTabletRequest - 88, // 88: vtctlservice.Vtctld.SourceShardAdd:input_type -> vtctldata.SourceShardAddRequest - 89, // 89: vtctlservice.Vtctld.SourceShardDelete:input_type -> vtctldata.SourceShardDeleteRequest - 90, // 90: vtctlservice.Vtctld.StartReplication:input_type -> vtctldata.StartReplicationRequest - 91, // 91: vtctlservice.Vtctld.StopReplication:input_type -> vtctldata.StopReplicationRequest - 92, // 92: vtctlservice.Vtctld.TabletExternallyReparented:input_type -> vtctldata.TabletExternallyReparentedRequest - 93, // 93: vtctlservice.Vtctld.UpdateCellInfo:input_type -> vtctldata.UpdateCellInfoRequest - 94, // 94: vtctlservice.Vtctld.UpdateCellsAlias:input_type -> vtctldata.UpdateCellsAliasRequest - 95, // 95: vtctlservice.Vtctld.Validate:input_type -> vtctldata.ValidateRequest - 96, // 96: vtctlservice.Vtctld.ValidateKeyspace:input_type -> vtctldata.ValidateKeyspaceRequest - 97, // 97: vtctlservice.Vtctld.ValidateSchemaKeyspace:input_type -> vtctldata.ValidateSchemaKeyspaceRequest - 98, // 98: vtctlservice.Vtctld.ValidateShard:input_type -> vtctldata.ValidateShardRequest - 99, // 99: vtctlservice.Vtctld.ValidateVersionKeyspace:input_type -> vtctldata.ValidateVersionKeyspaceRequest - 100, // 100: vtctlservice.Vtctld.ValidateVersionShard:input_type -> vtctldata.ValidateVersionShardRequest - 101, // 101: vtctlservice.Vtctld.ValidateVSchema:input_type -> vtctldata.ValidateVSchemaRequest - 102, // 102: vtctlservice.Vtctld.VDiffCreate:input_type -> vtctldata.VDiffCreateRequest - 103, // 103: vtctlservice.Vtctld.VDiffDelete:input_type -> vtctldata.VDiffDeleteRequest - 104, // 104: vtctlservice.Vtctld.VDiffResume:input_type -> vtctldata.VDiffResumeRequest - 105, // 105: vtctlservice.Vtctld.VDiffShow:input_type -> vtctldata.VDiffShowRequest - 106, // 106: vtctlservice.Vtctld.VDiffStop:input_type -> vtctldata.VDiffStopRequest - 107, // 107: vtctlservice.Vtctld.WorkflowDelete:input_type -> vtctldata.WorkflowDeleteRequest - 108, // 108: vtctlservice.Vtctld.WorkflowStatus:input_type -> vtctldata.WorkflowStatusRequest - 109, // 109: vtctlservice.Vtctld.WorkflowSwitchTraffic:input_type -> vtctldata.WorkflowSwitchTrafficRequest - 110, // 110: vtctlservice.Vtctld.WorkflowUpdate:input_type -> vtctldata.WorkflowUpdateRequest - 111, // 111: vtctlservice.Vtctl.ExecuteVtctlCommand:output_type -> vtctldata.ExecuteVtctlCommandResponse - 112, // 112: vtctlservice.Vtctld.AddCellInfo:output_type -> vtctldata.AddCellInfoResponse - 113, // 113: vtctlservice.Vtctld.AddCellsAlias:output_type -> vtctldata.AddCellsAliasResponse - 114, // 114: vtctlservice.Vtctld.ApplyRoutingRules:output_type -> vtctldata.ApplyRoutingRulesResponse - 115, // 115: vtctlservice.Vtctld.ApplySchema:output_type -> vtctldata.ApplySchemaResponse - 116, // 116: vtctlservice.Vtctld.ApplyShardRoutingRules:output_type -> vtctldata.ApplyShardRoutingRulesResponse - 117, // 117: vtctlservice.Vtctld.ApplyVSchema:output_type -> vtctldata.ApplyVSchemaResponse - 118, // 118: vtctlservice.Vtctld.Backup:output_type -> vtctldata.BackupResponse - 118, // 119: vtctlservice.Vtctld.BackupShard:output_type -> vtctldata.BackupResponse - 119, // 120: vtctlservice.Vtctld.CancelSchemaMigration:output_type -> vtctldata.CancelSchemaMigrationResponse - 120, // 121: vtctlservice.Vtctld.ChangeTabletType:output_type -> vtctldata.ChangeTabletTypeResponse - 121, // 122: vtctlservice.Vtctld.CleanupSchemaMigration:output_type -> vtctldata.CleanupSchemaMigrationResponse - 122, // 123: vtctlservice.Vtctld.CompleteSchemaMigration:output_type -> vtctldata.CompleteSchemaMigrationResponse - 123, // 124: vtctlservice.Vtctld.CreateKeyspace:output_type -> vtctldata.CreateKeyspaceResponse - 124, // 125: vtctlservice.Vtctld.CreateShard:output_type -> vtctldata.CreateShardResponse - 125, // 126: vtctlservice.Vtctld.DeleteCellInfo:output_type -> vtctldata.DeleteCellInfoResponse - 126, // 127: vtctlservice.Vtctld.DeleteCellsAlias:output_type -> vtctldata.DeleteCellsAliasResponse - 127, // 128: vtctlservice.Vtctld.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse - 128, // 129: vtctlservice.Vtctld.DeleteShards:output_type -> vtctldata.DeleteShardsResponse - 129, // 130: vtctlservice.Vtctld.DeleteSrvVSchema:output_type -> vtctldata.DeleteSrvVSchemaResponse - 130, // 131: vtctlservice.Vtctld.DeleteTablets:output_type -> vtctldata.DeleteTabletsResponse - 131, // 132: vtctlservice.Vtctld.EmergencyReparentShard:output_type -> vtctldata.EmergencyReparentShardResponse - 132, // 133: vtctlservice.Vtctld.ExecuteFetchAsApp:output_type -> vtctldata.ExecuteFetchAsAppResponse - 133, // 134: vtctlservice.Vtctld.ExecuteFetchAsDBA:output_type -> vtctldata.ExecuteFetchAsDBAResponse - 134, // 135: vtctlservice.Vtctld.ExecuteHook:output_type -> vtctldata.ExecuteHookResponse - 135, // 136: vtctlservice.Vtctld.FindAllShardsInKeyspace:output_type -> vtctldata.FindAllShardsInKeyspaceResponse - 136, // 137: vtctlservice.Vtctld.GetBackups:output_type -> vtctldata.GetBackupsResponse - 137, // 138: vtctlservice.Vtctld.GetCellInfo:output_type -> vtctldata.GetCellInfoResponse - 138, // 139: vtctlservice.Vtctld.GetCellInfoNames:output_type -> vtctldata.GetCellInfoNamesResponse - 139, // 140: vtctlservice.Vtctld.GetCellsAliases:output_type -> vtctldata.GetCellsAliasesResponse - 140, // 141: vtctlservice.Vtctld.GetFullStatus:output_type -> vtctldata.GetFullStatusResponse - 141, // 142: vtctlservice.Vtctld.GetKeyspace:output_type -> vtctldata.GetKeyspaceResponse - 142, // 143: vtctlservice.Vtctld.GetKeyspaces:output_type -> vtctldata.GetKeyspacesResponse - 143, // 144: vtctlservice.Vtctld.GetPermissions:output_type -> vtctldata.GetPermissionsResponse - 144, // 145: vtctlservice.Vtctld.GetRoutingRules:output_type -> vtctldata.GetRoutingRulesResponse - 145, // 146: vtctlservice.Vtctld.GetSchema:output_type -> vtctldata.GetSchemaResponse - 146, // 147: vtctlservice.Vtctld.GetSchemaMigrations:output_type -> vtctldata.GetSchemaMigrationsResponse - 147, // 148: vtctlservice.Vtctld.GetShard:output_type -> vtctldata.GetShardResponse - 148, // 149: vtctlservice.Vtctld.GetShardRoutingRules:output_type -> vtctldata.GetShardRoutingRulesResponse - 149, // 150: vtctlservice.Vtctld.GetSrvKeyspaceNames:output_type -> vtctldata.GetSrvKeyspaceNamesResponse - 150, // 151: vtctlservice.Vtctld.GetSrvKeyspaces:output_type -> vtctldata.GetSrvKeyspacesResponse - 151, // 152: vtctlservice.Vtctld.UpdateThrottlerConfig:output_type -> vtctldata.UpdateThrottlerConfigResponse - 152, // 153: vtctlservice.Vtctld.GetSrvVSchema:output_type -> vtctldata.GetSrvVSchemaResponse - 153, // 154: vtctlservice.Vtctld.GetSrvVSchemas:output_type -> vtctldata.GetSrvVSchemasResponse - 154, // 155: vtctlservice.Vtctld.GetTablet:output_type -> vtctldata.GetTabletResponse - 155, // 156: vtctlservice.Vtctld.GetTablets:output_type -> vtctldata.GetTabletsResponse - 156, // 157: vtctlservice.Vtctld.GetTopologyPath:output_type -> vtctldata.GetTopologyPathResponse - 157, // 158: vtctlservice.Vtctld.GetVersion:output_type -> vtctldata.GetVersionResponse - 158, // 159: vtctlservice.Vtctld.GetVSchema:output_type -> vtctldata.GetVSchemaResponse - 159, // 160: vtctlservice.Vtctld.GetWorkflows:output_type -> vtctldata.GetWorkflowsResponse - 160, // 161: vtctlservice.Vtctld.InitShardPrimary:output_type -> vtctldata.InitShardPrimaryResponse - 161, // 162: vtctlservice.Vtctld.LaunchSchemaMigration:output_type -> vtctldata.LaunchSchemaMigrationResponse - 162, // 163: vtctlservice.Vtctld.LookupVindexCreate:output_type -> vtctldata.LookupVindexCreateResponse - 163, // 164: vtctlservice.Vtctld.LookupVindexExternalize:output_type -> vtctldata.LookupVindexExternalizeResponse - 164, // 165: vtctlservice.Vtctld.MaterializeCreate:output_type -> vtctldata.MaterializeCreateResponse - 165, // 166: vtctlservice.Vtctld.MigrateCreate:output_type -> vtctldata.WorkflowStatusResponse - 166, // 167: vtctlservice.Vtctld.MountRegister:output_type -> vtctldata.MountRegisterResponse - 167, // 168: vtctlservice.Vtctld.MountUnregister:output_type -> vtctldata.MountUnregisterResponse - 168, // 169: vtctlservice.Vtctld.MountShow:output_type -> vtctldata.MountShowResponse - 169, // 170: vtctlservice.Vtctld.MountList:output_type -> vtctldata.MountListResponse - 165, // 171: vtctlservice.Vtctld.MoveTablesCreate:output_type -> vtctldata.WorkflowStatusResponse - 170, // 172: vtctlservice.Vtctld.MoveTablesComplete:output_type -> vtctldata.MoveTablesCompleteResponse - 171, // 173: vtctlservice.Vtctld.PingTablet:output_type -> vtctldata.PingTabletResponse - 172, // 174: vtctlservice.Vtctld.PlannedReparentShard:output_type -> vtctldata.PlannedReparentShardResponse - 173, // 175: vtctlservice.Vtctld.RebuildKeyspaceGraph:output_type -> vtctldata.RebuildKeyspaceGraphResponse - 174, // 176: vtctlservice.Vtctld.RebuildVSchemaGraph:output_type -> vtctldata.RebuildVSchemaGraphResponse - 175, // 177: vtctlservice.Vtctld.RefreshState:output_type -> vtctldata.RefreshStateResponse - 176, // 178: vtctlservice.Vtctld.RefreshStateByShard:output_type -> vtctldata.RefreshStateByShardResponse - 177, // 179: vtctlservice.Vtctld.ReloadSchema:output_type -> vtctldata.ReloadSchemaResponse - 178, // 180: vtctlservice.Vtctld.ReloadSchemaKeyspace:output_type -> vtctldata.ReloadSchemaKeyspaceResponse - 179, // 181: vtctlservice.Vtctld.ReloadSchemaShard:output_type -> vtctldata.ReloadSchemaShardResponse - 180, // 182: vtctlservice.Vtctld.RemoveBackup:output_type -> vtctldata.RemoveBackupResponse - 181, // 183: vtctlservice.Vtctld.RemoveKeyspaceCell:output_type -> vtctldata.RemoveKeyspaceCellResponse - 182, // 184: vtctlservice.Vtctld.RemoveShardCell:output_type -> vtctldata.RemoveShardCellResponse - 183, // 185: vtctlservice.Vtctld.ReparentTablet:output_type -> vtctldata.ReparentTabletResponse - 165, // 186: vtctlservice.Vtctld.ReshardCreate:output_type -> vtctldata.WorkflowStatusResponse - 184, // 187: vtctlservice.Vtctld.RestoreFromBackup:output_type -> vtctldata.RestoreFromBackupResponse - 185, // 188: vtctlservice.Vtctld.RetrySchemaMigration:output_type -> vtctldata.RetrySchemaMigrationResponse - 186, // 189: vtctlservice.Vtctld.RunHealthCheck:output_type -> vtctldata.RunHealthCheckResponse - 187, // 190: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:output_type -> vtctldata.SetKeyspaceDurabilityPolicyResponse - 188, // 191: vtctlservice.Vtctld.SetShardIsPrimaryServing:output_type -> vtctldata.SetShardIsPrimaryServingResponse - 189, // 192: vtctlservice.Vtctld.SetShardTabletControl:output_type -> vtctldata.SetShardTabletControlResponse - 190, // 193: vtctlservice.Vtctld.SetWritable:output_type -> vtctldata.SetWritableResponse - 191, // 194: vtctlservice.Vtctld.ShardReplicationAdd:output_type -> vtctldata.ShardReplicationAddResponse - 192, // 195: vtctlservice.Vtctld.ShardReplicationFix:output_type -> vtctldata.ShardReplicationFixResponse - 193, // 196: vtctlservice.Vtctld.ShardReplicationPositions:output_type -> vtctldata.ShardReplicationPositionsResponse - 194, // 197: vtctlservice.Vtctld.ShardReplicationRemove:output_type -> vtctldata.ShardReplicationRemoveResponse - 195, // 198: vtctlservice.Vtctld.SleepTablet:output_type -> vtctldata.SleepTabletResponse - 196, // 199: vtctlservice.Vtctld.SourceShardAdd:output_type -> vtctldata.SourceShardAddResponse - 197, // 200: vtctlservice.Vtctld.SourceShardDelete:output_type -> vtctldata.SourceShardDeleteResponse - 198, // 201: vtctlservice.Vtctld.StartReplication:output_type -> vtctldata.StartReplicationResponse - 199, // 202: vtctlservice.Vtctld.StopReplication:output_type -> vtctldata.StopReplicationResponse - 200, // 203: vtctlservice.Vtctld.TabletExternallyReparented:output_type -> vtctldata.TabletExternallyReparentedResponse - 201, // 204: vtctlservice.Vtctld.UpdateCellInfo:output_type -> vtctldata.UpdateCellInfoResponse - 202, // 205: vtctlservice.Vtctld.UpdateCellsAlias:output_type -> vtctldata.UpdateCellsAliasResponse - 203, // 206: vtctlservice.Vtctld.Validate:output_type -> vtctldata.ValidateResponse - 204, // 207: vtctlservice.Vtctld.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse - 205, // 208: vtctlservice.Vtctld.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse - 206, // 209: vtctlservice.Vtctld.ValidateShard:output_type -> vtctldata.ValidateShardResponse - 207, // 210: vtctlservice.Vtctld.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse - 208, // 211: vtctlservice.Vtctld.ValidateVersionShard:output_type -> vtctldata.ValidateVersionShardResponse - 209, // 212: vtctlservice.Vtctld.ValidateVSchema:output_type -> vtctldata.ValidateVSchemaResponse - 210, // 213: vtctlservice.Vtctld.VDiffCreate:output_type -> vtctldata.VDiffCreateResponse - 211, // 214: vtctlservice.Vtctld.VDiffDelete:output_type -> vtctldata.VDiffDeleteResponse - 212, // 215: vtctlservice.Vtctld.VDiffResume:output_type -> vtctldata.VDiffResumeResponse - 213, // 216: vtctlservice.Vtctld.VDiffShow:output_type -> vtctldata.VDiffShowResponse - 214, // 217: vtctlservice.Vtctld.VDiffStop:output_type -> vtctldata.VDiffStopResponse - 215, // 218: vtctlservice.Vtctld.WorkflowDelete:output_type -> vtctldata.WorkflowDeleteResponse - 165, // 219: vtctlservice.Vtctld.WorkflowStatus:output_type -> vtctldata.WorkflowStatusResponse - 216, // 220: vtctlservice.Vtctld.WorkflowSwitchTraffic:output_type -> vtctldata.WorkflowSwitchTrafficResponse - 217, // 221: vtctlservice.Vtctld.WorkflowUpdate:output_type -> vtctldata.WorkflowUpdateResponse - 111, // [111:222] is the sub-list for method output_type - 0, // [0:111] is the sub-list for method input_type + 26, // 26: vtctlservice.Vtctld.ForceCutOverSchemaMigration:input_type -> vtctldata.ForceCutOverSchemaMigrationRequest + 27, // 27: vtctlservice.Vtctld.GetBackups:input_type -> vtctldata.GetBackupsRequest + 28, // 28: vtctlservice.Vtctld.GetCellInfo:input_type -> vtctldata.GetCellInfoRequest + 29, // 29: vtctlservice.Vtctld.GetCellInfoNames:input_type -> vtctldata.GetCellInfoNamesRequest + 30, // 30: vtctlservice.Vtctld.GetCellsAliases:input_type -> vtctldata.GetCellsAliasesRequest + 31, // 31: vtctlservice.Vtctld.GetFullStatus:input_type -> vtctldata.GetFullStatusRequest + 32, // 32: vtctlservice.Vtctld.GetKeyspace:input_type -> vtctldata.GetKeyspaceRequest + 33, // 33: vtctlservice.Vtctld.GetKeyspaces:input_type -> vtctldata.GetKeyspacesRequest + 34, // 34: vtctlservice.Vtctld.GetPermissions:input_type -> vtctldata.GetPermissionsRequest + 35, // 35: vtctlservice.Vtctld.GetRoutingRules:input_type -> vtctldata.GetRoutingRulesRequest + 36, // 36: vtctlservice.Vtctld.GetSchema:input_type -> vtctldata.GetSchemaRequest + 37, // 37: vtctlservice.Vtctld.GetSchemaMigrations:input_type -> vtctldata.GetSchemaMigrationsRequest + 38, // 38: vtctlservice.Vtctld.GetShard:input_type -> vtctldata.GetShardRequest + 39, // 39: vtctlservice.Vtctld.GetShardRoutingRules:input_type -> vtctldata.GetShardRoutingRulesRequest + 40, // 40: vtctlservice.Vtctld.GetSrvKeyspaceNames:input_type -> vtctldata.GetSrvKeyspaceNamesRequest + 41, // 41: vtctlservice.Vtctld.GetSrvKeyspaces:input_type -> vtctldata.GetSrvKeyspacesRequest + 42, // 42: vtctlservice.Vtctld.UpdateThrottlerConfig:input_type -> vtctldata.UpdateThrottlerConfigRequest + 43, // 43: vtctlservice.Vtctld.GetSrvVSchema:input_type -> vtctldata.GetSrvVSchemaRequest + 44, // 44: vtctlservice.Vtctld.GetSrvVSchemas:input_type -> vtctldata.GetSrvVSchemasRequest + 45, // 45: vtctlservice.Vtctld.GetTablet:input_type -> vtctldata.GetTabletRequest + 46, // 46: vtctlservice.Vtctld.GetTablets:input_type -> vtctldata.GetTabletsRequest + 47, // 47: vtctlservice.Vtctld.GetTopologyPath:input_type -> vtctldata.GetTopologyPathRequest + 48, // 48: vtctlservice.Vtctld.GetVersion:input_type -> vtctldata.GetVersionRequest + 49, // 49: vtctlservice.Vtctld.GetVSchema:input_type -> vtctldata.GetVSchemaRequest + 50, // 50: vtctlservice.Vtctld.GetWorkflows:input_type -> vtctldata.GetWorkflowsRequest + 51, // 51: vtctlservice.Vtctld.InitShardPrimary:input_type -> vtctldata.InitShardPrimaryRequest + 52, // 52: vtctlservice.Vtctld.LaunchSchemaMigration:input_type -> vtctldata.LaunchSchemaMigrationRequest + 53, // 53: vtctlservice.Vtctld.LookupVindexCreate:input_type -> vtctldata.LookupVindexCreateRequest + 54, // 54: vtctlservice.Vtctld.LookupVindexExternalize:input_type -> vtctldata.LookupVindexExternalizeRequest + 55, // 55: vtctlservice.Vtctld.MaterializeCreate:input_type -> vtctldata.MaterializeCreateRequest + 56, // 56: vtctlservice.Vtctld.MigrateCreate:input_type -> vtctldata.MigrateCreateRequest + 57, // 57: vtctlservice.Vtctld.MountRegister:input_type -> vtctldata.MountRegisterRequest + 58, // 58: vtctlservice.Vtctld.MountUnregister:input_type -> vtctldata.MountUnregisterRequest + 59, // 59: vtctlservice.Vtctld.MountShow:input_type -> vtctldata.MountShowRequest + 60, // 60: vtctlservice.Vtctld.MountList:input_type -> vtctldata.MountListRequest + 61, // 61: vtctlservice.Vtctld.MoveTablesCreate:input_type -> vtctldata.MoveTablesCreateRequest + 62, // 62: vtctlservice.Vtctld.MoveTablesComplete:input_type -> vtctldata.MoveTablesCompleteRequest + 63, // 63: vtctlservice.Vtctld.PingTablet:input_type -> vtctldata.PingTabletRequest + 64, // 64: vtctlservice.Vtctld.PlannedReparentShard:input_type -> vtctldata.PlannedReparentShardRequest + 65, // 65: vtctlservice.Vtctld.RebuildKeyspaceGraph:input_type -> vtctldata.RebuildKeyspaceGraphRequest + 66, // 66: vtctlservice.Vtctld.RebuildVSchemaGraph:input_type -> vtctldata.RebuildVSchemaGraphRequest + 67, // 67: vtctlservice.Vtctld.RefreshState:input_type -> vtctldata.RefreshStateRequest + 68, // 68: vtctlservice.Vtctld.RefreshStateByShard:input_type -> vtctldata.RefreshStateByShardRequest + 69, // 69: vtctlservice.Vtctld.ReloadSchema:input_type -> vtctldata.ReloadSchemaRequest + 70, // 70: vtctlservice.Vtctld.ReloadSchemaKeyspace:input_type -> vtctldata.ReloadSchemaKeyspaceRequest + 71, // 71: vtctlservice.Vtctld.ReloadSchemaShard:input_type -> vtctldata.ReloadSchemaShardRequest + 72, // 72: vtctlservice.Vtctld.RemoveBackup:input_type -> vtctldata.RemoveBackupRequest + 73, // 73: vtctlservice.Vtctld.RemoveKeyspaceCell:input_type -> vtctldata.RemoveKeyspaceCellRequest + 74, // 74: vtctlservice.Vtctld.RemoveShardCell:input_type -> vtctldata.RemoveShardCellRequest + 75, // 75: vtctlservice.Vtctld.ReparentTablet:input_type -> vtctldata.ReparentTabletRequest + 76, // 76: vtctlservice.Vtctld.ReshardCreate:input_type -> vtctldata.ReshardCreateRequest + 77, // 77: vtctlservice.Vtctld.RestoreFromBackup:input_type -> vtctldata.RestoreFromBackupRequest + 78, // 78: vtctlservice.Vtctld.RetrySchemaMigration:input_type -> vtctldata.RetrySchemaMigrationRequest + 79, // 79: vtctlservice.Vtctld.RunHealthCheck:input_type -> vtctldata.RunHealthCheckRequest + 80, // 80: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:input_type -> vtctldata.SetKeyspaceDurabilityPolicyRequest + 81, // 81: vtctlservice.Vtctld.SetShardIsPrimaryServing:input_type -> vtctldata.SetShardIsPrimaryServingRequest + 82, // 82: vtctlservice.Vtctld.SetShardTabletControl:input_type -> vtctldata.SetShardTabletControlRequest + 83, // 83: vtctlservice.Vtctld.SetWritable:input_type -> vtctldata.SetWritableRequest + 84, // 84: vtctlservice.Vtctld.ShardReplicationAdd:input_type -> vtctldata.ShardReplicationAddRequest + 85, // 85: vtctlservice.Vtctld.ShardReplicationFix:input_type -> vtctldata.ShardReplicationFixRequest + 86, // 86: vtctlservice.Vtctld.ShardReplicationPositions:input_type -> vtctldata.ShardReplicationPositionsRequest + 87, // 87: vtctlservice.Vtctld.ShardReplicationRemove:input_type -> vtctldata.ShardReplicationRemoveRequest + 88, // 88: vtctlservice.Vtctld.SleepTablet:input_type -> vtctldata.SleepTabletRequest + 89, // 89: vtctlservice.Vtctld.SourceShardAdd:input_type -> vtctldata.SourceShardAddRequest + 90, // 90: vtctlservice.Vtctld.SourceShardDelete:input_type -> vtctldata.SourceShardDeleteRequest + 91, // 91: vtctlservice.Vtctld.StartReplication:input_type -> vtctldata.StartReplicationRequest + 92, // 92: vtctlservice.Vtctld.StopReplication:input_type -> vtctldata.StopReplicationRequest + 93, // 93: vtctlservice.Vtctld.TabletExternallyReparented:input_type -> vtctldata.TabletExternallyReparentedRequest + 94, // 94: vtctlservice.Vtctld.UpdateCellInfo:input_type -> vtctldata.UpdateCellInfoRequest + 95, // 95: vtctlservice.Vtctld.UpdateCellsAlias:input_type -> vtctldata.UpdateCellsAliasRequest + 96, // 96: vtctlservice.Vtctld.Validate:input_type -> vtctldata.ValidateRequest + 97, // 97: vtctlservice.Vtctld.ValidateKeyspace:input_type -> vtctldata.ValidateKeyspaceRequest + 98, // 98: vtctlservice.Vtctld.ValidateSchemaKeyspace:input_type -> vtctldata.ValidateSchemaKeyspaceRequest + 99, // 99: vtctlservice.Vtctld.ValidateShard:input_type -> vtctldata.ValidateShardRequest + 100, // 100: vtctlservice.Vtctld.ValidateVersionKeyspace:input_type -> vtctldata.ValidateVersionKeyspaceRequest + 101, // 101: vtctlservice.Vtctld.ValidateVersionShard:input_type -> vtctldata.ValidateVersionShardRequest + 102, // 102: vtctlservice.Vtctld.ValidateVSchema:input_type -> vtctldata.ValidateVSchemaRequest + 103, // 103: vtctlservice.Vtctld.VDiffCreate:input_type -> vtctldata.VDiffCreateRequest + 104, // 104: vtctlservice.Vtctld.VDiffDelete:input_type -> vtctldata.VDiffDeleteRequest + 105, // 105: vtctlservice.Vtctld.VDiffResume:input_type -> vtctldata.VDiffResumeRequest + 106, // 106: vtctlservice.Vtctld.VDiffShow:input_type -> vtctldata.VDiffShowRequest + 107, // 107: vtctlservice.Vtctld.VDiffStop:input_type -> vtctldata.VDiffStopRequest + 108, // 108: vtctlservice.Vtctld.WorkflowDelete:input_type -> vtctldata.WorkflowDeleteRequest + 109, // 109: vtctlservice.Vtctld.WorkflowStatus:input_type -> vtctldata.WorkflowStatusRequest + 110, // 110: vtctlservice.Vtctld.WorkflowSwitchTraffic:input_type -> vtctldata.WorkflowSwitchTrafficRequest + 111, // 111: vtctlservice.Vtctld.WorkflowUpdate:input_type -> vtctldata.WorkflowUpdateRequest + 112, // 112: vtctlservice.Vtctl.ExecuteVtctlCommand:output_type -> vtctldata.ExecuteVtctlCommandResponse + 113, // 113: vtctlservice.Vtctld.AddCellInfo:output_type -> vtctldata.AddCellInfoResponse + 114, // 114: vtctlservice.Vtctld.AddCellsAlias:output_type -> vtctldata.AddCellsAliasResponse + 115, // 115: vtctlservice.Vtctld.ApplyRoutingRules:output_type -> vtctldata.ApplyRoutingRulesResponse + 116, // 116: vtctlservice.Vtctld.ApplySchema:output_type -> vtctldata.ApplySchemaResponse + 117, // 117: vtctlservice.Vtctld.ApplyShardRoutingRules:output_type -> vtctldata.ApplyShardRoutingRulesResponse + 118, // 118: vtctlservice.Vtctld.ApplyVSchema:output_type -> vtctldata.ApplyVSchemaResponse + 119, // 119: vtctlservice.Vtctld.Backup:output_type -> vtctldata.BackupResponse + 119, // 120: vtctlservice.Vtctld.BackupShard:output_type -> vtctldata.BackupResponse + 120, // 121: vtctlservice.Vtctld.CancelSchemaMigration:output_type -> vtctldata.CancelSchemaMigrationResponse + 121, // 122: vtctlservice.Vtctld.ChangeTabletType:output_type -> vtctldata.ChangeTabletTypeResponse + 122, // 123: vtctlservice.Vtctld.CleanupSchemaMigration:output_type -> vtctldata.CleanupSchemaMigrationResponse + 123, // 124: vtctlservice.Vtctld.CompleteSchemaMigration:output_type -> vtctldata.CompleteSchemaMigrationResponse + 124, // 125: vtctlservice.Vtctld.CreateKeyspace:output_type -> vtctldata.CreateKeyspaceResponse + 125, // 126: vtctlservice.Vtctld.CreateShard:output_type -> vtctldata.CreateShardResponse + 126, // 127: vtctlservice.Vtctld.DeleteCellInfo:output_type -> vtctldata.DeleteCellInfoResponse + 127, // 128: vtctlservice.Vtctld.DeleteCellsAlias:output_type -> vtctldata.DeleteCellsAliasResponse + 128, // 129: vtctlservice.Vtctld.DeleteKeyspace:output_type -> vtctldata.DeleteKeyspaceResponse + 129, // 130: vtctlservice.Vtctld.DeleteShards:output_type -> vtctldata.DeleteShardsResponse + 130, // 131: vtctlservice.Vtctld.DeleteSrvVSchema:output_type -> vtctldata.DeleteSrvVSchemaResponse + 131, // 132: vtctlservice.Vtctld.DeleteTablets:output_type -> vtctldata.DeleteTabletsResponse + 132, // 133: vtctlservice.Vtctld.EmergencyReparentShard:output_type -> vtctldata.EmergencyReparentShardResponse + 133, // 134: vtctlservice.Vtctld.ExecuteFetchAsApp:output_type -> vtctldata.ExecuteFetchAsAppResponse + 134, // 135: vtctlservice.Vtctld.ExecuteFetchAsDBA:output_type -> vtctldata.ExecuteFetchAsDBAResponse + 135, // 136: vtctlservice.Vtctld.ExecuteHook:output_type -> vtctldata.ExecuteHookResponse + 136, // 137: vtctlservice.Vtctld.FindAllShardsInKeyspace:output_type -> vtctldata.FindAllShardsInKeyspaceResponse + 137, // 138: vtctlservice.Vtctld.ForceCutOverSchemaMigration:output_type -> vtctldata.ForceCutOverSchemaMigrationResponse + 138, // 139: vtctlservice.Vtctld.GetBackups:output_type -> vtctldata.GetBackupsResponse + 139, // 140: vtctlservice.Vtctld.GetCellInfo:output_type -> vtctldata.GetCellInfoResponse + 140, // 141: vtctlservice.Vtctld.GetCellInfoNames:output_type -> vtctldata.GetCellInfoNamesResponse + 141, // 142: vtctlservice.Vtctld.GetCellsAliases:output_type -> vtctldata.GetCellsAliasesResponse + 142, // 143: vtctlservice.Vtctld.GetFullStatus:output_type -> vtctldata.GetFullStatusResponse + 143, // 144: vtctlservice.Vtctld.GetKeyspace:output_type -> vtctldata.GetKeyspaceResponse + 144, // 145: vtctlservice.Vtctld.GetKeyspaces:output_type -> vtctldata.GetKeyspacesResponse + 145, // 146: vtctlservice.Vtctld.GetPermissions:output_type -> vtctldata.GetPermissionsResponse + 146, // 147: vtctlservice.Vtctld.GetRoutingRules:output_type -> vtctldata.GetRoutingRulesResponse + 147, // 148: vtctlservice.Vtctld.GetSchema:output_type -> vtctldata.GetSchemaResponse + 148, // 149: vtctlservice.Vtctld.GetSchemaMigrations:output_type -> vtctldata.GetSchemaMigrationsResponse + 149, // 150: vtctlservice.Vtctld.GetShard:output_type -> vtctldata.GetShardResponse + 150, // 151: vtctlservice.Vtctld.GetShardRoutingRules:output_type -> vtctldata.GetShardRoutingRulesResponse + 151, // 152: vtctlservice.Vtctld.GetSrvKeyspaceNames:output_type -> vtctldata.GetSrvKeyspaceNamesResponse + 152, // 153: vtctlservice.Vtctld.GetSrvKeyspaces:output_type -> vtctldata.GetSrvKeyspacesResponse + 153, // 154: vtctlservice.Vtctld.UpdateThrottlerConfig:output_type -> vtctldata.UpdateThrottlerConfigResponse + 154, // 155: vtctlservice.Vtctld.GetSrvVSchema:output_type -> vtctldata.GetSrvVSchemaResponse + 155, // 156: vtctlservice.Vtctld.GetSrvVSchemas:output_type -> vtctldata.GetSrvVSchemasResponse + 156, // 157: vtctlservice.Vtctld.GetTablet:output_type -> vtctldata.GetTabletResponse + 157, // 158: vtctlservice.Vtctld.GetTablets:output_type -> vtctldata.GetTabletsResponse + 158, // 159: vtctlservice.Vtctld.GetTopologyPath:output_type -> vtctldata.GetTopologyPathResponse + 159, // 160: vtctlservice.Vtctld.GetVersion:output_type -> vtctldata.GetVersionResponse + 160, // 161: vtctlservice.Vtctld.GetVSchema:output_type -> vtctldata.GetVSchemaResponse + 161, // 162: vtctlservice.Vtctld.GetWorkflows:output_type -> vtctldata.GetWorkflowsResponse + 162, // 163: vtctlservice.Vtctld.InitShardPrimary:output_type -> vtctldata.InitShardPrimaryResponse + 163, // 164: vtctlservice.Vtctld.LaunchSchemaMigration:output_type -> vtctldata.LaunchSchemaMigrationResponse + 164, // 165: vtctlservice.Vtctld.LookupVindexCreate:output_type -> vtctldata.LookupVindexCreateResponse + 165, // 166: vtctlservice.Vtctld.LookupVindexExternalize:output_type -> vtctldata.LookupVindexExternalizeResponse + 166, // 167: vtctlservice.Vtctld.MaterializeCreate:output_type -> vtctldata.MaterializeCreateResponse + 167, // 168: vtctlservice.Vtctld.MigrateCreate:output_type -> vtctldata.WorkflowStatusResponse + 168, // 169: vtctlservice.Vtctld.MountRegister:output_type -> vtctldata.MountRegisterResponse + 169, // 170: vtctlservice.Vtctld.MountUnregister:output_type -> vtctldata.MountUnregisterResponse + 170, // 171: vtctlservice.Vtctld.MountShow:output_type -> vtctldata.MountShowResponse + 171, // 172: vtctlservice.Vtctld.MountList:output_type -> vtctldata.MountListResponse + 167, // 173: vtctlservice.Vtctld.MoveTablesCreate:output_type -> vtctldata.WorkflowStatusResponse + 172, // 174: vtctlservice.Vtctld.MoveTablesComplete:output_type -> vtctldata.MoveTablesCompleteResponse + 173, // 175: vtctlservice.Vtctld.PingTablet:output_type -> vtctldata.PingTabletResponse + 174, // 176: vtctlservice.Vtctld.PlannedReparentShard:output_type -> vtctldata.PlannedReparentShardResponse + 175, // 177: vtctlservice.Vtctld.RebuildKeyspaceGraph:output_type -> vtctldata.RebuildKeyspaceGraphResponse + 176, // 178: vtctlservice.Vtctld.RebuildVSchemaGraph:output_type -> vtctldata.RebuildVSchemaGraphResponse + 177, // 179: vtctlservice.Vtctld.RefreshState:output_type -> vtctldata.RefreshStateResponse + 178, // 180: vtctlservice.Vtctld.RefreshStateByShard:output_type -> vtctldata.RefreshStateByShardResponse + 179, // 181: vtctlservice.Vtctld.ReloadSchema:output_type -> vtctldata.ReloadSchemaResponse + 180, // 182: vtctlservice.Vtctld.ReloadSchemaKeyspace:output_type -> vtctldata.ReloadSchemaKeyspaceResponse + 181, // 183: vtctlservice.Vtctld.ReloadSchemaShard:output_type -> vtctldata.ReloadSchemaShardResponse + 182, // 184: vtctlservice.Vtctld.RemoveBackup:output_type -> vtctldata.RemoveBackupResponse + 183, // 185: vtctlservice.Vtctld.RemoveKeyspaceCell:output_type -> vtctldata.RemoveKeyspaceCellResponse + 184, // 186: vtctlservice.Vtctld.RemoveShardCell:output_type -> vtctldata.RemoveShardCellResponse + 185, // 187: vtctlservice.Vtctld.ReparentTablet:output_type -> vtctldata.ReparentTabletResponse + 167, // 188: vtctlservice.Vtctld.ReshardCreate:output_type -> vtctldata.WorkflowStatusResponse + 186, // 189: vtctlservice.Vtctld.RestoreFromBackup:output_type -> vtctldata.RestoreFromBackupResponse + 187, // 190: vtctlservice.Vtctld.RetrySchemaMigration:output_type -> vtctldata.RetrySchemaMigrationResponse + 188, // 191: vtctlservice.Vtctld.RunHealthCheck:output_type -> vtctldata.RunHealthCheckResponse + 189, // 192: vtctlservice.Vtctld.SetKeyspaceDurabilityPolicy:output_type -> vtctldata.SetKeyspaceDurabilityPolicyResponse + 190, // 193: vtctlservice.Vtctld.SetShardIsPrimaryServing:output_type -> vtctldata.SetShardIsPrimaryServingResponse + 191, // 194: vtctlservice.Vtctld.SetShardTabletControl:output_type -> vtctldata.SetShardTabletControlResponse + 192, // 195: vtctlservice.Vtctld.SetWritable:output_type -> vtctldata.SetWritableResponse + 193, // 196: vtctlservice.Vtctld.ShardReplicationAdd:output_type -> vtctldata.ShardReplicationAddResponse + 194, // 197: vtctlservice.Vtctld.ShardReplicationFix:output_type -> vtctldata.ShardReplicationFixResponse + 195, // 198: vtctlservice.Vtctld.ShardReplicationPositions:output_type -> vtctldata.ShardReplicationPositionsResponse + 196, // 199: vtctlservice.Vtctld.ShardReplicationRemove:output_type -> vtctldata.ShardReplicationRemoveResponse + 197, // 200: vtctlservice.Vtctld.SleepTablet:output_type -> vtctldata.SleepTabletResponse + 198, // 201: vtctlservice.Vtctld.SourceShardAdd:output_type -> vtctldata.SourceShardAddResponse + 199, // 202: vtctlservice.Vtctld.SourceShardDelete:output_type -> vtctldata.SourceShardDeleteResponse + 200, // 203: vtctlservice.Vtctld.StartReplication:output_type -> vtctldata.StartReplicationResponse + 201, // 204: vtctlservice.Vtctld.StopReplication:output_type -> vtctldata.StopReplicationResponse + 202, // 205: vtctlservice.Vtctld.TabletExternallyReparented:output_type -> vtctldata.TabletExternallyReparentedResponse + 203, // 206: vtctlservice.Vtctld.UpdateCellInfo:output_type -> vtctldata.UpdateCellInfoResponse + 204, // 207: vtctlservice.Vtctld.UpdateCellsAlias:output_type -> vtctldata.UpdateCellsAliasResponse + 205, // 208: vtctlservice.Vtctld.Validate:output_type -> vtctldata.ValidateResponse + 206, // 209: vtctlservice.Vtctld.ValidateKeyspace:output_type -> vtctldata.ValidateKeyspaceResponse + 207, // 210: vtctlservice.Vtctld.ValidateSchemaKeyspace:output_type -> vtctldata.ValidateSchemaKeyspaceResponse + 208, // 211: vtctlservice.Vtctld.ValidateShard:output_type -> vtctldata.ValidateShardResponse + 209, // 212: vtctlservice.Vtctld.ValidateVersionKeyspace:output_type -> vtctldata.ValidateVersionKeyspaceResponse + 210, // 213: vtctlservice.Vtctld.ValidateVersionShard:output_type -> vtctldata.ValidateVersionShardResponse + 211, // 214: vtctlservice.Vtctld.ValidateVSchema:output_type -> vtctldata.ValidateVSchemaResponse + 212, // 215: vtctlservice.Vtctld.VDiffCreate:output_type -> vtctldata.VDiffCreateResponse + 213, // 216: vtctlservice.Vtctld.VDiffDelete:output_type -> vtctldata.VDiffDeleteResponse + 214, // 217: vtctlservice.Vtctld.VDiffResume:output_type -> vtctldata.VDiffResumeResponse + 215, // 218: vtctlservice.Vtctld.VDiffShow:output_type -> vtctldata.VDiffShowResponse + 216, // 219: vtctlservice.Vtctld.VDiffStop:output_type -> vtctldata.VDiffStopResponse + 217, // 220: vtctlservice.Vtctld.WorkflowDelete:output_type -> vtctldata.WorkflowDeleteResponse + 167, // 221: vtctlservice.Vtctld.WorkflowStatus:output_type -> vtctldata.WorkflowStatusResponse + 218, // 222: vtctlservice.Vtctld.WorkflowSwitchTraffic:output_type -> vtctldata.WorkflowSwitchTrafficResponse + 219, // 223: vtctlservice.Vtctld.WorkflowUpdate:output_type -> vtctldata.WorkflowUpdateResponse + 112, // [112:224] is the sub-list for method output_type + 0, // [0:112] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go b/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go index f0a73530047..e4774540d2a 100644 --- a/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice_grpc.pb.go @@ -159,7 +159,7 @@ type VtctldClient interface { Backup(ctx context.Context, in *vtctldata.BackupRequest, opts ...grpc.CallOption) (Vtctld_BackupClient, error) // BackupShard chooses a tablet in the shard and uses it to create a backup. BackupShard(ctx context.Context, in *vtctldata.BackupShardRequest, opts ...grpc.CallOption) (Vtctld_BackupShardClient, error) - // CancelSchemaMigration cancels one or all migrations, terminating any runnign ones as needed. + // CancelSchemaMigration cancels one or all migrations, terminating any running ones as needed. CancelSchemaMigration(ctx context.Context, in *vtctldata.CancelSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.CancelSchemaMigrationResponse, error) // ChangeTabletType changes the db type for the specified tablet, if possible. // This is used primarily to arrange replicas, and it will not convert a @@ -208,6 +208,8 @@ type VtctldClient interface { // FindAllShardsInKeyspace returns a map of shard names to shard references // for a given keyspace. FindAllShardsInKeyspace(ctx context.Context, in *vtctldata.FindAllShardsInKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.FindAllShardsInKeyspaceResponse, error) + // ForceCutOverSchemaMigration marks a schema migration for forced cut-over. + ForceCutOverSchemaMigration(ctx context.Context, in *vtctldata.ForceCutOverSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.ForceCutOverSchemaMigrationResponse, error) // GetBackups returns all the backups for a shard. GetBackups(ctx context.Context, in *vtctldata.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldata.GetBackupsResponse, error) // GetCellInfo returns the information for a cell. @@ -731,6 +733,15 @@ func (c *vtctldClient) FindAllShardsInKeyspace(ctx context.Context, in *vtctldat return out, nil } +func (c *vtctldClient) ForceCutOverSchemaMigration(ctx context.Context, in *vtctldata.ForceCutOverSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldata.ForceCutOverSchemaMigrationResponse, error) { + out := new(vtctldata.ForceCutOverSchemaMigrationResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/ForceCutOverSchemaMigration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vtctldClient) GetBackups(ctx context.Context, in *vtctldata.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldata.GetBackupsResponse, error) { out := new(vtctldata.GetBackupsResponse) err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetBackups", in, out, opts...) @@ -1546,7 +1557,7 @@ type VtctldServer interface { Backup(*vtctldata.BackupRequest, Vtctld_BackupServer) error // BackupShard chooses a tablet in the shard and uses it to create a backup. BackupShard(*vtctldata.BackupShardRequest, Vtctld_BackupShardServer) error - // CancelSchemaMigration cancels one or all migrations, terminating any runnign ones as needed. + // CancelSchemaMigration cancels one or all migrations, terminating any running ones as needed. CancelSchemaMigration(context.Context, *vtctldata.CancelSchemaMigrationRequest) (*vtctldata.CancelSchemaMigrationResponse, error) // ChangeTabletType changes the db type for the specified tablet, if possible. // This is used primarily to arrange replicas, and it will not convert a @@ -1595,6 +1606,8 @@ type VtctldServer interface { // FindAllShardsInKeyspace returns a map of shard names to shard references // for a given keyspace. FindAllShardsInKeyspace(context.Context, *vtctldata.FindAllShardsInKeyspaceRequest) (*vtctldata.FindAllShardsInKeyspaceResponse, error) + // ForceCutOverSchemaMigration marks a schema migration for forced cut-over. + ForceCutOverSchemaMigration(context.Context, *vtctldata.ForceCutOverSchemaMigrationRequest) (*vtctldata.ForceCutOverSchemaMigrationResponse, error) // GetBackups returns all the backups for a shard. GetBackups(context.Context, *vtctldata.GetBackupsRequest) (*vtctldata.GetBackupsResponse, error) // GetCellInfo returns the information for a cell. @@ -1919,6 +1932,9 @@ func (UnimplementedVtctldServer) ExecuteHook(context.Context, *vtctldata.Execute func (UnimplementedVtctldServer) FindAllShardsInKeyspace(context.Context, *vtctldata.FindAllShardsInKeyspaceRequest) (*vtctldata.FindAllShardsInKeyspaceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method FindAllShardsInKeyspace not implemented") } +func (UnimplementedVtctldServer) ForceCutOverSchemaMigration(context.Context, *vtctldata.ForceCutOverSchemaMigrationRequest) (*vtctldata.ForceCutOverSchemaMigrationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ForceCutOverSchemaMigration not implemented") +} func (UnimplementedVtctldServer) GetBackups(context.Context, *vtctldata.GetBackupsRequest) (*vtctldata.GetBackupsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBackups not implemented") } @@ -2643,6 +2659,24 @@ func _Vtctld_FindAllShardsInKeyspace_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _Vtctld_ForceCutOverSchemaMigration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.ForceCutOverSchemaMigrationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).ForceCutOverSchemaMigration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/ForceCutOverSchemaMigration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).ForceCutOverSchemaMigration(ctx, req.(*vtctldata.ForceCutOverSchemaMigrationRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Vtctld_GetBackups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(vtctldata.GetBackupsRequest) if err := dec(in); err != nil { @@ -4275,6 +4309,10 @@ var Vtctld_ServiceDesc = grpc.ServiceDesc{ MethodName: "FindAllShardsInKeyspace", Handler: _Vtctld_FindAllShardsInKeyspace_Handler, }, + { + MethodName: "ForceCutOverSchemaMigration", + Handler: _Vtctld_ForceCutOverSchemaMigration_Handler, + }, { MethodName: "GetBackups", Handler: _Vtctld_GetBackups_Handler, diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index aee90d134a4..298b71418d1 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtgate.proto diff --git a/go/vt/proto/vtgateservice/vtgateservice.pb.go b/go/vt/proto/vtgateservice/vtgateservice.pb.go index 2008d486dc9..876a4fc90a0 100644 --- a/go/vt/proto/vtgateservice/vtgateservice.pb.go +++ b/go/vt/proto/vtgateservice/vtgateservice.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtgateservice.proto diff --git a/go/vt/proto/vtrpc/vtrpc.pb.go b/go/vt/proto/vtrpc/vtrpc.pb.go index 0c82dc34bf5..2d2375d5794 100644 --- a/go/vt/proto/vtrpc/vtrpc.pb.go +++ b/go/vt/proto/vtrpc/vtrpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vtrpc.proto diff --git a/go/vt/proto/vttest/vttest.pb.go b/go/vt/proto/vttest/vttest.pb.go index 4b4f269d38c..5d1b0966ab3 100644 --- a/go/vt/proto/vttest/vttest.pb.go +++ b/go/vt/proto/vttest/vttest.pb.go @@ -41,7 +41,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vttest.proto @@ -134,8 +134,6 @@ type Keyspace struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // shards inside this keyspace. Ignored if redirect is set. Shards []*Shard `protobuf:"bytes,2,rep,name=shards,proto3" json:"shards,omitempty"` - // redirects all traffic to another keyspace. If set, shards is ignored. - ServedFrom string `protobuf:"bytes,5,opt,name=served_from,json=servedFrom,proto3" json:"served_from,omitempty"` // number of replica tablets to instantiate. This includes the primary tablet. ReplicaCount int32 `protobuf:"varint,6,opt,name=replica_count,json=replicaCount,proto3" json:"replica_count,omitempty"` // number of rdonly tablets to instantiate. @@ -188,13 +186,6 @@ func (x *Keyspace) GetShards() []*Shard { return nil } -func (x *Keyspace) GetServedFrom() string { - if x != nil { - return x.ServedFrom - } - return "" -} - func (x *Keyspace) GetReplicaCount() int32 { if x != nil { return x.ReplicaCount @@ -285,31 +276,30 @@ var file_vttest_proto_rawDesc = []byte{ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x62, - 0x4e, 0x61, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0xba, 0x01, 0x0a, + 0x4e, 0x61, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x06, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x66, - 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x64, - 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x72, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x04, 0x08, - 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x56, 0x54, - 0x54, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x2e, 0x0a, 0x09, - 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x76, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x63, 0x65, 0x6c, - 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x73, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x25, - 0x5a, 0x23, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, - 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x72, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x64, 0x6f, + 0x6e, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x72, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x92, + 0x01, 0x0a, 0x0e, 0x56, 0x54, 0x54, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x12, 0x2e, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x05, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x76, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x52, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x42, 0x25, 0x5a, 0x23, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, + 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x74, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/go/vt/proto/vttest/vttest_vtproto.pb.go b/go/vt/proto/vttest/vttest_vtproto.pb.go index f1dee298011..8000a036a5f 100644 --- a/go/vt/proto/vttest/vttest_vtproto.pb.go +++ b/go/vt/proto/vttest/vttest_vtproto.pb.go @@ -45,7 +45,6 @@ func (m *Keyspace) CloneVT() *Keyspace { } r := &Keyspace{ Name: m.Name, - ServedFrom: m.ServedFrom, ReplicaCount: m.ReplicaCount, RdonlyCount: m.RdonlyCount, } @@ -184,13 +183,6 @@ func (m *Keyspace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i-- dAtA[i] = 0x30 } - if len(m.ServedFrom) > 0 { - i -= len(m.ServedFrom) - copy(dAtA[i:], m.ServedFrom) - i = encodeVarint(dAtA, i, uint64(len(m.ServedFrom))) - i-- - dAtA[i] = 0x2a - } if len(m.Shards) > 0 { for iNdEx := len(m.Shards) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Shards[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) @@ -322,10 +314,6 @@ func (m *Keyspace) SizeVT() (n int) { n += 1 + l + sov(uint64(l)) } } - l = len(m.ServedFrom) - if l > 0 { - n += 1 + l + sov(uint64(l)) - } if m.ReplicaCount != 0 { n += 1 + sov(uint64(m.ReplicaCount)) } @@ -578,38 +566,6 @@ func (m *Keyspace) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServedFrom", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflow - } - 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 ErrInvalidLength - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLength - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServedFrom = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReplicaCount", wireType) diff --git a/go/vt/proto/vttime/vttime.pb.go b/go/vt/proto/vttime/vttime.pb.go index 5cdf3f616ce..82a20a3a303 100644 --- a/go/vt/proto/vttime/vttime.pb.go +++ b/go/vt/proto/vttime/vttime.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc v3.21.3 // source: vttime.proto diff --git a/go/vt/schema/ddl_strategy.go b/go/vt/schema/ddl_strategy.go index bc33c8cb3cf..71d434b5e09 100644 --- a/go/vt/schema/ddl_strategy.go +++ b/go/vt/schema/ddl_strategy.go @@ -27,9 +27,10 @@ import ( ) var ( - strategyParserRegexp = regexp.MustCompile(`^([\S]+)\s+(.*)$`) - cutOverThresholdFlagRegexp = regexp.MustCompile(fmt.Sprintf(`^[-]{1,2}%s=(.*?)$`, cutOverThresholdFlag)) - retainArtifactsFlagRegexp = regexp.MustCompile(fmt.Sprintf(`^[-]{1,2}%s=(.*?)$`, retainArtifactsFlag)) + strategyParserRegexp = regexp.MustCompile(`^([\S]+)\s+(.*)$`) + cutOverThresholdFlagRegexp = regexp.MustCompile(fmt.Sprintf(`^[-]{1,2}%s=(.*?)$`, cutOverThresholdFlag)) + forceCutOverAfterFlagRegexp = regexp.MustCompile(fmt.Sprintf(`^[-]{1,2}%s=(.*?)$`, forceCutOverAfterFlag)) + retainArtifactsFlagRegexp = regexp.MustCompile(fmt.Sprintf(`^[-]{1,2}%s=(.*?)$`, retainArtifactsFlag)) ) const ( @@ -45,6 +46,7 @@ const ( preferInstantDDL = "prefer-instant-ddl" fastRangeRotationFlag = "fast-range-rotation" cutOverThresholdFlag = "cut-over-threshold" + forceCutOverAfterFlag = "force-cut-over-after" retainArtifactsFlag = "retain-artifacts" vreplicationTestSuite = "vreplication-test-suite" allowForeignKeysFlag = "unsafe-allow-foreign-keys" @@ -116,6 +118,17 @@ func ParseDDLStrategy(strategyVariable string) (*DDLStrategySetting, error) { if _, err := setting.RetainArtifactsDuration(); err != nil { return nil, err } + cutoverAfter, err := setting.ForceCutOverAfter() + if err != nil { + return nil, err + } + switch setting.Strategy { + case DDLStrategyVitess, DDLStrategyOnline: + default: + if cutoverAfter != 0 { + return nil, fmt.Errorf("--force-cut-over-after is only valid in 'vitess' strategy. Found %v value in '%v' strategy", cutoverAfter, setting.Strategy) + } + } switch setting.Strategy { case DDLStrategyVitess, DDLStrategyOnline, DDLStrategyMySQL, DDLStrategyDirect: @@ -208,6 +221,15 @@ func isCutOverThresholdFlag(opt string) (string, bool) { return submatch[1], true } +// isForceCutOverFlag returns true when given option denotes a `--force-cut-over-after=[...]` flag +func isForceCutOverFlag(opt string) (string, bool) { + submatch := forceCutOverAfterFlagRegexp.FindStringSubmatch(opt) + if len(submatch) == 0 { + return "", false + } + return submatch[1], true +} + // isRetainArtifactsFlag returns true when given option denotes a `--retain-artifacts=[...]` flag func isRetainArtifactsFlag(opt string) (string, bool) { submatch := retainArtifactsFlagRegexp.FindStringSubmatch(opt) @@ -235,6 +257,24 @@ func (setting *DDLStrategySetting) CutOverThreshold() (d time.Duration, err erro return d, err } +// ForceCutOverAfter returns a the duration threshold indicated by --force-cut-over-after +func (setting *DDLStrategySetting) ForceCutOverAfter() (d time.Duration, err error) { + // We do some ugly manual parsing of --cut-over-threshold value + opts, _ := shlex.Split(setting.Options) + for _, opt := range opts { + if val, isCutOver := isForceCutOverFlag(opt); isCutOver { + // value is possibly quoted + if s, err := strconv.Unquote(val); err == nil { + val = s + } + if val != "" { + d, err = time.ParseDuration(val) + } + } + } + return d, err +} + // RetainArtifactsDuration returns a the duration indicated by --retain-artifacts func (setting *DDLStrategySetting) RetainArtifactsDuration() (d time.Duration, err error) { // We do some ugly manual parsing of --retain-artifacts @@ -276,6 +316,9 @@ func (setting *DDLStrategySetting) RuntimeOptions() []string { if _, ok := isCutOverThresholdFlag(opt); ok { continue } + if _, ok := isForceCutOverFlag(opt); ok { + continue + } if _, ok := isRetainArtifactsFlag(opt); ok { continue } diff --git a/go/vt/schema/ddl_strategy_test.go b/go/vt/schema/ddl_strategy_test.go index ba7d029b8b7..ae6c65815cc 100644 --- a/go/vt/schema/ddl_strategy_test.go +++ b/go/vt/schema/ddl_strategy_test.go @@ -198,6 +198,7 @@ func TestParseDDLStrategy(t *testing.T) { allowForeignKeys bool analyzeTable bool cutOverThreshold time.Duration + forceCutOverAfter time.Duration expireArtifacts time.Duration runtimeOptions string expectError string @@ -320,6 +321,25 @@ func TestParseDDLStrategy(t *testing.T) { runtimeOptions: "", cutOverThreshold: 5 * time.Minute, }, + { + strategyVariable: "vitess --force-cut-over-after=3m", + strategy: DDLStrategyVitess, + options: "--force-cut-over-after=3m", + runtimeOptions: "", + forceCutOverAfter: 3 * time.Minute, + }, + { + strategyVariable: "vitess --force-cut-over-after=r3m", + strategy: DDLStrategyVitess, + runtimeOptions: "", + expectError: "time: invalid duration", + }, + { + strategyVariable: "gh-ost --force-cut-over-after=3m", + strategy: DDLStrategyVitess, + runtimeOptions: "", + expectError: "--force-cut-over-after is only valid in 'vitess' strategy", + }, { strategyVariable: "vitess --retain-artifacts=4m", strategy: DDLStrategyVitess, @@ -338,14 +358,12 @@ func TestParseDDLStrategy(t *testing.T) { { strategyVariable: "vitess --alow-concrrnt", // intentional typo strategy: DDLStrategyVitess, - options: "", runtimeOptions: "", expectError: "invalid flags", }, { strategyVariable: "vitess --declarative --max-load=Threads_running=100", strategy: DDLStrategyVitess, - options: "--declarative --max-load=Threads_running=100", runtimeOptions: "--max-load=Threads_running=100", expectError: "invalid flags", }, @@ -372,6 +390,9 @@ func TestParseDDLStrategy(t *testing.T) { cutOverThreshold, err := setting.CutOverThreshold() assert.NoError(t, err) assert.Equal(t, ts.cutOverThreshold, cutOverThreshold) + forceCutOverAfter, err := setting.ForceCutOverAfter() + assert.NoError(t, err) + assert.Equal(t, ts.forceCutOverAfter, forceCutOverAfter) runtimeOptions := strings.Join(setting.RuntimeOptions(), " ") assert.Equal(t, ts.runtimeOptions, runtimeOptions) diff --git a/go/vt/schema/name.go b/go/vt/schema/name.go index 42d3878b302..c9754129c39 100644 --- a/go/vt/schema/name.go +++ b/go/vt/schema/name.go @@ -17,6 +17,8 @@ limitations under the License. package schema import ( + "fmt" + "regexp" "strings" "time" @@ -27,6 +29,30 @@ const ( readableTimeFormat = "20060102150405" ) +const ( + InternalTableNameExpression string = `^_vt_([a-zA-Z0-9]{3})_([0-f]{32})_([0-9]{14})_$` +) + +type InternalTableHint string + +const ( + InternalTableUnknownHint InternalTableHint = "nil" + InternalTableGCHoldHint InternalTableHint = "hld" + InternalTableGCPurgeHint InternalTableHint = "prg" + InternalTableGCEvacHint InternalTableHint = "evc" + InternalTableGCDropHint InternalTableHint = "drp" + InternalTableVreplicationHint InternalTableHint = "vrp" +) + +func (h InternalTableHint) String() string { + return string(h) +} + +var ( + // internalTableNameRegexp parses new internal table name format, e.g. _vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_ + internalTableNameRegexp = regexp.MustCompile(InternalTableNameExpression) +) + // CreateUUIDWithDelimiter creates a globally unique ID, with a given delimiter // example results: // - 1876a01a-354d-11eb-9a79-f8e4e33000bb (delimiter = "-") @@ -55,12 +81,53 @@ func ToReadableTimestamp(t time.Time) string { return t.Format(readableTimeFormat) } +// ReadableTimestamp returns the current timestamp, in seconds resolution, that is human readable +func ReadableTimestamp() string { + return ToReadableTimestamp(time.Now()) +} + +func condenseUUID(uuid string) string { + uuid = strings.ReplaceAll(uuid, "-", "") + uuid = strings.ReplaceAll(uuid, "_", "") + return uuid +} + +// isCondensedUUID answers 'true' when the given string is a condensed UUID, e.g.: +// a0638f6bec7b11ea9bf8000d3a9b8a9a +func isCondensedUUID(uuid string) bool { + return condensedUUIDRegexp.MatchString(uuid) +} + +// generateGCTableName creates an internal table name, based on desired hint and time, and with optional preset UUID. +// If uuid is given, then it must be in condensed-UUID format. If empty, the function auto-generates a UUID. +func GenerateInternalTableName(hint string, uuid string, t time.Time) (tableName string, err error) { + if len(hint) != 3 { + return "", fmt.Errorf("Invalid hint: %s, expected 3 characters", hint) + } + if uuid == "" { + uuid, err = CreateUUIDWithDelimiter("") + } else { + uuid = condenseUUID(uuid) + } + if err != nil { + return "", err + } + if !isCondensedUUID(uuid) { + return "", fmt.Errorf("Invalid UUID: %s, expected condensed 32 hexadecimals", uuid) + } + timestamp := ToReadableTimestamp(t) + return fmt.Sprintf("_vt_%s_%s_%s_", hint, uuid, timestamp), nil +} + // IsInternalOperationTableName answers 'true' when the given table name stands for an internal Vitess // table used for operations such as: // - Online DDL (gh-ost, pt-online-schema-change) // - Table GC (renamed before drop) // Apps such as VStreamer may choose to ignore such tables. func IsInternalOperationTableName(tableName string) bool { + if internalTableNameRegexp.MatchString(tableName) { + return true + } if IsGCTableName(tableName) { return true } @@ -69,3 +136,21 @@ func IsInternalOperationTableName(tableName string) bool { } return false } + +// AnalyzeInternalTableName analyzes a table name, and assumign it's a vitess internal table name, extracts +// the hint, uuid and time out of the name. +// An internal table name can be e.g. `_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_`, analyzed like so: +// - hint is `hld` +// - UUID is `6ace8bcef73211ea87e9f875a4d24e90` +// - Time is 2020-09-15 12:04:10 +func AnalyzeInternalTableName(tableName string) (isInternalTable bool, hint string, uuid string, t time.Time, err error) { + submatch := internalTableNameRegexp.FindStringSubmatch(tableName) + if len(submatch) == 0 { + return false, hint, uuid, t, nil + } + t, err = time.Parse(readableTimeFormat, submatch[3]) + if err != nil { + return false, hint, uuid, t, err + } + return true, submatch[1], submatch[2], t, nil +} diff --git a/go/vt/schema/name_test.go b/go/vt/schema/name_test.go index ab72f80644e..7d1d086cc45 100644 --- a/go/vt/schema/name_test.go +++ b/go/vt/schema/name_test.go @@ -18,8 +18,10 @@ package schema import ( "testing" + "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNameIsGCTableName(t *testing.T) { @@ -69,6 +71,14 @@ func TestIsInternalOperationTableName(t *testing.T) { "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", "_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_vrp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_gho_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_ghc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_xyz_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", } for _, tableName := range names { assert.True(t, IsInternalOperationTableName(tableName)) @@ -93,3 +103,124 @@ func TestIsInternalOperationTableName(t *testing.T) { assert.False(t, IsInternalOperationTableName(tableName)) } } + +func TestAnalyzeInternalTableName(t *testing.T) { + baseTime, err := time.Parse(time.RFC1123, "Tue, 15 Sep 2020 12:04:10 UTC") + assert.NoError(t, err) + tt := []struct { + tableName string + hint string + t time.Time + isInternal bool + }{ + { + tableName: "_84371a37_6153_11eb_9917_f875a4d24e90_20210128122816_vrepl", + isInternal: false, + }, + { + tableName: "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + isInternal: false, + }, + { + tableName: "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + isInternal: false, + }, + { + tableName: "_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + isInternal: false, + }, + { + tableName: "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + isInternal: false, + }, + { + tableName: "_vt_drop_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + isInternal: false, + }, + { + tableName: "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + hint: "drp", + t: baseTime, + isInternal: true, + }, + { + tableName: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + hint: "hld", + t: baseTime, + isInternal: true, + }, + { + tableName: "_vt_xyz_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + hint: "xyz", + t: baseTime, + isInternal: true, + }, + { + tableName: "_vt_xyz_6ace8bcef73211ea87e9f875a4d24e90_20200915129999_", + isInternal: false, + }, + } + for _, ts := range tt { + t.Run(ts.tableName, func(t *testing.T) { + isInternal, hint, uuid, tm, err := AnalyzeInternalTableName(ts.tableName) + assert.Equal(t, ts.isInternal, isInternal) + if ts.isInternal { + assert.NoError(t, err) + assert.True(t, isCondensedUUID(uuid)) + assert.Equal(t, ts.hint, hint) + assert.Equal(t, ts.t, tm) + } + }) + } +} + +func TestToReadableTimestamp(t *testing.T) { + ti, err := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015") + assert.NoError(t, err) + + readableTimestamp := ToReadableTimestamp(ti) + assert.Equal(t, "20150225110639", readableTimestamp) +} + +func TestGenerateInternalTableName(t *testing.T) { + ti, err := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015") + assert.NoError(t, err) + + { + uuid := "6ace8bcef73211ea87e9f875a4d24e90" + tableName, err := GenerateInternalTableName(InternalTableGCPurgeHint.String(), uuid, ti) + require.NoError(t, err) + assert.Equal(t, "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20150225110639_", tableName) + assert.True(t, IsInternalOperationTableName(tableName)) + } + { + uuid := "4e5dcf80_354b_11eb_82cd_f875a4d24e90" + tableName, err := GenerateInternalTableName(InternalTableGCPurgeHint.String(), uuid, ti) + require.NoError(t, err) + assert.Equal(t, "_vt_prg_4e5dcf80354b11eb82cdf875a4d24e90_20150225110639_", tableName) + assert.True(t, IsInternalOperationTableName(tableName)) + } + { + uuid := "4e5dcf80-354b-11eb-82cd-f875a4d24e90" + tableName, err := GenerateInternalTableName(InternalTableGCPurgeHint.String(), uuid, ti) + require.NoError(t, err) + assert.Equal(t, "_vt_prg_4e5dcf80354b11eb82cdf875a4d24e90_20150225110639_", tableName) + assert.True(t, IsInternalOperationTableName(tableName)) + } + { + uuid := "" + tableName, err := GenerateInternalTableName(InternalTableGCPurgeHint.String(), uuid, ti) + require.NoError(t, err) + assert.True(t, IsInternalOperationTableName(tableName)) + } + { + uuid := "4e5dcf80_354b_11eb_82cd_f875a4d24e90_00001111" + _, err := GenerateInternalTableName(InternalTableGCPurgeHint.String(), uuid, ti) + require.ErrorContains(t, err, "Invalid UUID") + } + { + uuid := "6ace8bcef73211ea87e9f875a4d24e90" + _, err := GenerateInternalTableName("abcdefg", uuid, ti) + require.ErrorContains(t, err, "Invalid hint") + } +} diff --git a/go/vt/schema/online_ddl.go b/go/vt/schema/online_ddl.go index a06866e996a..57ed075cf38 100644 --- a/go/vt/schema/online_ddl.go +++ b/go/vt/schema/online_ddl.go @@ -37,6 +37,16 @@ var ( migrationContextValidatorRegexp = regexp.MustCompile(`^[\w:-]*$`) ) +var ( + onlineDDLInternalTableHintsMap = map[string]bool{ + "vrp": true, // vreplication + "gho": true, // gh-ost + "ghc": true, // gh-ost + "del": true, // gh-ost + "new": true, // pt-osc + } +) + var ( // ErrDirectDDLDisabled is returned when direct DDL is disabled, and a user attempts to run a DDL statement ErrDirectDDLDisabled = errors.New("direct DDL is disabled") @@ -108,17 +118,10 @@ type OnlineDDL struct { WasReadyToComplete int64 `json:"was_ready_to_complete,omitempty"` } -// FromJSON creates an OnlineDDL from json -func FromJSON(bytes []byte) (*OnlineDDL, error) { - onlineDDL := &OnlineDDL{} - err := json.Unmarshal(bytes, onlineDDL) - return onlineDDL, err -} - // ParseOnlineDDLStatement parses the given SQL into a statement and returns the action type of the DDL statement, or error // if the statement is not a DDL -func ParseOnlineDDLStatement(sql string) (ddlStmt sqlparser.DDLStatement, action sqlparser.DDLAction, err error) { - stmt, err := sqlparser.Parse(sql) +func ParseOnlineDDLStatement(sql string, parser *sqlparser.Parser) (ddlStmt sqlparser.DDLStatement, action sqlparser.DDLAction, err error) { + stmt, err := parser.Parse(sql) if err != nil { return nil, 0, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "error parsing statement: SQL=%s, error=%+v", sql, err) } @@ -129,10 +132,10 @@ func ParseOnlineDDLStatement(sql string) (ddlStmt sqlparser.DDLStatement, action return ddlStmt, action, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported query type: %s", sql) } -func onlineDDLStatementSanity(sql string, ddlStmt sqlparser.DDLStatement, ddlStrategySetting *DDLStrategySetting) error { +func onlineDDLStatementSanity(sql string, ddlStmt sqlparser.DDLStatement, ddlStrategySetting *DDLStrategySetting, parser *sqlparser.Parser) error { // SQL statement sanity checks: if !ddlStmt.IsFullyParsed() { - if _, err := sqlparser.ParseStrictDDL(sql); err != nil { + if _, err := parser.ParseStrictDDL(sql); err != nil { // More information about the reason why the statement is not fully parsed: return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.SyntaxError, "%v", err) } @@ -154,12 +157,12 @@ func onlineDDLStatementSanity(sql string, ddlStmt sqlparser.DDLStatement, ddlStr } // NewOnlineDDLs takes a single DDL statement, normalizes it (potentially break down into multiple statements), and generates one or more OnlineDDL instances, one for each normalized statement -func NewOnlineDDLs(keyspace string, sql string, ddlStmt sqlparser.DDLStatement, ddlStrategySetting *DDLStrategySetting, migrationContext string, providedUUID string) (onlineDDLs [](*OnlineDDL), err error) { +func NewOnlineDDLs(keyspace string, sql string, ddlStmt sqlparser.DDLStatement, ddlStrategySetting *DDLStrategySetting, migrationContext string, providedUUID string, parser *sqlparser.Parser) (onlineDDLs []*OnlineDDL, err error) { appendOnlineDDL := func(tableName string, ddlStmt sqlparser.DDLStatement) error { - if err := onlineDDLStatementSanity(sql, ddlStmt, ddlStrategySetting); err != nil { + if err := onlineDDLStatementSanity(sql, ddlStmt, ddlStrategySetting, parser); err != nil { return err } - onlineDDL, err := NewOnlineDDL(keyspace, tableName, sqlparser.String(ddlStmt), ddlStrategySetting, migrationContext, providedUUID) + onlineDDL, err := NewOnlineDDL(keyspace, tableName, sqlparser.String(ddlStmt), ddlStrategySetting, migrationContext, providedUUID, parser) if err != nil { return err } @@ -190,7 +193,7 @@ func NewOnlineDDLs(keyspace string, sql string, ddlStmt sqlparser.DDLStatement, } // NewOnlineDDL creates a schema change request with self generated UUID and RequestTime -func NewOnlineDDL(keyspace string, table string, sql string, ddlStrategySetting *DDLStrategySetting, migrationContext string, providedUUID string) (onlineDDL *OnlineDDL, err error) { +func NewOnlineDDL(keyspace string, table string, sql string, ddlStrategySetting *DDLStrategySetting, migrationContext string, providedUUID string, parser *sqlparser.Parser) (onlineDDL *OnlineDDL, err error) { if ddlStrategySetting == nil { return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "NewOnlineDDL: found nil DDLStrategySetting") } @@ -224,7 +227,7 @@ func NewOnlineDDL(keyspace string, table string, sql string, ddlStrategySetting sql = fmt.Sprintf("revert vitess_migration '%s'", uuid) } - stmt, err := sqlparser.Parse(sql) + stmt, err := parser.Parse(sql) if err != nil { isLegacyRevertStatement := false // query validation and rebuilding @@ -347,9 +350,9 @@ func (onlineDDL *OnlineDDL) ToJSON() ([]byte, error) { } // sqlWithoutComments returns the SQL statement without comment directives. Useful for tests -func (onlineDDL *OnlineDDL) sqlWithoutComments() (sql string, err error) { +func (onlineDDL *OnlineDDL) sqlWithoutComments(parser *sqlparser.Parser) (sql string, err error) { sql = onlineDDL.SQL - stmt, err := sqlparser.Parse(sql) + stmt, err := parser.Parse(sql) if err != nil { // query validation and rebuilding if _, err := legacyParseRevertUUID(sql); err == nil { @@ -373,18 +376,18 @@ func (onlineDDL *OnlineDDL) sqlWithoutComments() (sql string, err error) { } // GetAction extracts the DDL action type from the online DDL statement -func (onlineDDL *OnlineDDL) GetAction() (action sqlparser.DDLAction, err error) { - if _, err := onlineDDL.GetRevertUUID(); err == nil { +func (onlineDDL *OnlineDDL) GetAction(parser *sqlparser.Parser) (action sqlparser.DDLAction, err error) { + if _, err := onlineDDL.GetRevertUUID(parser); err == nil { return sqlparser.RevertDDLAction, nil } - _, action, err = ParseOnlineDDLStatement(onlineDDL.SQL) + _, action, err = ParseOnlineDDLStatement(onlineDDL.SQL, parser) return action, err } // IsView returns 'true' when the statement affects a VIEW -func (onlineDDL *OnlineDDL) IsView() bool { - stmt, _, err := ParseOnlineDDLStatement(onlineDDL.SQL) +func (onlineDDL *OnlineDDL) IsView(parser *sqlparser.Parser) bool { + stmt, _, err := ParseOnlineDDLStatement(onlineDDL.SQL, parser) if err != nil { return false } @@ -396,8 +399,8 @@ func (onlineDDL *OnlineDDL) IsView() bool { } // GetActionStr returns a string representation of the DDL action -func (onlineDDL *OnlineDDL) GetActionStr() (action sqlparser.DDLAction, actionStr string, err error) { - action, err = onlineDDL.GetAction() +func (onlineDDL *OnlineDDL) GetActionStr(parser *sqlparser.Parser) (action sqlparser.DDLAction, actionStr string, err error) { + action, err = onlineDDL.GetAction(parser) if err != nil { return action, actionStr, err } @@ -417,11 +420,11 @@ func (onlineDDL *OnlineDDL) GetActionStr() (action sqlparser.DDLAction, actionSt // GetRevertUUID works when this migration is a revert for another migration. It returns the UUID // fo the reverted migration. // The function returns error when this is not a revert migration. -func (onlineDDL *OnlineDDL) GetRevertUUID() (uuid string, err error) { +func (onlineDDL *OnlineDDL) GetRevertUUID(parser *sqlparser.Parser) (uuid string, err error) { if uuid, err := legacyParseRevertUUID(onlineDDL.SQL); err == nil { return uuid, nil } - if stmt, err := sqlparser.Parse(onlineDDL.SQL); err == nil { + if stmt, err := parser.Parse(onlineDDL.SQL); err == nil { if revert, ok := stmt.(*sqlparser.RevertMigration); ok { return revert.UUID, nil } @@ -461,6 +464,12 @@ func OnlineDDLToGCUUID(uuid string) string { // by pt-online-schema-change. // There is no guarantee that the tables _was indeed_ generated by an online DDL flow. func IsOnlineDDLTableName(tableName string) bool { + // Try new naming format (e.g. `_vt_vrp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_`): + // The new naming format is accepted in v19, and actually _used_ in v20 + if isInternal, hint, _, _, _ := AnalyzeInternalTableName(tableName); isInternal { + return onlineDDLInternalTableHintsMap[hint] + } + if onlineDDLGeneratedTableNameRegexp.MatchString(tableName) { return true } diff --git a/go/vt/schema/online_ddl_test.go b/go/vt/schema/online_ddl_test.go index c616d64a698..c443f6b28ce 100644 --- a/go/vt/schema/online_ddl_test.go +++ b/go/vt/schema/online_ddl_test.go @@ -52,13 +52,14 @@ func TestIsOnlineDDLUUID(t *testing.T) { } func TestGetGCUUID(t *testing.T) { + parser := sqlparser.NewTestParser() uuids := map[string]bool{} count := 20 for i := 0; i < count; i++ { - onlineDDL, err := NewOnlineDDL("ks", "tbl", "alter table t drop column c", NewDDLStrategySetting(DDLStrategyDirect, ""), "", "") + onlineDDL, err := NewOnlineDDL("ks", "tbl", "alter table t drop column c", NewDDLStrategySetting(DDLStrategyDirect, ""), "", "", parser) assert.NoError(t, err) gcUUID := onlineDDL.GetGCUUID() - assert.True(t, IsGCUUID(gcUUID)) + assert.True(t, isCondensedUUID(gcUUID)) uuids[gcUUID] = true } assert.Equal(t, count, len(uuids)) @@ -86,10 +87,11 @@ func TestGetActionStr(t *testing.T) { isError: true, }, } + parser := sqlparser.NewTestParser() for _, ts := range tt { t.Run(ts.statement, func(t *testing.T) { onlineDDL := &OnlineDDL{SQL: ts.statement} - _, actionStr, err := onlineDDL.GetActionStr() + _, actionStr, err := onlineDDL.GetActionStr(parser) if ts.isError { assert.Error(t, err) } else { @@ -101,31 +103,50 @@ func TestGetActionStr(t *testing.T) { } func TestIsOnlineDDLTableName(t *testing.T) { - names := []string{ - "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_gho", - "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_ghc", - "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_del", - "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114013_new", - "_84371a37_6153_11eb_9917_f875a4d24e90_20210128122816_vrepl", - "_table_old", - "__table_old", - } - for _, tableName := range names { - assert.True(t, IsOnlineDDLTableName(tableName)) - } - irrelevantNames := []string{ - "t", - "_table_new", - "__table_new", - "_table_gho", - "_table_ghc", - "_table_del", - "_table_vrepl", - "table_old", - } - for _, tableName := range irrelevantNames { - assert.False(t, IsOnlineDDLTableName(tableName)) - } + t.Run("accept", func(t *testing.T) { + names := []string{ + "_vt_vrp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_gho_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_ghc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_del_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_new_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_gho", + "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_ghc", + "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114014_del", + "_4e5dcf80_354b_11eb_82cd_f875a4d24e90_20201203114013_new", + "_84371a37_6153_11eb_9917_f875a4d24e90_20210128122816_vrepl", + "_table_old", + "__table_old", + } + for _, tableName := range names { + t.Run(tableName, func(t *testing.T) { + assert.True(t, IsOnlineDDLTableName(tableName)) + }) + } + }) + t.Run("reject", func(t *testing.T) { + irrelevantNames := []string{ + "_vt_vrp_6ace8bcef73211ea87e9f875a4d24e90_20200915999999_", // time error + "_vt_xyz_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", // unrecognized hint + "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", // GC table + "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", // GC table + "_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", // GC table + "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", // GC table + "t", + "_table_new", + "__table_new", + "_table_gho", + "_table_ghc", + "_table_del", + "_table_vrepl", + "table_old", + } + for _, tableName := range irrelevantNames { + t.Run(tableName, func(t *testing.T) { + assert.False(t, IsOnlineDDLTableName(tableName)) + }) + } + }) } func TestGetRevertUUID(t *testing.T) { @@ -147,10 +168,11 @@ func TestGetRevertUUID(t *testing.T) { isError: true, }, } + parser := sqlparser.NewTestParser() for _, ts := range tt { t.Run(ts.statement, func(t *testing.T) { onlineDDL := &OnlineDDL{SQL: ts.statement} - uuid, err := onlineDDL.GetRevertUUID() + uuid, err := onlineDDL.GetRevertUUID(parser) if ts.isError { assert.Error(t, err) return @@ -162,10 +184,10 @@ func TestGetRevertUUID(t *testing.T) { migrationContext := "354b-11eb-82cd-f875a4d24e90" for _, ts := range tt { t.Run(ts.statement, func(t *testing.T) { - onlineDDL, err := NewOnlineDDL("test_ks", "t", ts.statement, NewDDLStrategySetting(DDLStrategyOnline, ""), migrationContext, "") + onlineDDL, err := NewOnlineDDL("test_ks", "t", ts.statement, NewDDLStrategySetting(DDLStrategyOnline, ""), migrationContext, "", parser) assert.NoError(t, err) require.NotNil(t, onlineDDL) - uuid, err := onlineDDL.GetRevertUUID() + uuid, err := onlineDDL.GetRevertUUID(parser) if ts.isError { assert.Error(t, err) return @@ -209,11 +231,12 @@ func TestNewOnlineDDL(t *testing.T) { NewDDLStrategySetting(DDLStrategyOnline, "-singleton"), } + parser := sqlparser.NewTestParser() for _, ts := range tt { t.Run(ts.sql, func(t *testing.T) { for _, stgy := range strategies { t.Run(stgy.ToString(), func(t *testing.T) { - onlineDDL, err := NewOnlineDDL("test_ks", "t", ts.sql, stgy, migrationContext, "") + onlineDDL, err := NewOnlineDDL("test_ks", "t", ts.sql, stgy, migrationContext, "", parser) if ts.isError { assert.Error(t, err) return @@ -231,19 +254,20 @@ func TestNewOnlineDDL(t *testing.T) { t.Run("explicit UUID", func(t *testing.T) { var err error var onlineDDL *OnlineDDL + parser := sqlparser.NewTestParser() - onlineDDL, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "") + onlineDDL, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "", parser) assert.NoError(t, err) assert.True(t, IsOnlineDDLUUID(onlineDDL.UUID)) - _, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyOnline, ""), migrationContext, "abc") + _, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyOnline, ""), migrationContext, "abc", parser) assert.Error(t, err) - onlineDDL, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "4e5dcf80_354b_11eb_82cd_f875a4d24e90") + onlineDDL, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "4e5dcf80_354b_11eb_82cd_f875a4d24e90", parser) assert.NoError(t, err) assert.Equal(t, "4e5dcf80_354b_11eb_82cd_f875a4d24e90", onlineDDL.UUID) - _, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, " 4e5dcf80_354b_11eb_82cd_f875a4d24e90") + _, err = NewOnlineDDL("test_ks", "t", "alter table t engine=innodb", NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, " 4e5dcf80_354b_11eb_82cd_f875a4d24e90", parser) assert.Error(t, err) }) } @@ -284,9 +308,10 @@ func TestNewOnlineDDLs(t *testing.T) { "CREATE TABLE if not exists t (id bigint unsigned NOT NULL AUTO_INCREMENT, ts datetime(6) DEFAULT NULL, error_column NO_SUCH_TYPE NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB": {isError: true, expectErrorText: "near"}, } migrationContext := "354b-11eb-82cd-f875a4d24e90" + parser := sqlparser.NewTestParser() for query, expect := range tests { t.Run(query, func(t *testing.T) { - stmt, err := sqlparser.Parse(query) + stmt, err := parser.Parse(query) if expect.parseError { assert.Error(t, err) return @@ -299,7 +324,7 @@ func TestNewOnlineDDLs(t *testing.T) { } assert.True(t, ok) - onlineDDLs, err := NewOnlineDDLs("test_ks", query, ddlStmt, NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "") + onlineDDLs, err := NewOnlineDDLs("test_ks", query, ddlStmt, NewDDLStrategySetting(DDLStrategyVitess, ""), migrationContext, "", parser) if expect.isError { assert.Error(t, err) assert.Contains(t, err.Error(), expect.expectErrorText) @@ -309,12 +334,12 @@ func TestNewOnlineDDLs(t *testing.T) { sqls := []string{} for _, onlineDDL := range onlineDDLs { - sql, err := onlineDDL.sqlWithoutComments() + sql, err := onlineDDL.sqlWithoutComments(parser) assert.NoError(t, err) sql = strings.ReplaceAll(sql, "\n", "") sql = strings.ReplaceAll(sql, "\t", "") sqls = append(sqls, sql) - assert.Equal(t, expect.isView, onlineDDL.IsView()) + assert.Equal(t, expect.isView, onlineDDL.IsView(parser)) } assert.Equal(t, expect.sqls, sqls) }) @@ -328,12 +353,13 @@ func TestNewOnlineDDLsForeignKeys(t *testing.T) { } migrationContext := "354b-11eb-82cd-f875a4d24e90" + parser := sqlparser.NewTestParser() for _, query := range queries { t.Run(query, func(t *testing.T) { for _, allowForeignKeys := range []bool{false, true} { testName := fmt.Sprintf("%t", allowForeignKeys) t.Run(testName, func(t *testing.T) { - stmt, err := sqlparser.Parse(query) + stmt, err := parser.Parse(query) require.NoError(t, err) ddlStmt, ok := stmt.(sqlparser.DDLStatement) require.True(t, ok) @@ -342,7 +368,7 @@ func TestNewOnlineDDLsForeignKeys(t *testing.T) { if allowForeignKeys { flags = "--unsafe-allow-foreign-keys" } - onlineDDLs, err := NewOnlineDDLs("test_ks", query, ddlStmt, NewDDLStrategySetting(DDLStrategyVitess, flags), migrationContext, "") + onlineDDLs, err := NewOnlineDDLs("test_ks", query, ddlStmt, NewDDLStrategySetting(DDLStrategyVitess, flags), migrationContext, "", parser) if allowForeignKeys { assert.NoError(t, err) } else { @@ -351,7 +377,7 @@ func TestNewOnlineDDLsForeignKeys(t *testing.T) { } for _, onlineDDL := range onlineDDLs { - sql, err := onlineDDL.sqlWithoutComments() + sql, err := onlineDDL.sqlWithoutComments(parser) assert.NoError(t, err) assert.NotEmpty(t, sql) } @@ -373,12 +399,13 @@ func TestOnlineDDLFromCommentedStatement(t *testing.T) { } strategySetting := NewDDLStrategySetting(DDLStrategyGhost, `-singleton -declarative --max-load="Threads_running=5"`) migrationContext := "354b-11eb-82cd-f875a4d24e90" + parser := sqlparser.NewTestParser() for _, query := range queries { t.Run(query, func(t *testing.T) { - o1, err := NewOnlineDDL("ks", "t", query, strategySetting, migrationContext, "") + o1, err := NewOnlineDDL("ks", "t", query, strategySetting, migrationContext, "", parser) require.NoError(t, err) - stmt, err := sqlparser.Parse(o1.SQL) + stmt, err := parser.Parse(o1.SQL) require.NoError(t, err) o2, err := OnlineDDLFromCommentedStatement(stmt) diff --git a/go/vt/schema/tablegc.go b/go/vt/schema/tablegc.go index 872fb42dbe5..fc1b8361fb4 100644 --- a/go/vt/schema/tablegc.go +++ b/go/vt/schema/tablegc.go @@ -44,44 +44,68 @@ const ( TableDroppedGCState TableGCState = "" ) +func (s TableGCState) TableHint() InternalTableHint { + if hint, ok := gcStatesTableHints[s]; ok { + return hint + } + return InternalTableUnknownHint +} + const ( - GCTableNameExpression string = `^_vt_(HOLD|PURGE|EVAC|DROP)_([0-f]{32})_([0-9]{14})$` + OldGCTableNameExpression string = `^_vt_(HOLD|PURGE|EVAC|DROP)_([0-f]{32})_([0-9]{14})$` + // GCTableNameExpression parses new internal table name format, e.g. _vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_ + GCTableNameExpression string = `^_vt_(hld|prg|evc|drp)_([0-f]{32})_([0-9]{14})_$` ) var ( - gcUUIDRegexp = regexp.MustCompile(`^[0-f]{32}$`) - gcTableNameRegexp = regexp.MustCompile(GCTableNameExpression) - - gcStates = map[string]TableGCState{ - string(HoldTableGCState): HoldTableGCState, - string(PurgeTableGCState): PurgeTableGCState, - string(EvacTableGCState): EvacTableGCState, - string(DropTableGCState): DropTableGCState, - } + condensedUUIDRegexp = regexp.MustCompile(`^[0-f]{32}$`) + oldGCTableNameRegexp = regexp.MustCompile(OldGCTableNameExpression) + + gcStates = map[string]TableGCState{} + gcStatesTableHints = map[TableGCState]InternalTableHint{} ) -// IsGCUUID answers 'true' when the given string is an GC UUID, e.g.: -// a0638f6bec7b11ea9bf8000d3a9b8a9a -func IsGCUUID(uuid string) bool { - return gcUUIDRegexp.MatchString(uuid) +func init() { + gcStatesTableHints[HoldTableGCState] = InternalTableGCHoldHint + gcStatesTableHints[PurgeTableGCState] = InternalTableGCPurgeHint + gcStatesTableHints[EvacTableGCState] = InternalTableGCEvacHint + gcStatesTableHints[DropTableGCState] = InternalTableGCDropHint + for _, gcState := range []TableGCState{HoldTableGCState, PurgeTableGCState, EvacTableGCState, DropTableGCState} { + gcStates[string(gcState)] = gcState + gcStates[gcState.TableHint().String()] = gcState + } } // generateGCTableName creates a GC table name, based on desired state and time, and with optional preset UUID. // If uuid is given, then it must be in GC-UUID format. If empty, the function auto-generates a UUID. -func generateGCTableName(state TableGCState, uuid string, t time.Time) (tableName string, err error) { +func generateGCTableNameOldFormat(state TableGCState, uuid string, t time.Time) (tableName string, err error) { if uuid == "" { uuid, err = CreateUUIDWithDelimiter("") } if err != nil { return "", err } - if !IsGCUUID(uuid) { + if !isCondensedUUID(uuid) { return "", fmt.Errorf("Not a valid GC UUID format: %s", uuid) } timestamp := ToReadableTimestamp(t) return fmt.Sprintf("_vt_%s_%s_%s", state, uuid, timestamp), nil } +// generateGCTableName creates a GC table name, based on desired state and time, and with optional preset UUID. +// If uuid is given, then it must be in GC-UUID format. If empty, the function auto-generates a UUID. +func generateGCTableName(state TableGCState, uuid string, t time.Time) (tableName string, err error) { + for k, v := range gcStates { + if v != state { + continue + } + if len(k) == 3 && k != string(state) { // the "new" format + return GenerateInternalTableName(k, uuid, t) + } + } + return "", fmt.Errorf("Unknown GC state: %v", state) +} + // GenerateGCTableName creates a GC table name, based on desired state and time, and with random UUID func GenerateGCTableName(state TableGCState, t time.Time) (tableName string, err error) { return generateGCTableName(state, "", t) @@ -90,17 +114,33 @@ func GenerateGCTableName(state TableGCState, t time.Time) (tableName string, err // AnalyzeGCTableName analyzes a given table name to see if it's a GC table, and if so, parse out // its state, uuid, and timestamp func AnalyzeGCTableName(tableName string) (isGCTable bool, state TableGCState, uuid string, t time.Time, err error) { - submatch := gcTableNameRegexp.FindStringSubmatch(tableName) + // Try new naming format (e.g. `_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_`): + // The new naming format is accepted in v19, and actually _used_ in v20 + if isInternal, hint, uuid, t, err := AnalyzeInternalTableName(tableName); isInternal { + gcState, ok := gcStates[hint] + return ok, gcState, uuid, t, err + } + // Try old naming formats. These names will not be generated in v20. + // TODO(shlomi): the code below should be remvoed in v21 + submatch := oldGCTableNameRegexp.FindStringSubmatch(tableName) if len(submatch) == 0 { return false, state, uuid, t, nil } + gcState, ok := gcStates[submatch[1]] + if !ok { + return false, state, uuid, t, nil + } t, err = time.Parse(readableTimeFormat, submatch[3]) - return true, TableGCState(submatch[1]), submatch[2], t, err + if err != nil { + return false, state, uuid, t, err + } + return true, gcState, submatch[2], t, nil } // IsGCTableName answers 'true' when the given table name stands for a GC table func IsGCTableName(tableName string) bool { - return gcTableNameRegexp.MatchString(tableName) + isGC, _, _, _, _ := AnalyzeGCTableName(tableName) + return isGC } // GenerateRenameStatementWithUUID generates a "RENAME TABLE" statement, where a table is renamed to a GC table, with preset UUID @@ -112,11 +152,25 @@ func GenerateRenameStatementWithUUID(fromTableName string, state TableGCState, u return fmt.Sprintf("RENAME TABLE `%s` TO %s", fromTableName, toTableName), toTableName, nil } +// GenerateRenameStatementWithUUIDNewFormat generates a "RENAME TABLE" statement, where a table is renamed to a GC table, with preset UUID +func generateRenameStatementWithUUIDOldFormat(fromTableName string, state TableGCState, uuid string, t time.Time) (statement string, toTableName string, err error) { + toTableName, err = generateGCTableNameOldFormat(state, uuid, t) + if err != nil { + return "", "", err + } + return fmt.Sprintf("RENAME TABLE `%s` TO %s", fromTableName, toTableName), toTableName, nil +} + // GenerateRenameStatement generates a "RENAME TABLE" statement, where a table is renamed to a GC table. func GenerateRenameStatement(fromTableName string, state TableGCState, t time.Time) (statement string, toTableName string, err error) { return GenerateRenameStatementWithUUID(fromTableName, state, "", t) } +// GenerateRenameStatement generates a "RENAME TABLE" statement, where a table is renamed to a GC table. +func GenerateRenameStatementOldFormat(fromTableName string, state TableGCState, t time.Time) (statement string, toTableName string, err error) { + return generateRenameStatementWithUUIDOldFormat(fromTableName, state, "", t) +} + // ParseGCLifecycle parses a comma separated list of gc states and returns a map of indicated states func ParseGCLifecycle(gcLifecycle string) (states map[TableGCState]bool, err error) { states = make(map[TableGCState]bool) diff --git a/go/vt/schema/tablegc_test.go b/go/vt/schema/tablegc_test.go index 90b31ff90fa..3f4e4e7bc09 100644 --- a/go/vt/schema/tablegc_test.go +++ b/go/vt/schema/tablegc_test.go @@ -17,6 +17,7 @@ limitations under the License. package schema import ( + "regexp" "testing" "time" @@ -24,28 +25,105 @@ import ( "github.com/stretchr/testify/require" ) +func TestGCStates(t *testing.T) { + // These are all hard coded + require.Equal(t, HoldTableGCState, gcStates["hld"]) + require.Equal(t, HoldTableGCState, gcStates["HOLD"]) + require.Equal(t, PurgeTableGCState, gcStates["prg"]) + require.Equal(t, PurgeTableGCState, gcStates["PURGE"]) + require.Equal(t, EvacTableGCState, gcStates["evc"]) + require.Equal(t, EvacTableGCState, gcStates["EVAC"]) + require.Equal(t, DropTableGCState, gcStates["drp"]) + require.Equal(t, DropTableGCState, gcStates["DROP"]) + _, ok := gcStates["purge"] + require.False(t, ok) + _, ok = gcStates["vrp"] + require.False(t, ok) + require.Equal(t, 2*4, len(gcStates)) // 4 states, 2 forms each +} + func TestIsGCTableName(t *testing.T) { tm := time.Now() states := []TableGCState{HoldTableGCState, PurgeTableGCState, EvacTableGCState, DropTableGCState} for _, state := range states { for i := 0; i < 10; i++ { - tableName, err := generateGCTableName(state, "", tm) + tableName, err := generateGCTableNameOldFormat(state, "", tm) + assert.NoError(t, err) + assert.Truef(t, IsGCTableName(tableName), "table name: %s", tableName) + + tableName, err = generateGCTableName(state, "6ace8bcef73211ea87e9f875a4d24e90", tm) + assert.NoError(t, err) + assert.Truef(t, IsGCTableName(tableName), "table name: %s", tableName) + + tableName, err = GenerateGCTableName(state, tm) assert.NoError(t, err) - assert.True(t, IsGCTableName(tableName)) + assert.Truef(t, IsGCTableName(tableName), "table name: %s", tableName) } } - names := []string{ - "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_202009151204100", - "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410 ", - "__vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", - "_vt_DROP_6ace8bcef73211ea87e9f875a4d2_20200915120410", - "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915", - "_vt_OTHER_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", - "_vt_OTHER_6ace8bcef73211ea87e9f875a4d24e90_zz20200915120410", - } - for _, tableName := range names { - assert.False(t, IsGCTableName(tableName)) - } + t.Run("accept", func(t *testing.T) { + names := []string{ + "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + } + for _, tableName := range names { + t.Run(tableName, func(t *testing.T) { + assert.True(t, IsGCTableName(tableName)) + }) + } + }) + t.Run("reject", func(t *testing.T) { + names := []string{ + "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_202009151204100", + "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410 ", + "__vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_DROP_6ace8bcef73211ea87e9f875a4d2_20200915120410", + "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915", + "_vt_OTHER_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_OTHER_6ace8bcef73211ea87e9f875a4d24e90_zz20200915120410", + "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915999999", + "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915999999_", + } + for _, tableName := range names { + t.Run(tableName, func(t *testing.T) { + assert.False(t, IsGCTableName(tableName)) + }) + } + }) + + t.Run("explicit regexp", func(t *testing.T) { + // NewGCTableNameExpression regexp is used externally by vreplication. Its a redundant form of + // InternalTableNameExpression, but is nonetheless required. We verify it works correctly + re := regexp.MustCompile(GCTableNameExpression) + t.Run("accept", func(t *testing.T) { + names := []string{ + "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + } + for _, tableName := range names { + t.Run(tableName, func(t *testing.T) { + assert.True(t, IsGCTableName(tableName)) + assert.True(t, re.MatchString(tableName)) + }) + } + }) + t.Run("reject", func(t *testing.T) { + names := []string{ + "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_vrp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_gho_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + } + for _, tableName := range names { + t.Run(tableName, func(t *testing.T) { + assert.False(t, re.MatchString(tableName)) + }) + } + }) + }) + } func TestAnalyzeGCTableName(t *testing.T) { @@ -55,35 +133,68 @@ func TestAnalyzeGCTableName(t *testing.T) { tableName string state TableGCState t time.Time + isGC bool }{ { tableName: "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", state: DropTableGCState, t: baseTime, + isGC: true, }, { tableName: "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", state: HoldTableGCState, t: baseTime, + isGC: true, }, { tableName: "_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", state: EvacTableGCState, t: baseTime, + isGC: true, }, { tableName: "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", state: PurgeTableGCState, t: baseTime, + isGC: true, + }, + { + tableName: "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200915999999", // time error + isGC: false, + }, + { + tableName: "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + state: DropTableGCState, + t: baseTime, + isGC: true, + }, + { + tableName: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + state: HoldTableGCState, + t: baseTime, + isGC: true, + }, + { + tableName: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915999999_", // time error + isGC: false, + }, + { + tableName: "_vt_xyz_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + isGC: false, }, } for _, ts := range tt { - isGC, state, uuid, tm, err := AnalyzeGCTableName(ts.tableName) - assert.NoError(t, err) - assert.True(t, isGC) - assert.True(t, IsGCUUID(uuid)) - assert.Equal(t, ts.state, state) - assert.Equal(t, ts.t, tm) + t.Run(ts.tableName, func(t *testing.T) { + isGC, state, uuid, tm, err := AnalyzeGCTableName(ts.tableName) + assert.Equal(t, ts.isGC, isGC) + if ts.isGC { + assert.NoError(t, err) + assert.True(t, isCondensedUUID(uuid)) + assert.Equal(t, ts.state, state) + assert.Equal(t, ts.t, tm) + } + }) } } diff --git a/go/vt/schemadiff/capability.go b/go/vt/schemadiff/capability.go new file mode 100644 index 00000000000..532cfd72fe8 --- /dev/null +++ b/go/vt/schemadiff/capability.go @@ -0,0 +1,177 @@ +package schemadiff + +import ( + "strings" + + "vitess.io/vitess/go/mysql/capabilities" + "vitess.io/vitess/go/vt/sqlparser" +) + +// alterOptionAvailableViaInstantDDL checks if the specific alter option is eligible to run via ALGORITHM=INSTANT +// reference: https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html +func alterOptionCapableOfInstantDDL(alterOption sqlparser.AlterOption, createTable *sqlparser.CreateTable, capableOf capabilities.CapableOf) (bool, error) { + findColumn := func(colName string) *sqlparser.ColumnDefinition { + if createTable == nil { + return nil + } + for _, col := range createTable.TableSpec.Columns { + if strings.EqualFold(colName, col.Name.String()) { + return col + } + } + return nil + } + findTableOption := func(optName string) *sqlparser.TableOption { + if createTable == nil { + return nil + } + for _, opt := range createTable.TableSpec.Options { + if strings.EqualFold(optName, opt.Name) { + return opt + } + } + return nil + } + isVirtualColumn := func(colName string) bool { + col := findColumn(colName) + if col == nil { + return false + } + if col.Type.Options == nil { + return false + } + if col.Type.Options.As == nil { + return false + } + return col.Type.Options.Storage == sqlparser.VirtualStorage + } + colStringStrippedDown := func(col *sqlparser.ColumnDefinition, stripDefault bool, stripEnum bool) string { + strippedCol := sqlparser.CloneRefOfColumnDefinition(col) + if stripDefault { + strippedCol.Type.Options.Default = nil + strippedCol.Type.Options.DefaultLiteral = false + } + if stripEnum { + strippedCol.Type.EnumValues = nil + } + return sqlparser.CanonicalString(strippedCol) + } + hasPrefix := func(vals []string, prefix []string) bool { + if len(vals) < len(prefix) { + return false + } + for i := range prefix { + if vals[i] != prefix[i] { + return false + } + } + return true + } + // Up to 8.0.26 we could only ADD COLUMN as last column + switch opt := alterOption.(type) { + case *sqlparser.ChangeColumn: + // We do not support INSTANT for renaming a column (ALTER TABLE ...CHANGE) because: + // 1. We discourage column rename + // 2. We do not produce CHANGE statements in declarative diff + // 3. The success of the operation depends on whether the column is referenced by a foreign key + // in another table. Which is a bit too much to compute here. + return false, nil + case *sqlparser.AddColumns: + if opt.First || opt.After != nil { + // not a "last" column. Only supported as of 8.0.29 + return capableOf(capabilities.InstantAddDropColumnFlavorCapability) + } + // Adding a *last* column is supported in 8.0 + return capableOf(capabilities.InstantAddLastColumnFlavorCapability) + case *sqlparser.DropColumn: + // Not supported in COMPRESSED tables + if opt := findTableOption("ROW_FORMAT"); opt != nil { + if strings.EqualFold(opt.String, "COMPRESSED") { + return false, nil + } + } + if isVirtualColumn(opt.Name.Name.String()) { + // supported by all 8.0 versions + return capableOf(capabilities.InstantAddDropVirtualColumnFlavorCapability) + } + return capableOf(capabilities.InstantAddDropColumnFlavorCapability) + case *sqlparser.ModifyColumn: + if col := findColumn(opt.NewColDefinition.Name.String()); col != nil { + // Check if only diff is change of default. + // We temporarily remove the DEFAULT expression (if any) from both + // table and ALTER statement, and compare the columns: if they're otherwise equal, + // then the only change can be an addition/change/removal of DEFAULT, which + // is instant-table. + tableColDefinition := colStringStrippedDown(col, true, false) + newColDefinition := colStringStrippedDown(opt.NewColDefinition, true, false) + if tableColDefinition == newColDefinition { + return capableOf(capabilities.InstantChangeColumnDefaultFlavorCapability) + } + // Check if: + // 1. this an ENUM/SET + // 2. and the change is to append values to the end of the list + // 3. and the number of added values does not increase the storage size for the enum/set + // 4. while still not caring about a change in the default value + if len(col.Type.EnumValues) > 0 && len(opt.NewColDefinition.Type.EnumValues) > 0 { + // both are enum or set + if !hasPrefix(opt.NewColDefinition.Type.EnumValues, col.Type.EnumValues) { + return false, nil + } + // we know the new column definition is identical to, or extends, the old definition. + // Now validate storage: + if strings.EqualFold(col.Type.Type, "enum") { + if len(col.Type.EnumValues) <= 255 && len(opt.NewColDefinition.Type.EnumValues) > 255 { + // this increases the SET storage size (1 byte for up to 8 values, 2 bytes beyond) + return false, nil + } + } + if strings.EqualFold(col.Type.Type, "set") { + if (len(col.Type.EnumValues)+7)/8 != (len(opt.NewColDefinition.Type.EnumValues)+7)/8 { + // this increases the SET storage size (1 byte for up to 8 values, 2 bytes for 8-15, etc.) + return false, nil + } + } + // Now don't care about change of default: + tableColDefinition := colStringStrippedDown(col, true, true) + newColDefinition := colStringStrippedDown(opt.NewColDefinition, true, true) + if tableColDefinition == newColDefinition { + return capableOf(capabilities.InstantExpandEnumCapability) + } + } + } + return false, nil + default: + return false, nil + } +} + +// AlterTableCapableOfInstantDDL checks if the specific ALTER TABLE is eligible to run via ALGORITHM=INSTANT, given the existing table schema and +// the MySQL server capabilities. +// The function is intentionally public, as it is intended to be used by other packages, such as onlineddl. +func AlterTableCapableOfInstantDDL(alterTable *sqlparser.AlterTable, createTable *sqlparser.CreateTable, capableOf capabilities.CapableOf) (bool, error) { + if capableOf == nil { + return false, nil + } + capable, err := capableOf(capabilities.InstantDDLFlavorCapability) + if err != nil { + return false, err + } + if !capable { + return false, nil + } + if alterTable.PartitionOption != nil || alterTable.PartitionSpec != nil { + // no INSTANT for partitions + return false, nil + } + // For the ALTER statement to qualify for ALGORITHM=INSTANT, all alter options must each qualify. + for _, alterOption := range alterTable.AlterOptions { + instantOK, err := alterOptionCapableOfInstantDDL(alterOption, createTable, capableOf) + if err != nil { + return false, err + } + if !instantOK { + return false, nil + } + } + return true, nil +} diff --git a/go/vt/schemadiff/capability_test.go b/go/vt/schemadiff/capability_test.go new file mode 100644 index 00000000000..b417c3589a3 --- /dev/null +++ b/go/vt/schemadiff/capability_test.go @@ -0,0 +1,195 @@ +package schemadiff + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/capabilities" + "vitess.io/vitess/go/vt/sqlparser" +) + +func TestAlterTableCapableOfInstantDDL(t *testing.T) { + capableOf := func(capability capabilities.FlavorCapability) (bool, error) { + switch capability { + case + capabilities.InstantDDLFlavorCapability, + capabilities.InstantAddLastColumnFlavorCapability, + capabilities.InstantAddDropVirtualColumnFlavorCapability, + capabilities.InstantAddDropColumnFlavorCapability, + capabilities.InstantChangeColumnDefaultFlavorCapability, + capabilities.InstantExpandEnumCapability: + return true, nil + } + return false, nil + } + incapableOf := func(capability capabilities.FlavorCapability) (bool, error) { + return false, nil + } + parser := sqlparser.NewTestParser() + + tcases := []struct { + name string + create string + alter string + expectCapableOfInstantDDL bool + capableOf capabilities.CapableOf + }{ + { + name: "add column", + create: "create table t1 (id int, i1 int)", + alter: "alter table t1 add column i2 int", + expectCapableOfInstantDDL: true, + }, + { + name: "add last column", + create: "create table t1 (id int, i1 int)", + alter: "alter table t1 add column i2 int after i1", + expectCapableOfInstantDDL: true, + }, + { + name: "add mid column", + create: "create table t1 (id int, i1 int)", + alter: "alter table t1 add column i2 int after id", + expectCapableOfInstantDDL: true, + }, + { + name: "add mid column, incapable", + create: "create table t1 (id int, i1 int)", + alter: "alter table t1 add column i2 int after id", + capableOf: incapableOf, + expectCapableOfInstantDDL: false, + }, + { + name: "drop virtual column", + create: "create table t(id int, i1 int not null, i2 int generated always as (i1 + 1) virtual, primary key(id))", + alter: "alter table t drop column i2", + expectCapableOfInstantDDL: true, + }, + { + name: "drop stored virtual column", + create: "create table t(id int, i1 int not null, i2 int generated always as (i1 + 1) stored, primary key(id))", + alter: "alter table t drop column i2", + expectCapableOfInstantDDL: true, + }, + { + name: "drop mid column", + create: "create table t(id int, i1 int not null, i2 int not null, primary key(id))", + alter: "alter table t drop column i1", + expectCapableOfInstantDDL: true, + }, + { + name: "fail due to row_format=compressed", + create: "create table t(id int, i1 int not null, i2 int not null, primary key(id)) row_format=compressed", + alter: "alter table t drop column i1", + expectCapableOfInstantDDL: false, + }, + { + name: "add two columns", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t add column i2 int not null after id, add column i3 int not null", + expectCapableOfInstantDDL: true, + }, + { + name: "multiple add/drop columns", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t add column i2 int not null after id, add column i3 int not null, drop column i1", + expectCapableOfInstantDDL: true, + }, + // change/remove column default + { + name: "set a default column value", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t modify column i1 int not null default 0", + expectCapableOfInstantDDL: true, + }, + { + name: "change a default column value", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t modify column i1 int not null default 3", + expectCapableOfInstantDDL: true, + }, + { + name: "change default column value to null", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t modify column i1 int default null", + expectCapableOfInstantDDL: false, + }, + { + name: "fail because on top of changing the default value, the datatype is changed, too", + create: "create table t(id int, i1 int not null, primary key(id))", + alter: "alter table t modify column i1 bigint not null default 3", + expectCapableOfInstantDDL: false, + }, + { + name: "set column dfault value to null", + create: "create table t(id int, i1 int, primary key(id))", + alter: "alter table t modify column i1 int default null", + expectCapableOfInstantDDL: true, + }, + // enum/set: + { + name: "change enum default value", + create: "create table t(id int, c1 enum('a', 'b', 'c'), primary key(id))", + alter: "alter table t modify column c1 enum('a', 'b', 'c') default 'b'", + expectCapableOfInstantDDL: true, + }, + { + name: "enum append", + create: "create table t(id int, c1 enum('a', 'b', 'c'), primary key(id))", + alter: "alter table t modify column c1 enum('a', 'b', 'c', 'd')", + expectCapableOfInstantDDL: true, + }, + { + name: "enum append with changed default", + create: "create table t(id int, c1 enum('a', 'b', 'c') default 'a', primary key(id))", + alter: "alter table t modify column c1 enum('a', 'b', 'c', 'd') default 'd'", + expectCapableOfInstantDDL: true, + }, + { + name: "enum: fail insert in middle", + create: "create table t(id int, c1 enum('a', 'b', 'c'), primary key(id))", + alter: "alter table t modify column c1 enum('a', 'b', 'x', 'c')", + expectCapableOfInstantDDL: false, + }, + { + name: "enum: fail change", + create: "create table t(id int, c1 enum('a', 'b', 'c'), primary key(id))", + alter: "alter table t modify column c1 enum('a', 'x', 'c')", + expectCapableOfInstantDDL: false, + }, + { + name: "set: append", + create: "create table t(id int, c1 set('a', 'b', 'c'), primary key(id))", + alter: "alter table t modify column c1 set('a', 'b', 'c', 'd')", + expectCapableOfInstantDDL: true, + }, + { + name: "fail set append when over threshold", // (increase from 8 to 9 values => storage goes from 1 byte to 2 bytes) + create: "create table t(id int, c1 set('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), primary key(id))", + alter: "alter table t modify column c1 set('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')", + expectCapableOfInstantDDL: false, + }, + } + for _, tcase := range tcases { + t.Run(tcase.name, func(t *testing.T) { + if tcase.capableOf == nil { + tcase.capableOf = capableOf + } + createTable, err := parser.Parse(tcase.create) + require.NoError(t, err, "failed to parse a CREATE TABLE statement from %q", tcase.create) + createTableStmt, ok := createTable.(*sqlparser.CreateTable) + require.True(t, ok) + + alterTable, err := parser.Parse(tcase.alter) + require.NoError(t, err, "failed to parse a ALTER TABLE statement from %q", tcase.alter) + alterTableStmt, ok := alterTable.(*sqlparser.AlterTable) + require.True(t, ok) + + isCapableOf, err := AlterTableCapableOfInstantDDL(alterTableStmt, createTableStmt, tcase.capableOf) + require.NoError(t, err) + assert.Equal(t, tcase.expectCapableOfInstantDDL, isCapableOf) + }) + } +} diff --git a/go/vt/schemadiff/column.go b/go/vt/schemadiff/column.go index 4b8022ac289..ae341776ccc 100644 --- a/go/vt/schemadiff/column.go +++ b/go/vt/schemadiff/column.go @@ -19,6 +19,7 @@ package schemadiff import ( "strings" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" ) @@ -79,14 +80,115 @@ func NewColumnDefinitionEntity(c *sqlparser.ColumnDefinition) *ColumnDefinitionE // ColumnDiff compares this table statement with another table statement, and sees what it takes to // change this table to look like the other table. -// It returns an AlterTable statement if changes are found, or nil if not. -// the other table may be of different name; its name is ignored. -func (c *ColumnDefinitionEntity) ColumnDiff(other *ColumnDefinitionEntity, _ *DiffHints) *ModifyColumnDiff { +// It returns an ModifyColumnDiff statement if changes are found, or nil if not. +// The function also requires the charset/collate on the source & target tables. This is because the column's +// charset & collation, if undefined, are really defined by the table's charset & collation. +// +// Anecdotally, in CreateTableEntity.normalize() we actually actively strip away the charset/collate properties +// from the column definition, to get a cleaner table definition. +// +// Things get complicated when we consider hints.TableCharsetCollateStrategy. Consider this test case: +// +// from: "create table t (a varchar(64)) default charset=latin1", +// to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", +// +// In both cases, the column is really a latin1. But the tables themselves have different collations. +// We need to denormalize the column's charset/collate properties, so that the comparison can be done. +func (c *ColumnDefinitionEntity) ColumnDiff( + env *Environment, + tableName string, + other *ColumnDefinitionEntity, + t1cc *charsetCollate, + t2cc *charsetCollate, + hints *DiffHints, +) (*ModifyColumnDiff, error) { + if c.IsTextual() || other.IsTextual() { + // We will now denormalize the columns charset & collate as needed (if empty, populate from table.) + + if c.columnDefinition.Type.Charset.Name == "" && c.columnDefinition.Type.Options.Collate != "" { + // Column has explicit collation but no charset. We can infer the charset from the collation. + collationID := env.CollationEnv().LookupByName(c.columnDefinition.Type.Options.Collate) + charset := env.CollationEnv().LookupCharsetName(collationID) + if charset == "" { + return nil, &UnknownColumnCollationCharsetError{Column: c.columnDefinition.Name.String(), Collation: c.columnDefinition.Type.Options.Collate} + } + defer func() { + c.columnDefinition.Type.Charset.Name = "" + }() + c.columnDefinition.Type.Charset.Name = charset + } + if c.columnDefinition.Type.Charset.Name == "" { + defer func() { + c.columnDefinition.Type.Charset.Name = "" + c.columnDefinition.Type.Options.Collate = "" + }() + c.columnDefinition.Type.Charset.Name = t1cc.charset + if c.columnDefinition.Type.Options.Collate == "" { + defer func() { + c.columnDefinition.Type.Options.Collate = "" + }() + c.columnDefinition.Type.Options.Collate = t1cc.collate + } + if c.columnDefinition.Type.Options.Collate = t1cc.collate; c.columnDefinition.Type.Options.Collate == "" { + collation := env.CollationEnv().DefaultCollationForCharset(t1cc.charset) + if collation == collations.Unknown { + return nil, &UnknownColumnCharsetCollationError{Column: c.columnDefinition.Name.String(), Charset: t1cc.charset} + } + c.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation) + } + } + if other.columnDefinition.Type.Charset.Name == "" && other.columnDefinition.Type.Options.Collate != "" { + // Column has explicit collation but no charset. We can infer the charset from the collation. + collationID := env.CollationEnv().LookupByName(other.columnDefinition.Type.Options.Collate) + charset := env.CollationEnv().LookupCharsetName(collationID) + if charset == "" { + return nil, &UnknownColumnCollationCharsetError{Column: other.columnDefinition.Name.String(), Collation: other.columnDefinition.Type.Options.Collate} + } + defer func() { + other.columnDefinition.Type.Charset.Name = "" + }() + other.columnDefinition.Type.Charset.Name = charset + } + + if other.columnDefinition.Type.Charset.Name == "" { + defer func() { + other.columnDefinition.Type.Charset.Name = "" + other.columnDefinition.Type.Options.Collate = "" + }() + other.columnDefinition.Type.Charset.Name = t2cc.charset + if other.columnDefinition.Type.Options.Collate = t2cc.collate; other.columnDefinition.Type.Options.Collate == "" { + collation := env.CollationEnv().DefaultCollationForCharset(t2cc.charset) + if collation == collations.Unknown { + return nil, &UnknownColumnCharsetCollationError{Column: other.columnDefinition.Name.String(), Charset: t2cc.charset} + } + other.columnDefinition.Type.Options.Collate = env.CollationEnv().LookupName(collation) + } + } + } + if sqlparser.Equals.RefOfColumnDefinition(c.columnDefinition, other.columnDefinition) { - return nil + return nil, nil } - return NewModifyColumnDiffByDefinition(other.columnDefinition) + getEnumValuesMap := func(enumValues []string) map[string]int { + m := make(map[string]int, len(enumValues)) + for i, enumValue := range enumValues { + m[enumValue] = i + } + return m + } + switch hints.EnumReorderStrategy { + case EnumReorderStrategyReject: + otherEnumValuesMap := getEnumValuesMap(other.columnDefinition.Type.EnumValues) + for ordinal, enumValue := range c.columnDefinition.Type.EnumValues { + if otherOrdinal, ok := otherEnumValuesMap[enumValue]; ok { + if ordinal != otherOrdinal { + return nil, &EnumValueOrdinalChangedError{Table: tableName, Column: c.columnDefinition.Name.String(), Value: enumValue, Ordinal: ordinal, NewOrdinal: otherOrdinal} + } + } + } + } + return NewModifyColumnDiffByDefinition(other.columnDefinition), nil } // IsTextual returns true when this column is of textual type, and is capable of having a character set property diff --git a/go/vt/schemadiff/diff.go b/go/vt/schemadiff/diff.go index fce1e5e99db..d65b9631515 100644 --- a/go/vt/schemadiff/diff.go +++ b/go/vt/schemadiff/diff.go @@ -27,11 +27,11 @@ func AllSubsequent(diff EntityDiff) (diffs []EntityDiff) { // DiffCreateTablesQueries compares two `CREATE TABLE ...` queries (in string form) and returns the diff from table1 to table2. // Either or both of the queries can be empty. Based on this, the diff could be // nil, CreateTable, DropTable or AlterTable -func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { +func DiffCreateTablesQueries(env *Environment, query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { var fromCreateTable *sqlparser.CreateTable var ok bool if query1 != "" { - stmt, err := sqlparser.ParseStrictDDL(query1) + stmt, err := env.Parser().ParseStrictDDL(query1) if err != nil { return nil, err } @@ -42,7 +42,7 @@ func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints) (En } var toCreateTable *sqlparser.CreateTable if query2 != "" { - stmt, err := sqlparser.ParseStrictDDL(query2) + stmt, err := env.Parser().ParseStrictDDL(query2) if err != nil { return nil, err } @@ -51,34 +51,34 @@ func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints) (En return nil, ErrExpectedCreateTable } } - return DiffTables(fromCreateTable, toCreateTable, hints) + return DiffTables(env, fromCreateTable, toCreateTable, hints) } // DiffTables compares two tables and returns the diff from table1 to table2. // Either or both of the CreateTable statements can be nil. Based on this, the diff could be // nil, CreateTable, DropTable or AlterTable -func DiffTables(create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, hints *DiffHints) (EntityDiff, error) { +func DiffTables(env *Environment, create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, hints *DiffHints) (EntityDiff, error) { switch { case create1 == nil && create2 == nil: return nil, nil case create1 == nil: - c2, err := NewCreateTableEntity(create2) + c2, err := NewCreateTableEntity(env, create2) if err != nil { return nil, err } return c2.Create(), nil case create2 == nil: - c1, err := NewCreateTableEntity(create1) + c1, err := NewCreateTableEntity(env, create1) if err != nil { return nil, err } return c1.Drop(), nil default: - c1, err := NewCreateTableEntity(create1) + c1, err := NewCreateTableEntity(env, create1) if err != nil { return nil, err } - c2, err := NewCreateTableEntity(create2) + c2, err := NewCreateTableEntity(env, create2) if err != nil { return nil, err } @@ -89,11 +89,11 @@ func DiffTables(create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, // DiffCreateViewsQueries compares two `CREATE TABLE ...` queries (in string form) and returns the diff from table1 to table2. // Either or both of the queries can be empty. Based on this, the diff could be // nil, CreateView, DropView or AlterView -func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { +func DiffCreateViewsQueries(env *Environment, query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { var fromCreateView *sqlparser.CreateView var ok bool if query1 != "" { - stmt, err := sqlparser.ParseStrictDDL(query1) + stmt, err := env.Parser().ParseStrictDDL(query1) if err != nil { return nil, err } @@ -104,7 +104,7 @@ func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints) (Ent } var toCreateView *sqlparser.CreateView if query2 != "" { - stmt, err := sqlparser.ParseStrictDDL(query2) + stmt, err := env.Parser().ParseStrictDDL(query2) if err != nil { return nil, err } @@ -113,34 +113,34 @@ func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints) (Ent return nil, ErrExpectedCreateView } } - return DiffViews(fromCreateView, toCreateView, hints) + return DiffViews(env, fromCreateView, toCreateView, hints) } // DiffViews compares two views and returns the diff from view1 to view2 // Either or both of the CreateView statements can be nil. Based on this, the diff could be // nil, CreateView, DropView or AlterView -func DiffViews(create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hints *DiffHints) (EntityDiff, error) { +func DiffViews(env *Environment, create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hints *DiffHints) (EntityDiff, error) { switch { case create1 == nil && create2 == nil: return nil, nil case create1 == nil: - c2, err := NewCreateViewEntity(create2) + c2, err := NewCreateViewEntity(env, create2) if err != nil { return nil, err } return c2.Create(), nil case create2 == nil: - c1, err := NewCreateViewEntity(create1) + c1, err := NewCreateViewEntity(env, create1) if err != nil { return nil, err } return c1.Drop(), nil default: - c1, err := NewCreateViewEntity(create1) + c1, err := NewCreateViewEntity(env, create1) if err != nil { return nil, err } - c2, err := NewCreateViewEntity(create2) + c2, err := NewCreateViewEntity(env, create2) if err != nil { return nil, err } @@ -151,12 +151,12 @@ func DiffViews(create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hin // DiffSchemasSQL compares two schemas and returns the rich diff that turns // 1st schema into 2nd. Schemas are build from SQL, each of which can contain an arbitrary number of // CREATE TABLE and CREATE VIEW statements. -func DiffSchemasSQL(sql1 string, sql2 string, hints *DiffHints) (*SchemaDiff, error) { - schema1, err := NewSchemaFromSQL(sql1) +func DiffSchemasSQL(env *Environment, sql1 string, sql2 string, hints *DiffHints) (*SchemaDiff, error) { + schema1, err := NewSchemaFromSQL(env, sql1) if err != nil { return nil, err } - schema2, err := NewSchemaFromSQL(sql2) + schema2, err := NewSchemaFromSQL(env, sql2) if err != nil { return nil, err } @@ -165,12 +165,12 @@ func DiffSchemasSQL(sql1 string, sql2 string, hints *DiffHints) (*SchemaDiff, er // DiffSchemas compares two schemas and returns the list of diffs that turn // 1st schema into 2nd. Any of the schemas may be nil. -func DiffSchemas(schema1 *Schema, schema2 *Schema, hints *DiffHints) (*SchemaDiff, error) { +func DiffSchemas(env *Environment, schema1 *Schema, schema2 *Schema, hints *DiffHints) (*SchemaDiff, error) { if schema1 == nil { - schema1 = newEmptySchema() + schema1 = newEmptySchema(env) } if schema2 == nil { - schema2 = newEmptySchema() + schema2 = newEmptySchema(env) } return schema1.SchemaDiff(schema2, hints) } diff --git a/go/vt/schemadiff/diff_test.go b/go/vt/schemadiff/diff_test.go index d2a170f4752..fbe7238e3fd 100644 --- a/go/vt/schemadiff/diff_test.go +++ b/go/vt/schemadiff/diff_test.go @@ -23,21 +23,26 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" ) func TestDiffTables(t *testing.T) { + env57, err := vtenv.New(vtenv.Options{MySQLServerVersion: "5.7.9"}) + require.NoError(t, err) tt := []struct { - name string - from string - to string - diff string - cdiff string - fromName string - toName string - action string - isError bool - hints *DiffHints + name string + from string + to string + diff string + cdiff string + fromName string + toName string + action string + expectError string + hints *DiffHints + env *Environment }{ { name: "identical", @@ -189,7 +194,122 @@ func TestDiffTables(t *testing.T) { TableQualifierHint: TableQualifierDeclared, }, }, + { + name: "changing table level defaults with column specific settings, ignore charset", + from: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "changing table level defaults with column specific settings based on collation, ignore charset", + from: "create table t (a varchar(64) COLLATE latin1_bin) default charset=utf8mb4", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "error on unknown collation", + from: "create table t (a varchar(64) COLLATE latin1_nonexisting) default charset=utf8mb4", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + expectError: (&UnknownColumnCollationCharsetError{Column: "a", Collation: "latin1_nonexisting"}).Error(), + }, + { + name: "error on unknown charset", + from: "create table t (a varchar(64)) default charset=latin_nonexisting collate=''", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + }, + expectError: (&UnknownColumnCharsetCollationError{Column: "a", Charset: "latin_nonexisting"}).Error(), + }, + { + name: "changing table level defaults with column specific settings", + from: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + diff: "alter table t charset utf8mb4, algorithm = COPY", + cdiff: "ALTER TABLE `t` CHARSET utf8mb4, ALGORITHM = COPY", + action: "alter", + fromName: "t", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateStrict, + }, + }, + { + name: "changing table level defaults with column specific settings, table already normalized", + from: "create table t (a varchar(64)) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + diff: "alter table t modify column a varchar(64) character set latin1 collate latin1_bin, charset utf8mb4, algorithm = COPY", + cdiff: "ALTER TABLE `t` MODIFY COLUMN `a` varchar(64) CHARACTER SET latin1 COLLATE latin1_bin, CHARSET utf8mb4, ALGORITHM = COPY", + action: "alter", + fromName: "t", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateStrict, + }, + }, + { + name: "changing table level charset to default", + from: `create table t (i int) default charset=latin1`, + to: `create table t (i int)`, + action: "alter", + diff: "alter table t charset utf8mb4", + cdiff: "ALTER TABLE `t` CHARSET utf8mb4", + }, + { + name: "no changes with normalization and utf8mb4", + from: `CREATE TABLE IF NOT EXISTS tables + ( + TABLE_SCHEMA varchar(64) NOT NULL, + TABLE_NAME varchar(64) NOT NULL, + CREATE_STATEMENT longtext, + CREATE_TIME BIGINT, + PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) + ) engine = InnoDB`, + to: "CREATE TABLE `tables` (" + + "`TABLE_SCHEMA` varchar(64) NOT NULL," + + "`TABLE_NAME` varchar(64) NOT NULL," + + "`CREATE_STATEMENT` longtext," + + "`CREATE_TIME` bigint DEFAULT NULL," + + "PRIMARY KEY (`TABLE_SCHEMA`,`TABLE_NAME`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + hints: &DiffHints{ + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "no changes with normalization and utf8mb3", + from: `CREATE TABLE IF NOT EXISTS tables + ( + TABLE_SCHEMA varchar(64) NOT NULL, + TABLE_NAME varchar(64) NOT NULL, + CREATE_STATEMENT longtext, + CREATE_TIME BIGINT, + PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) + ) engine = InnoDB`, + to: "CREATE TABLE `tables` (" + + "`TABLE_SCHEMA` varchar(64) NOT NULL," + + "`TABLE_NAME` varchar(64) NOT NULL," + + "`CREATE_STATEMENT` longtext," + + "`CREATE_TIME` bigint DEFAULT NULL," + + "PRIMARY KEY (`TABLE_SCHEMA`,`TABLE_NAME`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci", + hints: &DiffHints{ + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + env: NewEnv(env57, collations.CollationUtf8mb3ID), + }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { var fromCreateTable *sqlparser.CreateTable @@ -197,8 +317,11 @@ func TestDiffTables(t *testing.T) { if ts.hints != nil { hints = ts.hints } + if ts.env != nil { + env = ts.env + } if ts.from != "" { - fromStmt, err := sqlparser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser().ParseStrictDDL(ts.from) assert.NoError(t, err) var ok bool fromCreateTable, ok = fromStmt.(*sqlparser.CreateTable) @@ -206,7 +329,7 @@ func TestDiffTables(t *testing.T) { } var toCreateTable *sqlparser.CreateTable if ts.to != "" { - toStmt, err := sqlparser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser().ParseStrictDDL(ts.to) assert.NoError(t, err) var ok bool toCreateTable, ok = toStmt.(*sqlparser.CreateTable) @@ -218,17 +341,21 @@ func TestDiffTables(t *testing.T) { // Technically, DiffCreateTablesQueries calls DiffTables, // but we expose both to users of this library. so we want to make sure // both work as expected irrespective of any relationship between them. - dq, dqerr := DiffCreateTablesQueries(ts.from, ts.to, hints) - d, err := DiffTables(fromCreateTable, toCreateTable, hints) + dq, dqerr := DiffCreateTablesQueries(env, ts.from, ts.to, hints) + d, err := DiffTables(env, fromCreateTable, toCreateTable, hints) switch { - case ts.isError: - assert.Error(t, err) - assert.Error(t, dqerr) + case ts.expectError != "": + assert.ErrorContains(t, err, ts.expectError) + assert.ErrorContains(t, dqerr, ts.expectError) case ts.diff == "": assert.NoError(t, err) assert.NoError(t, dqerr) - assert.Nil(t, d) - assert.Nil(t, dq) + if !assert.Nil(t, d) { + assert.Failf(t, "found unexpected diff", "%v", d.CanonicalStatementString()) + } + if !assert.Nil(t, dq) { + assert.Failf(t, "found unexpected diff", "%v", dq.CanonicalStatementString()) + } default: assert.NoError(t, err) require.NotNil(t, d) @@ -241,7 +368,7 @@ func TestDiffTables(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = sqlparser.ParseStrictDDL(diff) + _, err = env.Parser().ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := d.Entities() @@ -260,7 +387,7 @@ func TestDiffTables(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = sqlparser.ParseStrictDDL(canonicalDiff) + _, err = env.Parser().ParseStrictDDL(canonicalDiff) assert.NoError(t, err) } // let's also check dq, and also validate that dq's statement is identical to d's @@ -322,11 +449,12 @@ func TestDiffViews(t *testing.T) { }, } hints := &DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { var fromCreateView *sqlparser.CreateView if ts.from != "" { - fromStmt, err := sqlparser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser().ParseStrictDDL(ts.from) assert.NoError(t, err) var ok bool fromCreateView, ok = fromStmt.(*sqlparser.CreateView) @@ -334,7 +462,7 @@ func TestDiffViews(t *testing.T) { } var toCreateView *sqlparser.CreateView if ts.to != "" { - toStmt, err := sqlparser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser().ParseStrictDDL(ts.to) assert.NoError(t, err) var ok bool toCreateView, ok = toStmt.(*sqlparser.CreateView) @@ -346,8 +474,8 @@ func TestDiffViews(t *testing.T) { // Technically, DiffCreateTablesQueries calls DiffTables, // but we expose both to users of this library. so we want to make sure // both work as expected irrespective of any relationship between them. - dq, dqerr := DiffCreateViewsQueries(ts.from, ts.to, hints) - d, err := DiffViews(fromCreateView, toCreateView, hints) + dq, dqerr := DiffCreateViewsQueries(env, ts.from, ts.to, hints) + d, err := DiffViews(env, fromCreateView, toCreateView, hints) switch { case ts.isError: assert.Error(t, err) @@ -355,8 +483,12 @@ func TestDiffViews(t *testing.T) { case ts.diff == "": assert.NoError(t, err) assert.NoError(t, dqerr) - assert.Nil(t, d) - assert.Nil(t, dq) + if !assert.Nil(t, d) { + assert.Failf(t, "found unexpected diff", "%v", d.CanonicalStatementString()) + } + if !assert.Nil(t, dq) { + assert.Failf(t, "found unexpected diff", "%v", dq.CanonicalStatementString()) + } default: assert.NoError(t, err) require.NotNil(t, d) @@ -369,7 +501,7 @@ func TestDiffViews(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = sqlparser.ParseStrictDDL(diff) + _, err = env.Parser().ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := d.Entities() @@ -388,7 +520,7 @@ func TestDiffViews(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = sqlparser.ParseStrictDDL(canonicalDiff) + _, err = env.Parser().ParseStrictDDL(canonicalDiff) assert.NoError(t, err) } @@ -796,12 +928,13 @@ func TestDiffSchemas(t *testing.T) { }, }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { hints := &DiffHints{ TableRenameStrategy: ts.tableRename, } - diff, err := DiffSchemasSQL(ts.from, ts.to, hints) + diff, err := DiffSchemasSQL(env, ts.from, ts.to, hints) if ts.expectError != "" { require.Error(t, err) assert.Contains(t, err.Error(), ts.expectError) @@ -827,21 +960,21 @@ func TestDiffSchemas(t *testing.T) { // validate we can parse back the diff statements for _, s := range statements { - _, err := sqlparser.ParseStrictDDL(s) + _, err := env.Parser().ParseStrictDDL(s) assert.NoError(t, err) } for _, s := range cstatements { - _, err := sqlparser.ParseStrictDDL(s) + _, err := env.Parser().ParseStrictDDL(s) assert.NoError(t, err) } { // Validate "apply()" on "from" converges with "to" - schema1, err := NewSchemaFromSQL(ts.from) + schema1, err := NewSchemaFromSQL(env, ts.from) require.NoError(t, err) schema1SQL := schema1.ToSQL() - schema2, err := NewSchemaFromSQL(ts.to) + schema2, err := NewSchemaFromSQL(env, ts.to) require.NoError(t, err) applied, err := schema1.Apply(diffs) require.NoError(t, err) @@ -892,12 +1025,13 @@ func TestSchemaApplyError(t *testing.T) { }, } hints := &DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { // Validate "apply()" on "from" converges with "to" - schema1, err := NewSchemaFromSQL(ts.from) + schema1, err := NewSchemaFromSQL(env, ts.from) assert.NoError(t, err) - schema2, err := NewSchemaFromSQL(ts.to) + schema2, err := NewSchemaFromSQL(env, ts.to) assert.NoError(t, err) { diff --git a/go/vt/schemadiff/env.go b/go/vt/schemadiff/env.go new file mode 100644 index 00000000000..9037de40b01 --- /dev/null +++ b/go/vt/schemadiff/env.go @@ -0,0 +1,25 @@ +package schemadiff + +import ( + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/vtenv" +) + +type Environment struct { + *vtenv.Environment + DefaultColl collations.ID +} + +func NewTestEnv() *Environment { + return &Environment{ + Environment: vtenv.NewTestEnv(), + DefaultColl: collations.MySQL8().DefaultConnectionCharset(), + } +} + +func NewEnv(env *vtenv.Environment, defaultColl collations.ID) *Environment { + return &Environment{ + Environment: env, + DefaultColl: defaultColl, + } +} diff --git a/go/vt/schemadiff/errors.go b/go/vt/schemadiff/errors.go index 8317fbe9cea..5268db76ff3 100644 --- a/go/vt/schemadiff/errors.go +++ b/go/vt/schemadiff/errors.go @@ -282,10 +282,53 @@ type ForeignKeyDependencyUnresolvedError struct { } func (e *ForeignKeyDependencyUnresolvedError) Error() string { - return fmt.Sprintf("table %s has unresolved/loop foreign key dependencies", + return fmt.Sprintf("table %s has unresolved foreign key dependencies", sqlescape.EscapeID(e.Table)) } +type ForeignKeyLoopError struct { + Table string + Loop []string +} + +func (e *ForeignKeyLoopError) Error() string { + tableIsInsideLoop := false + + escaped := make([]string, len(e.Loop)) + for i, t := range e.Loop { + escaped[i] = sqlescape.EscapeID(t) + if t == e.Table { + tableIsInsideLoop = true + } + } + if tableIsInsideLoop { + return fmt.Sprintf("table %s participates in a circular foreign key reference: %s", + sqlescape.EscapeID(e.Table), strings.Join(escaped, ", ")) + } + return fmt.Sprintf("table %s references a circular foreign key reference: %s", + sqlescape.EscapeID(e.Table), strings.Join(escaped, ", ")) +} + +type ForeignKeyNonexistentReferencedTableError struct { + Table string + ReferencedTable string +} + +func (e *ForeignKeyNonexistentReferencedTableError) Error() string { + return fmt.Sprintf("table %s foreign key references nonexistent table %s", + sqlescape.EscapeID(e.Table), sqlescape.EscapeID(e.ReferencedTable)) +} + +type ForeignKeyReferencesViewError struct { + Table string + ReferencedView string +} + +func (e *ForeignKeyReferencesViewError) Error() string { + return fmt.Sprintf("table %s foreign key references view %s", + sqlescape.EscapeID(e.Table), sqlescape.EscapeID(e.ReferencedView)) +} + type InvalidColumnInForeignKeyConstraintError struct { Table string Constraint string @@ -403,3 +446,33 @@ type EntityNotFoundError struct { func (e *EntityNotFoundError) Error() string { return fmt.Sprintf("entity %s not found", sqlescape.EscapeID(e.Name)) } + +type EnumValueOrdinalChangedError struct { + Table string + Column string + Value string + Ordinal int + NewOrdinal int +} + +func (e *EnumValueOrdinalChangedError) Error() string { + return fmt.Sprintf("ordinal of %s changed in enum or set column %s.%s, from %d to %d", e.Value, sqlescape.EscapeID(e.Table), sqlescape.EscapeID(e.Column), e.Ordinal, e.NewOrdinal) +} + +type UnknownColumnCharsetCollationError struct { + Column string + Charset string +} + +func (e *UnknownColumnCharsetCollationError) Error() string { + return fmt.Sprintf("unable to determine collation for column %s with charset %q", sqlescape.EscapeID(e.Column), e.Charset) +} + +type UnknownColumnCollationCharsetError struct { + Column string + Collation string +} + +func (e *UnknownColumnCollationCharsetError) Error() string { + return fmt.Sprintf("unable to determine charset for column %s with collation %q", sqlescape.EscapeID(e.Column), e.Collation) +} diff --git a/go/vt/schemadiff/schema.go b/go/vt/schemadiff/schema.go index 9180012676f..084b703b14f 100644 --- a/go/vt/schemadiff/schema.go +++ b/go/vt/schemadiff/schema.go @@ -17,16 +17,12 @@ limitations under the License. package schemadiff import ( - "bytes" "errors" - "fmt" - "io" "sort" "strings" - "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -41,10 +37,13 @@ type Schema struct { foreignKeyParents []*CreateTableEntity // subset of tables foreignKeyChildren []*CreateTableEntity // subset of tables + foreignKeyLoopMap map[string][]string // map of table name that either participate, or directly or indirectly reference foreign key loops + + env *Environment } // newEmptySchema is used internally to initialize a Schema object -func newEmptySchema() *Schema { +func newEmptySchema(env *Environment) *Schema { schema := &Schema{ tables: []*CreateTableEntity{}, views: []*CreateViewEntity{}, @@ -53,13 +52,16 @@ func newEmptySchema() *Schema { foreignKeyParents: []*CreateTableEntity{}, foreignKeyChildren: []*CreateTableEntity{}, + foreignKeyLoopMap: map[string][]string{}, + + env: env, } return schema } // NewSchemaFromEntities creates a valid and normalized schema based on list of entities -func NewSchemaFromEntities(entities []Entity) (*Schema, error) { - schema := newEmptySchema() +func NewSchemaFromEntities(env *Environment, entities []Entity) (*Schema, error) { + schema := newEmptySchema(env) for _, e := range entities { switch c := e.(type) { case *CreateTableEntity: @@ -75,18 +77,18 @@ func NewSchemaFromEntities(entities []Entity) (*Schema, error) { } // NewSchemaFromStatements creates a valid and normalized schema based on list of valid statements -func NewSchemaFromStatements(statements []sqlparser.Statement) (*Schema, error) { +func NewSchemaFromStatements(env *Environment, statements []sqlparser.Statement) (*Schema, error) { entities := make([]Entity, 0, len(statements)) for _, s := range statements { switch stmt := s.(type) { case *sqlparser.CreateTable: - c, err := NewCreateTableEntity(stmt) + c, err := NewCreateTableEntity(env, stmt) if err != nil { return nil, err } entities = append(entities, c) case *sqlparser.CreateView: - v, err := NewCreateViewEntity(stmt) + v, err := NewCreateViewEntity(env, stmt) if err != nil { return nil, err } @@ -95,41 +97,33 @@ func NewSchemaFromStatements(statements []sqlparser.Statement) (*Schema, error) return nil, &UnsupportedStatementError{Statement: sqlparser.CanonicalString(s)} } } - return NewSchemaFromEntities(entities) + return NewSchemaFromEntities(env, entities) } // NewSchemaFromQueries creates a valid and normalized schema based on list of queries -func NewSchemaFromQueries(queries []string) (*Schema, error) { +func NewSchemaFromQueries(env *Environment, queries []string) (*Schema, error) { statements := make([]sqlparser.Statement, 0, len(queries)) for _, q := range queries { - stmt, err := sqlparser.ParseStrictDDL(q) + stmt, err := env.Parser().ParseStrictDDL(q) if err != nil { return nil, err } statements = append(statements, stmt) } - return NewSchemaFromStatements(statements) + return NewSchemaFromStatements(env, statements) } // NewSchemaFromSQL creates a valid and normalized schema based on a SQL blob that contains // CREATE statements for various objects (tables, views) -func NewSchemaFromSQL(sql string) (*Schema, error) { - var statements []sqlparser.Statement - tokenizer := sqlparser.NewStringTokenizer(sql) - for { - stmt, err := sqlparser.ParseNextStrictDDL(tokenizer) - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return nil, fmt.Errorf("could not parse statement in SQL: %v: %w", sql, err) - } - statements = append(statements, stmt) +func NewSchemaFromSQL(env *Environment, sql string) (*Schema, error) { + statements, err := env.Parser().SplitStatements(sql) + if err != nil { + return nil, err } - return NewSchemaFromStatements(statements) + return NewSchemaFromStatements(env, statements) } -// getForeignKeyParentTableNames analyzes a CREATE TABLE definition and extracts all referened foreign key tables names. +// getForeignKeyParentTableNames analyzes a CREATE TABLE definition and extracts all referenced foreign key tables names. // A table name may appear twice in the result output, if it is referenced by more than one foreign key func getForeignKeyParentTableNames(createTable *sqlparser.CreateTable) (names []string) { for _, cs := range createTable.TableSpec.Constraints { @@ -141,6 +135,42 @@ func getForeignKeyParentTableNames(createTable *sqlparser.CreateTable) (names [] return names } +// findForeignKeyLoop is a stateful recursive function that determines whether a given table participates in a foreign +// key loop or derives from one. It returns a list of table names that form a loop, or nil if no loop is found. +// The function updates and checks the stateful map s.foreignKeyLoopMap to avoid re-analyzing the same table twice. +func (s *Schema) findForeignKeyLoop(tableName string, seen []string) (loop []string) { + if loop := s.foreignKeyLoopMap[tableName]; loop != nil { + return loop + } + t := s.Table(tableName) + if t == nil { + return nil + } + seen = append(seen, tableName) + for i, seenTable := range seen { + if i == len(seen)-1 { + // as we've just appended the table name to the end of the slice, we should skip it. + break + } + if seenTable == tableName { + // This table alreay appears in `seen`. + // We only return the suffix of `seen` that starts (and now ends) with this table. + return seen[i:] + } + } + for _, referencedTableName := range getForeignKeyParentTableNames(t.CreateTable) { + if loop := s.findForeignKeyLoop(referencedTableName, seen); loop != nil { + // Found loop. Update cache. + // It's possible for one table to participate in more than one foreign key loop, but + // we suffice with one loop, since we already only ever report one foreign key error + // per table. + s.foreignKeyLoopMap[tableName] = loop + return loop + } + } + return nil +} + // getViewDependentTableNames analyzes a CREATE VIEW definition and extracts all tables/views read by this view func getViewDependentTableNames(createView *sqlparser.CreateView) (names []string) { _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { @@ -215,6 +245,18 @@ func (s *Schema) normalize() error { return true } + // Utility map and function to only record one foreign-key error per table. We make this limitation + // because the search algorithm below could review the same table twice, thus potentially unnecessarily duplicating + // found errors. + entityFkErrors := map[string]error{} + addEntityFkError := func(e Entity, err error) error { + if _, ok := entityFkErrors[e.Name()]; ok { + // error already recorded for this entity + return nil + } + entityFkErrors[e.Name()] = err + return err + } // We now iterate all tables. We iterate "dependency levels": // - first we want all tables that don't have foreign keys or which only reference themselves // - then we only want tables that reference 1st level tables. these are 2nd level tables @@ -240,6 +282,16 @@ func (s *Schema) normalize() error { if referencedTableName != name { nonSelfReferenceNames = append(nonSelfReferenceNames, referencedTableName) } + referencedEntity, ok := s.named[referencedTableName] + if !ok { + errs = errors.Join(errs, addEntityFkError(t, &ForeignKeyNonexistentReferencedTableError{Table: name, ReferencedTable: referencedTableName})) + continue + } + if _, ok := referencedEntity.(*CreateViewEntity); ok { + errs = errors.Join(errs, addEntityFkError(t, &ForeignKeyReferencesViewError{Table: name, ReferencedView: referencedTableName})) + continue + } + fkParents[referencedTableName] = true } if allNamesFoundInLowerLevel(nonSelfReferenceNames, iterationLevel) { @@ -267,7 +319,7 @@ func (s *Schema) normalize() error { // It's possible that there's never been any tables in this schema. Which means // iterationLevel remains zero. // To deal with views, we must have iterationLevel at least 1. This is because any view reads - // from _something_: at the very least it reads from DUAL (inplicitly or explicitly). Which + // from _something_: at the very least it reads from DUAL (implicitly or explicitly). Which // puts the view at a higher level. if iterationLevel < 1 { iterationLevel = 1 @@ -293,7 +345,16 @@ func (s *Schema) normalize() error { } iterationLevel++ } + if len(s.sorted) != len(s.tables)+len(s.views) { + + for _, t := range s.tables { + if _, ok := dependencyLevels[t.Name()]; !ok { + if loop := s.findForeignKeyLoop(t.Name(), nil); loop != nil { + errs = errors.Join(errs, addEntityFkError(t, &ForeignKeyLoopError{Table: t.Name(), Loop: loop})) + } + } + } // We have leftover tables or views. This can happen if the schema definition is invalid: // - a table's foreign key references a nonexistent table // - two or more tables have circular FK dependency @@ -303,7 +364,8 @@ func (s *Schema) normalize() error { if _, ok := dependencyLevels[t.Name()]; !ok { // We _know_ that in this iteration, at least one foreign key is not found. // We return the first one. - return &ForeignKeyDependencyUnresolvedError{Table: t.Name()} + errs = errors.Join(errs, addEntityFkError(t, &ForeignKeyDependencyUnresolvedError{Table: t.Name()})) + s.sorted = append(s.sorted, t) } } for _, v := range s.views { @@ -328,12 +390,29 @@ func (s *Schema) normalize() error { return errors.Join(errs, err) } } - colTypeEqualForForeignKey := func(a, b *sqlparser.ColumnType) bool { - return a.Type == b.Type && - a.Unsigned == b.Unsigned && - a.Zerofill == b.Zerofill && - sqlparser.Equals.ColumnCharset(a.Charset, b.Charset) && - sqlparser.Equals.SliceOfString(a.EnumValues, b.EnumValues) + colTypeCompatibleForForeignKey := func(child, parent *sqlparser.ColumnType) bool { + if child.Type == parent.Type { + return true + } + if child.Type == "char" && parent.Type == "varchar" { + return true + } + if child.Type == "varchar" && parent.Type == "char" { + return true + } + return false + } + colTypeEqualForForeignKey := func(child, parent *sqlparser.ColumnType) bool { + if colTypeCompatibleForForeignKey(child, parent) && + child.Unsigned == parent.Unsigned && + child.Zerofill == parent.Zerofill && + sqlparser.Equals.ColumnCharset(child.Charset, parent.Charset) && + child.Options.Collate == parent.Options.Collate && + sqlparser.Equals.SliceOfString(child.EnumValues, parent.EnumValues) { + // Complete identify (other than precision which is ignored) + return true + } + return false } // Now validate foreign key columns: @@ -357,7 +436,12 @@ func (s *Schema) normalize() error { continue } referencedTableName := check.ReferenceDefinition.ReferencedTable.Name.String() - referencedTable := s.Table(referencedTableName) // we know this exists because we validated foreign key dependencies earlier on + referencedTable := s.Table(referencedTableName) + if referencedTable == nil { + // This can happen because earlier, when we validated existence of reference table, we took note + // of nonexisting tables, but kept on going. + continue + } referencedColumns := map[string]*sqlparser.ColumnDefinition{} for _, col := range referencedTable.CreateTable.TableSpec.Columns { @@ -452,7 +536,7 @@ func (s *Schema) diff(other *Schema, hints *DiffHints) (diffs []EntityDiff, err if _, ok := other.named[e.Name()]; !ok { // other schema does not have the entity // Entities are sorted in foreign key CREATE TABLE valid order (create parents first, then children). - // When issuing DROPs, we want to reverse that order. We want to first frop children, then parents. + // When issuing DROPs, we want to reverse that order. We want to first do it for children, then parents. // Instead of analyzing all relationships again, we just reverse the entire order of DROPs, foreign key // related or not. dropDiffs = append([]EntityDiff{e.Drop()}, dropDiffs...) @@ -613,7 +697,7 @@ func (s *Schema) ToQueries() []string { // ToSQL returns a SQL blob with ordered sequence of queries which can be applied to create the schema func (s *Schema) ToSQL() string { - var buf bytes.Buffer + var buf strings.Builder for _, query := range s.ToQueries() { buf.WriteString(query) buf.WriteString(";\n") @@ -624,7 +708,7 @@ func (s *Schema) ToSQL() string { // copy returns a shallow copy of the schema. This is used when applying changes for example. // applying changes will ensure we copy new entities themselves separately. func (s *Schema) copy() *Schema { - dup := newEmptySchema() + dup := newEmptySchema(s.env) dup.tables = make([]*CreateTableEntity, len(s.tables)) copy(dup.tables, s.tables) dup.views = make([]*CreateViewEntity, len(s.views)) @@ -767,10 +851,10 @@ func (s *Schema) Apply(diffs []EntityDiff) (*Schema, error) { return dup, nil } -// SchemaDiff calulates a rich diff between this schema and the given schema. It builds on top of diff(): +// SchemaDiff calculates a rich diff between this schema and the given schema. It builds on top of diff(): // on top of the list of diffs that can take this schema into the given schema, this function also // evaluates the dependencies between those diffs, if any, and the resulting SchemaDiff object offers OrderedDiffs(), -// the safe ordering of diffs that, when appleid sequentially, does not produce any conflicts and keeps schema valid +// the safe ordering of diffs that, when applied sequentially, does not produce any conflicts and keeps schema valid // at each step. func (s *Schema) SchemaDiff(other *Schema, hints *DiffHints) (*SchemaDiff, error) { diffs, err := s.diff(other, hints) @@ -916,12 +1000,31 @@ func (s *Schema) SchemaDiff(other *Schema, hints *DiffHints) (*SchemaDiff, error // No need to handle. Any dependencies will be resolved by any of the other cases } } + + // Check and assign capabilities: + // Reminder: schemadiff assumes a MySQL flavor, so we only check for MySQL capabilities. + if capableOf := capabilities.MySQLVersionCapableOf(s.env.MySQLVersion()); capableOf != nil { + for _, diff := range schemaDiff.UnorderedDiffs() { + switch diff := diff.(type) { + case *AlterTableEntityDiff: + instantDDLCapable, err := AlterTableCapableOfInstantDDL(diff.AlterTable(), diff.from.CreateTable, capableOf) + if err != nil { + return nil, err + } + if instantDDLCapable { + diff.instantDDLCapability = InstantDDLCapabilityPossible + } else { + diff.instantDDLCapability = InstantDDLCapabilityImpossible + } + } + } + } return schemaDiff, nil } func (s *Schema) ValidateViewReferences() error { var errs error - schemaInformation := newDeclarativeSchemaInformation() + schemaInformation := newDeclarativeSchemaInformation(s.env) // Remember that s.Entities() is already ordered by dependency. ie. tables first, then views // that only depend on those tables (or on dual), then 2nd tier views, etc. @@ -988,7 +1091,7 @@ func (s *Schema) getEntityColumnNames(entityName string, schemaInformation *decl case *CreateViewEntity: return s.getViewColumnNames(entity, schemaInformation) } - return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected entity type for %v", entityName) + return nil, &UnsupportedEntityError{Entity: entity.Name(), Statement: entity.Create().CanonicalStatementString()} } // getTableColumnNames returns the names of columns in given table. @@ -1000,10 +1103,8 @@ func (s *Schema) getTableColumnNames(t *CreateTableEntity) (columnNames []*sqlpa } // getViewColumnNames returns the names of aliased columns returned by a given view. -func (s *Schema) getViewColumnNames(v *CreateViewEntity, schemaInformation *declarativeSchemaInformation) ( - columnNames []*sqlparser.IdentifierCI, - err error, -) { +func (s *Schema) getViewColumnNames(v *CreateViewEntity, schemaInformation *declarativeSchemaInformation) ([]*sqlparser.IdentifierCI, error) { + var columnNames []*sqlparser.IdentifierCI for _, node := range v.Select.GetColumns() { switch node := node.(type) { case *sqlparser.StarExpr: @@ -1033,8 +1134,5 @@ func (s *Schema) getViewColumnNames(v *CreateViewEntity, schemaInformation *decl } } - if err != nil { - return nil, err - } return columnNames, nil } diff --git a/go/vt/schemadiff/schema_diff.go b/go/vt/schemadiff/schema_diff.go index 8fef7c29d28..d2f5e012220 100644 --- a/go/vt/schemadiff/schema_diff.go +++ b/go/vt/schemadiff/schema_diff.go @@ -92,7 +92,7 @@ func permutateDiffs(ctx context.Context, diffs []EntityDiff, callback func([]Ent if len(diffs) == 0 { return false, nil } - // Sort by a heristic (DROPs first, ALTERs next, CREATEs last). This ordering is then used first in the permutation + // Sort by a heuristic (DROPs first, ALTERs next, CREATEs last). This ordering is then used first in the permutation // search and serves as seed for the rest of permutations. return permDiff(ctx, diffs, callback, 0) @@ -296,7 +296,7 @@ func (d *SchemaDiff) OrderedDiffs(ctx context.Context) ([]EntityDiff, error) { for i, diff := range d.UnorderedDiffs() { unorderedDiffsMap[diff.CanonicalStatementString()] = i } - // The order of classes in the quivalence relation is, generally speaking, loyal to the order of original diffs. + // The order of classes in the equivalence relation is, generally speaking, loyal to the order of original diffs. for _, class := range d.r.OrderedClasses() { classDiffs := []EntityDiff{} // Which diffs are in this equivalence class? @@ -343,3 +343,25 @@ func (d *SchemaDiff) OrderedDiffs(ctx context.Context) ([]EntityDiff, error) { } return orderedDiffs, nil } + +// InstantDDLCapability returns an overall summary of the ability of the diffs to run with ALGORITHM=INSTANT. +// It is a convenience method, whose logic anyone can reimplement. +func (d *SchemaDiff) InstantDDLCapability() InstantDDLCapability { + // The general logic: we return "InstantDDLCapabilityPossible" if there is one or more diffs that is capable of + // ALGORITHM=INSTANT, and zero or more diffs that are irrelevant, and no diffs that are impossible to run with + // ALGORITHM=INSTANT. + capability := InstantDDLCapabilityIrrelevant + for _, diff := range d.UnorderedDiffs() { + switch diff.InstantDDLCapability() { + case InstantDDLCapabilityUnknown: + return InstantDDLCapabilityUnknown // Early break + case InstantDDLCapabilityImpossible: + return InstantDDLCapabilityImpossible // Early break + case InstantDDLCapabilityPossible: + capability = InstantDDLCapabilityPossible + case InstantDDLCapabilityIrrelevant: + // do nothing + } + } + return capability +} diff --git a/go/vt/schemadiff/schema_diff_test.go b/go/vt/schemadiff/schema_diff_test.go index df7d893356f..4fbc31a6492 100644 --- a/go/vt/schemadiff/schema_diff_test.go +++ b/go/vt/schemadiff/schema_diff_test.go @@ -23,6 +23,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/vtenv" ) func TestPermutations(t *testing.T) { @@ -160,14 +163,15 @@ func TestPermutations(t *testing.T) { }, } hints := &DiffHints{RangeRotationStrategy: RangeRotationDistinctStatements} + env := NewTestEnv() for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - fromSchema, err := NewSchemaFromQueries(tc.fromQueries) + fromSchema, err := NewSchemaFromQueries(env, tc.fromQueries) require.NoError(t, err) require.NotNil(t, fromSchema) - toSchema, err := NewSchemaFromQueries(tc.toQueries) + toSchema, err := NewSchemaFromQueries(env, tc.toQueries) require.NoError(t, err) require.NotNil(t, toSchema) @@ -258,19 +262,22 @@ func TestSchemaDiff(t *testing.T) { } ) tt := []struct { - name string - fromQueries []string - toQueries []string - expectDiffs int - expectDeps int - sequential bool - conflictingDiffs int - entityOrder []string // names of tables/views in expected diff order + name string + fromQueries []string + toQueries []string + expectDiffs int + expectDeps int + sequential bool + conflictingDiffs int + entityOrder []string // names of tables/views in expected diff order + mysqlServerVersion string + instantCapability InstantDDLCapability }{ { - name: "no change", - toQueries: createQueries, - entityOrder: []string{}, + name: "no change", + toQueries: createQueries, + entityOrder: []string{}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "three unrelated changes", @@ -280,8 +287,32 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select 1 from dual", }, - expectDiffs: 3, - entityOrder: []string{"t1", "t2", "v2"}, + expectDiffs: 3, + entityOrder: []string{"t1", "t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, + }, + { + name: "instant DDL possible on 8.0.32", + toQueries: []string{ + "create table t1 (id int primary key, ts timestamp, info int not null);", + "create table t2 (id int primary key, ts timestamp);", + "create view v1 as select id from t1", + }, + expectDiffs: 1, + entityOrder: []string{"t1"}, + instantCapability: InstantDDLCapabilityPossible, + }, + { + name: "instant DDL impossible on 8.0.17", + toQueries: []string{ + "create table t1 (id int primary key, ts timestamp, info int not null);", + "create table t2 (id int primary key, ts timestamp);", + "create view v1 as select id from t1", + }, + mysqlServerVersion: "8.0.17", + expectDiffs: 1, + entityOrder: []string{"t1"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "three unrelated changes 2", @@ -290,8 +321,9 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, v varchar);", "create view v2 as select 1 from dual", }, - expectDiffs: 3, - entityOrder: []string{"v1", "t2", "v2"}, + expectDiffs: 3, + entityOrder: []string{"v1", "t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, // Subsequent { @@ -301,9 +333,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, v varchar, fulltext key ftk1 (v));", "create view v1 as select id from t1", }, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"t2"}, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { // MySQL limitation: you cannot add two FULLTEXT keys in a single statement. `schemadiff` complies @@ -314,10 +347,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, v varchar, fulltext key ftk1 (v), fulltext key ftk2 (v));", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t2", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t2", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add partition", @@ -331,9 +365,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp) partition by range (id) (partition p0 values less than (0), partition p1 values less than (1), partition p2 values less than (2));", "create view v1 as select id from t1", }, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"t2"}, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { // In MySQL, you cannot ALTER TABLE ADD COLUMN ..., ADD PARTITION in a single statement @@ -348,10 +383,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, v varchar) partition by range (id) (partition p0 values less than (0), partition p1 values less than (1), partition p2 values less than (2));", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t2", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t2", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add view", @@ -359,8 +395,9 @@ func TestSchemaDiff(t *testing.T) { createQueries, "create view v2 as select id from t2", ), - expectDiffs: 1, - entityOrder: []string{"v2"}, + expectDiffs: 1, + entityOrder: []string{"v2"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "add view, alter table", @@ -370,9 +407,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select id from t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t2", "v2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "alter view, alter table", @@ -381,10 +419,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp);", "create view v1 as select the_id from t1", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "v1"}, - conflictingDiffs: 2, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "v1"}, + conflictingDiffs: 2, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "alter table, add view", @@ -394,9 +433,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select id, v from t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t2", "v2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "create view depending on 2 tables, alter table", @@ -406,9 +446,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select info, v from t1, t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t2", "v2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "create view depending on 2 tables, alter other table", @@ -420,9 +461,10 @@ func TestSchemaDiff(t *testing.T) { "create view v2 as select info, ts from t1, t2", // "create view v2 as select info, ts from t1, t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "v2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "create view depending on 2 tables, alter both tables", @@ -432,9 +474,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select info, ts from t1, t2", }, - expectDiffs: 3, - expectDeps: 2, - entityOrder: []string{"t1", "t2", "v2"}, + expectDiffs: 3, + expectDeps: 2, + entityOrder: []string{"t1", "t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "alter view depending on 2 tables, uses new column, alter tables", @@ -444,9 +487,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create view v2 as select info, v from t1, t2", }, - expectDiffs: 3, - expectDeps: 2, - entityOrder: []string{"t1", "t2", "v2"}, + expectDiffs: 3, + expectDeps: 2, + entityOrder: []string{"t1", "t2", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "drop view", @@ -454,9 +498,10 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, info int not null);", "create table t2 (id int primary key, ts timestamp);", }, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"v1"}, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"v1"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "drop view, alter dependent table", @@ -464,27 +509,30 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, info int not null, dt datetime);", "create table t2 (id int primary key, ts timestamp);", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"v1", "t1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"v1", "t1"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "drop view, drop dependent table", toQueries: []string{ "create table t2 (id int primary key, ts timestamp);", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"v1", "t1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"v1", "t1"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "drop view, drop unrelated table", toQueries: []string{ "create table t1 (id int primary key, info int not null);", }, - expectDiffs: 2, - expectDeps: 0, - entityOrder: []string{"v1", "t2"}, + expectDiffs: 2, + expectDeps: 0, + entityOrder: []string{"v1", "t2"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "alter view, drop table", @@ -492,9 +540,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp);", "create view v1 as select id from t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"v1", "t1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"v1", "t1"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "alter view, add view", @@ -504,9 +553,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id, info from t1", "create view v2 as select info from v1", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"v1", "v2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"v1", "v2"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "alter view, add view, 2", @@ -516,9 +566,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id, ts from v2", "create view v2 as select id, ts from t2", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"v2", "v1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"v2", "v1"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "alter table, alter view, add view", @@ -528,9 +579,10 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select ts from t2", "create view v2 as select v from t2", }, - expectDiffs: 3, - expectDeps: 2, - entityOrder: []string{"t2", "v1", "v2"}, + expectDiffs: 3, + expectDeps: 2, + entityOrder: []string{"t2", "v1", "v2"}, + instantCapability: InstantDDLCapabilityPossible, }, { name: "alter table, alter view, impossible sequence", @@ -542,9 +594,10 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, newcol int not null);", "create view v1 as select id, newcol from t1", }, - expectDiffs: 2, - expectDeps: 1, - conflictingDiffs: 2, + expectDiffs: 2, + expectDeps: 1, + conflictingDiffs: 2, + instantCapability: InstantDDLCapabilityPossible, }, // FKs @@ -554,8 +607,9 @@ func TestSchemaDiff(t *testing.T) { createQueries, "create table t3 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", ), - expectDiffs: 1, - entityOrder: []string{"t3"}, + expectDiffs: 1, + entityOrder: []string{"t3"}, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "create two tables with fk", @@ -564,10 +618,11 @@ func TestSchemaDiff(t *testing.T) { "create table tp (id int primary key, info int not null);", "create table t3 (id int primary key, ts timestamp, tp_id int, foreign key (tp_id) references tp (id) on delete no action);", ), - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"tp", "t3"}, - sequential: true, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"tp", "t3"}, + sequential: true, + instantCapability: InstantDDLCapabilityIrrelevant, }, { name: "add FK", @@ -576,9 +631,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"t2"}, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add FK pointing to new table", @@ -588,10 +644,11 @@ func TestSchemaDiff(t *testing.T) { "create table tp (id int primary key, info int not null);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"tp", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"tp", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add FK, unrelated alter", @@ -600,9 +657,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add FK, add unrelated column", @@ -611,9 +669,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add FK, alter unrelated column", @@ -622,9 +681,10 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add FK, alter referenced column", @@ -633,10 +693,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id bigint, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add column. create FK table referencing new column", @@ -646,10 +707,11 @@ func TestSchemaDiff(t *testing.T) { "create view v1 as select id from t1", "create table t3 (id int primary key, ts timestamp, t1_p int, foreign key (t1_p) references t1 (p) on delete no action);", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t1", "t3"}, - sequential: true, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t1", "t3"}, + sequential: true, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add column. add FK referencing new column", @@ -658,10 +720,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_p int, foreign key (t1_p) references t1 (p) on delete no action);", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add column. add FK referencing new column, alphabetically desc", @@ -670,21 +733,24 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, p int, key p_idx (p));", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t2", "t1"}, - }, { + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t2", "t1"}, + instantCapability: InstantDDLCapabilityImpossible, + }, + { name: "add index on parent. add FK to index column", toQueries: []string{ "create table t1 (id int primary key, info int not null, key info_idx(info));", "create table t2 (id int primary key, ts timestamp, t1_info int not null, constraint parent_info_fk foreign key (t1_info) references t1 (info));", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add index on parent with existing index. add FK to index column", @@ -698,10 +764,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_info int not null, constraint parent_info_fk foreign key (t1_info) references t1 (info));", "create view v1 as select id from t1", }, - expectDiffs: 2, - expectDeps: 1, - sequential: false, - entityOrder: []string{"t1", "t2"}, + expectDiffs: 2, + expectDeps: 1, + sequential: false, + entityOrder: []string{"t1", "t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "modify fk column types, fail", @@ -713,10 +780,11 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id bigint primary key);", "create table t2 (id int primary key, ts timestamp, t1_id bigint, foreign key (t1_id) references t1 (id) on delete no action);", }, - expectDiffs: 2, - expectDeps: 0, - sequential: false, - conflictingDiffs: 1, + expectDiffs: 2, + expectDeps: 0, + sequential: false, + conflictingDiffs: 1, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add hierarchical constraints", @@ -734,10 +802,11 @@ func TestSchemaDiff(t *testing.T) { "create table t4 (id int primary key, ref int, key ref_idx (ref), foreign key (ref) references t3 (id) on delete no action);", "create table t5 (id int primary key, ref int, key ref_idx (ref), foreign key (ref) references t1 (id) on delete no action);", }, - expectDiffs: 4, - expectDeps: 2, // t2<->t3, t3<->t4 - sequential: false, - entityOrder: []string{"t2", "t3", "t4", "t5"}, + expectDiffs: 4, + expectDeps: 2, // t2<->t3, t3<->t4 + sequential: false, + entityOrder: []string{"t2", "t3", "t4", "t5"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "drop fk", @@ -746,10 +815,11 @@ func TestSchemaDiff(t *testing.T) { "create table t2 (id int primary key, ts timestamp, t1_id int, foreign key (t1_id) references t1 (id) on delete no action);", "create view v1 as select id from t1", }, - toQueries: createQueries, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"t2"}, + toQueries: createQueries, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"t2"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "drop fk, drop table", @@ -760,9 +830,10 @@ func TestSchemaDiff(t *testing.T) { toQueries: []string{ "create table t2 (id int primary key, ts timestamp, t1_id int);", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t2", "t1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t2", "t1"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "drop fk, drop column", @@ -774,9 +845,10 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, info int not null);", "create table t2 (id int primary key, ts timestamp, t1_p int);", }, - expectDiffs: 2, - expectDeps: 1, - entityOrder: []string{"t2", "t1"}, + expectDiffs: 2, + expectDeps: 1, + entityOrder: []string{"t2", "t1"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "reverse fk", @@ -788,9 +860,10 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, p int, key p_idx (p), foreign key (p) references t2 (p) on delete no action);", "create table t2 (id int primary key, p int, key p_idx (p));", }, - expectDiffs: 2, - expectDeps: 2, - entityOrder: []string{"t2", "t1"}, + expectDiffs: 2, + expectDeps: 2, + entityOrder: []string{"t2", "t1"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "add and drop FK, add and drop column, impossible order", @@ -802,10 +875,11 @@ func TestSchemaDiff(t *testing.T) { "create table t1 (id int primary key, q int, key q_idx (q));", "create table t2 (id int primary key, q int, key q_idx (q), foreign key (q) references t1 (q) on delete no action);", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - conflictingDiffs: 2, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + conflictingDiffs: 2, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "two identical foreign keys in table, drop one", @@ -817,9 +891,10 @@ func TestSchemaDiff(t *testing.T) { "create table parent (id int primary key)", "create table t1 (id int primary key, i int, key i_idex (i), constraint f1 foreign key (i) references parent(id))", }, - expectDiffs: 1, - expectDeps: 0, - entityOrder: []string{"t1"}, + expectDiffs: 1, + expectDeps: 0, + entityOrder: []string{"t1"}, + instantCapability: InstantDDLCapabilityImpossible, }, { name: "test", @@ -830,27 +905,36 @@ func TestSchemaDiff(t *testing.T) { "CREATE TABLE t1 (id bigint NOT NULL, name varchar(255), PRIMARY KEY (id), KEY idx_name (name))", "CREATE TABLE t3 (id bigint NOT NULL, name varchar(255), t1_id bigint, PRIMARY KEY (id), KEY t1_id (t1_id), KEY nameidx (name), CONSTRAINT t3_ibfk_1 FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT t3_ibfk_2 FOREIGN KEY (name) REFERENCES t1 (name) ON DELETE CASCADE ON UPDATE CASCADE)", }, - expectDiffs: 2, - expectDeps: 1, - sequential: true, - entityOrder: []string{"t1", "t3"}, + expectDiffs: 2, + expectDeps: 1, + sequential: true, + entityOrder: []string{"t1", "t3"}, + instantCapability: InstantDDLCapabilityImpossible, }, } - hints := &DiffHints{RangeRotationStrategy: RangeRotationDistinctStatements} + baseHints := &DiffHints{ + RangeRotationStrategy: RangeRotationDistinctStatements, + } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { + vtenv, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: tc.mysqlServerVersion, + }) + require.NoError(t, err) + env := NewEnv(vtenv, collations.CollationUtf8mb4ID) + if tc.fromQueries == nil { tc.fromQueries = createQueries } - fromSchema, err := NewSchemaFromQueries(tc.fromQueries) + fromSchema, err := NewSchemaFromQueries(env, tc.fromQueries) require.NoError(t, err) require.NotNil(t, fromSchema) - toSchema, err := NewSchemaFromQueries(tc.toQueries) + toSchema, err := NewSchemaFromQueries(env, tc.toQueries) require.NoError(t, err) require.NotNil(t, toSchema) - schemaDiff, err := fromSchema.SchemaDiff(toSchema, hints) + schemaDiff, err := fromSchema.SchemaDiff(toSchema, baseHints) require.NoError(t, err) allDiffs := schemaDiff.UnorderedDiffs() @@ -902,6 +986,9 @@ func TestSchemaDiff(t *testing.T) { _, err := schemaDiff.r.ElementClass(s) require.NoError(t, err) } + instantCapability := schemaDiff.InstantDDLCapability() + assert.Equal(t, tc.instantCapability, instantCapability) }) + } } diff --git a/go/vt/schemadiff/schema_test.go b/go/vt/schemadiff/schema_test.go index 79bf44117e2..0913b9a1165 100644 --- a/go/vt/schemadiff/schema_test.go +++ b/go/vt/schemadiff/schema_test.go @@ -17,6 +17,7 @@ limitations under the License. package schemadiff import ( + "errors" "fmt" "math/rand" "sort" @@ -27,7 +28,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/errors" + vterrors "vitess.io/vitess/go/errors" "vitess.io/vitess/go/vt/sqlparser" ) @@ -83,7 +84,7 @@ var schemaTestExpectSortedViewNames = []string{ var schemaTestToSQL = "CREATE TABLE `t1` (\n\t`id` int\n);\nCREATE TABLE `t2` (\n\t`id` int\n);\nCREATE TABLE `t3` (\n\t`id` int,\n\t`type` enum('foo', 'bar') NOT NULL DEFAULT 'foo'\n);\nCREATE TABLE `t5` (\n\t`id` int\n);\nCREATE VIEW `v0` AS SELECT 1 FROM `dual`;\nCREATE VIEW `v3` AS SELECT *, `id` + 1 AS `id_plus`, `id` + 2 FROM `t3` AS `t3`;\nCREATE VIEW `v9` AS SELECT 1 FROM `dual`;\nCREATE VIEW `v1` AS SELECT * FROM `v3`;\nCREATE VIEW `v2` AS SELECT * FROM `v3`, `t2`;\nCREATE VIEW `v4` AS SELECT * FROM `t2` AS `something_else`, `v3`;\nCREATE VIEW `v5` AS SELECT * FROM `t1`, (SELECT * FROM `v3`) AS `some_alias`;\nCREATE VIEW `v6` AS SELECT * FROM `v4`;\n" func TestNewSchemaFromQueries(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -93,7 +94,7 @@ func TestNewSchemaFromQueries(t *testing.T) { } func TestNewSchemaFromSQL(t *testing.T) { - schema, err := NewSchemaFromSQL(strings.Join(schemaTestCreateQueries, ";")) + schema, err := NewSchemaFromSQL(NewTestEnv(), strings.Join(schemaTestCreateQueries, ";")) assert.NoError(t, err) require.NotNil(t, schema) @@ -107,7 +108,7 @@ func TestNewSchemaFromQueriesWithDuplicate(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v2 as select * from v1, t2", ) - _, err := NewSchemaFromQueries(queries) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ApplyDuplicateEntityError{Entity: "v2"}).Error()) } @@ -117,7 +118,7 @@ func TestNewSchemaFromQueriesUnresolved(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v7 as select * from v8, t2", ) - schema, err := NewSchemaFromQueries(queries) + schema, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) v := schema.sorted[len(schema.sorted)-1] @@ -130,7 +131,7 @@ func TestNewSchemaFromQueriesUnresolvedAlias(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v7 as select * from something_else as t1, t2", ) - _, err := NewSchemaFromQueries(queries) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) } @@ -140,7 +141,7 @@ func TestNewSchemaFromQueriesViewFromDual(t *testing.T) { queries := []string{ "create view v20 as select 1 from dual", } - _, err := NewSchemaFromQueries(queries) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.NoError(t, err) } @@ -149,7 +150,7 @@ func TestNewSchemaFromQueriesViewFromDualImplicit(t *testing.T) { queries := []string{ "create view v20 as select 1", } - _, err := NewSchemaFromQueries(queries) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.NoError(t, err) } @@ -159,14 +160,14 @@ func TestNewSchemaFromQueriesLoop(t *testing.T) { "create view v7 as select * from v8, t2", "create view v8 as select * from t1, v7", ) - _, err := NewSchemaFromQueries(queries) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) require.Error(t, err) - err = errors.UnwrapFirst(err) + err = vterrors.UnwrapFirst(err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) } func TestToSQL(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -175,7 +176,7 @@ func TestToSQL(t *testing.T) { } func TestCopy(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -222,7 +223,7 @@ func TestGetViewDependentTableNames(t *testing.T) { } for _, ts := range tt { t.Run(ts.view, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(ts.view) + stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.view) require.NoError(t, err) createView, ok := stmt.(*sqlparser.CreateView) require.True(t, ok) @@ -262,7 +263,7 @@ func TestGetForeignKeyParentTableNames(t *testing.T) { } for _, ts := range tt { t.Run(ts.table, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(ts.table) + stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.table) require.NoError(t, err) createTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) @@ -298,7 +299,7 @@ func TestTableForeignKeyOrdering(t *testing.T) { "v13", "v09", } - schema, err := NewSchemaFromQueries(fkQueries) + schema, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) require.NoError(t, err) assert.NotNil(t, schema) @@ -309,8 +310,9 @@ func TestTableForeignKeyOrdering(t *testing.T) { func TestInvalidSchema(t *testing.T) { tt := []struct { - schema string - expectErr error + schema string + expectErr error + expectLoopTables int }{ { schema: "create table t11 (id int primary key, i int, key ix(i), constraint f11 foreign key (i) references t11(id) on delete restrict)", @@ -331,8 +333,68 @@ func TestInvalidSchema(t *testing.T) { expectErr: &ForeignKeyColumnCountMismatchError{Table: "t11", Constraint: "f11", ColumnCount: 2, ReferencedTable: "t11", ReferencedColumnCount: 1}, }, { - schema: "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12(id) on delete restrict)", - expectErr: &ForeignKeyDependencyUnresolvedError{Table: "t11"}, + schema: "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12 (id) on delete restrict)", + expectErr: &ForeignKeyNonexistentReferencedTableError{Table: "t11", ReferencedTable: "t12"}, + }, + { + schema: "create view v as select 1 as id from dual; create table t11 (id int primary key, i int, constraint fv foreign key (i) references v (id) on delete restrict)", + expectErr: &ForeignKeyReferencesViewError{Table: "t11", ReferencedView: "v"}, + }, + { + // t11 self loop + schema: "create table t11 (id int primary key, i int, constraint f11 foreign key (i) references t11 (id) on delete restrict)", + }, + { + // t12<->t11 + schema: "create table t11 (id int primary key, i int, constraint f11 foreign key (i) references t12 (id) on delete restrict); create table t12 (id int primary key, i int, constraint f12 foreign key (i) references t11 (id) on delete restrict)", + expectErr: errors.Join( + &ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t11"}}, + ), + expectLoopTables: 2, + }, + { + // t10, t12<->t11 + schema: "create table t10(id int primary key); create table t11 (id int primary key, i int, constraint f11 foreign key (i) references t12 (id) on delete restrict); create table t12 (id int primary key, i int, constraint f12 foreign key (i) references t11 (id) on delete restrict)", + expectErr: errors.Join( + &ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t11"}}, + ), + expectLoopTables: 2, + }, + { + // t10, t12<->t11<-t13 + schema: "create table t10(id int primary key); create table t11 (id int primary key, i int, constraint f11 foreign key (i) references t12 (id) on delete restrict); create table t12 (id int primary key, i int, constraint f12 foreign key (i) references t11 (id) on delete restrict); create table t13 (id int primary key, i int, constraint f13 foreign key (i) references t11 (id) on delete restrict)", + expectErr: errors.Join( + &ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t13", Loop: []string{"t11", "t12", "t11"}}, + ), + expectLoopTables: 3, + }, + { + // t10 + // ^ + // | + //t12<->t11<-t13 + schema: "create table t10(id int primary key); create table t11 (id int primary key, i int, i10 int, constraint f11 foreign key (i) references t12 (id) on delete restrict, constraint f1110 foreign key (i10) references t10 (id) on delete restrict); create table t12 (id int primary key, i int, constraint f12 foreign key (i) references t11 (id) on delete restrict); create table t13 (id int primary key, i int, constraint f13 foreign key (i) references t11 (id) on delete restrict)", + expectErr: errors.Join( + &ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t13", Loop: []string{"t11", "t12", "t11"}}, + ), + expectLoopTables: 3, + }, + { + // t10, t12<->t11<-t13<-t14 + schema: "create table t10(id int primary key); create table t11 (id int primary key, i int, i10 int, constraint f11 foreign key (i) references t12 (id) on delete restrict, constraint f1110 foreign key (i10) references t10 (id) on delete restrict); create table t12 (id int primary key, i int, constraint f12 foreign key (i) references t11 (id) on delete restrict); create table t13 (id int primary key, i int, constraint f13 foreign key (i) references t11 (id) on delete restrict); create table t14 (id int primary key, i int, constraint f14 foreign key (i) references t13 (id) on delete restrict)", + expectErr: errors.Join( + &ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t13", Loop: []string{"t11", "t12", "t11"}}, + &ForeignKeyLoopError{Table: "t14", Loop: []string{"t11", "t12", "t11"}}, + ), + expectLoopTables: 4, }, { schema: "create table t11 (id int primary key, i int, key ix(i), constraint f11 foreign key (i) references t11(id2) on delete restrict)", @@ -366,21 +428,42 @@ func TestInvalidSchema(t *testing.T) { // InnoDB allows different string length schema: "create table t10(id varchar(50) primary key); create table t11 (id int primary key, i varchar(100), key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", }, + { + // explicit charset/collation + schema: "create table t10(id varchar(50) charset utf8mb4 collate utf8mb4_0900_ai_ci primary key); create table t11 (id int primary key, i varchar(100) charset utf8mb4 collate utf8mb4_0900_ai_ci, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", + }, + { + // allowed: varchar->char + schema: "create table t10(id varchar(50) charset utf8mb4 collate utf8mb4_0900_ai_ci primary key); create table t11 (id int primary key, i char(100) charset utf8mb4 collate utf8mb4_0900_ai_ci, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", + }, + { + // allowed: char->varchar + schema: "create table t10(id char(50) charset utf8mb4 collate utf8mb4_0900_ai_ci primary key); create table t11 (id int primary key, i varchar(50) charset utf8mb4 collate utf8mb4_0900_ai_ci, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", + }, { schema: "create table t10(id varchar(50) charset utf8mb3 primary key); create table t11 (id int primary key, i varchar(100) charset utf8mb4, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", expectErr: &ForeignKeyColumnTypeMismatchError{Table: "t11", Constraint: "f10", Column: "i", ReferencedTable: "t10", ReferencedColumn: "id"}, }, + { + schema: "create table t10(id varchar(50) charset utf8mb4 collate utf8mb4_0900_ai_ci primary key); create table t11 (id int primary key, i varchar(100) charset utf8mb4 collate utf8mb4_general_ci, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", + expectErr: &ForeignKeyColumnTypeMismatchError{Table: "t11", Constraint: "f10", Column: "i", ReferencedTable: "t10", ReferencedColumn: "id"}, + }, + { + schema: "create table t10(id VARCHAR(50) charset utf8mb4 collate utf8mb4_0900_ai_ci primary key); create table t11 (id int primary key, i VARCHAR(100) charset utf8mb4 collate utf8mb4_general_ci, key ix(i), constraint f10 foreign key (i) references t10(id) on delete restrict)", + expectErr: &ForeignKeyColumnTypeMismatchError{Table: "t11", Constraint: "f10", Column: "i", ReferencedTable: "t10", ReferencedColumn: "id"}, + }, } for _, ts := range tt { t.Run(ts.schema, func(t *testing.T) { - _, err := NewSchemaFromSQL(ts.schema) + s, err := NewSchemaFromSQL(NewTestEnv(), ts.schema) if ts.expectErr == nil { assert.NoError(t, err) } else { assert.Error(t, err) assert.EqualError(t, err, ts.expectErr.Error()) } + assert.Equal(t, ts.expectLoopTables, len(s.foreignKeyLoopMap)) }) } } @@ -388,12 +471,21 @@ func TestInvalidSchema(t *testing.T) { func TestInvalidTableForeignKeyReference(t *testing.T) { { fkQueries := []string{ + "create table t10 (id int primary key)", "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12(id) on delete restrict)", "create table t15(id int, primary key(id))", } - _, err := NewSchemaFromQueries(fkQueries) + s, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) assert.Error(t, err) - assert.EqualError(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t11"}).Error()) + // Even though there's errors, we still expect the schema to have been created. + assert.NotNil(t, s) + // Even though t11 caused an error, we still expect the schema to have parsed all tables. + assert.Equal(t, 3, len(s.Entities())) + t11 := s.Table("t11") + assert.NotNil(t, t11) + // validate t11 table definition is complete, even though it was invalid. + assert.Equal(t, "create table t11 (\n\tid int,\n\ti int,\n\tprimary key (id),\n\tkey f12 (i),\n\tconstraint f12 foreign key (i) references t12 (id) on delete restrict\n)", t11.Create().StatementString()) + assert.EqualError(t, err, (&ForeignKeyNonexistentReferencedTableError{Table: "t11", ReferencedTable: "t12"}).Error()) } { fkQueries := []string{ @@ -401,9 +493,35 @@ func TestInvalidTableForeignKeyReference(t *testing.T) { "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12(id) on delete restrict)", "create table t12 (id int primary key, i int, constraint f13 foreign key (i) references t13(id) on delete restrict)", } - _, err := NewSchemaFromQueries(fkQueries) + _, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) + assert.Error(t, err) + assert.ErrorContains(t, err, (&ForeignKeyLoopError{Table: "t11", Loop: []string{"t11", "t12", "t13", "t11"}}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyLoopError{Table: "t12", Loop: []string{"t11", "t12", "t13", "t11"}}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyLoopError{Table: "t13", Loop: []string{"t11", "t12", "t13", "t11"}}).Error()) + } + { + fkQueries := []string{ + "create table t13 (id int primary key, i int, constraint f11 foreign key (i) references t11(id) on delete restrict)", + "create table t11 (id int primary key, i int, constraint f0 foreign key (i) references t0(id) on delete restrict)", + "create table t12 (id int primary key, i int, constraint f13 foreign key (i) references t13(id) on delete restrict)", + } + _, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) + assert.Error(t, err) + assert.ErrorContains(t, err, (&ForeignKeyNonexistentReferencedTableError{Table: "t11", ReferencedTable: "t0"}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t12"}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t13"}).Error()) + } + { + fkQueries := []string{ + "create table t13 (id int primary key, i int, constraint f11 foreign key (i) references t11(id) on delete restrict, constraint f12 foreign key (i) references t12(id) on delete restrict)", + "create table t11 (id int primary key, i int, constraint f0 foreign key (i) references t0(id) on delete restrict)", + "create table t12 (id int primary key, i int, constraint f13 foreign key (i) references t13(id) on delete restrict)", + } + _, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) assert.Error(t, err) - assert.EqualError(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t11"}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyNonexistentReferencedTableError{Table: "t11", ReferencedTable: "t0"}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyLoopError{Table: "t12", Loop: []string{"t12", "t13", "t12"}}).Error()) + assert.ErrorContains(t, err, (&ForeignKeyLoopError{Table: "t13", Loop: []string{"t12", "t13", "t12"}}).Error()) } } @@ -424,7 +542,7 @@ func TestGetEntityColumnNames(t *testing.T) { "create view vb as select *, now() from v8", } - schema, err := NewSchemaFromQueries(queries) + schema, err := NewSchemaFromQueries(NewTestEnv(), queries) require.NoError(t, err) require.NotNil(t, schema) @@ -446,7 +564,7 @@ func TestGetEntityColumnNames(t *testing.T) { entities := schema.Entities() require.Equal(t, len(entities), len(expectedColNames)) - tcmap := newDeclarativeSchemaInformation() + tcmap := newDeclarativeSchemaInformation(NewTestEnv()) // we test by order of dependency: for _, e := range entities { tbl := e.Name() @@ -702,13 +820,13 @@ func TestViewReferences(t *testing.T) { } for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - schema, err := NewSchemaFromQueries(ts.queries) + schema, err := NewSchemaFromQueries(NewTestEnv(), ts.queries) if ts.expectErr == nil { require.NoError(t, err) require.NotNil(t, schema) } else { require.Error(t, err) - err = errors.UnwrapFirst(err) + err = vterrors.UnwrapFirst(err) require.Equal(t, ts.expectErr, err, "received error: %v", err) } }) @@ -716,7 +834,7 @@ func TestViewReferences(t *testing.T) { } // TestMassiveSchema loads thousands of tables into one schema, and thousands of tables, some of which are different, into another schema. -// It compares the two shemas. +// It compares the two schemas. // The objective of this test is to verify that execution time is _reasonable_. Since this will run in GitHub CI, which is very slow, we allow // for 1 minute total for all operations. func TestMassiveSchema(t *testing.T) { @@ -794,9 +912,9 @@ func TestMassiveSchema(t *testing.T) { queries1 = append(queries1, query) tableNames[tableName] = true } - schema0, err = NewSchemaFromQueries(queries0) + schema0, err = NewSchemaFromQueries(NewTestEnv(), queries0) require.NoError(t, err) - schema1, err = NewSchemaFromQueries(queries1) + schema1, err = NewSchemaFromQueries(NewTestEnv(), queries1) require.NoError(t, err) require.Equal(t, countModifiedTables, modifyTables) diff --git a/go/vt/schemadiff/semantics.go b/go/vt/schemadiff/semantics.go index da9c6b1e2a9..ee175a37966 100644 --- a/go/vt/schemadiff/semantics.go +++ b/go/vt/schemadiff/semantics.go @@ -22,6 +22,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -34,15 +35,17 @@ var semanticKS = &vindexes.Keyspace{ var _ semantics.SchemaInformation = (*declarativeSchemaInformation)(nil) -// declarativeSchemaInformation is a utility wrapper arounf FakeSI, and adds a few utility functions +// declarativeSchemaInformation is a utility wrapper around FakeSI, and adds a few utility functions // to make it more simple and accessible to schemadiff's logic. type declarativeSchemaInformation struct { Tables map[string]*vindexes.Table + env *Environment } -func newDeclarativeSchemaInformation() *declarativeSchemaInformation { +func newDeclarativeSchemaInformation(env *Environment) *declarativeSchemaInformation { return &declarativeSchemaInformation{ Tables: make(map[string]*vindexes.Table), + env: env, } } @@ -53,7 +56,11 @@ func (si *declarativeSchemaInformation) FindTableOrVindex(tablename sqlparser.Ta } func (si *declarativeSchemaInformation) ConnCollation() collations.ID { - return 45 + return si.env.DefaultColl +} + +func (si *declarativeSchemaInformation) Environment() *vtenv.Environment { + return si.env.Environment } func (si *declarativeSchemaInformation) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { @@ -64,6 +71,10 @@ func (si *declarativeSchemaInformation) KeyspaceError(keyspace string) error { return nil } +func (si *declarativeSchemaInformation) GetForeignKeyChecksState() *bool { + return nil +} + // addTable adds a fake table with an empty column list func (si *declarativeSchemaInformation) addTable(tableName string) { tbl := &vindexes.Table{ diff --git a/go/vt/schemadiff/table.go b/go/vt/schemadiff/table.go index 3f256889721..aab697c2bf0 100644 --- a/go/vt/schemadiff/table.go +++ b/go/vt/schemadiff/table.go @@ -25,12 +25,15 @@ import ( golcs "github.com/yudai/golcs" - "vitess.io/vitess/go/mysql/collations/colldata" - - "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/ptr" "vitess.io/vitess/go/vt/sqlparser" ) +type charsetCollate struct { + charset string + collate string +} + type AlterTableEntityDiff struct { from *CreateTableEntity to *CreateTableEntity @@ -38,6 +41,7 @@ type AlterTableEntityDiff struct { canonicalStatementString string subsequentDiff *AlterTableEntityDiff + instantDDLCapability InstantDDLCapability } // IsEmpty implements EntityDiff @@ -121,6 +125,14 @@ func (d *AlterTableEntityDiff) addSubsequentDiff(diff *AlterTableEntityDiff) { } } +// InstantDDLCapability implements EntityDiff +func (d *AlterTableEntityDiff) InstantDDLCapability() InstantDDLCapability { + if d == nil { + return InstantDDLCapabilityUnknown + } + return d.instantDDLCapability +} + type CreateTableEntityDiff struct { to *CreateTableEntity createTable *sqlparser.CreateTable @@ -189,6 +201,11 @@ func (d *CreateTableEntityDiff) SubsequentDiff() EntityDiff { func (d *CreateTableEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *CreateTableEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + type DropTableEntityDiff struct { from *CreateTableEntity dropTable *sqlparser.DropTable @@ -257,6 +274,11 @@ func (d *DropTableEntityDiff) SubsequentDiff() EntityDiff { func (d *DropTableEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *DropTableEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + type RenameTableEntityDiff struct { from *CreateTableEntity to *CreateTableEntity @@ -326,16 +348,22 @@ func (d *RenameTableEntityDiff) SubsequentDiff() EntityDiff { func (d *RenameTableEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *RenameTableEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + // CreateTableEntity stands for a TABLE construct. It contains the table's CREATE statement. type CreateTableEntity struct { *sqlparser.CreateTable + Env *Environment } -func NewCreateTableEntity(c *sqlparser.CreateTable) (*CreateTableEntity, error) { +func NewCreateTableEntity(env *Environment, c *sqlparser.CreateTable) (*CreateTableEntity, error) { if !c.IsFullyParsed() { return nil, &NotFullyParsedError{Entity: c.Table.Name.String(), Statement: sqlparser.CanonicalString(c)} } - entity := &CreateTableEntity{CreateTable: c} + entity := &CreateTableEntity{CreateTable: c, Env: env} entity.normalize() return entity, nil } @@ -362,12 +390,12 @@ func (c *CreateTableEntity) normalizeTableOptions() { switch opt.Name { case "charset": opt.String = strings.ToLower(opt.String) - if charset, ok := collationEnv.CharsetAlias(opt.String); ok { + if charset, ok := c.Env.CollationEnv().CharsetAlias(opt.String); ok { opt.String = charset } case "collate": opt.String = strings.ToLower(opt.String) - if collation, ok := collationEnv.CollationAlias(opt.String); ok { + if collation, ok := c.Env.CollationEnv().CollationAlias(opt.String); ok { opt.String = collation } case "engine": @@ -387,7 +415,7 @@ func (c *CreateTableEntity) GetCharset() string { for _, opt := range c.CreateTable.TableSpec.Options { if strings.ToLower(opt.Name) == "charset" { opt.String = strings.ToLower(opt.String) - if charsetName, ok := collationEnv.CharsetAlias(opt.String); ok { + if charsetName, ok := c.Env.CollationEnv().CharsetAlias(opt.String); ok { return charsetName } return opt.String @@ -402,7 +430,7 @@ func (c *CreateTableEntity) GetCollation() string { for _, opt := range c.CreateTable.TableSpec.Options { if strings.ToLower(opt.Name) == "collate" { opt.String = strings.ToLower(opt.String) - if collationName, ok := collationEnv.CollationAlias(opt.String); ok { + if collationName, ok := c.Env.CollationEnv().CollationAlias(opt.String); ok { return collationName } return opt.String @@ -412,45 +440,27 @@ func (c *CreateTableEntity) GetCollation() string { } func (c *CreateTableEntity) Clone() Entity { - return &CreateTableEntity{CreateTable: sqlparser.CloneRefOfCreateTable(c.CreateTable)} + return &CreateTableEntity{CreateTable: sqlparser.CloneRefOfCreateTable(c.CreateTable), Env: c.Env} } -// Right now we assume MySQL 8.0 for the collation normalization handling. -const mysqlCollationVersion = "8.0.0" - -var collationEnv = collations.NewEnvironment(mysqlCollationVersion) - -func defaultCharset() string { - collation := colldata.Lookup(collations.ID(collationEnv.DefaultConnectionCharset())) - if collation == nil { - return "" +func getTableCharsetCollate(env *Environment, tableOptions *sqlparser.TableOptions) *charsetCollate { + cc := &charsetCollate{ + charset: env.CollationEnv().LookupCharsetName(env.DefaultColl), + collate: env.CollationEnv().LookupName(env.DefaultColl), } - return collation.Charset().Name() -} - -func defaultCharsetCollation(charset string) string { - collation := collationEnv.DefaultCollationForCharset(charset) - if collation == collations.Unknown { - return "" + for _, option := range *tableOptions { + if strings.EqualFold(option.Name, "charset") { + cc.charset = option.String + } + if strings.EqualFold(option.Name, "collate") { + cc.collate = option.String + } } - return collationEnv.LookupName(collation) + return cc } func (c *CreateTableEntity) normalizeColumnOptions() { - tableCharset := defaultCharset() - tableCollation := "" - for _, option := range c.CreateTable.TableSpec.Options { - switch strings.ToUpper(option.Name) { - case "CHARSET": - tableCharset = option.String - case "COLLATE": - tableCollation = option.String - } - } - defaultCollation := defaultCharsetCollation(tableCharset) - if tableCollation == "" { - tableCollation = defaultCollation - } + cc := getTableCharsetCollate(c.Env, &c.CreateTable.TableSpec.Options) for _, col := range c.CreateTable.TableSpec.Columns { if col.Type.Options == nil { @@ -497,13 +507,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { // Map any charset aliases to the real charset. This applies mainly right // now to utf8 being an alias for utf8mb3. - if charset, ok := collationEnv.CharsetAlias(col.Type.Charset.Name); ok { + if charset, ok := c.Env.CollationEnv().CharsetAlias(col.Type.Charset.Name); ok { col.Type.Charset.Name = charset } // Map any collation aliases to the real collation. This applies mainly right // now to utf8 being an alias for utf8mb3 collations. - if collation, ok := collationEnv.CollationAlias(col.Type.Options.Collate); ok { + if collation, ok := c.Env.CollationEnv().CollationAlias(col.Type.Options.Collate); ok { col.Type.Options.Collate = collation } @@ -521,10 +531,7 @@ func (c *CreateTableEntity) normalizeColumnOptions() { // "show create table" reports it as a tinyint(1). if col.Type.Type == "boolean" { col.Type.Type = "tinyint" - col.Type.Length = &sqlparser.Literal{ - Type: sqlparser.IntVal, - Val: "1", - } + col.Type.Length = ptr.Of(1) if col.Type.Options.Default != nil { val, ok := col.Type.Options.Default.(sqlparser.BoolVal) @@ -553,16 +560,14 @@ func (c *CreateTableEntity) normalizeColumnOptions() { col.Type.Type = "double" } - if col.Type.Length != nil && col.Type.Scale == nil && col.Type.Length.Type == sqlparser.IntVal { - if l, err := strconv.ParseInt(col.Type.Length.Val, 10, 64); err == nil { - // See https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html, but the docs are - // subtly wrong. We use a float for a precision of 24, not a double as the documentation - // mentioned. Validated against the actual behavior of MySQL. - if l <= 24 { - col.Type.Type = "float" - } else { - col.Type.Type = "double" - } + if col.Type.Length != nil && col.Type.Scale == nil { + // See https://dev.mysql.com/doc/refman/8.0/en/floating-point-types.html, but the docs are + // subtly wrong. We use a float for a precision of 24, not a double as the documentation + // mentioned. Validated against the actual behavior of MySQL. + if *col.Type.Length <= 24 { + col.Type.Type = "float" + } else { + col.Type.Type = "double" } col.Type.Length = nil } @@ -571,13 +576,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { if _, ok := charsetTypes[col.Type.Type]; ok { // If the charset is explicitly configured and it mismatches, we don't normalize // anything for charsets or collations and move on. - if col.Type.Charset.Name != "" && col.Type.Charset.Name != tableCharset { + if col.Type.Charset.Name != "" && col.Type.Charset.Name != cc.charset { continue } // Alright, first check if both charset and collation are the same as // the table level options, in that case we can remove both since that's equivalent. - if col.Type.Charset.Name == tableCharset && col.Type.Options.Collate == tableCollation { + if col.Type.Charset.Name == cc.charset && col.Type.Options.Collate == cc.collate { col.Type.Charset.Name = "" col.Type.Options.Collate = "" } @@ -595,13 +600,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { if col.Type.Charset.Name != "" { col.Type.Charset.Name = "" if col.Type.Options.Collate == "" { - col.Type.Options.Collate = defaultCollation + col.Type.Options.Collate = c.Env.CollationEnv().LookupName(c.Env.DefaultColl) } } // We now have one case left, which is when we have set a collation but it's the same // as the table level. In that case, we can clear it since that is equivalent. - if col.Type.Options.Collate == tableCollation { + if col.Type.Options.Collate == cc.collate { col.Type.Options.Collate = "" } } @@ -618,7 +623,7 @@ func (c *CreateTableEntity) normalizeIndexOptions() { } func isBool(colType *sqlparser.ColumnType) bool { - return colType.Type == sqlparser.KeywordString(sqlparser.TINYINT) && colType.Length != nil && sqlparser.CanonicalString(colType.Length) == "1" + return colType.Type == sqlparser.KeywordString(sqlparser.TINYINT) && colType.Length != nil && *colType.Length == 1 } func (c *CreateTableEntity) normalizePartitionOptions() { @@ -826,21 +831,21 @@ func (c *CreateTableEntity) TableDiff(other *CreateTableEntity, hints *DiffHints alterTable.Table.Qualifier = other.Table.Qualifier } - diffedTableCharset := "" + t1cc := getTableCharsetCollate(c.Env, &c.CreateTable.TableSpec.Options) + t2cc := getTableCharsetCollate(c.Env, &other.CreateTable.TableSpec.Options) + var parentAlterTableEntityDiff *AlterTableEntityDiff var partitionSpecs []*sqlparser.PartitionSpec var superfluousFulltextKeys []*sqlparser.AddIndexDefinition - { - t1Options := c.CreateTable.TableSpec.Options - t2Options := other.CreateTable.TableSpec.Options - diffedTableCharset = c.diffTableCharset(t1Options, t2Options) - } { // diff columns // ordered columns for both tables: + t1Columns := c.CreateTable.TableSpec.Columns t2Columns := other.CreateTable.TableSpec.Columns - c.diffColumns(alterTable, t1Columns, t2Columns, hints, diffedTableCharset != "") + if err := c.diffColumns(alterTable, t1Columns, t2Columns, hints, t1cc, t2cc); err != nil { + return nil, err + } } { // diff keys @@ -927,21 +932,11 @@ func (c *CreateTableEntity) TableDiff(other *CreateTableEntity, hints *DiffHints } func (c *CreateTableEntity) diffTableCharset( - t1Options sqlparser.TableOptions, - t2Options sqlparser.TableOptions, + t1cc *charsetCollate, + t2cc *charsetCollate, ) string { - getcharset := func(options sqlparser.TableOptions) string { - for _, option := range options { - if strings.EqualFold(option.Name, "CHARSET") { - return option.String - } - } - return "" - } - t1Charset := getcharset(t1Options) - t2Charset := getcharset(t2Options) - if t1Charset != t2Charset { - return t2Charset + if t1cc.charset != t2cc.charset { + return t2cc.charset } return "" } @@ -1019,7 +1014,7 @@ func (c *CreateTableEntity) diffOptions(alterTable *sqlparser.AlterTable, case "CHARSET": switch hints.TableCharsetCollateStrategy { case TableCharsetCollateStrict: - tableOption = &sqlparser.TableOption{String: ""} + tableOption = &sqlparser.TableOption{Name: "CHARSET", String: c.Env.CollationEnv().LookupCharsetName(c.Env.DefaultColl), CaseSensitive: true} // in all other strategies we ignore the charset } case "CHECKSUM": @@ -1159,7 +1154,7 @@ func (c *CreateTableEntity) diffOptions(alterTable *sqlparser.AlterTable, // rangePartitionsAddedRemoved returns true when: // - both table partitions are RANGE type -// - there is exactly one consequitive non-empty shared sequence of partitions (same names, same range values, in same order) +// - there is exactly one consecutive non-empty shared sequence of partitions (same names, same range values, in same order) // - table1 may have non-empty list of partitions _preceding_ this sequence, and table2 may not // - table2 may have non-empty list of partitions _following_ this sequence, and table1 may not func (c *CreateTableEntity) isRangePartitionsRotation( @@ -1189,7 +1184,7 @@ func (c *CreateTableEntity) isRangePartitionsRotation( definitions1 = definitions1[1:] } if len(definitions1) == 0 { - // We've exhaused definition1 trying to find a shared partition with definitions2. Nothing found. + // We've exhausted definition1 trying to find a shared partition with definitions2. Nothing found. // so there is no shared sequence between the two tables. return false, nil, nil } @@ -1251,9 +1246,9 @@ func (c *CreateTableEntity) diffPartitions(alterTable *sqlparser.AlterTable, return nil, nil default: // partitioning was changed - // For most cases, we produce a complete re-partitioing schema: we don't try and figure out the minimal + // For most cases, we produce a complete re-partitioning schema: we don't try and figure out the minimal // needed change. For example, maybe the minimal change is to REORGANIZE a specific partition and split - // into two, thus unaffecting the rest of the partitions. But we don't evaluate that, we just set a + // into two, thus not affecting the rest of the partitions. But we don't evaluate that, we just set a // complete new ALTER TABLE ... PARTITION BY statement. // The idea is that it doesn't matter: we're not looking to do optimal in-place ALTERs, we run // Online DDL alters, where we create a new table anyway. Thus, the optimization is meaningless. @@ -1536,8 +1531,9 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, t1Columns []*sqlparser.ColumnDefinition, t2Columns []*sqlparser.ColumnDefinition, hints *DiffHints, - tableCharsetChanged bool, -) { + t1cc *charsetCollate, + t2cc *charsetCollate, +) error { getColumnsMap := func(cols []*sqlparser.ColumnDefinition) map[string]*columnDetails { var prevCol *columnDetails m := map[string]*columnDetails{} @@ -1599,13 +1595,16 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, t2ColEntity := NewColumnDefinitionEntity(t2Col) // check diff between before/after columns: - modifyColumnDiff := t1ColEntity.ColumnDiff(t2ColEntity, hints) + modifyColumnDiff, err := t1ColEntity.ColumnDiff(c.Env, c.Name(), t2ColEntity, t1cc, t2cc, hints) + if err != nil { + return err + } if modifyColumnDiff == nil { // even if there's no apparent change, there can still be implicit changes - // it is possible that the table charset is changed. the column may be some col1 TEXT NOT NULL, possibly in both varsions 1 and 2, - // but implicitly the column has changed its characters set. So we need to explicitly ass a MODIFY COLUMN statement, so that + // it is possible that the table charset is changed. the column may be some col1 TEXT NOT NULL, possibly in both versions 1 and 2, + // but implicitly the column has changed its character set. So we need to explicitly add a MODIFY COLUMN statement, so that // MySQL rebuilds it. - if tableCharsetChanged && t2ColEntity.IsTextual() && t2Col.Type.Charset.Name == "" { + if t1cc.charset != t2cc.charset && t2ColEntity.IsTextual() && t2Col.Type.Charset.Name == "" { modifyColumnDiff = NewModifyColumnDiffByDefinition(t2Col) } } @@ -1666,6 +1665,7 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, for _, c := range addColumns { alterTable.AlterOptions = append(alterTable.AlterOptions, c) } + return nil } func heuristicallyDetectColumnRenames( @@ -1684,7 +1684,7 @@ func heuristicallyDetectColumnRenames( // - the DROP and ADD column definitions are identical other than the column name, and // - the DROPped and ADDded column are both FIRST, or they come AFTER the same column, and // - the DROPped and ADDded column are both last, or they come before the same column - // This v1 chcek therefore cannot handle a case where two successive columns are renamed. + // This v1 check therefore cannot handle a case where two successive columns are renamed. // the problem is complex, and with successive renamed, or drops and adds, it can be // impossible to tell apart different scenarios. // At any case, once we heuristically decide that we found a RENAME, we cancel the DROP, diff --git a/go/vt/schemadiff/table_test.go b/go/vt/schemadiff/table_test.go index e2ef58c1a6f..af72f15776b 100644 --- a/go/vt/schemadiff/table_test.go +++ b/go/vt/schemadiff/table_test.go @@ -28,24 +28,24 @@ import ( func TestCreateTableDiff(t *testing.T) { tt := []struct { - name string - from string - to string - fromName string - toName string - diff string - diffs []string - cdiff string - cdiffs []string - isError bool - errorMsg string - autoinc int - rotation int - fulltext int - colrename int - constraint int - charset int - algorithm int + name string + from string + to string + fromName string + toName string + diff string + diffs []string + cdiff string + cdiffs []string + errorMsg string + autoinc int + rotation int + fulltext int + colrename int + constraint int + charset int + algorithm int + enumreorder int }{ { name: "identical", @@ -300,6 +300,80 @@ func TestCreateTableDiff(t *testing.T) { diff: "alter table t1 modify column c int after a, add column x int after c, add column y int", cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int AFTER `a`, ADD COLUMN `x` int AFTER `c`, ADD COLUMN `y` int", }, + // enum + { + name: "expand enum", + from: "create table t1 (id int primary key, e enum('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e enum('a', 'b', 'c', 'd'))", + diff: "alter table t1 modify column e enum('a', 'b', 'c', 'd')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` enum('a', 'b', 'c', 'd')", + }, + { + name: "truncate enum", + from: "create table t1 (id int primary key, e enum('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e enum('a', 'b'))", + diff: "alter table t1 modify column e enum('a', 'b')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` enum('a', 'b')", + }, + { + name: "rename enum value", + from: "create table t1 (id int primary key, e enum('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e enum('a', 'b', 'd'))", + diff: "alter table t1 modify column e enum('a', 'b', 'd')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` enum('a', 'b', 'd')", + }, + { + name: "reorder enum, fail", + from: "create table t1 (id int primary key, e enum('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e enum('b', 'a', 'c'))", + enumreorder: EnumReorderStrategyReject, + errorMsg: (&EnumValueOrdinalChangedError{Table: "t1", Column: "e", Value: "'a'", Ordinal: 0, NewOrdinal: 1}).Error(), + }, + { + name: "reorder enum, allow", + from: "create table t1 (id int primary key, e enum('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e enum('b', 'a', 'c'))", + diff: "alter table t1 modify column e enum('b', 'a', 'c')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` enum('b', 'a', 'c')", + enumreorder: EnumReorderStrategyAllow, + }, + { + name: "expand set", + from: "create table t1 (id int primary key, e set('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e set('a', 'b', 'c', 'd'))", + diff: "alter table t1 modify column e set('a', 'b', 'c', 'd')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` set('a', 'b', 'c', 'd')", + }, + { + name: "truncate set", + from: "create table t1 (id int primary key, e set('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e set('a', 'b'))", + diff: "alter table t1 modify column e set('a', 'b')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` set('a', 'b')", + }, + { + name: "rename set value", + from: "create table t1 (id int primary key, e set('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e set('a', 'b', 'd'))", + diff: "alter table t1 modify column e set('a', 'b', 'd')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` set('a', 'b', 'd')", + }, + { + name: "reorder set, fail", + from: "create table t1 (id int primary key, e set('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e set('b', 'a', 'c'))", + enumreorder: EnumReorderStrategyReject, + errorMsg: (&EnumValueOrdinalChangedError{Table: "t1", Column: "e", Value: "'a'", Ordinal: 0, NewOrdinal: 1}).Error(), + }, + { + name: "reorder set, allow", + from: "create table t1 (id int primary key, e set('a', 'b', 'c'))", + to: "create table t2 (id int primary key, e set('b', 'a', 'c'))", + diff: "alter table t1 modify column e set('b', 'a', 'c')", + cdiff: "ALTER TABLE `t1` MODIFY COLUMN `e` set('b', 'a', 'c')", + enumreorder: EnumReorderStrategyAllow, + }, + // keys { name: "added key", @@ -1149,6 +1223,27 @@ func TestCreateTableDiff(t *testing.T) { diff: "alter table t modify column t1 varchar(128) not null, modify column t2 varchar(128) not null, modify column t3 tinytext, charset utf8mb4", cdiff: "ALTER TABLE `t` MODIFY COLUMN `t1` varchar(128) NOT NULL, MODIFY COLUMN `t2` varchar(128) NOT NULL, MODIFY COLUMN `t3` tinytext, CHARSET utf8mb4", }, + { + name: "change table collation", + from: "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` COLLATE utf8mb4_0900_bin", + }, + { + name: "change table collation with textual column", + from: "create table t (id int, t varchar(192) not null) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, t varchar(192) not null) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t modify column t varchar(192) not null, collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` MODIFY COLUMN `t` varchar(192) NOT NULL, COLLATE utf8mb4_0900_bin", + }, + { + name: "change table collation with textual column that has collation", + from: "create table t (id int, t varchar(192) not null collate utf8mb4_0900_bin) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, t varchar(192) not null collate utf8mb4_0900_bin) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` COLLATE utf8mb4_0900_bin", + }, { name: "normalized unsigned attribute", from: "create table t1 (id int primary key)", @@ -1265,21 +1360,22 @@ func TestCreateTableDiff(t *testing.T) { }, } standardHints := DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - fromStmt, err := sqlparser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser().ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := fromStmt.(*sqlparser.CreateTable) require.True(t, ok) - toStmt, err := sqlparser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser().ParseStrictDDL(ts.to) require.NoError(t, err) toCreateTable, ok := toStmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := NewCreateTableEntity(fromCreateTable) + c, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) - other, err := NewCreateTableEntity(toCreateTable) + other, err := NewCreateTableEntity(env, toCreateTable) require.NoError(t, err) hints := standardHints @@ -1290,6 +1386,7 @@ func TestCreateTableDiff(t *testing.T) { hints.FullTextKeyStrategy = ts.fulltext hints.TableCharsetCollateStrategy = ts.charset hints.AlterTableAlgorithmStrategy = ts.algorithm + hints.EnumReorderStrategy = ts.enumreorder alter, err := c.Diff(other, &hints) require.Equal(t, len(ts.diffs), len(ts.cdiffs)) @@ -1297,13 +1394,20 @@ func TestCreateTableDiff(t *testing.T) { ts.diff = ts.diffs[0] ts.cdiff = ts.cdiffs[0] } - switch { - case ts.isError: - require.Error(t, err) - if ts.errorMsg != "" { - assert.Contains(t, err.Error(), ts.errorMsg) - } - case ts.diff == "": + + if ts.diff != "" { + _, err := env.Parser().ParseStrictDDL(ts.diff) + require.NoError(t, err) + } + if ts.cdiff != "" { + _, err := env.Parser().ParseStrictDDL(ts.cdiff) + require.NoError(t, err) + } + if ts.errorMsg != "" { + require.ErrorContains(t, err, ts.errorMsg) + return + } + if ts.diff == "" { assert.NoError(t, err) assert.True(t, alter.IsEmpty(), "expected empty diff, found changes") if !alter.IsEmpty() { @@ -1312,60 +1416,61 @@ func TestCreateTableDiff(t *testing.T) { t.Logf("c: %v", sqlparser.CanonicalString(c.CreateTable)) t.Logf("other: %v", sqlparser.CanonicalString(other.CreateTable)) } - default: - assert.NoError(t, err) - require.NotNil(t, alter) - assert.False(t, alter.IsEmpty(), "expected changes, found empty diff") + return + } - { - diff := alter.StatementString() - assert.Equal(t, ts.diff, diff) + // Expecting diff + assert.NoError(t, err) + require.NotNil(t, alter) + assert.False(t, alter.IsEmpty(), "expected changes, found empty diff") - if len(ts.diffs) > 0 { + { + diff := alter.StatementString() + assert.Equal(t, ts.diff, diff) - allSubsequentDiffs := AllSubsequent(alter) - require.Equal(t, len(ts.diffs), len(allSubsequentDiffs)) - require.Equal(t, len(ts.cdiffs), len(allSubsequentDiffs)) - for i := range ts.diffs { - assert.Equal(t, ts.diffs[i], allSubsequentDiffs[i].StatementString()) - assert.Equal(t, ts.cdiffs[i], allSubsequentDiffs[i].CanonicalStatementString()) - } - } - // validate we can parse back the statement - _, err := sqlparser.ParseStrictDDL(diff) - assert.NoError(t, err) + if len(ts.diffs) > 0 { - // Validate "from/to" entities - eFrom, eTo := alter.Entities() - if ts.fromName != "" { - assert.Equal(t, ts.fromName, eFrom.Name()) - } - if ts.toName != "" { - assert.Equal(t, ts.toName, eTo.Name()) + allSubsequentDiffs := AllSubsequent(alter) + require.Equal(t, len(ts.diffs), len(allSubsequentDiffs)) + require.Equal(t, len(ts.cdiffs), len(allSubsequentDiffs)) + for i := range ts.diffs { + assert.Equal(t, ts.diffs[i], allSubsequentDiffs[i].StatementString()) + assert.Equal(t, ts.cdiffs[i], allSubsequentDiffs[i].CanonicalStatementString()) } + } + // validate we can parse back the statement + _, err := env.Parser().ParseStrictDDL(diff) + assert.NoError(t, err) - { // Validate "apply()" on "from" converges with "to" - applied, err := c.Apply(alter) - assert.NoError(t, err) - require.NotNil(t, applied) - appliedDiff, err := eTo.Diff(applied, &hints) - require.NoError(t, err) - assert.True(t, appliedDiff.IsEmpty(), "expected empty diff, found changes: %v.\nc=%v\n,alter=%v\n,eTo=%v\napplied=%v\n", - appliedDiff.CanonicalStatementString(), - c.Create().CanonicalStatementString(), - alter.CanonicalStatementString(), - eTo.Create().CanonicalStatementString(), - applied.Create().CanonicalStatementString(), - ) - } + // Validate "from/to" entities + eFrom, eTo := alter.Entities() + if ts.fromName != "" { + assert.Equal(t, ts.fromName, eFrom.Name()) } - { - cdiff := alter.CanonicalStatementString() - assert.Equal(t, ts.cdiff, cdiff) - _, err := sqlparser.ParseStrictDDL(cdiff) - assert.NoError(t, err) + if ts.toName != "" { + assert.Equal(t, ts.toName, eTo.Name()) } + { // Validate "apply()" on "from" converges with "to" + applied, err := c.Apply(alter) + assert.NoError(t, err) + require.NotNil(t, applied) + appliedDiff, err := eTo.Diff(applied, &hints) + require.NoError(t, err) + assert.True(t, appliedDiff.IsEmpty(), "expected empty diff, found changes: %v.\nc=%v\n,alter=%v\n,eTo=%v\napplied=%v\n", + appliedDiff.CanonicalStatementString(), + c.Create().CanonicalStatementString(), + alter.CanonicalStatementString(), + eTo.Create().CanonicalStatementString(), + applied.Create().CanonicalStatementString(), + ) + } + } + { + cdiff := alter.CanonicalStatementString() + assert.Equal(t, ts.cdiff, cdiff) + _, err := env.Parser().ParseStrictDDL(cdiff) + assert.NoError(t, err) } }) } @@ -1857,19 +1962,20 @@ func TestValidate(t *testing.T) { }, } hints := DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(ts.from) + stmt, err := env.Parser().ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - stmt, err = sqlparser.ParseStrictDDL(ts.alter) + stmt, err = env.Parser().ParseStrictDDL(ts.alter) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) - from, err := NewCreateTableEntity(fromCreateTable) + from, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) a := &AlterTableEntityDiff{from: from, alterTable: alterTable} applied, err := from.Apply(a) @@ -1888,12 +1994,12 @@ func TestValidate(t *testing.T) { require.True(t, ok) applied = c.normalize() - stmt, err := sqlparser.ParseStrictDDL(ts.to) + stmt, err := env.Parser().ParseStrictDDL(ts.to) require.NoError(t, err) toCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - to, err := NewCreateTableEntity(toCreateTable) + to, err := NewCreateTableEntity(env, toCreateTable) require.NoError(t, err) diff, err := applied.Diff(to, &hints) require.NoError(t, err) @@ -2170,14 +2276,15 @@ func TestNormalize(t *testing.T) { to: "CREATE TABLE `t` (\n\t`id` tinyint(1),\n\t`b` tinyint(1),\n\tPRIMARY KEY (`id`)\n)", }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(ts.from) + stmt, err := env.Parser().ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - from, err := NewCreateTableEntity(fromCreateTable) + from, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) assert.Equal(t, ts.to, sqlparser.CanonicalString(from)) }) @@ -2261,11 +2368,12 @@ func TestIndexesCoveringForeignKeyColumns(t *testing.T) { }, } - stmt, err := sqlparser.ParseStrictDDL(sql) + env := NewTestEnv() + stmt, err := env.Parser().ParseStrictDDL(sql) require.NoError(t, err) createTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := NewCreateTableEntity(createTable) + c, err := NewCreateTableEntity(env, createTable) require.NoError(t, err) tableColumns := map[string]sqlparser.IdentifierCI{} for _, col := range c.CreateTable.TableSpec.Columns { diff --git a/go/vt/schemadiff/types.go b/go/vt/schemadiff/types.go index 86e5a8d06bf..30814dfc26c 100644 --- a/go/vt/schemadiff/types.go +++ b/go/vt/schemadiff/types.go @@ -20,6 +20,15 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) +type InstantDDLCapability int + +const ( + InstantDDLCapabilityUnknown InstantDDLCapability = iota + InstantDDLCapabilityIrrelevant + InstantDDLCapabilityImpossible + InstantDDLCapabilityPossible +) + // Entity stands for a database object we can diff: // - A table // - A view @@ -55,6 +64,8 @@ type EntityDiff interface { SubsequentDiff() EntityDiff // SetSubsequentDiff updates the existing subsequent diff to the given one SetSubsequentDiff(EntityDiff) + // InstantDDLCapability returns the ability of this diff to run with ALGORITHM=INSTANT + InstantDDLCapability() InstantDDLCapability } const ( @@ -108,6 +119,11 @@ const ( AlterTableAlgorithmStrategyCopy ) +const ( + EnumReorderStrategyReject int = iota + EnumReorderStrategyAllow +) + // DiffHints is an assortment of rules for diffing entities type DiffHints struct { StrictIndexOrdering bool @@ -120,6 +136,7 @@ type DiffHints struct { TableCharsetCollateStrategy int TableQualifierHint int AlterTableAlgorithmStrategy int + EnumReorderStrategy int } const ( diff --git a/go/vt/schemadiff/view.go b/go/vt/schemadiff/view.go index 4e32dfd9910..c28be710116 100644 --- a/go/vt/schemadiff/view.go +++ b/go/vt/schemadiff/view.go @@ -91,6 +91,11 @@ func (d *AlterViewEntityDiff) SubsequentDiff() EntityDiff { func (d *AlterViewEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *AlterViewEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + type CreateViewEntityDiff struct { createView *sqlparser.CreateView @@ -159,6 +164,11 @@ func (d *CreateViewEntityDiff) SubsequentDiff() EntityDiff { func (d *CreateViewEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *CreateViewEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + type DropViewEntityDiff struct { from *CreateViewEntity dropView *sqlparser.DropView @@ -227,16 +237,22 @@ func (d *DropViewEntityDiff) SubsequentDiff() EntityDiff { func (d *DropViewEntityDiff) SetSubsequentDiff(EntityDiff) { } +// InstantDDLCapability implements EntityDiff +func (d *DropViewEntityDiff) InstantDDLCapability() InstantDDLCapability { + return InstantDDLCapabilityIrrelevant +} + // CreateViewEntity stands for a VIEW construct. It contains the view's CREATE statement. type CreateViewEntity struct { *sqlparser.CreateView + env *Environment } -func NewCreateViewEntity(c *sqlparser.CreateView) (*CreateViewEntity, error) { +func NewCreateViewEntity(env *Environment, c *sqlparser.CreateView) (*CreateViewEntity, error) { if !c.IsFullyParsed() { return nil, &NotFullyParsedError{Entity: c.ViewName.Name.String(), Statement: sqlparser.CanonicalString(c)} } - entity := &CreateViewEntity{CreateView: c} + entity := &CreateViewEntity{CreateView: c, env: env} entity.normalize() return entity, nil } diff --git a/go/vt/schemadiff/view_test.go b/go/vt/schemadiff/view_test.go index 939308d056c..e5be9055970 100644 --- a/go/vt/schemadiff/view_test.go +++ b/go/vt/schemadiff/view_test.go @@ -146,21 +146,22 @@ func TestCreateViewDiff(t *testing.T) { }, } hints := &DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - fromStmt, err := sqlparser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser().ParseStrictDDL(ts.from) assert.NoError(t, err) fromCreateView, ok := fromStmt.(*sqlparser.CreateView) assert.True(t, ok) - toStmt, err := sqlparser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser().ParseStrictDDL(ts.to) assert.NoError(t, err) toCreateView, ok := toStmt.(*sqlparser.CreateView) assert.True(t, ok) - c, err := NewCreateViewEntity(fromCreateView) + c, err := NewCreateViewEntity(env, fromCreateView) require.NoError(t, err) - other, err := NewCreateViewEntity(toCreateView) + other, err := NewCreateViewEntity(env, toCreateView) require.NoError(t, err) alter, err := c.Diff(other, hints) switch { @@ -177,7 +178,7 @@ func TestCreateViewDiff(t *testing.T) { diff := alter.StatementString() assert.Equal(t, ts.diff, diff) // validate we can parse back the statement - _, err := sqlparser.ParseStrictDDL(diff) + _, err := env.Parser().ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := alter.Entities() @@ -199,7 +200,7 @@ func TestCreateViewDiff(t *testing.T) { { cdiff := alter.CanonicalStatementString() assert.Equal(t, ts.cdiff, cdiff) - _, err := sqlparser.ParseStrictDDL(cdiff) + _, err := env.Parser().ParseStrictDDL(cdiff) assert.NoError(t, err) } } @@ -239,14 +240,15 @@ func TestNormalizeView(t *testing.T) { to: "CREATE SQL SECURITY INVOKER VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`", }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(ts.from) + stmt, err := env.Parser().ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateView, ok := stmt.(*sqlparser.CreateView) require.True(t, ok) - from, err := NewCreateViewEntity(fromCreateView) + from, err := NewCreateViewEntity(env, fromCreateView) require.NoError(t, err) assert.Equal(t, ts.to, sqlparser.CanonicalString(from)) }) diff --git a/go/vt/schemamanager/schemamanager_test.go b/go/vt/schemamanager/schemamanager_test.go index 154d985bba4..b4724241cd1 100644 --- a/go/vt/schemamanager/schemamanager_test.go +++ b/go/vt/schemamanager/schemamanager_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl/tmutils" "vitess.io/vitess/go/vt/topo" @@ -94,7 +96,7 @@ func TestSchemaManagerExecutorOpenFail(t *testing.T) { controller := newFakeController( []string{"create table test_table (pk int);"}, false, false, false) controller.SetKeyspace("unknown_keyspace") - executor := NewTabletExecutor("TestSchemaManagerExecutorOpenFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestSchemaManagerExecutorOpenFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) ctx := context.Background() _, err := Run(ctx, controller, executor) @@ -125,7 +127,7 @@ func TestSchemaManagerRun(t *testing.T) { }) fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) - executor := NewTabletExecutor("TestSchemaManagerRun", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestSchemaManagerRun", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) ctx := context.Background() resp, err := Run(ctx, controller, executor) @@ -176,7 +178,7 @@ func TestSchemaManagerExecutorFail(t *testing.T) { fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) fakeTmc.EnableExecuteFetchAsDbaError = true - executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) ctx := context.Background() resp, err := Run(ctx, controller, executor) @@ -196,7 +198,7 @@ func TestSchemaManagerExecutorBatchVsStrategyFail(t *testing.T) { fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) fakeTmc.EnableExecuteFetchAsDbaError = true - executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10) + executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10, sqlparser.NewTestParser()) executor.SetDDLStrategy("online") ctx := context.Background() @@ -212,7 +214,7 @@ func TestSchemaManagerExecutorBatchVsQueriesFail(t *testing.T) { fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) fakeTmc.EnableExecuteFetchAsDbaError = true - executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10) + executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10, sqlparser.NewTestParser()) executor.SetDDLStrategy("direct") ctx := context.Background() @@ -228,7 +230,7 @@ func TestSchemaManagerExecutorBatchVsUUIDsFail(t *testing.T) { fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) fakeTmc.EnableExecuteFetchAsDbaError = true - executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10) + executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 10, sqlparser.NewTestParser()) executor.SetDDLStrategy("direct") executor.SetUUIDList([]string{"4e5dcf80_354b_11eb_82cd_f875a4d24e90"}) @@ -271,7 +273,7 @@ func TestSchemaManagerRegisterControllerFactory(t *testing.T) { } func newFakeExecutor(t *testing.T) *TabletExecutor { - return NewTabletExecutor("newFakeExecutor", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + return NewTabletExecutor("newFakeExecutor", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) } func newFakeTabletManagerClient() *fakeTabletManagerClient { diff --git a/go/vt/schemamanager/tablet_executor.go b/go/vt/schemamanager/tablet_executor.go index a56a95d5034..4f0326f70b1 100644 --- a/go/vt/schemamanager/tablet_executor.go +++ b/go/vt/schemamanager/tablet_executor.go @@ -53,10 +53,11 @@ type TabletExecutor struct { ddlStrategySetting *schema.DDLStrategySetting uuids []string batchSize int64 + parser *sqlparser.Parser } // NewTabletExecutor creates a new TabletExecutor instance -func NewTabletExecutor(migrationContext string, ts *topo.Server, tmc tmclient.TabletManagerClient, logger logutil.Logger, waitReplicasTimeout time.Duration, batchSize int64) *TabletExecutor { +func NewTabletExecutor(migrationContext string, ts *topo.Server, tmc tmclient.TabletManagerClient, logger logutil.Logger, waitReplicasTimeout time.Duration, batchSize int64, parser *sqlparser.Parser) *TabletExecutor { return &TabletExecutor{ ts: ts, tmc: tmc, @@ -65,6 +66,7 @@ func NewTabletExecutor(migrationContext string, ts *topo.Server, tmc tmclient.Ta waitReplicasTimeout: waitReplicasTimeout, migrationContext: migrationContext, batchSize: batchSize, + parser: parser, } } @@ -105,16 +107,12 @@ func (exec *TabletExecutor) Open(ctx context.Context, keyspace string) error { return nil } exec.keyspace = keyspace - shardNames, err := exec.ts.GetShardNames(ctx, keyspace) + shards, err := exec.ts.FindAllShardsInKeyspace(ctx, keyspace, nil) if err != nil { - return fmt.Errorf("unable to get shard names for keyspace: %s, error: %v", keyspace, err) + return fmt.Errorf("unable to get shards for keyspace: %s, error: %v", keyspace, err) } - exec.tablets = make([]*topodatapb.Tablet, len(shardNames)) - for i, shardName := range shardNames { - shardInfo, err := exec.ts.GetShard(ctx, keyspace, shardName) - if err != nil { - return fmt.Errorf("unable to get shard info, keyspace: %s, shard: %s, error: %v", keyspace, shardName, err) - } + exec.tablets = make([]*topodatapb.Tablet, 0, len(shards)) + for shardName, shardInfo := range shards { if !shardInfo.HasPrimary() { return fmt.Errorf("shard: %s does not have a primary", shardName) } @@ -122,7 +120,7 @@ func (exec *TabletExecutor) Open(ctx context.Context, keyspace string) error { if err != nil { return fmt.Errorf("unable to get primary tablet info, keyspace: %s, shard: %s, error: %v", keyspace, shardName, err) } - exec.tablets[i] = tabletInfo.Tablet + exec.tablets = append(exec.tablets, tabletInfo.Tablet) } if len(exec.tablets) == 0 { @@ -146,7 +144,7 @@ func (exec *TabletExecutor) Validate(ctx context.Context, sqls []string) error { func (exec *TabletExecutor) parseDDLs(sqls []string) error { for _, sql := range sqls { - stmt, err := sqlparser.Parse(sql) + stmt, err := exec.parser.Parse(sql) if err != nil { return vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "failed to parse sql: %s, got error: %v", sql, err) } @@ -204,14 +202,14 @@ func (exec *TabletExecutor) executeSQL(ctx context.Context, sql string, provided return executeViaFetch() } // Analyze what type of query this is: - stmt, err := sqlparser.Parse(sql) + stmt, err := exec.parser.Parse(sql) if err != nil { return false, err } switch stmt := stmt.(type) { case sqlparser.DDLStatement: if exec.isOnlineSchemaDDL(stmt) { - onlineDDLs, err := schema.NewOnlineDDLs(exec.keyspace, sql, stmt, exec.ddlStrategySetting, exec.migrationContext, providedUUID) + onlineDDLs, err := schema.NewOnlineDDLs(exec.keyspace, sql, stmt, exec.ddlStrategySetting, exec.migrationContext, providedUUID, exec.parser) if err != nil { execResult.ExecutorErr = err.Error() return false, err @@ -227,7 +225,7 @@ func (exec *TabletExecutor) executeSQL(ctx context.Context, sql string, provided } case *sqlparser.RevertMigration: strategySetting := schema.NewDDLStrategySetting(schema.DDLStrategyOnline, exec.ddlStrategySetting.Options) - onlineDDL, err := schema.NewOnlineDDL(exec.keyspace, "", sqlparser.String(stmt), strategySetting, exec.migrationContext, providedUUID) + onlineDDL, err := schema.NewOnlineDDL(exec.keyspace, "", sqlparser.String(stmt), strategySetting, exec.migrationContext, providedUUID, exec.parser) if err != nil { execResult.ExecutorErr = err.Error() return false, err @@ -265,9 +263,9 @@ func batchSQLs(sqls []string, batchSize int) (batchedSQLs []string) { // allSQLsAreCreateQueries returns 'true' when all given queries are CREATE TABLE|VIEW // This function runs pretty fast even for thousands of tables (its overhead is insignificant compared with // the time it would take to apply the changes). -func allSQLsAreCreateQueries(sqls []string) (bool, error) { +func allSQLsAreCreateQueries(sqls []string, parser *sqlparser.Parser) (bool, error) { for _, sql := range sqls { - stmt, err := sqlparser.Parse(sql) + stmt, err := parser.Parse(sql) if err != nil { return false, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "failed to parse sql: %s, got error: %v", sql, err) } @@ -377,7 +375,7 @@ func (exec *TabletExecutor) Execute(ctx context.Context, sqls []string) *Execute if exec.hasProvidedUUIDs() { return errorExecResult(fmt.Errorf("--batch-size conflicts with --uuid-list. Batching does not support UUIDs.")) } - allSQLsAreCreate, err := allSQLsAreCreateQueries(sqls) + allSQLsAreCreate, err := allSQLsAreCreateQueries(sqls, exec.parser) if err != nil { return errorExecResult(err) } @@ -444,16 +442,16 @@ func (exec *TabletExecutor) executeOnAllTablets(ctx context.Context, execResult // applyAllowZeroInDate takes a SQL string which may contain one or more statements, // and, assuming those are DDLs, adds a /*vt+ allowZeroInDate=true */ directive to all of them, // returning the result again as one long SQL. -func applyAllowZeroInDate(sql string) (string, error) { +func applyAllowZeroInDate(sql string, parser *sqlparser.Parser) (string, error) { // sql may be a batch of multiple statements - sqls, err := sqlparser.SplitStatementToPieces(sql) + sqls, err := parser.SplitStatementToPieces(sql) if err != nil { return sql, err } var modifiedSqls []string for _, singleSQL := range sqls { // --allow-zero-in-date Applies to DDLs - stmt, err := sqlparser.Parse(singleSQL) + stmt, err := parser.Parse(singleSQL) if err != nil { return sql, err } @@ -486,7 +484,7 @@ func (exec *TabletExecutor) executeOneTablet( } else { if exec.ddlStrategySetting != nil && exec.ddlStrategySetting.IsAllowZeroInDateFlag() { // --allow-zero-in-date Applies to DDLs - sql, err = applyAllowZeroInDate(sql) + sql, err = applyAllowZeroInDate(sql, exec.parser) if err != nil { errChan <- ShardWithError{Shard: tablet.Shard, Err: err.Error()} return diff --git a/go/vt/schemamanager/tablet_executor_test.go b/go/vt/schemamanager/tablet_executor_test.go index 175e10dfb66..0ae960e6e9c 100644 --- a/go/vt/schemamanager/tablet_executor_test.go +++ b/go/vt/schemamanager/tablet_executor_test.go @@ -72,7 +72,7 @@ func TestTabletExecutorOpenWithEmptyPrimaryAlias(t *testing.T) { if err := ts.InitTablet(ctx, tablet, false /*allowPrimaryOverride*/, true /*createShardAndKeyspace*/, false /*allowUpdate*/); err != nil { t.Fatalf("InitTablet failed: %v", err) } - executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyPrimaryAlias", ts, newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyPrimaryAlias", ts, newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) if err := executor.Open(ctx, "test_keyspace"); err == nil || !strings.Contains(err.Error(), "does not have a primary") { t.Fatalf("executor.Open() = '%v', want error", err) } @@ -105,7 +105,7 @@ func TestTabletExecutorValidate(t *testing.T) { }, }) - executor := NewTabletExecutor("TestTabletExecutorValidate", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestTabletExecutorValidate", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) ctx := context.Background() sqls := []string{ @@ -179,7 +179,7 @@ func TestTabletExecutorDML(t *testing.T) { }, }) - executor := NewTabletExecutor("TestTabletExecutorDML", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0) + executor := NewTabletExecutor("TestTabletExecutorDML", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout, 0, sqlparser.NewTestParser()) ctx := context.Background() executor.Open(ctx, "unsharded_keyspace") @@ -269,12 +269,13 @@ func TestIsOnlineSchemaDDL(t *testing.T) { }, } + parser := sqlparser.NewTestParser() for _, ts := range tt { e := &TabletExecutor{} err := e.SetDDLStrategy(ts.ddlStrategy) assert.NoError(t, err) - stmt, err := sqlparser.Parse(ts.query) + stmt, err := parser.Parse(ts.query) assert.NoError(t, err) ddlStmt, ok := stmt.(sqlparser.DDLStatement) @@ -402,7 +403,7 @@ func TestAllSQLsAreCreateQueries(t *testing.T) { for _, tcase := range tcases { t.Run(tcase.name, func(t *testing.T) { - result, err := allSQLsAreCreateQueries(tcase.sqls) + result, err := allSQLsAreCreateQueries(tcase.sqls, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, tcase.expect, result) }) @@ -437,7 +438,7 @@ func TestApplyAllowZeroInDate(t *testing.T) { } for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - result, err := applyAllowZeroInDate(tcase.sql) + result, err := applyAllowZeroInDate(tcase.sql, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, tcase.expect, result) }) diff --git a/go/vt/servenv/buildinfo.go b/go/vt/servenv/buildinfo.go index 15e34217dae..d55e01d84c0 100644 --- a/go/vt/servenv/buildinfo.go +++ b/go/vt/servenv/buildinfo.go @@ -33,6 +33,7 @@ var ( buildTime = "" buildGitRev = "" buildGitBranch = "" + statsBuildVersion *stats.String jenkinsBuildNumberStr = "" // version registers the command line flag to expose build info. @@ -121,6 +122,8 @@ func init() { stats.NewString("BuildHost").Set(AppVersion.buildHost) stats.NewString("BuildUser").Set(AppVersion.buildUser) stats.NewGauge("BuildTimestamp", "build timestamp").Set(AppVersion.buildTime) + statsBuildVersion = stats.NewString("BuildVersion") + statsBuildVersion.Set(AppVersion.version) stats.NewString("BuildGitRev").Set(AppVersion.buildGitRev) stats.NewString("BuildGitBranch").Set(AppVersion.buildGitBranch) stats.NewGauge("BuildNumber", "build number").Set(AppVersion.jenkinsBuildNumber) diff --git a/go/vt/servenv/buildinfo_test.go b/go/vt/servenv/buildinfo_test.go index be35511a036..bc972df03ea 100644 --- a/go/vt/servenv/buildinfo_test.go +++ b/go/vt/servenv/buildinfo_test.go @@ -47,3 +47,8 @@ func TestVersionString(t *testing.T) { assert.Equal(t, "8.0.30-Vitess", v.MySQLVersion()) } + +func TestBuildVersionStats(t *testing.T) { + buildVersion := statsBuildVersion.Get() + assert.Equal(t, buildVersion, versionName) +} diff --git a/go/vt/servenv/exporter.go b/go/vt/servenv/exporter.go index a3d23dc4b74..7adb3e18f2c 100644 --- a/go/vt/servenv/exporter.go +++ b/go/vt/servenv/exporter.go @@ -19,6 +19,7 @@ package servenv import ( "expvar" "net/http" + "net/url" "sync" "time" @@ -150,9 +151,10 @@ func (e *Exporter) URLPrefix() string { // There are two other places where this logic is duplicated: // status.go and go/vt/vtgate/discovery/healthcheck.go. if e.name == "" { - return e.name + return "" } - return "/" + e.name + prefix, _ := url.JoinPath("/", e.name) + return prefix } // HandleFunc sets or overwrites the handler for url. If Exporter has a name, diff --git a/go/vt/servenv/grpc_server.go b/go/vt/servenv/grpc_server.go index 7a41cca389a..96fe3c25ea9 100644 --- a/go/vt/servenv/grpc_server.go +++ b/go/vt/servenv/grpc_server.go @@ -99,6 +99,9 @@ var ( // even when there are no active streams (RPCs). If false, and client sends ping when // there are no active streams, server will send GOAWAY and close the connection. gRPCKeepAliveEnforcementPolicyPermitWithoutStream bool + + gRPCKeepaliveTime = 10 * time.Second + gRPCKeepaliveTimeout = 10 * time.Second ) // TLS variables. @@ -141,6 +144,8 @@ func RegisterGRPCServerFlags() { fs.StringVar(&gRPCCRL, "grpc_crl", gRPCCRL, "path to a certificate revocation list in PEM format, client certificates will be further verified against this file during TLS handshake") fs.BoolVar(&gRPCEnableOptionalTLS, "grpc_enable_optional_tls", gRPCEnableOptionalTLS, "enable optional TLS mode when a server accepts both TLS and plain-text connections on the same port") fs.StringVar(&gRPCServerCA, "grpc_server_ca", gRPCServerCA, "path to server CA in PEM format, which will be combine with server cert, return full certificate chain to clients") + fs.DurationVar(&gRPCKeepaliveTime, "grpc_server_keepalive_time", gRPCKeepaliveTime, "After a duration of this time, if the server doesn't see any activity, it pings the client to see if the transport is still alive.") + fs.DurationVar(&gRPCKeepaliveTimeout, "grpc_server_keepalive_timeout", gRPCKeepaliveTimeout, "After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that the connection is closed.") }) } @@ -233,6 +238,8 @@ func createGRPCServer() { ka := keepalive.ServerParameters{ MaxConnectionAge: gRPCMaxConnectionAge, MaxConnectionAgeGrace: gRPCMaxConnectionAgeGrace, + Time: gRPCKeepaliveTime, + Timeout: gRPCKeepaliveTimeout, } opts = append(opts, grpc.KeepaliveParams(ka)) diff --git a/go/vt/servenv/http.go b/go/vt/servenv/http.go index f4b001383d1..c4b14e9b4e6 100644 --- a/go/vt/servenv/http.go +++ b/go/vt/servenv/http.go @@ -46,6 +46,10 @@ func HTTPServe(l net.Listener) error { // HTTPRegisterProfile registers the default pprof HTTP endpoints with the internal servenv mux. func HTTPRegisterProfile() { + if !httpPprof { + return + } + HTTPHandleFunc("/debug/pprof/", pprof.Index) HTTPHandleFunc("/debug/pprof/cmdline", pprof.Cmdline) HTTPHandleFunc("/debug/pprof/profile", pprof.Profile) diff --git a/go/vt/servenv/mysql.go b/go/vt/servenv/mysql.go index 94019a1c42c..c0af2a7ee39 100644 --- a/go/vt/servenv/mysql.go +++ b/go/vt/servenv/mysql.go @@ -17,13 +17,17 @@ limitations under the License. package servenv import ( + "fmt" + "github.com/spf13/pflag" + + "vitess.io/vitess/go/mysql/config" ) // mySQLServerVersion is what Vitess will present as it's version during the connection handshake, // and as the value to the @@version system variable. If nothing is provided, Vitess will report itself as // a specific MySQL version with the vitess version appended to it -var mySQLServerVersion = "8.0.30-Vitess" +var mySQLServerVersion = fmt.Sprintf("%s-Vitess", config.DefaultMySQLVersion) // RegisterMySQLServerFlags installs the flags needed to specify or expose a // particular MySQL server version from Vitess. @@ -36,14 +40,6 @@ func MySQLServerVersion() string { return mySQLServerVersion } -// SetMySQLServerVersionForTest sets the value of the `--mysql_server_version` -// flag. It is intended for use in tests that require a specific MySQL server -// version (for example, collations) that cannot specify that via the command -// line. -func SetMySQLServerVersionForTest(version string) { - mySQLServerVersion = version -} - func init() { for _, cmd := range []string{ "mysqlctl", @@ -51,6 +47,7 @@ func init() { "vtbackup", "vtcombo", "vtctl", + "vtctld", "vtctldclient", "vtexplain", "vtgate", diff --git a/go/vt/servenv/pprof.go b/go/vt/servenv/pprof.go index d1d8e99588f..957c0504c00 100644 --- a/go/vt/servenv/pprof.go +++ b/go/vt/servenv/pprof.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "os" - "os/signal" "path/filepath" "runtime" "runtime/pprof" @@ -28,7 +27,6 @@ import ( "strconv" "strings" "sync/atomic" - "syscall" "github.com/spf13/pflag" @@ -37,6 +35,7 @@ import ( var ( pprofFlag []string + httpPprof bool ) type profmode string @@ -298,47 +297,9 @@ func (prof *profile) init() (start func(), stop func()) { } } -func pprofInit() { - prof, err := parseProfileFlag(pprofFlag) - if err != nil { - log.Fatal(err) - } - if prof != nil { - start, stop := prof.init() - startSignal := make(chan os.Signal, 1) - stopSignal := make(chan os.Signal, 1) - - if prof.waitSig { - signal.Notify(startSignal, syscall.SIGUSR1) - } else { - start() - signal.Notify(stopSignal, syscall.SIGUSR1) - } - - go func() { - for { - <-startSignal - start() - signal.Reset(syscall.SIGUSR1) - signal.Notify(stopSignal, syscall.SIGUSR1) - } - }() - - go func() { - for { - <-stopSignal - stop() - signal.Reset(syscall.SIGUSR1) - signal.Notify(startSignal, syscall.SIGUSR1) - } - }() - - OnTerm(stop) - } -} - func init() { OnParse(func(fs *pflag.FlagSet) { + fs.BoolVar(&httpPprof, "pprof-http", httpPprof, "enable pprof http endpoints") fs.StringSliceVar(&pprofFlag, "pprof", pprofFlag, "enable profiling") }) OnInit(pprofInit) diff --git a/go/vt/servenv/pprof_unix.go b/go/vt/servenv/pprof_unix.go new file mode 100644 index 00000000000..097abc08720 --- /dev/null +++ b/go/vt/servenv/pprof_unix.go @@ -0,0 +1,66 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servenv + +import ( + "os" + "os/signal" + "syscall" + + "vitess.io/vitess/go/vt/log" +) + +func pprofInit() { + prof, err := parseProfileFlag(pprofFlag) + if err != nil { + log.Fatal(err) + } + if prof != nil { + start, stop := prof.init() + startSignal := make(chan os.Signal, 1) + stopSignal := make(chan os.Signal, 1) + + if prof.waitSig { + signal.Notify(startSignal, syscall.SIGUSR1) + } else { + start() + signal.Notify(stopSignal, syscall.SIGUSR1) + } + + go func() { + for { + <-startSignal + start() + signal.Reset(syscall.SIGUSR1) + signal.Notify(stopSignal, syscall.SIGUSR1) + } + }() + + go func() { + for { + <-stopSignal + stop() + signal.Reset(syscall.SIGUSR1) + signal.Notify(startSignal, syscall.SIGUSR1) + } + }() + + OnTerm(stop) + } +} diff --git a/go/vt/servenv/pprof_windows.go b/go/vt/servenv/pprof_windows.go new file mode 100644 index 00000000000..7ec4be816df --- /dev/null +++ b/go/vt/servenv/pprof_windows.go @@ -0,0 +1,27 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servenv + +import ( + "vitess.io/vitess/go/vt/log" +) + +func pprofInit() { + log.Warningf("pprof is not supported on Windows") +} diff --git a/go/vt/servenv/servenv.go b/go/vt/servenv/servenv.go index e7c28855997..6a7898501f8 100644 --- a/go/vt/servenv/servenv.go +++ b/go/vt/servenv/servenv.go @@ -33,11 +33,8 @@ import ( "fmt" "net/url" "os" - "os/signal" - "runtime/debug" "strings" "sync" - "syscall" "time" "github.com/spf13/cobra" @@ -111,63 +108,6 @@ func GetInitStartTime() time.Time { return initStartTime } -// Init is the first phase of the server startup. -func Init() { - mu.Lock() - defer mu.Unlock() - initStartTime = time.Now() - - // Uptime metric - _ = stats.NewGaugeFunc("Uptime", "Uptime in nanoseconds", func() int64 { - return int64(time.Since(serverStart).Nanoseconds()) - }) - - // Ignore SIGPIPE if specified - // The Go runtime catches SIGPIPE for us on all fds except stdout/stderr - // See https://golang.org/pkg/os/signal/#hdr-SIGPIPE - if catchSigpipe { - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGPIPE) - go func() { - <-sigChan - log.Warning("Caught SIGPIPE (ignoring all future SIGPIPEs)") - signal.Ignore(syscall.SIGPIPE) - }() - } - - // Add version tag to every info log - log.Infof(AppVersion.String()) - if inited { - log.Fatal("servenv.Init called second time") - } - inited = true - - // Once you run as root, you pretty much destroy the chances of a - // non-privileged user starting the program correctly. - if uid := os.Getuid(); uid == 0 { - log.Exitf("servenv.Init: running this as root makes no sense") - } - - // We used to set this limit directly, but you pretty much have to - // use a root account to allow increasing a limit reliably. Dropping - // privileges is also tricky. The best strategy is to make a shell - // script set up the limits as root and switch users before starting - // the server. - fdLimit := &syscall.Rlimit{} - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, fdLimit); err != nil { - log.Errorf("max-open-fds failed: %v", err) - } - fdl := stats.NewGauge("MaxFds", "File descriptor limit") - fdl.Set(int64(fdLimit.Cur)) - - // Limit the stack size. We don't need huge stacks and smaller limits mean - // any infinite recursion fires earlier and on low memory systems avoids - // out of memory issues in favor of a stack overflow error. - debug.SetMaxStack(maxStackSize) - - onInitHooks.Fire() -} - func populateListeningURL(port int32) { host, err := netutil.FullyQualifiedHostname() if err != nil { diff --git a/go/vt/servenv/servenv_unix.go b/go/vt/servenv/servenv_unix.go new file mode 100644 index 00000000000..17fa85c4167 --- /dev/null +++ b/go/vt/servenv/servenv_unix.go @@ -0,0 +1,87 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servenv + +import ( + "os" + "os/signal" + "runtime/debug" + "syscall" + "time" + + "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/vt/log" +) + +// Init is the first phase of the server startup. +func Init() { + mu.Lock() + defer mu.Unlock() + initStartTime = time.Now() + + // Uptime metric + _ = stats.NewGaugeFunc("Uptime", "Uptime in nanoseconds", func() int64 { + return int64(time.Since(serverStart).Nanoseconds()) + }) + + // Ignore SIGPIPE if specified + // The Go runtime catches SIGPIPE for us on all fds except stdout/stderr + // See https://golang.org/pkg/os/signal/#hdr-SIGPIPE + if catchSigpipe { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGPIPE) + go func() { + <-sigChan + log.Warning("Caught SIGPIPE (ignoring all future SIGPIPEs)") + signal.Ignore(syscall.SIGPIPE) + }() + } + + // Add version tag to every info log + log.Infof(AppVersion.String()) + if inited { + log.Fatal("servenv.Init called second time") + } + inited = true + + // Once you run as root, you pretty much destroy the chances of a + // non-privileged user starting the program correctly. + if uid := os.Getuid(); uid == 0 { + log.Exitf("servenv.Init: running this as root makes no sense") + } + + // We used to set this limit directly, but you pretty much have to + // use a root account to allow increasing a limit reliably. Dropping + // privileges is also tricky. The best strategy is to make a shell + // script set up the limits as root and switch users before starting + // the server. + fdLimit := &syscall.Rlimit{} + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, fdLimit); err != nil { + log.Errorf("max-open-fds failed: %v", err) + } + fdl := stats.NewGauge("MaxFds", "File descriptor limit") + fdl.Set(int64(fdLimit.Cur)) + + // Limit the stack size. We don't need huge stacks and smaller limits mean + // any infinite recursion fires earlier and on low memory systems avoids + // out of memory issues in favor of a stack overflow error. + debug.SetMaxStack(maxStackSize) + + onInitHooks.Fire() +} diff --git a/go/vt/servenv/servenv_windows.go b/go/vt/servenv/servenv_windows.go new file mode 100644 index 00000000000..bd610b1f245 --- /dev/null +++ b/go/vt/servenv/servenv_windows.go @@ -0,0 +1,21 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servenv + +func Init() {} diff --git a/go/vt/servenv/status.go b/go/vt/servenv/status.go index ac912fd881e..422e6907a76 100644 --- a/go/vt/servenv/status.go +++ b/go/vt/servenv/status.go @@ -17,11 +17,11 @@ limitations under the License. package servenv import ( - "bytes" "fmt" "io" "net" "net/http" + "net/url" "os" "path/filepath" "runtime" @@ -172,7 +172,8 @@ func newStatusPage(name string) *statusPage { registerDebugBlockProfileRate() registerDebugMutexProfileFraction() } else { - HTTPHandleFunc("/"+name+StatusURLPath(), sp.statusHandler) + pat, _ := url.JoinPath("/", name, StatusURLPath()) + HTTPHandleFunc(pat, sp.statusHandler) } return sp } @@ -256,7 +257,7 @@ func (sp *statusPage) statusHandler(w http.ResponseWriter, r *http.Request) { } func (sp *statusPage) reparse(sections []section) (*template.Template, error) { - var buf bytes.Buffer + var buf strings.Builder io.WriteString(&buf, `{{define "status"}}`) io.WriteString(&buf, statusHTML) @@ -301,7 +302,7 @@ func registerDebugBlockProfileRate() { runtime.SetBlockProfileRate(rate) log.Infof("Set block profile rate to: %d", rate) w.Header().Set("Content-Type", "text/plain") - w.Write([]byte(message)) + io.WriteString(w, message) }) } @@ -329,7 +330,7 @@ func registerDebugMutexProfileFraction() { runtime.SetMutexProfileFraction(fraction) log.Infof("Set mutex profiling fraction to: %d", fraction) w.Header().Set("Content-Type", "text/plain") - w.Write([]byte(message)) + io.WriteString(w, message) }) } diff --git a/go/vt/servenv/truncate_query.go b/go/vt/servenv/truncate_query.go new file mode 100644 index 00000000000..fdb618c5c6a --- /dev/null +++ b/go/vt/servenv/truncate_query.go @@ -0,0 +1,34 @@ +package servenv + +import ( + "github.com/spf13/pflag" +) + +var ( + // TruncateUILen truncate queries in debug UIs to the given length. 0 means unlimited. + TruncateUILen = 512 + + // TruncateErrLen truncate queries in error logs to the given length. 0 means unlimited. + TruncateErrLen = 0 +) + +func registerQueryTruncationFlags(fs *pflag.FlagSet) { + fs.IntVar(&TruncateUILen, "sql-max-length-ui", TruncateUILen, "truncate queries in debug UIs to the given length (default 512)") + fs.IntVar(&TruncateErrLen, "sql-max-length-errors", TruncateErrLen, "truncate queries in error logs to the given length (default unlimited)") +} + +func init() { + for _, cmd := range []string{ + "vtgate", + "vttablet", + "vtcombo", + "vtctld", + "vtctl", + "vtexplain", + "vtbackup", + "vttestserver", + "vtbench", + } { + OnParseFor(cmd, registerQueryTruncationFlags) + } +} diff --git a/go/vt/servenv/version.go b/go/vt/servenv/version.go index 23bac777b4d..ca036720381 100644 --- a/go/vt/servenv/version.go +++ b/go/vt/servenv/version.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Vitess Authors. +Copyright 2024 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// THIS FILE IS AUTO-GENERATED DURING NEW RELEASES -// DO NOT EDIT - package servenv -const versionName = "19.0.0-SNAPSHOT" +// DO NOT EDIT +// THIS FILE IS AUTO-GENERATED DURING NEW RELEASES BY THE VITESS-RELEASER + +const versionName = "20.0.0-SNAPSHOT" diff --git a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql index 40fdeef2683..2926ec76f28 100644 --- a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql +++ b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql @@ -71,6 +71,8 @@ CREATE TABLE IF NOT EXISTS schema_migrations `reviewed_timestamp` timestamp NULL DEFAULT NULL, `ready_to_complete_timestamp` timestamp NULL DEFAULT NULL, `removed_foreign_key_names` text NOT NULL, + `last_cutover_attempt_timestamp` timestamp NULL DEFAULT NULL, + `force_cutover` tinyint unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uuid_idx` (`migration_uuid`), KEY `keyspace_shard_idx` (`keyspace`(64), `shard`(64)), diff --git a/go/vt/sidecardb/schema/schemaengine/tables.sql b/go/vt/sidecardb/schema/schemaengine/tables.sql index 00fd0194d67..3aadc7c9635 100644 --- a/go/vt/sidecardb/schema/schemaengine/tables.sql +++ b/go/vt/sidecardb/schema/schemaengine/tables.sql @@ -16,8 +16,8 @@ limitations under the License. CREATE TABLE IF NOT EXISTS tables ( - TABLE_SCHEMA varchar(64) NOT NULL, - TABLE_NAME varchar(64) NOT NULL, + TABLE_SCHEMA varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL, + TABLE_NAME varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL, CREATE_STATEMENT longtext, CREATE_TIME BIGINT, PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) diff --git a/go/vt/sidecardb/schema/schemaengine/views.sql b/go/vt/sidecardb/schema/schemaengine/views.sql index 1fee077202f..dd242e6567f 100644 --- a/go/vt/sidecardb/schema/schemaengine/views.sql +++ b/go/vt/sidecardb/schema/schemaengine/views.sql @@ -16,8 +16,8 @@ limitations under the License. CREATE TABLE IF NOT EXISTS views ( - TABLE_SCHEMA varchar(64) NOT NULL, - TABLE_NAME varchar(64) NOT NULL, + TABLE_SCHEMA varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL, + TABLE_NAME varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL, CREATE_STATEMENT longtext, VIEW_DEFINITION longtext NOT NULL, PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) diff --git a/go/vt/sidecardb/sidecardb.go b/go/vt/sidecardb/sidecardb.go index 0bb64611607..0947355cd46 100644 --- a/go/vt/sidecardb/sidecardb.go +++ b/go/vt/sidecardb/sidecardb.go @@ -29,7 +29,10 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/history" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/mysql/fakesqldb" @@ -45,8 +48,9 @@ import ( ) const ( - sidecarDBExistsQuery = "select 'true' as 'dbexists' from information_schema.SCHEMATA where SCHEMA_NAME = %a" - showCreateTableQuery = "show create table %s.%s" + sidecarDBExistsQuery = "select 'true' as 'dbexists' from information_schema.SCHEMATA where SCHEMA_NAME = %a" + showCreateTableQuery = "show create table %s.%s" + sidecarCollationQuery = "select @@global.collation_server" maxDDLErrorHistoryLength = 100 @@ -113,8 +117,8 @@ func init() { })) } -func validateSchemaDefinition(name, schema string) (string, error) { - stmt, err := sqlparser.ParseStrictDDL(schema) +func validateSchemaDefinition(name, schema string, parser *sqlparser.Parser) (string, error) { + stmt, err := parser.ParseStrictDDL(schema) if err != nil { return "", err @@ -142,7 +146,7 @@ func validateSchemaDefinition(name, schema string) (string, error) { // loadSchemaDefinitions loads the embedded schema definitions // into a slice of sidecarTables for processing. -func loadSchemaDefinitions() { +func loadSchemaDefinitions(parser *sqlparser.Parser) { sqlFileExtension := ".sql" err := fs.WalkDir(schemaLocation, ".", func(path string, entry fs.DirEntry, err error) error { if err != nil { @@ -171,7 +175,7 @@ func loadSchemaDefinitions() { panic(err) } var normalizedSchema string - if normalizedSchema, err = validateSchemaDefinition(name, string(schema)); err != nil { + if normalizedSchema, err = validateSchemaDefinition(name, string(schema), parser); err != nil { return err } sidecarTables = append(sidecarTables, &sidecarTable{name: name, module: module, path: path, schema: normalizedSchema}) @@ -194,8 +198,10 @@ func printCallerDetails() { type schemaInit struct { ctx context.Context + env *vtenv.Environment exec Exec dbCreated bool // The first upgrade/create query will also create the sidecar database if required. + coll collations.ID } // Exec is a callback that has to be passed to Init() to @@ -227,15 +233,18 @@ func getDDLErrorHistory() []*ddlError { // Init creates or upgrades the sidecar database based on // the declarative schema defined for all tables. -func Init(ctx context.Context, exec Exec) error { +func Init(ctx context.Context, env *vtenv.Environment, exec Exec) error { printCallerDetails() // for debug purposes only, remove in v17 log.Infof("Starting sidecardb.Init()") - once.Do(loadSchemaDefinitions) + once.Do(func() { + loadSchemaDefinitions(env.Parser()) + }) si := &schemaInit{ ctx: ctx, exec: exec, + env: env, } // There are paths in the tablet initialization where we @@ -264,6 +273,10 @@ func Init(ctx context.Context, exec Exec) error { } defer resetSQLMode() + if si.coll, err = si.collation(); err != nil { + return err + } + for _, table := range sidecarTables { if err := si.ensureSchema(table); err != nil { return err @@ -337,6 +350,22 @@ func (si *schemaInit) setCurrentDatabase(dbName string) error { return err } +func (si *schemaInit) collation() (collations.ID, error) { + rs, err := si.exec(si.ctx, sidecarCollationQuery, 2, false) + if err != nil { + log.Error(err) + return collations.Unknown, err + } + + switch len(rs.Rows) { + case 1: + return si.env.CollationEnv().LookupByName(rs.Rows[0][0].ToString()), nil + default: + // This should never happen. + return collations.Unknown, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid results for SidecarDB query %q as it produced %d rows", sidecarCollationQuery, len(rs.Rows)) + } +} + // Gets existing schema of a table in the sidecar database. func (si *schemaInit) getCurrentSchema(tableName string) (string, error) { var currentTableSchema string @@ -361,7 +390,7 @@ func (si *schemaInit) getCurrentSchema(tableName string) (string, error) { } // findTableSchemaDiff gets the diff which needs to be applied -// to the current table schema in order toreach the desired one. +// to the current table schema in order to reach the desired one. // The result will be an empty string if they match. // This will be a CREATE statement if the table does not exist // or an ALTER if the table exists but has a different schema. @@ -370,7 +399,8 @@ func (si *schemaInit) findTableSchemaDiff(tableName, current, desired string) (s TableCharsetCollateStrategy: schemadiff.TableCharsetCollateIgnoreAlways, AlterTableAlgorithmStrategy: schemadiff.AlterTableAlgorithmStrategyCopy, } - diff, err := schemadiff.DiffCreateTablesQueries(current, desired, hints) + env := schemadiff.NewEnv(si.env, si.coll) + diff, err := schemadiff.DiffCreateTablesQueries(env, current, desired, hints) if err != nil { return "", err } @@ -458,8 +488,10 @@ func (t *sidecarTable) String() string { // AddSchemaInitQueries adds sidecar database schema related // queries to a mock db. // This is for unit tests only! -func AddSchemaInitQueries(db *fakesqldb.DB, populateTables bool) { - once.Do(loadSchemaDefinitions) +func AddSchemaInitQueries(db *fakesqldb.DB, populateTables bool, parser *sqlparser.Parser) { + once.Do(func() { + loadSchemaDefinitions(parser) + }) result := &sqltypes.Result{} for _, q := range sidecar.DBInitQueryPatterns { db.AddQueryPattern(q, result) @@ -485,10 +517,15 @@ func AddSchemaInitQueries(db *fakesqldb.DB, populateTables bool) { sqlModeResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields( "sql_mode", "varchar"), - "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION", + config.DefaultSQLMode, ) db.AddQuery("select @@session.sql_mode as sql_mode", sqlModeResult) - + collationResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "@@global.collation_server ", + "varchar"), + "utf8mb4_0900_ai_ci", + ) + db.AddQuery("select @@global.collation_server", collationResult) db.AddQuery("set @@session.sql_mode=''", &sqltypes.Result{}) } diff --git a/go/vt/sidecardb/sidecardb_test.go b/go/vt/sidecardb/sidecardb_test.go index 22147c960e9..55c2c6cd6b5 100644 --- a/go/vt/sidecardb/sidecardb_test.go +++ b/go/vt/sidecardb/sidecardb_test.go @@ -25,7 +25,9 @@ import ( "testing" "vitess.io/vitess/go/constants/sidecar" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/require" @@ -41,12 +43,13 @@ func TestInitErrors(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - AddSchemaInitQueries(db, false) + env := vtenv.NewTestEnv() + AddSchemaInitQueries(db, false, env.Parser()) ddlErrorCount.Set(0) ddlCount.Set(0) - cp := db.ConnParams() + cp := dbconfigs.New(db.ConnParams()) conn, err := cp.Connect(ctx) require.NoError(t, err) @@ -69,7 +72,7 @@ func TestInitErrors(t *testing.T) { } // simulate errors for the table creation DDLs applied for tables specified in schemaErrors - stmt, err := sqlparser.Parse(query) + stmt, err := env.Parser().Parse(query) if err != nil { return nil, err } @@ -85,7 +88,7 @@ func TestInitErrors(t *testing.T) { } require.Equal(t, int64(0), getDDLCount()) - err = Init(ctx, exec) + err = Init(ctx, env, exec) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)-len(schemaErrors)), getDDLCount()) require.Equal(t, int64(len(schemaErrors)), getDDLErrorCount()) @@ -124,11 +127,12 @@ func TestMiscSidecarDB(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - AddSchemaInitQueries(db, false) + env := vtenv.NewTestEnv() + AddSchemaInitQueries(db, false, env.Parser()) db.AddQuery("use dbname", &sqltypes.Result{}) db.AddQueryPattern("set @@session.sql_mode=.*", &sqltypes.Result{}) - cp := db.ConnParams() + cp := dbconfigs.New(db.ConnParams()) conn, err := cp.Connect(ctx) require.NoError(t, err) exec := func(ctx context.Context, query string, maxRows int, useDB bool) (*sqltypes.Result, error) { @@ -149,22 +153,22 @@ func TestMiscSidecarDB(t *testing.T) { require.NoError(t, err) db.AddQuery(dbeq, result) db.AddQuery(sidecar.GetCreateQuery(), &sqltypes.Result{}) - AddSchemaInitQueries(db, false) + AddSchemaInitQueries(db, false, env.Parser()) // tests init on empty db ddlErrorCount.Set(0) ddlCount.Set(0) require.Equal(t, int64(0), getDDLCount()) - err = Init(ctx, exec) + err = Init(ctx, env, exec) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)), getDDLCount()) // Include the table DDLs in the expected queries. // This causes them to NOT be created again. - AddSchemaInitQueries(db, true) + AddSchemaInitQueries(db, true, env.Parser()) // tests init on already inited db - err = Init(ctx, exec) + err = Init(ctx, env, exec) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)), getDDLCount()) @@ -172,6 +176,7 @@ func TestMiscSidecarDB(t *testing.T) { si := &schemaInit{ ctx: ctx, exec: exec, + env: env, } err = si.setCurrentDatabase(sidecar.GetIdentifier()) @@ -196,9 +201,10 @@ func TestValidateSchema(t *testing.T) { {"invalid table name", "t1", "create table if not exists t2(i int)", true}, {"qualifier", "t1", "create table if not exists vt_product.t1(i int)", true}, } + parser := sqlparser.NewTestParser() for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { - _, err := validateSchemaDefinition(tc.name, tc.schema) + _, err := validateSchemaDefinition(tc.name, tc.schema, parser) if tc.mustError { require.Error(t, err) } else { @@ -220,13 +226,15 @@ func TestAlterTableAlgorithm(t *testing.T) { {"add column", "t1", "create table if not exists _vt.t1(i int)", "create table if not exists _vt.t1(i int, i1 int)"}, {"modify column", "t1", "create table if not exists _vt.t1(i int)", "create table if not exists _vt.t(i float)"}, } - si := &schemaInit{} + si := &schemaInit{ + env: vtenv.NewTestEnv(), + } copyAlgo := sqlparser.AlgorithmValue("COPY") for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { diff, err := si.findTableSchemaDiff(tc.tableName, tc.currentSchema, tc.desiredSchema) require.NoError(t, err) - stmt, err := sqlparser.Parse(diff) + stmt, err := si.env.Parser().Parse(diff) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) diff --git a/go/vt/sqlparser/analyzer.go b/go/vt/sqlparser/analyzer.go index b4015f7937b..ea0773d99cc 100644 --- a/go/vt/sqlparser/analyzer.go +++ b/go/vt/sqlparser/analyzer.go @@ -344,8 +344,8 @@ func IsDMLStatement(stmt Statement) bool { // TableFromStatement returns the qualified table name for the query. // This works only for select statements. -func TableFromStatement(sql string) (TableName, error) { - stmt, err := Parse(sql) +func (p *Parser) TableFromStatement(sql string) (TableName, error) { + stmt, err := p.Parse(sql) if err != nil { return TableName{}, err } diff --git a/go/vt/sqlparser/analyzer_test.go b/go/vt/sqlparser/analyzer_test.go index 9f6a451770e..0a2de52ef19 100644 --- a/go/vt/sqlparser/analyzer_test.go +++ b/go/vt/sqlparser/analyzer_test.go @@ -145,8 +145,9 @@ func TestSplitAndExpression(t *testing.T) { sql: "select * from t where (a = 1 and ((b = 1 and c = 1)))", out: []string{"a = 1", "b = 1", "c = 1"}, }} + parser := NewTestParser() for _, tcase := range testcases { - stmt, err := Parse(tcase.sql) + stmt, err := parser.Parse(tcase.sql) assert.NoError(t, err) var expr Expr if where := stmt.(*Select).Where; where != nil { @@ -259,9 +260,9 @@ func TestTableFromStatement(t *testing.T) { in: "bad query", out: "syntax error at position 4 near 'bad'", }} - + parser := NewTestParser() for _, tc := range testcases { - name, err := TableFromStatement(tc.in) + name, err := parser.TableFromStatement(tc.in) var got string if err != nil { got = err.Error() @@ -288,8 +289,9 @@ func TestGetTableName(t *testing.T) { out: "", }} + parser := NewTestParser() for _, tc := range testcases { - tree, err := Parse(tc.in) + tree, err := parser.Parse(tc.in) if err != nil { t.Error(err) continue diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 1ff48b8be78..569148a224a 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -53,16 +53,17 @@ type ( Commented } + OrderAndLimit interface { + AddOrder(*Order) + SetLimit(*Limit) + } + // SelectStatement any SELECT statement. SelectStatement interface { Statement InsertRows + OrderAndLimit iSelectStatement() - AddOrder(*Order) - SetOrderBy(OrderBy) - GetOrderBy() OrderBy - GetLimit() *Limit - SetLimit(*Limit) GetLock() Lock SetLock(lock Lock) SetInto(into *SelectInto) @@ -72,6 +73,9 @@ type ( GetColumns() SelectExprs Commented IsDistinct() bool + GetOrderBy() OrderBy + SetOrderBy(OrderBy) + GetLimit() *Limit } // DDLStatement represents any DDL Statement @@ -352,7 +356,7 @@ type ( With *With Comments *ParsedComments Ignore Ignore - TableExprs TableExprs + TableExprs []TableExpr Exprs UpdateExprs Where *Where OrderBy OrderBy @@ -365,8 +369,8 @@ type ( With *With Ignore Ignore Comments *ParsedComments + TableExprs []TableExpr Targets TableNames - TableExprs TableExprs Partitions Partitions Where *Where OrderBy OrderBy @@ -712,6 +716,10 @@ type ( IndexType int8 ) +var _ OrderAndLimit = (*Select)(nil) +var _ OrderAndLimit = (*Update)(nil) +var _ OrderAndLimit = (*Delete)(nil) + func (*Union) iStatement() {} func (*Select) iStatement() {} func (*Stream) iStatement() {} @@ -1807,10 +1815,10 @@ type ColumnType struct { Options *ColumnTypeOptions // Numeric field options - Length *Literal + Length *int Unsigned bool Zerofill bool - Scale *Literal + Scale *int // Text field options Charset ColumnCharset @@ -3419,8 +3427,8 @@ func (ListArg) iColTuple() {} // ConvertType represents the type in call to CONVERT(expr, type) type ConvertType struct { Type string - Length *Literal - Scale *Literal + Length *int + Scale *int Charset ColumnCharset } diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go index b29b4c90047..9b1128c3cbf 100644 --- a/go/vt/sqlparser/ast_clone.go +++ b/go/vt/sqlparser/ast_clone.go @@ -964,8 +964,8 @@ func CloneRefOfColumnType(n *ColumnType) *ColumnType { } out := *n out.Options = CloneRefOfColumnTypeOptions(n.Options) - out.Length = CloneRefOfLiteral(n.Length) - out.Scale = CloneRefOfLiteral(n.Scale) + out.Length = CloneRefOfInt(n.Length) + out.Scale = CloneRefOfInt(n.Scale) out.Charset = CloneColumnCharset(n.Charset) out.EnumValues = CloneSliceOfString(n.EnumValues) return &out @@ -1054,8 +1054,8 @@ func CloneRefOfConvertType(n *ConvertType) *ConvertType { return nil } out := *n - out.Length = CloneRefOfLiteral(n.Length) - out.Scale = CloneRefOfLiteral(n.Scale) + out.Length = CloneRefOfInt(n.Length) + out.Scale = CloneRefOfInt(n.Scale) out.Charset = CloneColumnCharset(n.Charset) return &out } @@ -1175,8 +1175,8 @@ func CloneRefOfDelete(n *Delete) *Delete { out := *n out.With = CloneRefOfWith(n.With) out.Comments = CloneRefOfParsedComments(n.Comments) + out.TableExprs = CloneSliceOfTableExpr(n.TableExprs) out.Targets = CloneTableNames(n.Targets) - out.TableExprs = CloneTableExprs(n.TableExprs) out.Partitions = ClonePartitions(n.Partitions) out.Where = CloneRefOfWhere(n.Where) out.OrderBy = CloneOrderBy(n.OrderBy) @@ -3143,7 +3143,7 @@ func CloneRefOfUpdate(n *Update) *Update { out := *n out.With = CloneRefOfWith(n.With) out.Comments = CloneRefOfParsedComments(n.Comments) - out.TableExprs = CloneTableExprs(n.TableExprs) + out.TableExprs = CloneSliceOfTableExpr(n.TableExprs) out.Exprs = CloneUpdateExprs(n.Exprs) out.Where = CloneRefOfWhere(n.Where) out.OrderBy = CloneOrderBy(n.OrderBy) @@ -4334,6 +4334,15 @@ func CloneRefOfColumnTypeOptions(n *ColumnTypeOptions) *ColumnTypeOptions { return &out } +// CloneRefOfInt creates a deep clone of the input. +func CloneRefOfInt(n *int) *int { + if n == nil { + return nil + } + out := *n + return &out +} + // CloneColumnCharset creates a deep clone of the input. func CloneColumnCharset(n ColumnCharset) ColumnCharset { return *CloneRefOfColumnCharset(&n) @@ -4349,6 +4358,18 @@ func CloneSliceOfString(n []string) []string { return res } +// CloneSliceOfTableExpr creates a deep clone of the input. +func CloneSliceOfTableExpr(n []TableExpr) []TableExpr { + if n == nil { + return nil + } + res := make([]TableExpr, len(n)) + for i, x := range n { + res[i] = CloneTableExpr(x) + } + return res +} + // CloneSliceOfRefOfVariable creates a deep clone of the input. func CloneSliceOfRefOfVariable(n []*Variable) []*Variable { if n == nil { @@ -4510,15 +4531,6 @@ func CloneComments(n Comments) Comments { return res } -// CloneRefOfInt creates a deep clone of the input. -func CloneRefOfInt(n *int) *int { - if n == nil { - return nil - } - out := *n - return &out -} - // CloneSliceOfRefOfPartitionDefinition creates a deep clone of the input. func CloneSliceOfRefOfPartitionDefinition(n []*PartitionDefinition) []*PartitionDefinition { if n == nil { @@ -4553,18 +4565,6 @@ func CloneRefOfRootNode(n *RootNode) *RootNode { return &out } -// CloneSliceOfTableExpr creates a deep clone of the input. -func CloneSliceOfTableExpr(n []TableExpr) []TableExpr { - if n == nil { - return nil - } - res := make([]TableExpr, len(n)) - for i, x := range n { - res[i] = CloneTableExpr(x) - } - return res -} - // CloneRefOfTableName creates a deep clone of the input. func CloneRefOfTableName(n *TableName) *TableName { if n == nil { @@ -4666,7 +4666,7 @@ func CloneRefOfIndexColumn(n *IndexColumn) *IndexColumn { } out := *n out.Column = CloneIdentifierCI(n.Column) - out.Length = CloneRefOfLiteral(n.Length) + out.Length = CloneRefOfInt(n.Length) out.Expression = CloneExpr(n.Expression) return &out } diff --git a/go/vt/sqlparser/ast_copy_on_rewrite.go b/go/vt/sqlparser/ast_copy_on_rewrite.go index 86dda29ebcf..1ce13a61b0a 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite.go @@ -1445,18 +1445,6 @@ func (c *cow) copyOnRewriteRefOfColumnType(n *ColumnType, parent SQLNode) (out S } out = n if c.pre == nil || c.pre(n, parent) { - _Length, changedLength := c.copyOnRewriteRefOfLiteral(n.Length, n) - _Scale, changedScale := c.copyOnRewriteRefOfLiteral(n.Scale, n) - if changedLength || changedScale { - res := *n - res.Length, _ = _Length.(*Literal) - res.Scale, _ = _Scale.(*Literal) - out = &res - if c.cloned != nil { - c.cloned(n, out) - } - changed = true - } } if c.post != nil { out, changed = c.postVisit(out, parent, changed) @@ -1616,18 +1604,6 @@ func (c *cow) copyOnRewriteRefOfConvertType(n *ConvertType, parent SQLNode) (out } out = n if c.pre == nil || c.pre(n, parent) { - _Length, changedLength := c.copyOnRewriteRefOfLiteral(n.Length, n) - _Scale, changedScale := c.copyOnRewriteRefOfLiteral(n.Scale, n) - if changedLength || changedScale { - res := *n - res.Length, _ = _Length.(*Literal) - res.Scale, _ = _Scale.(*Literal) - out = &res - if c.cloned != nil { - c.cloned(n, out) - } - changed = true - } } if c.post != nil { out, changed = c.postVisit(out, parent, changed) @@ -1850,18 +1826,26 @@ func (c *cow) copyOnRewriteRefOfDelete(n *Delete, parent SQLNode) (out SQLNode, if c.pre == nil || c.pre(n, parent) { _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Comments, changedComments := c.copyOnRewriteRefOfParsedComments(n.Comments, n) + var changedTableExprs bool + _TableExprs := make([]TableExpr, len(n.TableExprs)) + for x, el := range n.TableExprs { + this, changed := c.copyOnRewriteTableExpr(el, n) + _TableExprs[x] = this.(TableExpr) + if changed { + changedTableExprs = true + } + } _Targets, changedTargets := c.copyOnRewriteTableNames(n.Targets, n) - _TableExprs, changedTableExprs := c.copyOnRewriteTableExprs(n.TableExprs, n) _Partitions, changedPartitions := c.copyOnRewritePartitions(n.Partitions, n) _Where, changedWhere := c.copyOnRewriteRefOfWhere(n.Where, n) _OrderBy, changedOrderBy := c.copyOnRewriteOrderBy(n.OrderBy, n) _Limit, changedLimit := c.copyOnRewriteRefOfLimit(n.Limit, n) - if changedWith || changedComments || changedTargets || changedTableExprs || changedPartitions || changedWhere || changedOrderBy || changedLimit { + if changedWith || changedComments || changedTableExprs || changedTargets || changedPartitions || changedWhere || changedOrderBy || changedLimit { res := *n res.With, _ = _With.(*With) res.Comments, _ = _Comments.(*ParsedComments) + res.TableExprs = _TableExprs res.Targets, _ = _Targets.(TableNames) - res.TableExprs, _ = _TableExprs.(TableExprs) res.Partitions, _ = _Partitions.(Partitions) res.Where, _ = _Where.(*Where) res.OrderBy, _ = _OrderBy.(OrderBy) @@ -6023,7 +6007,15 @@ func (c *cow) copyOnRewriteRefOfUpdate(n *Update, parent SQLNode) (out SQLNode, if c.pre == nil || c.pre(n, parent) { _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Comments, changedComments := c.copyOnRewriteRefOfParsedComments(n.Comments, n) - _TableExprs, changedTableExprs := c.copyOnRewriteTableExprs(n.TableExprs, n) + var changedTableExprs bool + _TableExprs := make([]TableExpr, len(n.TableExprs)) + for x, el := range n.TableExprs { + this, changed := c.copyOnRewriteTableExpr(el, n) + _TableExprs[x] = this.(TableExpr) + if changed { + changedTableExprs = true + } + } _Exprs, changedExprs := c.copyOnRewriteUpdateExprs(n.Exprs, n) _Where, changedWhere := c.copyOnRewriteRefOfWhere(n.Where, n) _OrderBy, changedOrderBy := c.copyOnRewriteOrderBy(n.OrderBy, n) @@ -6032,7 +6024,7 @@ func (c *cow) copyOnRewriteRefOfUpdate(n *Update, parent SQLNode) (out SQLNode, res := *n res.With, _ = _With.(*With) res.Comments, _ = _Comments.(*ParsedComments) - res.TableExprs, _ = _TableExprs.(TableExprs) + res.TableExprs = _TableExprs res.Exprs, _ = _Exprs.(UpdateExprs) res.Where, _ = _Where.(*Where) res.OrderBy, _ = _OrderBy.(OrderBy) diff --git a/go/vt/sqlparser/ast_copy_on_rewrite_test.go b/go/vt/sqlparser/ast_copy_on_rewrite_test.go index 389b2a4bc29..bb2bd5b886e 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite_test.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite_test.go @@ -24,8 +24,9 @@ import ( ) func TestCopyOnRewrite(t *testing.T) { + parser := NewTestParser() // rewrite an expression without changing the original - expr, err := ParseExpr("a = b") + expr, err := parser.ParseExpr("a = b") require.NoError(t, err) out := CopyOnRewrite(expr, nil, func(cursor *CopyOnWriteCursor) { col, ok := cursor.Node().(*ColName) @@ -42,9 +43,10 @@ func TestCopyOnRewrite(t *testing.T) { } func TestCopyOnRewriteDeeper(t *testing.T) { + parser := NewTestParser() // rewrite an expression without changing the original. the changed happens deep in the syntax tree, // here we are testing that all ancestors up to the root are cloned correctly - expr, err := ParseExpr("a + b * c = 12") + expr, err := parser.ParseExpr("a + b * c = 12") require.NoError(t, err) var path []string out := CopyOnRewrite(expr, nil, func(cursor *CopyOnWriteCursor) { @@ -72,8 +74,9 @@ func TestCopyOnRewriteDeeper(t *testing.T) { } func TestDontCopyWithoutRewrite(t *testing.T) { + parser := NewTestParser() // when no rewriting happens, we want the original back - expr, err := ParseExpr("a = b") + expr, err := parser.ParseExpr("a = b") require.NoError(t, err) out := CopyOnRewrite(expr, nil, func(cursor *CopyOnWriteCursor) {}, nil) @@ -81,9 +84,10 @@ func TestDontCopyWithoutRewrite(t *testing.T) { } func TestStopTreeWalk(t *testing.T) { + parser := NewTestParser() // stop walking down part of the AST original := "a = b + c" - expr, err := ParseExpr(original) + expr, err := parser.ParseExpr(original) require.NoError(t, err) out := CopyOnRewrite(expr, func(node, parent SQLNode) bool { _, ok := node.(*BinaryExpr) @@ -102,9 +106,10 @@ func TestStopTreeWalk(t *testing.T) { } func TestStopTreeWalkButStillVisit(t *testing.T) { + parser := NewTestParser() // here we are asserting that even when we stop at the binary expression, we still visit it in the post visitor original := "1337 = b + c" - expr, err := ParseExpr(original) + expr, err := parser.ParseExpr(original) require.NoError(t, err) out := CopyOnRewrite(expr, func(node, parent SQLNode) bool { _, ok := node.(*BinaryExpr) diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index 9beed3a8242..47ba31fcd20 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -2112,8 +2112,8 @@ func (cmp *Comparator) RefOfColumnType(a, b *ColumnType) bool { a.Unsigned == b.Unsigned && a.Zerofill == b.Zerofill && cmp.RefOfColumnTypeOptions(a.Options, b.Options) && - cmp.RefOfLiteral(a.Length, b.Length) && - cmp.RefOfLiteral(a.Scale, b.Scale) && + cmp.RefOfInt(a.Length, b.Length) && + cmp.RefOfInt(a.Scale, b.Scale) && cmp.ColumnCharset(a.Charset, b.Charset) && cmp.SliceOfString(a.EnumValues, b.EnumValues) } @@ -2213,8 +2213,8 @@ func (cmp *Comparator) RefOfConvertType(a, b *ConvertType) bool { return false } return a.Type == b.Type && - cmp.RefOfLiteral(a.Length, b.Length) && - cmp.RefOfLiteral(a.Scale, b.Scale) && + cmp.RefOfInt(a.Length, b.Length) && + cmp.RefOfInt(a.Scale, b.Scale) && cmp.ColumnCharset(a.Charset, b.Charset) } @@ -2362,8 +2362,8 @@ func (cmp *Comparator) RefOfDelete(a, b *Delete) bool { return cmp.RefOfWith(a.With, b.With) && a.Ignore == b.Ignore && cmp.RefOfParsedComments(a.Comments, b.Comments) && + cmp.SliceOfTableExpr(a.TableExprs, b.TableExprs) && cmp.TableNames(a.Targets, b.Targets) && - cmp.TableExprs(a.TableExprs, b.TableExprs) && cmp.Partitions(a.Partitions, b.Partitions) && cmp.RefOfWhere(a.Where, b.Where) && cmp.OrderBy(a.OrderBy, b.OrderBy) && @@ -4621,7 +4621,7 @@ func (cmp *Comparator) RefOfUpdate(a, b *Update) bool { return cmp.RefOfWith(a.With, b.With) && cmp.RefOfParsedComments(a.Comments, b.Comments) && a.Ignore == b.Ignore && - cmp.TableExprs(a.TableExprs, b.TableExprs) && + cmp.SliceOfTableExpr(a.TableExprs, b.TableExprs) && cmp.UpdateExprs(a.Exprs, b.Exprs) && cmp.RefOfWhere(a.Where, b.Where) && cmp.OrderBy(a.OrderBy, b.OrderBy) && @@ -7173,6 +7173,17 @@ func (cmp *Comparator) RefOfColumnTypeOptions(a, b *ColumnTypeOptions) bool { cmp.RefOfLiteral(a.SRID, b.SRID) } +// RefOfInt does deep equals between the two objects. +func (cmp *Comparator) RefOfInt(a, b *int) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return *a == *b +} + // ColumnCharset does deep equals between the two objects. func (cmp *Comparator) ColumnCharset(a, b ColumnCharset) bool { return a.Name == b.Name && @@ -7192,6 +7203,19 @@ func (cmp *Comparator) SliceOfString(a, b []string) bool { return true } +// SliceOfTableExpr does deep equals between the two objects. +func (cmp *Comparator) SliceOfTableExpr(a, b []TableExpr) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !cmp.TableExpr(a[i], b[i]) { + return false + } + } + return true +} + // SliceOfRefOfVariable does deep equals between the two objects. func (cmp *Comparator) SliceOfRefOfVariable(a, b []*Variable) bool { if len(a) != len(b) { @@ -7371,17 +7395,6 @@ func (cmp *Comparator) Comments(a, b Comments) bool { return true } -// RefOfInt does deep equals between the two objects. -func (cmp *Comparator) RefOfInt(a, b *int) bool { - if a == b { - return true - } - if a == nil || b == nil { - return false - } - return *a == *b -} - // SliceOfRefOfPartitionDefinition does deep equals between the two objects. func (cmp *Comparator) SliceOfRefOfPartitionDefinition(a, b []*PartitionDefinition) bool { if len(a) != len(b) { @@ -7419,19 +7432,6 @@ func (cmp *Comparator) RefOfRootNode(a, b *RootNode) bool { return cmp.SQLNode(a.SQLNode, b.SQLNode) } -// SliceOfTableExpr does deep equals between the two objects. -func (cmp *Comparator) SliceOfTableExpr(a, b []TableExpr) bool { - if len(a) != len(b) { - return false - } - for i := 0; i < len(a); i++ { - if !cmp.TableExpr(a[i], b[i]) { - return false - } - } - return true -} - // RefOfTableName does deep equals between the two objects. func (cmp *Comparator) RefOfTableName(a, b *TableName) bool { if a == b { @@ -7551,7 +7551,7 @@ func (cmp *Comparator) RefOfIndexColumn(a, b *IndexColumn) bool { return false } return cmp.IdentifierCI(a.Column, b.Column) && - cmp.RefOfLiteral(a.Length, b.Length) && + cmp.RefOfInt(a.Length, b.Length) && cmp.Expr(a.Expression, b.Expression) && a.Direction == b.Direction } diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go index 3176ea2c12e..0e7ca0a8231 100644 --- a/go/vt/sqlparser/ast_format.go +++ b/go/vt/sqlparser/ast_format.go @@ -158,9 +158,14 @@ func (node *Update) Format(buf *TrackedBuffer) { if node.With != nil { buf.astPrintf(node, "%v", node.With) } - buf.astPrintf(node, "update %v%s%v set %v%v%v%v", - node.Comments, node.Ignore.ToString(), node.TableExprs, - node.Exprs, node.Where, node.OrderBy, node.Limit) + buf.astPrintf(node, "update %v%s", + node.Comments, node.Ignore.ToString()) + prefix := "" + for _, expr := range node.TableExprs { + buf.astPrintf(node, "%s%v", prefix, expr) + prefix = ", " + } + buf.astPrintf(node, " set %v%v%v%v", node.Exprs, node.Where, node.OrderBy, node.Limit) } // Format formats the node. @@ -172,10 +177,15 @@ func (node *Delete) Format(buf *TrackedBuffer) { if node.Ignore { buf.literal("ignore ") } - if node.Targets != nil { + if node.Targets != nil && !node.IsSingleAliasExpr() { buf.astPrintf(node, "%v ", node.Targets) } - buf.astPrintf(node, "from %v%v%v%v%v", node.TableExprs, node.Partitions, node.Where, node.OrderBy, node.Limit) + prefix := "from " + for _, expr := range node.TableExprs { + buf.astPrintf(node, "%s%v", prefix, expr) + prefix = ", " + } + buf.astPrintf(node, "%v%v%v%v", node.Partitions, node.Where, node.OrderBy, node.Limit) } // Format formats the node. @@ -289,6 +299,10 @@ func (node *AlterMigration) Format(buf *TrackedBuffer) { alterType = "unthrottle" case UnthrottleAllMigrationType: alterType = "unthrottle all" + case ForceCutOverMigrationType: + alterType = "force_cutover" + case ForceCutOverAllMigrationType: + alterType = "force_cutover all" } buf.astPrintf(node, " %#s", alterType) if node.Expire != "" { @@ -682,10 +696,10 @@ func (ct *ColumnType) Format(buf *TrackedBuffer) { buf.astPrintf(ct, "%#s", ct.Type) if ct.Length != nil && ct.Scale != nil { - buf.astPrintf(ct, "(%v,%v)", ct.Length, ct.Scale) + buf.astPrintf(ct, "(%d,%d)", *ct.Length, *ct.Scale) } else if ct.Length != nil { - buf.astPrintf(ct, "(%v)", ct.Length) + buf.astPrintf(ct, "(%d)", *ct.Length) } if ct.EnumValues != nil { @@ -810,7 +824,7 @@ func (idx *IndexDefinition) Format(buf *TrackedBuffer) { } else { buf.astPrintf(idx, "%v", col.Column) if col.Length != nil { - buf.astPrintf(idx, "(%v)", col.Length) + buf.astPrintf(idx, "(%d)", *col.Length) } } if col.Direction == DescOrder { @@ -831,7 +845,7 @@ func (idx *IndexDefinition) Format(buf *TrackedBuffer) { // Format formats the node. func (ii *IndexInfo) Format(buf *TrackedBuffer) { - if !ii.ConstraintName.IsEmpty() { + if ii.ConstraintName.NotEmpty() { buf.astPrintf(ii, "constraint %v ", ii.ConstraintName) } switch ii.Type { @@ -847,7 +861,7 @@ func (ii *IndexInfo) Format(buf *TrackedBuffer) { case IndexTypeFullText: buf.astPrintf(ii, "%s %s", keywordStrings[FULLTEXT], keywordStrings[KEY]) } - if !ii.Name.IsEmpty() { + if ii.Name.NotEmpty() { buf.astPrintf(ii, " %v", ii.Name) } } @@ -883,7 +897,7 @@ func (node VindexParam) Format(buf *TrackedBuffer) { // Format formats the node. func (c *ConstraintDefinition) Format(buf *TrackedBuffer) { - if !c.Name.IsEmpty() { + if c.Name.NotEmpty() { buf.astPrintf(c, "constraint %v ", c.Name) } c.Details.Format(buf) @@ -1114,7 +1128,7 @@ func (node *StarExpr) Format(buf *TrackedBuffer) { // Format formats the node. func (node *AliasedExpr) Format(buf *TrackedBuffer) { buf.astPrintf(node, "%v", node.Expr) - if !node.As.IsEmpty() { + if node.As.NotEmpty() { buf.astPrintf(node, " as %v", node.As) } } @@ -1163,7 +1177,7 @@ func (node TableExprs) Format(buf *TrackedBuffer) { // Format formats the node. func (node *AliasedTableExpr) Format(buf *TrackedBuffer) { buf.astPrintf(node, "%v%v", node.Expr, node.Partitions) - if !node.As.IsEmpty() { + if node.As.NotEmpty() { buf.astPrintf(node, " as %v", node.As) if len(node.Columns) != 0 { buf.astPrintf(node, "%v", node.Columns) @@ -1189,7 +1203,7 @@ func (node TableName) Format(buf *TrackedBuffer) { if node.IsEmpty() { return } - if !node.Qualifier.IsEmpty() { + if node.Qualifier.NotEmpty() { buf.astPrintf(node, "%v.", node.Qualifier) } buf.astPrintf(node, "%v", node.Name) @@ -1227,7 +1241,7 @@ func (node IndexHints) Format(buf *TrackedBuffer) { // Format formats the node. func (node *IndexHint) Format(buf *TrackedBuffer) { - buf.astPrintf(node, " %sindex ", node.Type.ToString()) + buf.astPrintf(node, " %s ", node.Type.ToString()) if node.ForType != NoForType { buf.astPrintf(node, "for %s ", node.ForType.ToString()) } @@ -1544,7 +1558,7 @@ func (node *CollateExpr) Format(buf *TrackedBuffer) { // Format formats the node. func (node *FuncExpr) Format(buf *TrackedBuffer) { - if !node.Qualifier.IsEmpty() { + if node.Qualifier.NotEmpty() { buf.astPrintf(node, "%v.", node.Qualifier) } // Function names should not be back-quoted even @@ -1598,7 +1612,7 @@ func (node *JSONStorageSizeExpr) Format(buf *TrackedBuffer) { // Format formats the node func (node *OverClause) Format(buf *TrackedBuffer) { buf.WriteString("over") - if !node.WindowName.IsEmpty() { + if node.WindowName.NotEmpty() { buf.astPrintf(node, " %v", node.WindowName) } if node.WindowSpec != nil { @@ -1608,7 +1622,7 @@ func (node *OverClause) Format(buf *TrackedBuffer) { // Format formats the node func (node *WindowSpecification) Format(buf *TrackedBuffer) { - if !node.Name.IsEmpty() { + if node.Name.NotEmpty() { buf.astPrintf(node, " %v", node.Name) } if node.PartitionClause != nil { @@ -1838,9 +1852,9 @@ func (node *ConvertUsingExpr) Format(buf *TrackedBuffer) { func (node *ConvertType) Format(buf *TrackedBuffer) { buf.astPrintf(node, "%#s", node.Type) if node.Length != nil { - buf.astPrintf(node, "(%v", node.Length) + buf.astPrintf(node, "(%d", *node.Length) if node.Scale != nil { - buf.astPrintf(node, ", %v", node.Scale) + buf.astPrintf(node, ", %d", *node.Scale) } buf.astPrintf(node, ")") } @@ -2020,7 +2034,7 @@ func (node *ShowBasic) Format(buf *TrackedBuffer) { if !node.Tbl.IsEmpty() { buf.astPrintf(node, " from %v", node.Tbl) } - if !node.DbName.IsEmpty() { + if node.DbName.NotEmpty() { buf.astPrintf(node, " from %v", node.DbName) } buf.astPrintf(node, "%v", node.Filter) @@ -2070,7 +2084,7 @@ func (node *CreateDatabase) Format(buf *TrackedBuffer) { // Format formats the node. func (node *AlterDatabase) Format(buf *TrackedBuffer) { buf.literal("alter database") - if !node.DBName.IsEmpty() { + if node.DBName.NotEmpty() { buf.astPrintf(node, " %v", node.DBName) } if node.UpdateDataDirectory { @@ -2354,7 +2368,7 @@ func (node *DropColumn) Format(buf *TrackedBuffer) { // Format formats the node func (node *DropKey) Format(buf *TrackedBuffer) { buf.astPrintf(node, "drop %s", node.Type.ToString()) - if !node.Name.IsEmpty() { + if node.Name.NotEmpty() { buf.astPrintf(node, " %v", node.Name) } } diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go index b99c96c87ab..a7f1a3c1e93 100644 --- a/go/vt/sqlparser/ast_format_fast.go +++ b/go/vt/sqlparser/ast_format_fast.go @@ -234,17 +234,17 @@ func (node *Update) FormatFast(buf *TrackedBuffer) { buf.WriteString("update ") node.Comments.FormatFast(buf) buf.WriteString(node.Ignore.ToString()) - node.TableExprs.FormatFast(buf) + prefix := "" + for _, expr := range node.TableExprs { + buf.WriteString(prefix) + expr.FormatFast(buf) + prefix = ", " + } buf.WriteString(" set ") - node.Exprs.FormatFast(buf) - node.Where.FormatFast(buf) - node.OrderBy.FormatFast(buf) - node.Limit.FormatFast(buf) - } // FormatFast formats the node. @@ -257,12 +257,16 @@ func (node *Delete) FormatFast(buf *TrackedBuffer) { if node.Ignore { buf.WriteString("ignore ") } - if node.Targets != nil { + if node.Targets != nil && !node.IsSingleAliasExpr() { node.Targets.FormatFast(buf) buf.WriteByte(' ') } - buf.WriteString("from ") - node.TableExprs.FormatFast(buf) + prefix := "from " + for _, expr := range node.TableExprs { + buf.WriteString(prefix) + expr.FormatFast(buf) + prefix = ", " + } node.Partitions.FormatFast(buf) node.Where.FormatFast(buf) node.OrderBy.FormatFast(buf) @@ -416,6 +420,10 @@ func (node *AlterMigration) FormatFast(buf *TrackedBuffer) { alterType = "unthrottle" case UnthrottleAllMigrationType: alterType = "unthrottle all" + case ForceCutOverMigrationType: + alterType = "force_cutover" + case ForceCutOverAllMigrationType: + alterType = "force_cutover all" } buf.WriteByte(' ') buf.WriteString(alterType) @@ -899,14 +907,14 @@ func (ct *ColumnType) FormatFast(buf *TrackedBuffer) { if ct.Length != nil && ct.Scale != nil { buf.WriteByte('(') - ct.Length.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *ct.Length)) buf.WriteByte(',') - ct.Scale.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *ct.Scale)) buf.WriteByte(')') } else if ct.Length != nil { buf.WriteByte('(') - ct.Length.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *ct.Length)) buf.WriteByte(')') } @@ -1103,7 +1111,7 @@ func (idx *IndexDefinition) FormatFast(buf *TrackedBuffer) { col.Column.FormatFast(buf) if col.Length != nil { buf.WriteByte('(') - col.Length.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *col.Length)) buf.WriteByte(')') } } @@ -1128,7 +1136,7 @@ func (idx *IndexDefinition) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node. func (ii *IndexInfo) FormatFast(buf *TrackedBuffer) { - if !ii.ConstraintName.IsEmpty() { + if ii.ConstraintName.NotEmpty() { buf.WriteString("constraint ") ii.ConstraintName.FormatFast(buf) buf.WriteByte(' ') @@ -1154,7 +1162,7 @@ func (ii *IndexInfo) FormatFast(buf *TrackedBuffer) { buf.WriteByte(' ') buf.WriteString(keywordStrings[KEY]) } - if !ii.Name.IsEmpty() { + if ii.Name.NotEmpty() { buf.WriteByte(' ') ii.Name.FormatFast(buf) } @@ -1196,7 +1204,7 @@ func (node VindexParam) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node. func (c *ConstraintDefinition) FormatFast(buf *TrackedBuffer) { - if !c.Name.IsEmpty() { + if c.Name.NotEmpty() { buf.WriteString("constraint ") c.Name.FormatFast(buf) buf.WriteByte(' ') @@ -1474,7 +1482,7 @@ func (node *StarExpr) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node. func (node *AliasedExpr) FormatFast(buf *TrackedBuffer) { node.Expr.FormatFast(buf) - if !node.As.IsEmpty() { + if node.As.NotEmpty() { buf.WriteString(" as ") node.As.FormatFast(buf) } @@ -1530,7 +1538,7 @@ func (node TableExprs) FormatFast(buf *TrackedBuffer) { func (node *AliasedTableExpr) FormatFast(buf *TrackedBuffer) { node.Expr.FormatFast(buf) node.Partitions.FormatFast(buf) - if !node.As.IsEmpty() { + if node.As.NotEmpty() { buf.WriteString(" as ") node.As.FormatFast(buf) if len(node.Columns) != 0 { @@ -1558,7 +1566,7 @@ func (node TableName) FormatFast(buf *TrackedBuffer) { if node.IsEmpty() { return } - if !node.Qualifier.IsEmpty() { + if node.Qualifier.NotEmpty() { node.Qualifier.FormatFast(buf) buf.WriteByte('.') } @@ -1608,7 +1616,7 @@ func (node IndexHints) FormatFast(buf *TrackedBuffer) { func (node *IndexHint) FormatFast(buf *TrackedBuffer) { buf.WriteByte(' ') buf.WriteString(node.Type.ToString()) - buf.WriteString("index ") + buf.WriteByte(' ') if node.ForType != NoForType { buf.WriteString("for ") buf.WriteString(node.ForType.ToString()) @@ -2064,7 +2072,7 @@ func (node *CollateExpr) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node. func (node *FuncExpr) FormatFast(buf *TrackedBuffer) { - if !node.Qualifier.IsEmpty() { + if node.Qualifier.NotEmpty() { node.Qualifier.FormatFast(buf) buf.WriteByte('.') } @@ -2138,7 +2146,7 @@ func (node *JSONStorageSizeExpr) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node func (node *OverClause) FormatFast(buf *TrackedBuffer) { buf.WriteString("over") - if !node.WindowName.IsEmpty() { + if node.WindowName.NotEmpty() { buf.WriteByte(' ') node.WindowName.FormatFast(buf) } @@ -2151,7 +2159,7 @@ func (node *OverClause) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node func (node *WindowSpecification) FormatFast(buf *TrackedBuffer) { - if !node.Name.IsEmpty() { + if node.Name.NotEmpty() { buf.WriteByte(' ') node.Name.FormatFast(buf) } @@ -2478,10 +2486,10 @@ func (node *ConvertType) FormatFast(buf *TrackedBuffer) { buf.WriteString(node.Type) if node.Length != nil { buf.WriteByte('(') - node.Length.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *node.Length)) if node.Scale != nil { buf.WriteString(", ") - node.Scale.FormatFast(buf) + buf.WriteString(fmt.Sprintf("%d", *node.Scale)) } buf.WriteByte(')') } @@ -2690,7 +2698,7 @@ func (node *ShowBasic) FormatFast(buf *TrackedBuffer) { buf.WriteString(" from ") node.Tbl.FormatFast(buf) } - if !node.DbName.IsEmpty() { + if node.DbName.NotEmpty() { buf.WriteString(" from ") node.DbName.FormatFast(buf) } @@ -2751,7 +2759,7 @@ func (node *CreateDatabase) FormatFast(buf *TrackedBuffer) { // FormatFast formats the node. func (node *AlterDatabase) FormatFast(buf *TrackedBuffer) { buf.WriteString("alter database") - if !node.DBName.IsEmpty() { + if node.DBName.NotEmpty() { buf.WriteByte(' ') node.DBName.FormatFast(buf) } @@ -3118,7 +3126,7 @@ func (node *DropColumn) FormatFast(buf *TrackedBuffer) { func (node *DropKey) FormatFast(buf *TrackedBuffer) { buf.WriteString("drop ") buf.WriteString(node.Type.ToString()) - if !node.Name.IsEmpty() { + if node.Name.NotEmpty() { buf.WriteByte(' ') node.Name.FormatFast(buf) } diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index 951d9879bdb..2537cf0020f 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -24,13 +24,11 @@ import ( "strconv" "strings" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" ) // Walk calls postVisit on every node. @@ -71,7 +69,7 @@ type IndexColumn struct { // Only one of Column or Expression can be specified // Length is an optional field which is only applicable when Column is used Column IdentifierCI - Length *Literal + Length *int Expression Expr Direction OrderDirection } @@ -79,8 +77,8 @@ type IndexColumn struct { // LengthScaleOption is used for types that have an optional length // and scale type LengthScaleOption struct { - Length *Literal - Scale *Literal + Length *int + Scale *int } // IndexOption is used for trailing options for indexes: COMMENT, KEY_BLOCK_SIZE, USING, WITH PARSER @@ -357,6 +355,20 @@ func (node *ParsedComments) AddQueryHint(queryHint string) (Comments, error) { return newComments, nil } +// FkChecksStateString prints the foreign key checks state. +func FkChecksStateString(state *bool) string { + if state == nil { + return "" + } + switch *state { + case false: + return "Off" + case true: + return "On" + } + return "" +} + // ParseParams parses the vindex parameter list, pulling out the special-case // "owner" parameter func (node *VindexSpec) ParseParams() (string, map[string]string) { @@ -400,7 +412,7 @@ func (node *AliasedTableExpr) RemoveHints() *AliasedTableExpr { // TableName returns a TableName pointing to this table expr func (node *AliasedTableExpr) TableName() (TableName, error) { - if !node.As.IsEmpty() { + if node.As.NotEmpty() { return TableName{Name: node.As}, nil } @@ -417,6 +429,7 @@ func (node TableName) IsEmpty() bool { // If Name is empty, Qualifier is also empty. return node.Name.IsEmpty() } +func (node TableName) NonEmpty() bool { return !node.Name.IsEmpty() } // NewWhere creates a WHERE or HAVING clause out // of a Expr. If the expression is nil, it returns nil. @@ -868,6 +881,11 @@ func (node IdentifierCI) IsEmpty() bool { return node.val == "" } +// NonEmpty returns true if the name is not empty. +func (node IdentifierCI) NotEmpty() bool { + return !node.IsEmpty() +} + // String returns the unescaped column name. It must // not be used for SQL generation. Use sqlparser.String // instead. The Stringer conformance is for usage @@ -935,6 +953,11 @@ func (node IdentifierCS) IsEmpty() bool { return node.v == "" } +// NonEmpty returns true if TabIdent is not empty. +func (node IdentifierCS) NotEmpty() bool { + return !node.IsEmpty() +} + // String returns the unescaped table name. It must // not be used for SQL generation. Use sqlparser.String // instead. The Stringer conformance is for usage @@ -1315,6 +1338,16 @@ func (lock Lock) ToString() string { return NoLockStr case ForUpdateLock: return ForUpdateStr + case ForUpdateLockNoWait: + return ForUpdateNoWaitStr + case ForUpdateLockSkipLocked: + return ForUpdateSkipLockedStr + case ForShareLock: + return ForShareStr + case ForShareLockNoWait: + return ForShareNoWaitStr + case ForShareLockSkipLocked: + return ForShareSkipLockedStr case ShareModeLock: return ShareModeStr default: @@ -1526,14 +1559,28 @@ func (ty IndexHintType) ToString() string { case UseOp: return UseStr case IgnoreOp: - return IgnoreStr + return IgnoreIndexStr case ForceOp: return ForceStr + case UseVindexOp: + return UseVindexStr + case IgnoreVindexOp: + return IgnoreVindexStr default: return "Unknown IndexHintType" } } +// IsVindexHint returns if the given hint is a Vindex hint or not. +func (ty IndexHintType) IsVindexHint() bool { + switch ty { + case UseVindexOp, IgnoreVindexOp: + return true + default: + return false + } +} + // ToString returns the type as a string func (ty IndexHintForType) ToString() string { switch ty { @@ -1783,10 +1830,6 @@ func (ty ExplainType) ToString() string { return TreeStr case JSONType: return JSONStr - case VitessType: - return VitessStr - case VTExplainType: - return VTExplainStr case TraditionalType: return TraditionalStr case AnalyzeType: @@ -1929,6 +1972,8 @@ func (ty ShowCommandType) ToString() string { return VitessVariablesStr case VschemaTables: return VschemaTablesStr + case VschemaKeyspaces: + return VschemaKeyspacesStr case VschemaVindexes: return VschemaVindexesStr case Warnings: @@ -2099,7 +2144,7 @@ func GetAllSelects(selStmt SelectStatement) []*Select { // ColumnName returns the alias if one was provided, otherwise prints the AST func (ae *AliasedExpr) ColumnName() string { - if !ae.As.IsEmpty() { + if ae.As.NotEmpty() { return ae.As.String() } @@ -2120,23 +2165,47 @@ func (s SelectExprs) AllAggregation() bool { return true } -// RemoveKeyspaceFromColName removes the Qualifier.Qualifier on all ColNames in the expression tree -func RemoveKeyspaceFromColName(expr Expr) { - RemoveKeyspace(expr) +// RemoveKeyspaceInCol removes the Qualifier.Qualifier on all ColNames in the AST +func RemoveKeyspaceInCol(in SQLNode) { + // Walk will only return an error if we return an error from the inner func. safe to ignore here + _ = Walk(func(node SQLNode) (kontinue bool, err error) { + if col, ok := node.(*ColName); ok && col.Qualifier.Qualifier.NotEmpty() { + col.Qualifier.Qualifier = NewIdentifierCS("") + } + + return true, nil + }, in) +} + +// RemoveKeyspaceInTables removes the Qualifier on all TableNames in the AST +func RemoveKeyspaceInTables(in SQLNode) { + // Walk will only return an error if we return an error from the inner func. safe to ignore here + Rewrite(in, nil, func(cursor *Cursor) bool { + if tbl, ok := cursor.Node().(TableName); ok && tbl.Qualifier.NotEmpty() { + tbl.Qualifier = NewIdentifierCS("") + cursor.Replace(tbl) + } + + return true + }) } -// RemoveKeyspace removes the Qualifier.Qualifier on all ColNames in the AST +// RemoveKeyspace removes the Qualifier.Qualifier on all ColNames and Qualifier on all TableNames in the AST func RemoveKeyspace(in SQLNode) { - // Walk will only return an error if we return an error from the inner func. safe to ignore here - _ = Walk(func(node SQLNode) (kontinue bool, err error) { - switch col := node.(type) { + Rewrite(in, nil, func(cursor *Cursor) bool { + switch expr := cursor.Node().(type) { case *ColName: - if !col.Qualifier.Qualifier.IsEmpty() { - col.Qualifier.Qualifier = NewIdentifierCS("") + if expr.Qualifier.Qualifier.NotEmpty() { + expr.Qualifier.Qualifier = NewIdentifierCS("") + } + case TableName: + if expr.Qualifier.NotEmpty() { + expr.Qualifier = NewIdentifierCS("") + cursor.Replace(expr) } } - return true, nil - }, in) + return true + }) } func convertStringToInt(integer string) int { @@ -2497,6 +2566,123 @@ func IsLiteral(expr Expr) bool { } } +// AppendString appends a string to the expression provided. +// This is intended to be used in the parser only for concatenating multiple strings together. +func AppendString(expr Expr, in string) Expr { + switch node := expr.(type) { + case *Literal: + node.Val = node.Val + in + return node + case *UnaryExpr: + node.Expr = AppendString(node.Expr, in) + return node + case *IntroducerExpr: + node.Expr = AppendString(node.Expr, in) + return node + } + return nil +} + func (ct *ColumnType) Invisible() bool { return ct.Options.Invisible != nil && *ct.Options.Invisible } + +func (node *Delete) IsSingleAliasExpr() bool { + if len(node.Targets) > 1 { + return false + } + if len(node.TableExprs) != 1 { + return false + } + _, isAliasExpr := node.TableExprs[0].(*AliasedTableExpr) + return isAliasExpr +} + +func MultiTable(node []TableExpr) bool { + if len(node) > 1 { + return true + } + _, singleTbl := node[0].(*AliasedTableExpr) + return !singleTbl +} + +func (node *Update) AddOrder(order *Order) { + node.OrderBy = append(node.OrderBy, order) +} + +func (node *Update) SetLimit(limit *Limit) { + node.Limit = limit +} + +func (node *Delete) AddOrder(order *Order) { + node.OrderBy = append(node.OrderBy, order) +} + +func (node *Delete) SetLimit(limit *Limit) { + node.Limit = limit +} + +func (node *Select) GetFrom() []TableExpr { + return node.From +} + +func (node *Select) SetFrom(exprs []TableExpr) { + node.From = exprs +} + +func (node *Select) GetWherePredicate() Expr { + if node.Where == nil { + return nil + } + return node.Where.Expr +} + +func (node *Select) SetWherePredicate(expr Expr) { + node.Where = &Where{ + Type: WhereClause, + Expr: expr, + } +} +func (node *Delete) GetFrom() []TableExpr { + return node.TableExprs +} + +func (node *Delete) SetFrom(exprs []TableExpr) { + node.TableExprs = exprs +} + +func (node *Delete) GetWherePredicate() Expr { + if node.Where == nil { + return nil + } + return node.Where.Expr +} + +func (node *Delete) SetWherePredicate(expr Expr) { + node.Where = &Where{ + Type: WhereClause, + Expr: expr, + } +} + +func (node *Update) GetFrom() []TableExpr { + return node.TableExprs +} + +func (node *Update) SetFrom(exprs []TableExpr) { + node.TableExprs = exprs +} + +func (node *Update) GetWherePredicate() Expr { + if node.Where == nil { + return nil + } + return node.Where.Expr +} + +func (node *Update) SetWherePredicate(expr Expr) { + node.Where = &Where{ + Type: WhereClause, + Expr: expr, + } +} diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go index 0121695fe8c..02308ed0758 100644 --- a/go/vt/sqlparser/ast_rewrite.go +++ b/go/vt/sqlparser/ast_rewrite.go @@ -1817,20 +1817,12 @@ func (a *application) rewriteRefOfColumnType(parent SQLNode, node *ColumnType, r return true } } - if !a.rewriteRefOfLiteral(node, node.Length, func(newNode, parent SQLNode) { - parent.(*ColumnType).Length = newNode.(*Literal) - }) { - return false - } - if !a.rewriteRefOfLiteral(node, node.Scale, func(newNode, parent SQLNode) { - parent.(*ColumnType).Scale = newNode.(*Literal) - }) { - return false - } if a.post != nil { - a.cur.replacer = replacer - a.cur.parent = parent - a.cur.node = node + if a.pre == nil { + a.cur.replacer = replacer + a.cur.parent = parent + a.cur.node = node + } if !a.post(&a.cur) { return false } @@ -2082,20 +2074,12 @@ func (a *application) rewriteRefOfConvertType(parent SQLNode, node *ConvertType, return true } } - if !a.rewriteRefOfLiteral(node, node.Length, func(newNode, parent SQLNode) { - parent.(*ConvertType).Length = newNode.(*Literal) - }) { - return false - } - if !a.rewriteRefOfLiteral(node, node.Scale, func(newNode, parent SQLNode) { - parent.(*ConvertType).Scale = newNode.(*Literal) - }) { - return false - } if a.post != nil { - a.cur.replacer = replacer - a.cur.parent = parent - a.cur.node = node + if a.pre == nil { + a.cur.replacer = replacer + a.cur.parent = parent + a.cur.node = node + } if !a.post(&a.cur) { return false } @@ -2455,16 +2439,20 @@ func (a *application) rewriteRefOfDelete(parent SQLNode, node *Delete, replacer }) { return false } + for x, el := range node.TableExprs { + if !a.rewriteTableExpr(node, el, func(idx int) replacerFunc { + return func(newNode, parent SQLNode) { + parent.(*Delete).TableExprs[idx] = newNode.(TableExpr) + } + }(x)) { + return false + } + } if !a.rewriteTableNames(node, node.Targets, func(newNode, parent SQLNode) { parent.(*Delete).Targets = newNode.(TableNames) }) { return false } - if !a.rewriteTableExprs(node, node.TableExprs, func(newNode, parent SQLNode) { - parent.(*Delete).TableExprs = newNode.(TableExprs) - }) { - return false - } if !a.rewritePartitions(node, node.Partitions, func(newNode, parent SQLNode) { parent.(*Delete).Partitions = newNode.(Partitions) }) { @@ -8680,10 +8668,14 @@ func (a *application) rewriteRefOfUpdate(parent SQLNode, node *Update, replacer }) { return false } - if !a.rewriteTableExprs(node, node.TableExprs, func(newNode, parent SQLNode) { - parent.(*Update).TableExprs = newNode.(TableExprs) - }) { - return false + for x, el := range node.TableExprs { + if !a.rewriteTableExpr(node, el, func(idx int) replacerFunc { + return func(newNode, parent SQLNode) { + parent.(*Update).TableExprs[idx] = newNode.(TableExpr) + } + }(x)) { + return false + } } if !a.rewriteUpdateExprs(node, node.Exprs, func(newNode, parent SQLNode) { parent.(*Update).Exprs = newNode.(UpdateExprs) diff --git a/go/vt/sqlparser/ast_rewriting.go b/go/vt/sqlparser/ast_rewriting.go index 45711f8d535..659383ec6d6 100644 --- a/go/vt/sqlparser/ast_rewriting.go +++ b/go/vt/sqlparser/ast_rewriting.go @@ -51,6 +51,7 @@ func PrepareAST( selectLimit int, setVarComment string, sysVars map[string]string, + fkChecksState *bool, views VSchemaViews, ) (*RewriteASTResult, error) { if parameterize { @@ -59,7 +60,7 @@ func PrepareAST( return nil, err } } - return RewriteAST(in, keyspace, selectLimit, setVarComment, sysVars, views) + return RewriteAST(in, keyspace, selectLimit, setVarComment, sysVars, fkChecksState, views) } // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries. @@ -70,9 +71,10 @@ func RewriteAST( selectLimit int, setVarComment string, sysVars map[string]string, + fkChecksState *bool, views VSchemaViews, ) (*RewriteASTResult, error) { - er := newASTRewriter(keyspace, selectLimit, setVarComment, sysVars, views) + er := newASTRewriter(keyspace, selectLimit, setVarComment, sysVars, fkChecksState, views) er.shouldRewriteDatabaseFunc = shouldRewriteDatabaseFunc(in) result := SafeRewrite(in, er.rewriteDown, er.rewriteUp) if er.err != nil { @@ -121,16 +123,18 @@ type astRewriter struct { keyspace string selectLimit int setVarComment string + fkChecksState *bool sysVars map[string]string views VSchemaViews } -func newASTRewriter(keyspace string, selectLimit int, setVarComment string, sysVars map[string]string, views VSchemaViews) *astRewriter { +func newASTRewriter(keyspace string, selectLimit int, setVarComment string, sysVars map[string]string, fkChecksState *bool, views VSchemaViews) *astRewriter { return &astRewriter{ bindVars: &BindVarNeeds{}, keyspace: keyspace, selectLimit: selectLimit, setVarComment: setVarComment, + fkChecksState: fkChecksState, sysVars: sysVars, views: views, } @@ -154,7 +158,7 @@ const ( ) func (er *astRewriter) rewriteAliasedExpr(node *AliasedExpr) (*BindVarNeeds, error) { - inner := newASTRewriter(er.keyspace, er.selectLimit, er.setVarComment, er.sysVars, er.views) + inner := newASTRewriter(er.keyspace, er.selectLimit, er.setVarComment, er.sysVars, nil, er.views) inner.shouldRewriteDatabaseFunc = er.shouldRewriteDatabaseFunc tmp := SafeRewrite(node.Expr, inner.rewriteDown, inner.rewriteUp) newExpr, ok := tmp.(Expr) @@ -177,13 +181,19 @@ func (er *astRewriter) rewriteDown(node SQLNode, _ SQLNode) bool { func (er *astRewriter) rewriteUp(cursor *Cursor) bool { // Add SET_VAR comment to this node if it supports it and is needed - if supportOptimizerHint, supportsOptimizerHint := cursor.Node().(SupportOptimizerHint); supportsOptimizerHint && er.setVarComment != "" { - newComments, err := supportOptimizerHint.GetParsedComments().AddQueryHint(er.setVarComment) - if err != nil { - er.err = err - return false + if supportOptimizerHint, supportsOptimizerHint := cursor.Node().(SupportOptimizerHint); supportsOptimizerHint { + if er.setVarComment != "" { + newComments, err := supportOptimizerHint.GetParsedComments().AddQueryHint(er.setVarComment) + if err != nil { + er.err = err + return false + } + supportOptimizerHint.SetComments(newComments) + } + if er.fkChecksState != nil { + newComments := supportOptimizerHint.GetParsedComments().SetMySQLSetVarValue(sysvars.ForeignKeyChecks, FkChecksStateString(er.fkChecksState)) + supportOptimizerHint.SetComments(newComments) } - supportOptimizerHint.SetComments(newComments) } switch node := cursor.Node().(type) { @@ -307,7 +317,7 @@ func (er *astRewriter) visitSelect(node *Select) { } aliasedExpr, ok := col.(*AliasedExpr) - if !ok || !aliasedExpr.As.IsEmpty() { + if !ok || aliasedExpr.As.NotEmpty() { continue } buf := NewTrackedBuffer(nil) diff --git a/go/vt/sqlparser/ast_rewriting_test.go b/go/vt/sqlparser/ast_rewriting_test.go index 2ed92201296..3ad9a5298c4 100644 --- a/go/vt/sqlparser/ast_rewriting_test.go +++ b/go/vt/sqlparser/ast_rewriting_test.go @@ -37,12 +37,12 @@ type testCaseSysVar struct { } type myTestCase struct { - in, expected string - liid, db, foundRows, rowCount, rawGTID, rawTimeout, sessTrackGTID bool - ddlStrategy, migrationContext, sessionUUID, sessionEnableSystemSettings bool - udv int - autocommit, clientFoundRows, skipQueryPlanCache, socket, queryTimeout bool - sqlSelectLimit, transactionMode, workload, version, versionComment bool + in, expected string + liid, db, foundRows, rowCount, rawGTID, rawTimeout, sessTrackGTID bool + ddlStrategy, migrationContext, sessionUUID, sessionEnableSystemSettings bool + udv int + autocommit, foreignKeyChecks, clientFoundRows, skipQueryPlanCache, socket, queryTimeout bool + sqlSelectLimit, transactionMode, workload, version, versionComment bool } func TestRewrites(in *testing.T) { @@ -296,6 +296,7 @@ func TestRewrites(in *testing.T) { in: "SHOW VARIABLES", expected: "SHOW VARIABLES", autocommit: true, + foreignKeyChecks: true, clientFoundRows: true, skipQueryPlanCache: true, sqlSelectLimit: true, @@ -316,6 +317,7 @@ func TestRewrites(in *testing.T) { in: "SHOW GLOBAL VARIABLES", expected: "SHOW GLOBAL VARIABLES", autocommit: true, + foreignKeyChecks: true, clientFoundRows: true, skipQueryPlanCache: true, sqlSelectLimit: true, @@ -333,11 +335,11 @@ func TestRewrites(in *testing.T) { socket: true, queryTimeout: true, }} - + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { require := require.New(t) - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(err) result, err := RewriteAST( @@ -346,11 +348,12 @@ func TestRewrites(in *testing.T) { SQLSelectLimitUnset, "", nil, + nil, &fakeViews{}, ) require.NoError(err) - expected, err := Parse(tc.expected) + expected, err := parser.Parse(tc.expected) require.NoError(err, "test expectation does not parse [%s]", tc.expected) s := String(expected) @@ -362,6 +365,7 @@ func TestRewrites(in *testing.T) { assert.Equal(tc.rowCount, result.NeedsFuncResult(RowCountName), "should need row count") assert.Equal(tc.udv, len(result.NeedUserDefinedVariables), "count of user defined variables") assert.Equal(tc.autocommit, result.NeedsSysVar(sysvars.Autocommit.Name), "should need :__vtautocommit") + assert.Equal(tc.foreignKeyChecks, result.NeedsSysVar(sysvars.ForeignKeyChecks), "should need :__vtforeignKeyChecks") assert.Equal(tc.clientFoundRows, result.NeedsSysVar(sysvars.ClientFoundRows.Name), "should need :__vtclientFoundRows") assert.Equal(tc.skipQueryPlanCache, result.NeedsSysVar(sysvars.SkipQueryPlanCache.Name), "should need :__vtskipQueryPlanCache") assert.Equal(tc.sqlSelectLimit, result.NeedsSysVar(sysvars.SQLSelectLimit.Name), "should need :__vtsqlSelectLimit") @@ -388,7 +392,8 @@ func (*fakeViews) FindView(name TableName) SelectStatement { if name.Name.String() != "user_details" { return nil } - statement, err := Parse("select user.id, user.name, user_extra.salary from user join user_extra where user.id = user_extra.user_id") + parser := NewTestParser() + statement, err := parser.Parse("select user.id, user.name, user_extra.salary from user join user_extra where user.id = user_extra.user_id") if err != nil { return nil } @@ -430,16 +435,17 @@ func TestRewritesWithSetVarComment(in *testing.T) { setVarComment: "AA(a)", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { require := require.New(t) - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(err) - result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, tc.setVarComment, nil, &fakeViews{}) + result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, tc.setVarComment, nil, nil, &fakeViews{}) require.NoError(err) - expected, err := Parse(tc.expected) + expected, err := parser.Parse(tc.expected) require.NoError(err, "test expectation does not parse [%s]", tc.expected) assert.Equal(t, String(expected), String(result.AST)) @@ -478,16 +484,17 @@ func TestRewritesSysVar(in *testing.T) { expected: "select :__vttransaction_isolation as `@@session.transaction_isolation` from dual", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { require := require.New(t) - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(err) - result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, "", tc.sysVar, &fakeViews{}) + result, err := RewriteAST(stmt, "ks", SQLSelectLimitUnset, "", tc.sysVar, nil, &fakeViews{}) require.NoError(err) - expected, err := Parse(tc.expected) + expected, err := parser.Parse(tc.expected) require.NoError(err, "test expectation does not parse [%s]", tc.expected) assert.Equal(t, String(expected), String(result.AST)) @@ -528,16 +535,17 @@ func TestRewritesWithDefaultKeyspace(in *testing.T) { expected: "SELECT 2 as `(select 2 from dual)` from DUAL", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { require := require.New(t) - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(err) - result, err := RewriteAST(stmt, "sys", SQLSelectLimitUnset, "", nil, &fakeViews{}) + result, err := RewriteAST(stmt, "sys", SQLSelectLimitUnset, "", nil, nil, &fakeViews{}) require.NoError(err) - expected, err := Parse(tc.expected) + expected, err := parser.Parse(tc.expected) require.NoError(err, "test expectation does not parse [%s]", tc.expected) assert.Equal(t, String(expected), String(result.AST)) diff --git a/go/vt/sqlparser/ast_test.go b/go/vt/sqlparser/ast_test.go index 97b93a80379..f01b47cbd7b 100644 --- a/go/vt/sqlparser/ast_test.go +++ b/go/vt/sqlparser/ast_test.go @@ -30,8 +30,9 @@ import ( ) func TestAppend(t *testing.T) { + parser := NewTestParser() query := "select * from t where a = 1" - tree, err := Parse(query) + tree, err := parser.Parse(query) require.NoError(t, err) var b strings.Builder Append(&b, tree) @@ -49,9 +50,10 @@ func TestAppend(t *testing.T) { } func TestSelect(t *testing.T) { - e1, err := ParseExpr("a = 1") + parser := NewTestParser() + e1, err := parser.ParseExpr("a = 1") require.NoError(t, err) - e2, err := ParseExpr("b = 2") + e2, err := parser.ParseExpr("b = 2") require.NoError(t, err) t.Run("single predicate where", func(t *testing.T) { sel := &Select{} @@ -81,7 +83,8 @@ func TestSelect(t *testing.T) { } func TestUpdate(t *testing.T) { - tree, err := Parse("update t set a = 1") + parser := NewTestParser() + tree, err := parser.Parse("update t set a = 1") require.NoError(t, err) upd, ok := tree.(*Update) @@ -103,11 +106,12 @@ func TestUpdate(t *testing.T) { } func TestRemoveHints(t *testing.T) { + parser := NewTestParser() for _, query := range []string{ "select * from t use index (i)", "select * from t force index (i)", } { - tree, err := Parse(query) + tree, err := parser.Parse(query) if err != nil { t.Fatal(err) } @@ -124,16 +128,17 @@ func TestRemoveHints(t *testing.T) { } func TestAddOrder(t *testing.T) { - src, err := Parse("select foo, bar from baz order by foo") + parser := NewTestParser() + src, err := parser.Parse("select foo, bar from baz order by foo") require.NoError(t, err) order := src.(*Select).OrderBy[0] - dst, err := Parse("select * from t") + dst, err := parser.Parse("select * from t") require.NoError(t, err) dst.(*Select).AddOrder(order) buf := NewTrackedBuffer(nil) dst.Format(buf) require.Equal(t, "select * from t order by foo asc", buf.String()) - dst, err = Parse("select * from t union select * from s") + dst, err = parser.Parse("select * from t union select * from s") require.NoError(t, err) dst.(*Union).AddOrder(order) buf = NewTrackedBuffer(nil) @@ -142,16 +147,17 @@ func TestAddOrder(t *testing.T) { } func TestSetLimit(t *testing.T) { - src, err := Parse("select foo, bar from baz limit 4") + parser := NewTestParser() + src, err := parser.Parse("select foo, bar from baz limit 4") require.NoError(t, err) limit := src.(*Select).Limit - dst, err := Parse("select * from t") + dst, err := parser.Parse("select * from t") require.NoError(t, err) dst.(*Select).SetLimit(limit) buf := NewTrackedBuffer(nil) dst.Format(buf) require.Equal(t, "select * from t limit 4", buf.String()) - dst, err = Parse("select * from t union select * from s") + dst, err = parser.Parse("select * from t union select * from s") require.NoError(t, err) dst.(*Union).SetLimit(limit) buf = NewTrackedBuffer(nil) @@ -213,8 +219,9 @@ func TestDDL(t *testing.T) { }, affected: []string{"a", "b"}, }} + parser := NewTestParser() for _, tcase := range testcases { - got, err := Parse(tcase.query) + got, err := parser.Parse(tcase.query) if err != nil { t.Fatal(err) } @@ -232,7 +239,8 @@ func TestDDL(t *testing.T) { } func TestSetAutocommitON(t *testing.T) { - stmt, err := Parse("SET autocommit=ON") + parser := NewTestParser() + stmt, err := parser.Parse("SET autocommit=ON") require.NoError(t, err) s, ok := stmt.(*Set) if !ok { @@ -257,7 +265,7 @@ func TestSetAutocommitON(t *testing.T) { t.Errorf("SET statement expression is not Literal: %T", e.Expr) } - stmt, err = Parse("SET @@session.autocommit=ON") + stmt, err = parser.Parse("SET @@session.autocommit=ON") require.NoError(t, err) s, ok = stmt.(*Set) if !ok { @@ -284,7 +292,8 @@ func TestSetAutocommitON(t *testing.T) { } func TestSetAutocommitOFF(t *testing.T) { - stmt, err := Parse("SET autocommit=OFF") + parser := NewTestParser() + stmt, err := parser.Parse("SET autocommit=OFF") require.NoError(t, err) s, ok := stmt.(*Set) if !ok { @@ -309,7 +318,7 @@ func TestSetAutocommitOFF(t *testing.T) { t.Errorf("SET statement expression is not Literal: %T", e.Expr) } - stmt, err = Parse("SET @@session.autocommit=OFF") + stmt, err = parser.Parse("SET @@session.autocommit=OFF") require.NoError(t, err) s, ok = stmt.(*Set) if !ok { @@ -491,9 +500,10 @@ func TestReplaceExpr(t *testing.T) { out: "case a when b then c when d then c else :a end", }} to := NewArgument("a") + parser := NewTestParser() for _, tcase := range tcases { t.Run(tcase.in, func(t *testing.T) { - tree, err := Parse(tcase.in) + tree, err := parser.Parse(tcase.in) require.NoError(t, err) var from *Subquery _ = Walk(func(node SQLNode) (kontinue bool, err error) { @@ -687,6 +697,77 @@ func TestColumns_FindColumn(t *testing.T) { } } +func TestSplitStatements(t *testing.T) { + testcases := []struct { + input string + stmts int + wantErr bool + }{ + { + input: "select * from table1; \t; \n; \n\t\t ;select * from table1;", + stmts: 2, + }, { + input: "select * from table1", + stmts: 1, + }, { + input: "select * from table1;", + stmts: 1, + }, { + input: "select * from table1; ", + stmts: 1, + }, { + input: "select * from table1; select * from table2;", + stmts: 2, + }, { + input: "create /*vt+ directive=true */ table t1 (id int); create table t2 (id int); create table t3 (id int)", + stmts: 3, + }, { + input: "create /*vt+ directive=true */ table t1 (id int); create table t2 (id int); create table t3 (id int);", + stmts: 3, + }, { + input: "select * from /* comment ; */ table1;", + stmts: 1, + }, { + input: "select * from table1 where semi = ';';", + stmts: 1, + }, { + input: "CREATE TABLE `total_data` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', " + + "`region` varchar(32) NOT NULL COMMENT 'region name, like zh; th; kepler'," + + "`data_size` bigint NOT NULL DEFAULT '0' COMMENT 'data size;'," + + "`createtime` datetime NOT NULL DEFAULT NOW() COMMENT 'create time;'," + + "`comment` varchar(100) NOT NULL DEFAULT '' COMMENT 'comment'," + + "PRIMARY KEY (`id`))", + stmts: 1, + }, { + input: "create table t1 (id int primary key); create table t2 (id int primary key);", + stmts: 2, + }, { + input: ";;; create table t1 (id int primary key);;; ;create table t2 (id int primary key);", + stmts: 2, + }, { + input: ";create table t1 ;create table t2 (id;", + wantErr: true, + }, { + // Ignore quoted semicolon + input: ";create table t1 ';';;;create table t2 (id;", + wantErr: true, + }, + } + + parser := NewTestParser() + for _, tcase := range testcases { + t.Run(tcase.input, func(t *testing.T) { + statements, err := parser.SplitStatements(tcase.input) + if tcase.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tcase.stmts, len(statements)) + } + }) + } +} + func TestSplitStatementToPieces(t *testing.T) { testcases := []struct { input string @@ -735,16 +816,21 @@ func TestSplitStatementToPieces(t *testing.T) { // Ignore quoted semicolon input: ";create table t1 ';';;;create table t2 (id;", output: "create table t1 ';';create table t2 (id", + }, { + // Ignore quoted semicolon + input: "stop replica; start replica", + output: "stop replica; start replica", }, } + parser := NewTestParser() for _, tcase := range testcases { t.Run(tcase.input, func(t *testing.T) { if tcase.output == "" { tcase.output = tcase.input } - stmtPieces, err := SplitStatementToPieces(tcase.input) + stmtPieces, err := parser.SplitStatementToPieces(tcase.input) require.NoError(t, err) out := strings.Join(stmtPieces, ";") @@ -766,13 +852,15 @@ func TestDefaultStatus(t *testing.T) { } func TestShowTableStatus(t *testing.T) { + parser := NewTestParser() query := "Show Table Status FROM customer" - tree, err := Parse(query) + tree, err := parser.Parse(query) require.NoError(t, err) require.NotNil(t, tree) } func BenchmarkStringTraces(b *testing.B) { + parser := NewTestParser() for _, trace := range []string{"django_queries.txt", "lobsters.sql.gz"} { b.Run(trace, func(b *testing.B) { queries := loadQueries(b, trace) @@ -782,7 +870,7 @@ func BenchmarkStringTraces(b *testing.B) { parsed := make([]Statement, 0, len(queries)) for _, q := range queries { - pp, err := Parse(q) + pp, err := parser.Parse(q) if err != nil { b.Fatal(err) } diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go index a88d689f102..007b4048da9 100644 --- a/go/vt/sqlparser/ast_visit.go +++ b/go/vt/sqlparser/ast_visit.go @@ -1105,12 +1105,6 @@ func VisitRefOfColumnType(in *ColumnType, f Visit) error { if cont, err := f(in); err != nil || !cont { return err } - if err := VisitRefOfLiteral(in.Length, f); err != nil { - return err - } - if err := VisitRefOfLiteral(in.Scale, f); err != nil { - return err - } return nil } func VisitColumns(in Columns, f Visit) error { @@ -1218,12 +1212,6 @@ func VisitRefOfConvertType(in *ConvertType, f Visit) error { if cont, err := f(in); err != nil || !cont { return err } - if err := VisitRefOfLiteral(in.Length, f); err != nil { - return err - } - if err := VisitRefOfLiteral(in.Scale, f); err != nil { - return err - } return nil } func VisitRefOfConvertUsingExpr(in *ConvertUsingExpr, f Visit) error { @@ -1377,10 +1365,12 @@ func VisitRefOfDelete(in *Delete, f Visit) error { if err := VisitRefOfParsedComments(in.Comments, f); err != nil { return err } - if err := VisitTableNames(in.Targets, f); err != nil { - return err + for _, el := range in.TableExprs { + if err := VisitTableExpr(el, f); err != nil { + return err + } } - if err := VisitTableExprs(in.TableExprs, f); err != nil { + if err := VisitTableNames(in.Targets, f); err != nil { return err } if err := VisitPartitions(in.Partitions, f); err != nil { @@ -4014,8 +4004,10 @@ func VisitRefOfUpdate(in *Update, f Visit) error { if err := VisitRefOfParsedComments(in.Comments, f); err != nil { return err } - if err := VisitTableExprs(in.TableExprs, f); err != nil { - return err + for _, el := range in.TableExprs { + if err := VisitTableExpr(el, f); err != nil { + return err + } } if err := VisitUpdateExprs(in.Exprs, f); err != nil { return err diff --git a/go/vt/sqlparser/cached_size.go b/go/vt/sqlparser/cached_size.go index d86b8a21155..bf5620f8b09 100644 --- a/go/vt/sqlparser/cached_size.go +++ b/go/vt/sqlparser/cached_size.go @@ -723,10 +723,10 @@ func (cached *ColumnType) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Type))) // field Options *vitess.io/vitess/go/vt/sqlparser.ColumnTypeOptions size += cached.Options.CachedSize(true) - // field Length *vitess.io/vitess/go/vt/sqlparser.Literal - size += cached.Length.CachedSize(true) - // field Scale *vitess.io/vitess/go/vt/sqlparser.Literal - size += cached.Scale.CachedSize(true) + // field Length *int + size += hack.RuntimeAllocSize(int64(8)) + // field Scale *int + size += hack.RuntimeAllocSize(int64(8)) // field Charset vitess.io/vitess/go/vt/sqlparser.ColumnCharset size += cached.Charset.CachedSize(false) // field EnumValues []string @@ -905,10 +905,10 @@ func (cached *ConvertType) CachedSize(alloc bool) int64 { } // field Type string size += hack.RuntimeAllocSize(int64(len(cached.Type))) - // field Length *vitess.io/vitess/go/vt/sqlparser.Literal - size += cached.Length.CachedSize(true) - // field Scale *vitess.io/vitess/go/vt/sqlparser.Literal - size += cached.Scale.CachedSize(true) + // field Length *int + size += hack.RuntimeAllocSize(int64(8)) + // field Scale *int + size += hack.RuntimeAllocSize(int64(8)) // field Charset vitess.io/vitess/go/vt/sqlparser.ColumnCharset size += cached.Charset.CachedSize(false) return size @@ -1106,14 +1106,7 @@ func (cached *Delete) CachedSize(alloc bool) int64 { size += cached.With.CachedSize(true) // field Comments *vitess.io/vitess/go/vt/sqlparser.ParsedComments size += cached.Comments.CachedSize(true) - // field Targets vitess.io/vitess/go/vt/sqlparser.TableNames - { - size += hack.RuntimeAllocSize(int64(cap(cached.Targets)) * int64(32)) - for _, elem := range cached.Targets { - size += elem.CachedSize(false) - } - } - // field TableExprs vitess.io/vitess/go/vt/sqlparser.TableExprs + // field TableExprs []vitess.io/vitess/go/vt/sqlparser.TableExpr { size += hack.RuntimeAllocSize(int64(cap(cached.TableExprs)) * int64(16)) for _, elem := range cached.TableExprs { @@ -1122,6 +1115,13 @@ func (cached *Delete) CachedSize(alloc bool) int64 { } } } + // field Targets vitess.io/vitess/go/vt/sqlparser.TableNames + { + size += hack.RuntimeAllocSize(int64(cap(cached.Targets)) * int64(32)) + for _, elem := range cached.Targets { + size += elem.CachedSize(false) + } + } // field Partitions vitess.io/vitess/go/vt/sqlparser.Partitions { size += hack.RuntimeAllocSize(int64(cap(cached.Partitions)) * int64(32)) @@ -1731,8 +1731,8 @@ func (cached *IndexColumn) CachedSize(alloc bool) int64 { } // field Column vitess.io/vitess/go/vt/sqlparser.IdentifierCI size += cached.Column.CachedSize(false) - // field Length *vitess.io/vitess/go/vt/sqlparser.Literal - size += cached.Length.CachedSize(true) + // field Length *int + size += hack.RuntimeAllocSize(int64(8)) // field Expression vitess.io/vitess/go/vt/sqlparser.Expr if cc, ok := cached.Expression.(cachedObject); ok { size += cc.CachedSize(true) @@ -3064,12 +3064,24 @@ func (cached *ParsedQuery) CachedSize(alloc bool) int64 { } // field Query string size += hack.RuntimeAllocSize(int64(len(cached.Query))) - // field bindLocations []vitess.io/vitess/go/vt/sqlparser.bindLocation + // field bindLocations []vitess.io/vitess/go/vt/sqlparser.BindLocation { size += hack.RuntimeAllocSize(int64(cap(cached.bindLocations)) * int64(16)) } return size } +func (cached *Parser) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(32) + } + // field version string + size += hack.RuntimeAllocSize(int64(len(cached.version))) + return size +} func (cached *PartitionDefinition) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -4226,7 +4238,7 @@ func (cached *Update) CachedSize(alloc bool) int64 { size += cached.With.CachedSize(true) // field Comments *vitess.io/vitess/go/vt/sqlparser.ParsedComments size += cached.Comments.CachedSize(true) - // field TableExprs vitess.io/vitess/go/vt/sqlparser.TableExprs + // field TableExprs []vitess.io/vitess/go/vt/sqlparser.TableExpr { size += hack.RuntimeAllocSize(int64(cap(cached.TableExprs)) * int64(16)) for _, elem := range cached.TableExprs { diff --git a/go/vt/sqlparser/comments.go b/go/vt/sqlparser/comments.go index 84b73f8e81c..780f1e67594 100644 --- a/go/vt/sqlparser/comments.go +++ b/go/vt/sqlparser/comments.go @@ -17,11 +17,13 @@ limitations under the License. package sqlparser import ( + "fmt" "strconv" "strings" "unicode" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" @@ -60,6 +62,9 @@ const ( // MaxPriorityValue specifies the maximum value allowed for the priority query directive. Valid priority values are // between zero and MaxPriorityValue. MaxPriorityValue = 100 + + // OptimizerHintSetVar is the optimizer hint used in MySQL to set the value of a specific session variable for a query. + OptimizerHintSetVar = "SET_VAR" ) var ErrInvalidPriority = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Invalid priority value specified in query") @@ -266,6 +271,206 @@ func (c *ParsedComments) Directives() *CommentDirectives { return c._directives } +// GetMySQLSetVarValue gets the value of the given variable if it is part of a /*+ SET_VAR() */ MySQL optimizer hint. +func (c *ParsedComments) GetMySQLSetVarValue(key string) string { + if c == nil { + // If we have no parsed comments, then we return an empty string. + return "" + } + for _, commentStr := range c.comments { + // Skip all the comments that don't start with the query optimizer prefix. + if commentStr[0:3] != queryOptimizerPrefix { + continue + } + + pos := 4 + for pos < len(commentStr) { + // Go over the entire comment and extract an optimizer hint. + // We get back the final position of the cursor, along with the start and end of + // the optimizer hint name and content. + finalPos, ohNameStart, ohNameEnd, ohContentStart, ohContentEnd := getOptimizerHint(pos, commentStr) + pos = finalPos + 1 + // If we didn't find an optimizer hint or if it was malformed, we skip it. + if ohContentEnd == -1 { + break + } + // Construct the name and the content from the starts and ends. + ohName := commentStr[ohNameStart:ohNameEnd] + ohContent := commentStr[ohContentStart:ohContentEnd] + // Check if the optimizer hint name matches `SET_VAR`. + if strings.EqualFold(strings.TrimSpace(ohName), OptimizerHintSetVar) { + // If it does, then we cut the string at the first occurrence of "=". + // That gives us the name of the variable, and the value that it is being set to. + // If the variable matches what we are looking for, we return its value. + setVarName, setVarValue, isValid := strings.Cut(ohContent, "=") + if !isValid { + continue + } + if strings.EqualFold(strings.TrimSpace(setVarName), key) { + return strings.TrimSpace(setVarValue) + } + } + } + + // MySQL only parses the first comment that has the optimizer hint prefix. The following ones are ignored. + return "" + } + return "" +} + +// SetMySQLSetVarValue updates or sets the value of the given variable as part of a /*+ SET_VAR() */ MySQL optimizer hint. +func (c *ParsedComments) SetMySQLSetVarValue(key string, value string) (newComments Comments) { + if c == nil { + // If we have no parsed comments, then we create a new one with the required optimizer hint and return it. + newComments = append(newComments, fmt.Sprintf("/*+ %v(%v=%v) */", OptimizerHintSetVar, key, value)) + return + } + seenFirstOhComment := false + for _, commentStr := range c.comments { + // Skip all the comments that don't start with the query optimizer prefix. + // Also, since MySQL only parses the first comment that has the optimizer hint prefix and ignores the following ones, + // we skip over all the comments that come after we have seen the first comment with the optimizer hint. + if seenFirstOhComment || commentStr[0:3] != queryOptimizerPrefix { + newComments = append(newComments, commentStr) + continue + } + + seenFirstOhComment = true + finalComment := "/*+" + keyPresent := false + pos := 4 + for pos < len(commentStr) { + // Go over the entire comment and extract an optimizer hint. + // We get back the final position of the cursor, along with the start and end of + // the optimizer hint name and content. + finalPos, ohNameStart, ohNameEnd, ohContentStart, ohContentEnd := getOptimizerHint(pos, commentStr) + pos = finalPos + 1 + // If we didn't find an optimizer hint or if it was malformed, we skip it. + if ohContentEnd == -1 { + break + } + // Construct the name and the content from the starts and ends. + ohName := commentStr[ohNameStart:ohNameEnd] + ohContent := commentStr[ohContentStart:ohContentEnd] + // Check if the optimizer hint name matches `SET_VAR`. + if strings.EqualFold(strings.TrimSpace(ohName), OptimizerHintSetVar) { + // If it does, then we cut the string at the first occurrence of "=". + // That gives us the name of the variable, and the value that it is being set to. + // If the variable matches what we are looking for, we can change its value. + // Otherwise we add the comment as is to our final comments and move on. + setVarName, _, isValid := strings.Cut(ohContent, "=") + if !isValid || !strings.EqualFold(strings.TrimSpace(setVarName), key) { + finalComment += fmt.Sprintf(" %v(%v)", ohName, ohContent) + continue + } + if strings.EqualFold(strings.TrimSpace(setVarName), key) { + keyPresent = true + finalComment += fmt.Sprintf(" %v(%v=%v)", ohName, strings.TrimSpace(setVarName), value) + } + } else { + // If it doesn't match, we add it to our final comment and move on. + finalComment += fmt.Sprintf(" %v(%v)", ohName, ohContent) + } + } + // If we haven't found any SET_VAR optimizer hint with the matching variable, + // then we add a new optimizer hint to introduce this variable. + if !keyPresent { + finalComment += fmt.Sprintf(" %v(%v=%v)", OptimizerHintSetVar, key, value) + } + + finalComment += " */" + newComments = append(newComments, finalComment) + } + // If we have not seen even a single comment that has the optimizer hint prefix, + // then we add a new optimizer hint to introduce this variable. + if !seenFirstOhComment { + newComments = append(newComments, fmt.Sprintf("/*+ %v(%v=%v) */", OptimizerHintSetVar, key, value)) + } + return newComments +} + +// getOptimizerHint goes over the comment string from the given initial position. +// It returns back the final position of the cursor, along with the start and end of +// the optimizer hint name and content. +func getOptimizerHint(initialPos int, commentStr string) (pos int, ohNameStart int, ohNameEnd int, ohContentStart int, ohContentEnd int) { + ohContentEnd = -1 + // skip spaces as they aren't interesting. + pos = skipBlanks(initialPos, commentStr) + ohNameStart = pos + pos++ + // All characters until we get a space of a opening bracket are part of the optimizer hint name. + for pos < len(commentStr) { + if commentStr[pos] == ' ' || commentStr[pos] == '(' { + break + } + pos++ + } + // Mark the end of the optimizer hint name and skip spaces. + ohNameEnd = pos + pos = skipBlanks(pos, commentStr) + // Verify that the comment is not malformed. If it doesn't contain an opening bracket + // at the current position, then something is wrong. + if pos >= len(commentStr) || commentStr[pos] != '(' { + return + } + // Seeing the opening bracket, marks the start of the optimizer hint content. + // We skip over the comment until we see the end of the parenthesis. + pos++ + ohContentStart = pos + pos = skipUntilParenthesisEnd(pos, commentStr) + ohContentEnd = pos + return +} + +// skipUntilParenthesisEnd reads the comment string given the initial position and skips over until +// it has seen the end of opening bracket. +func skipUntilParenthesisEnd(pos int, commentStr string) int { + for pos < len(commentStr) { + switch commentStr[pos] { + case ')': + // If we see a closing bracket, we have found the ending of our parenthesis. + return pos + case '\'': + // If we see a single quote character, then it signifies the start of a new string. + // We wait until we see the end of this string. + pos++ + pos = skipUntilCharacter(pos, commentStr, '\'') + case '"': + // If we see a double quote character, then it signifies the start of a new string. + // We wait until we see the end of this string. + pos++ + pos = skipUntilCharacter(pos, commentStr, '"') + } + pos++ + } + + return pos +} + +// skipUntilCharacter skips until the given character has been seen in the comment string, given the starting position. +func skipUntilCharacter(pos int, commentStr string, ch byte) int { + for pos < len(commentStr) { + if commentStr[pos] != ch { + pos++ + continue + } + break + } + return pos +} + +// skipBlanks skips over space characters from the comment string, given the starting position. +func skipBlanks(pos int, commentStr string) int { + for pos < len(commentStr) { + if commentStr[pos] == ' ' { + pos++ + continue + } + break + } + return pos +} + func (c *ParsedComments) Length() int { if c == nil { return 0 @@ -349,6 +554,27 @@ func AllowScatterDirective(stmt Statement) bool { return checkDirective(stmt, DirectiveAllowScatter) } +// ForeignKeyChecksState returns the state of foreign_key_checks variable if it is part of a SET_VAR optimizer hint in the comments. +func ForeignKeyChecksState(stmt Statement) *bool { + cmt, ok := stmt.(Commented) + if ok { + fkChecksVal := cmt.GetParsedComments().GetMySQLSetVarValue(sysvars.ForeignKeyChecks) + // If the value of the `foreign_key_checks` optimizer hint is something that doesn't make sense, + // then MySQL just ignores it and treats it like the case, where it is unspecified. We are choosing + // to have the same behaviour here. If the value doesn't match any of the acceptable values, we return nil, + // that signifies that no value was specified. + switch strings.ToLower(fkChecksVal) { + case "on", "1": + fkState := true + return &fkState + case "off", "0": + fkState := false + return &fkState + } + } + return nil +} + func checkDirective(stmt Statement, key string) bool { cmt, ok := stmt.(Commented) if ok { diff --git a/go/vt/sqlparser/comments_test.go b/go/vt/sqlparser/comments_test.go index b3c1bf9fec8..42d02e35652 100644 --- a/go/vt/sqlparser/comments_test.go +++ b/go/vt/sqlparser/comments_test.go @@ -18,12 +18,12 @@ package sqlparser import ( "fmt" - "reflect" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/vt/sysvars" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -142,15 +142,9 @@ func TestSplitComments(t *testing.T) { gotSQL, gotComments := SplitMarginComments(testCase.input) gotLeadingComments, gotTrailingComments := gotComments.Leading, gotComments.Trailing - if gotSQL != testCase.outSQL { - t.Errorf("test input: '%s', got SQL\n%+v, want\n%+v", testCase.input, gotSQL, testCase.outSQL) - } - if gotLeadingComments != testCase.outLeadingComments { - t.Errorf("test input: '%s', got LeadingComments\n%+v, want\n%+v", testCase.input, gotLeadingComments, testCase.outLeadingComments) - } - if gotTrailingComments != testCase.outTrailingComments { - t.Errorf("test input: '%s', got TrailingComments\n%+v, want\n%+v", testCase.input, gotTrailingComments, testCase.outTrailingComments) - } + assert.Equal(t, testCase.outSQL, gotSQL, "SQL mismatch") + assert.Equal(t, testCase.outLeadingComments, gotLeadingComments, "LeadingComments mismatch") + assert.Equal(t, testCase.outTrailingComments, gotTrailingComments, "TrailingCommints mismatch") }) } } @@ -224,10 +218,7 @@ a`, }} for _, testCase := range testCases { gotSQL := StripLeadingComments(testCase.input) - - if gotSQL != testCase.outSQL { - t.Errorf("test input: '%s', got SQL\n%+v, want\n%+v", testCase.input, gotSQL, testCase.outSQL) - } + assert.Equal(t, testCase.outSQL, gotSQL) } } @@ -253,10 +244,8 @@ func TestExtractMysqlComment(t *testing.T) { }} for _, testCase := range testCases { gotVersion, gotSQL := ExtractMysqlComment(testCase.input) + assert.Equal(t, testCase.outVersion, gotVersion, "version mismatch") - if gotVersion != testCase.outVersion { - t.Errorf("test input: '%s', got version\n%+v, want\n%+v", testCase.input, gotVersion, testCase.outVersion) - } if gotSQL != testCase.outSQL { t.Errorf("test input: '%s', got SQL\n%+v, want\n%+v", testCase.input, gotSQL, testCase.outSQL) } @@ -321,6 +310,7 @@ func TestExtractCommentDirectives(t *testing.T) { }, }} + parser := NewTestParser() for _, testCase := range testCases { t.Run(testCase.input, func(t *testing.T) { sqls := []string{ @@ -338,7 +328,7 @@ func TestExtractCommentDirectives(t *testing.T) { for _, sql := range sqls { t.Run(sql, func(t *testing.T) { var comments *ParsedComments - stmt, _ := Parse(sql) + stmt, _ := parser.Parse(sql) switch s := stmt.(type) { case *Select: comments = s.Comments @@ -367,9 +357,8 @@ func TestExtractCommentDirectives(t *testing.T) { require.Nil(t, vals) return } - if !reflect.DeepEqual(vals.m, testCase.vals) { - t.Errorf("test input: '%v', got vals %T:\n%+v, want %T\n%+v", testCase.input, vals, vals, testCase.vals, testCase.vals) - } + + assert.Equal(t, testCase.vals, vals.m) }) } }) @@ -393,19 +382,20 @@ func TestExtractCommentDirectives(t *testing.T) { } func TestSkipQueryPlanCacheDirective(t *testing.T) { - stmt, _ := Parse("insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)") + parser := NewTestParser() + stmt, _ := parser.Parse("insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)") assert.False(t, CachePlan(stmt)) - stmt, _ = Parse("insert into user(id) values (1), (2)") + stmt, _ = parser.Parse("insert into user(id) values (1), (2)") assert.True(t, CachePlan(stmt)) - stmt, _ = Parse("update /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ users set name=1") + stmt, _ = parser.Parse("update /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ users set name=1") assert.False(t, CachePlan(stmt)) - stmt, _ = Parse("select /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ * from users") + stmt, _ = parser.Parse("select /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ * from users") assert.False(t, CachePlan(stmt)) - stmt, _ = Parse("delete /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ from users") + stmt, _ = parser.Parse("delete /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ from users") assert.False(t, CachePlan(stmt)) } @@ -426,9 +416,10 @@ func TestIgnoreMaxPayloadSizeDirective(t *testing.T) { {"show create table users", false}, } + parser := NewTestParser() for _, test := range testCases { t.Run(test.query, func(t *testing.T) { - stmt, _ := Parse(test.query) + stmt, _ := parser.Parse(test.query) got := IgnoreMaxPayloadSizeDirective(stmt) assert.Equalf(t, test.expected, got, fmt.Sprintf("IgnoreMaxPayloadSizeDirective(stmt) returned %v but expected %v", got, test.expected)) }) @@ -452,9 +443,10 @@ func TestIgnoreMaxMaxMemoryRowsDirective(t *testing.T) { {"show create table users", false}, } + parser := NewTestParser() for _, test := range testCases { t.Run(test.query, func(t *testing.T) { - stmt, _ := Parse(test.query) + stmt, _ := parser.Parse(test.query) got := IgnoreMaxMaxMemoryRowsDirective(stmt) assert.Equalf(t, test.expected, got, fmt.Sprintf("IgnoreMaxPayloadSizeDirective(stmt) returned %v but expected %v", got, test.expected)) }) @@ -478,9 +470,10 @@ func TestConsolidator(t *testing.T) { {"select /*vt+ CONSOLIDATOR=enabled_replicas */ * from users", querypb.ExecuteOptions_CONSOLIDATOR_ENABLED_REPLICAS}, } + parser := NewTestParser() for _, test := range testCases { t.Run(test.query, func(t *testing.T) { - stmt, _ := Parse(test.query) + stmt, _ := parser.Parse(test.query) got := Consolidator(stmt) assert.Equalf(t, test.expected, got, fmt.Sprintf("Consolidator(stmt) returned %v but expected %v", got, test.expected)) }) @@ -535,19 +528,136 @@ func TestGetPriorityFromStatement(t *testing.T) { }, } + parser := NewTestParser() for _, testCase := range testCases { - theThestCase := testCase - t.Run(theThestCase.query, func(t *testing.T) { + t.Run(testCase.query, func(t *testing.T) { t.Parallel() - stmt, err := Parse(theThestCase.query) + stmt, err := parser.Parse(testCase.query) assert.NoError(t, err) actualPriority, actualError := GetPriorityFromStatement(stmt) - if theThestCase.expectedError != nil { - assert.ErrorIs(t, actualError, theThestCase.expectedError) + if testCase.expectedError != nil { + assert.ErrorIs(t, actualError, testCase.expectedError) } else { assert.NoError(t, err) - assert.Equal(t, theThestCase.expectedPriority, actualPriority) + assert.Equal(t, testCase.expectedPriority, actualPriority) + } + }) + } +} + +// TestGetMySQLSetVarValue tests the functionality of GetMySQLSetVarValue +func TestGetMySQLSetVarValue(t *testing.T) { + tests := []struct { + name string + comments []string + valToFind string + want string + }{ + { + name: "SET_VAR clause in the middle", + comments: []string{"/*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) SET_VAR(foreign_key_checks=OFF) NO_ICP(t1, t2) */"}, + valToFind: sysvars.ForeignKeyChecks, + want: "OFF", + }, + { + name: "Single SET_VAR clause", + comments: []string{"/*+ SET_VAR(sort_buffer_size = 16M) */"}, + valToFind: "sort_buffer_size", + want: "16M", + }, + { + name: "No comments", + comments: nil, + valToFind: "sort_buffer_size", + want: "", + }, + { + name: "Multiple SET_VAR clauses", + comments: []string{"/*+ SET_VAR(sort_buffer_size = 16M) */", "/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') */", "/*+ SET_VAR( foReiGn_key_checks = On) */"}, + valToFind: sysvars.ForeignKeyChecks, + want: "", + }, + { + name: "Verify casing", + comments: []string{"/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') SET_VAR( foReiGn_key_checks = On) */"}, + valToFind: sysvars.ForeignKeyChecks, + want: "On", + }, + { + name: "Leading comment is a normal comment", + comments: []string{"/* This is a normal comment */", "/*+ MAX_EXECUTION_TIME(1000) SET_VAR( foreign_key_checks = 1) */"}, + valToFind: sysvars.ForeignKeyChecks, + want: "1", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ParsedComments{ + comments: tt.comments, + } + assert.Equal(t, tt.want, c.GetMySQLSetVarValue(tt.valToFind)) + }) + } +} + +func TestSetMySQLSetVarValue(t *testing.T) { + tests := []struct { + name string + comments []string + key string + value string + commentsWanted Comments + }{ + { + name: "SET_VAR clause in the middle", + comments: []string{"/*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) SET_VAR(foreign_key_checks=OFF) NO_ICP(t1, t2) */"}, + key: sysvars.ForeignKeyChecks, + value: "On", + commentsWanted: []string{"/*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) SET_VAR(foreign_key_checks=On) NO_ICP(t1, t2) */"}, + }, + { + name: "Single SET_VAR clause", + comments: []string{"/*+ SET_VAR(sort_buffer_size = 16M) */"}, + key: "sort_buffer_size", + value: "1Mb", + commentsWanted: []string{"/*+ SET_VAR(sort_buffer_size=1Mb) */"}, + }, + { + name: "No comments", + comments: nil, + key: "sort_buffer_size", + value: "13M", + commentsWanted: []string{"/*+ SET_VAR(sort_buffer_size=13M) */"}, + }, + { + name: "Multiple SET_VAR clauses", + comments: []string{"/*+ SET_VAR(sort_buffer_size = 16M) */", "/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') */", "/*+ SET_VAR( foReiGn_key_checks = On) */"}, + key: sysvars.ForeignKeyChecks, + value: "1", + commentsWanted: []string{"/*+ SET_VAR(sort_buffer_size = 16M) SET_VAR(foreign_key_checks=1) */", "/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') */", "/*+ SET_VAR( foReiGn_key_checks = On) */"}, + }, + { + name: "Verify casing", + comments: []string{"/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') SET_VAR( foReiGn_key_checks = On) */"}, + key: sysvars.ForeignKeyChecks, + value: "off", + commentsWanted: []string{"/*+ SET_VAR(optimizer_switch = 'mrr_cost_b(ased=of\"f') SET_VAR(foReiGn_key_checks=off) */"}, + }, + { + name: "Leading comment is a normal comment", + comments: []string{"/* This is a normal comment */", "/*+ MAX_EXECUTION_TIME(1000) SET_VAR( foreign_key_checks = 1) */"}, + key: sysvars.ForeignKeyChecks, + value: "Off", + commentsWanted: []string{"/* This is a normal comment */", "/*+ MAX_EXECUTION_TIME(1000) SET_VAR(foreign_key_checks=Off) */"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &ParsedComments{ + comments: tt.comments, } + newComments := c.SetMySQLSetVarValue(tt.key, tt.value) + require.EqualValues(t, tt.commentsWanted, newComments) }) } } diff --git a/go/vt/sqlparser/constants.go b/go/vt/sqlparser/constants.go index 3848c53f3e0..becaad2a2fe 100644 --- a/go/vt/sqlparser/constants.go +++ b/go/vt/sqlparser/constants.go @@ -27,9 +27,14 @@ const ( SQLCalcFoundRowsStr = "sql_calc_found_rows " // Select.Lock - NoLockStr = "" - ForUpdateStr = " for update" - ShareModeStr = " lock in share mode" + NoLockStr = "" + ForUpdateStr = " for update" + ForUpdateNoWaitStr = " for update nowait" + ForUpdateSkipLockedStr = " for update skip locked" + ForShareStr = " for share" + ForShareNoWaitStr = " for share nowait" + ForShareSkipLockedStr = " for share skip locked" + ShareModeStr = " lock in share mode" // Select.Cache SQLCacheStr = "sql_cache " @@ -117,10 +122,15 @@ const ( NaturalLeftJoinStr = "natural left join" NaturalRightJoinStr = "natural right join" - // Index hints. - UseStr = "use " + // IgnoreStr string. IgnoreStr = "ignore " - ForceStr = "force " + + // Index hints. + UseStr = "use index" + IgnoreIndexStr = "ignore index" + ForceStr = "force index" + UseVindexStr = "use vindex" + IgnoreVindexStr = "ignore vindex" // Index hints For types. JoinForStr = "join" @@ -257,10 +267,8 @@ const ( EmptyStr = "" TreeStr = "tree" JSONStr = "json" - VitessStr = "vitess" TraditionalStr = "traditional" AnalyzeStr = "analyze" - VTExplainStr = "vtexplain" QueriesStr = "queries" AllVExplainStr = "all" PlanStr = "plan" @@ -309,6 +317,7 @@ const ( VitessTargetStr = " vitess_target" VitessVariablesStr = " vitess_metadata variables" VschemaTablesStr = " vschema tables" + VschemaKeyspacesStr = " vschema keyspaces" VschemaVindexesStr = " vschema vindexes" WarningsStr = " warnings" @@ -515,6 +524,11 @@ const ( NoLock Lock = iota ForUpdateLock ShareModeLock + ForShareLock + ForShareLockNoWait + ForShareLockSkipLocked + ForUpdateLockNoWait + ForUpdateLockSkipLocked ) // Constants for Enum Type - TrimType @@ -751,6 +765,8 @@ const ( UseOp IndexHintType = iota IgnoreOp ForceOp + UseVindexOp + IgnoreVindexOp ) // Constant for Enum Type - IndexHintForType @@ -799,8 +815,6 @@ const ( EmptyType ExplainType = iota TreeType JSONType - VitessType - VTExplainType TraditionalType AnalyzeType ) @@ -881,6 +895,7 @@ const ( VitessTarget VitessVariables VschemaTables + VschemaKeyspaces VschemaVindexes Warnings Keyspace @@ -916,6 +931,8 @@ const ( ThrottleAllMigrationType UnthrottleMigrationType UnthrottleAllMigrationType + ForceCutOverMigrationType + ForceCutOverAllMigrationType ) // ColumnStorage constants diff --git a/go/vt/sqlparser/goyacc/goyacc.go b/go/vt/sqlparser/goyacc/goyacc.go index 5864b5090b4..51650b0891e 100644 --- a/go/vt/sqlparser/goyacc/goyacc.go +++ b/go/vt/sqlparser/goyacc/goyacc.go @@ -49,7 +49,6 @@ import ( "bufio" "bytes" "fmt" - "go/format" "os" "regexp" "sort" @@ -58,6 +57,8 @@ import ( "unicode" "github.com/spf13/pflag" + + "vitess.io/vitess/go/tools/codegen" ) // the following are adjustable @@ -3326,7 +3327,7 @@ func exit(status int) { if ftable != nil { ftable.Flush() ftable = nil - gofmt() + _ = codegen.GoImports(oflag) } if foutput != nil { foutput.Flush() @@ -3339,18 +3340,6 @@ func exit(status int) { os.Exit(status) } -func gofmt() { - src, err := os.ReadFile(oflag) - if err != nil { - return - } - src, err = format.Source(src) - if err != nil { - return - } - os.WriteFile(oflag, src, 0666) -} - const fastAppendHelperText = ` func $$Iaddr(v any) __yyunsafe__.Pointer { type h struct { diff --git a/go/vt/sqlparser/impossible_query.go b/go/vt/sqlparser/impossible_query.go index 512931f1db7..a6bf1ea8736 100644 --- a/go/vt/sqlparser/impossible_query.go +++ b/go/vt/sqlparser/impossible_query.go @@ -27,6 +27,9 @@ package sqlparser func FormatImpossibleQuery(buf *TrackedBuffer, node SQLNode) { switch node := node.(type) { case *Select: + if node.With != nil { + node.With.Format(buf) + } buf.Myprintf("select %v from ", node.SelectExprs) var prefix string for _, n := range node.From { diff --git a/go/vt/sqlparser/keywords.go b/go/vt/sqlparser/keywords.go index 36c329d8e0a..c425858542e 100644 --- a/go/vt/sqlparser/keywords.go +++ b/go/vt/sqlparser/keywords.go @@ -285,6 +285,7 @@ var keywords = []keyword{ {"following", FOLLOWING}, {"for", FOR}, {"force", FORCE}, + {"force_cutover", FORCE_CUTOVER}, {"foreign", FOREIGN}, {"format", FORMAT}, {"format_bytes", FORMAT_BYTES}, @@ -413,6 +414,7 @@ var keywords = []keyword{ {"localtimestamp", LOCALTIMESTAMP}, {"locate", LOCATE}, {"lock", LOCK}, + {"locked", LOCKED}, {"logs", LOGS}, {"long", UNUSED}, {"longblob", LONGBLOB}, @@ -420,6 +422,7 @@ var keywords = []keyword{ {"loop", UNUSED}, {"low_priority", LOW_PRIORITY}, {"ltrim", LTRIM}, + {"mid", MID}, {"min", MIN}, {"manifest", MANIFEST}, {"master_bind", UNUSED}, @@ -457,6 +460,7 @@ var keywords = []keyword{ {"none", NONE}, {"not", NOT}, {"now", NOW}, + {"nowait", NOWAIT}, {"no_write_to_binlog", NO_WRITE_TO_BINLOG}, {"nth_value", NTH_VALUE}, {"ntile", NTILE}, @@ -575,6 +579,7 @@ var keywords = []keyword{ {"signal", UNUSED}, {"signed", SIGNED}, {"simple", SIMPLE}, + {"skip", SKIP}, {"slow", SLOW}, {"smallint", SMALLINT}, {"snapshot", SNAPSHOT}, @@ -814,14 +819,6 @@ func (cit *caseInsensitiveTable) LookupString(name string) (int, bool) { return 0, false } -func (cit *caseInsensitiveTable) Lookup(name []byte) (int, bool) { - hash := fnv1aI(offset64, name) - if candidate, ok := cit.h[hash]; ok { - return candidate.id, candidate.match(name) - } - return 0, false -} - func init() { for _, kw := range keywords { if kw.id == UNUSED { @@ -849,16 +846,6 @@ func KeywordString(id int) string { const offset64 = uint64(14695981039346656037) const prime64 = uint64(1099511628211) -func fnv1aI(h uint64, s []byte) uint64 { - for _, c := range s { - if 'A' <= c && c <= 'Z' { - c += 'a' - 'A' - } - h = (h ^ uint64(c)) * prime64 - } - return h -} - func fnv1aIstr(h uint64, s string) uint64 { for i := 0; i < len(s); i++ { c := s[i] diff --git a/go/vt/sqlparser/keywords_test.go b/go/vt/sqlparser/keywords_test.go index 0209ee20352..d386339a57f 100644 --- a/go/vt/sqlparser/keywords_test.go +++ b/go/vt/sqlparser/keywords_test.go @@ -32,6 +32,7 @@ func TestCompatibility(t *testing.T) { require.NoError(t, err) defer file.Close() + parser := NewTestParser() scanner := bufio.NewScanner(file) skipStep := 4 for scanner.Scan() { @@ -46,7 +47,7 @@ func TestCompatibility(t *testing.T) { word = "`" + word + "`" } sql := fmt.Sprintf("create table %s(c1 int)", word) - _, err := ParseStrictDDL(sql) + _, err := parser.ParseStrictDDL(sql) if err != nil { t.Errorf("%s is not compatible with mysql", word) } diff --git a/go/vt/sqlparser/like_filter_test.go b/go/vt/sqlparser/like_filter_test.go index 242e45e2f8d..3249eb152b9 100644 --- a/go/vt/sqlparser/like_filter_test.go +++ b/go/vt/sqlparser/like_filter_test.go @@ -30,7 +30,8 @@ func TestEmptyLike(t *testing.T) { } func TestLikePrefixRegexp(t *testing.T) { - show, e := Parse("show vitess_metadata variables like 'key%'") + parser := NewTestParser() + show, e := parser.Parse("show vitess_metadata variables like 'key%'") if e != nil { t.Error(e) } @@ -42,7 +43,8 @@ func TestLikePrefixRegexp(t *testing.T) { } func TestLikeAnyCharsRegexp(t *testing.T) { - show, e := Parse("show vitess_metadata variables like '%val1%val2%'") + parser := NewTestParser() + show, e := parser.Parse("show vitess_metadata variables like '%val1%val2%'") if e != nil { t.Error(e) } @@ -54,7 +56,8 @@ func TestLikeAnyCharsRegexp(t *testing.T) { } func TestSingleAndMultipleCharsRegexp(t *testing.T) { - show, e := Parse("show vitess_metadata variables like '_val1_val2%'") + parser := NewTestParser() + show, e := parser.Parse("show vitess_metadata variables like '_val1_val2%'") if e != nil { t.Error(e) } @@ -66,7 +69,8 @@ func TestSingleAndMultipleCharsRegexp(t *testing.T) { } func TestSpecialCharactersRegexp(t *testing.T) { - show, e := Parse("show vitess_metadata variables like '?.*?'") + parser := NewTestParser() + show, e := parser.Parse("show vitess_metadata variables like '?.*?'") if e != nil { t.Error(e) } @@ -78,7 +82,8 @@ func TestSpecialCharactersRegexp(t *testing.T) { } func TestQuoteLikeSpecialCharacters(t *testing.T) { - show, e := Parse(`show vitess_metadata variables like 'part1_part2\\%part3_part4\\_part5%'`) + parser := NewTestParser() + show, e := parser.Parse(`show vitess_metadata variables like 'part1_part2\\%part3_part4\\_part5%'`) if e != nil { t.Error(e) } diff --git a/go/vt/sqlparser/literal.go b/go/vt/sqlparser/literal.go index 71fed3d7d16..bde53798a19 100644 --- a/go/vt/sqlparser/literal.go +++ b/go/vt/sqlparser/literal.go @@ -87,8 +87,8 @@ func LiteralToValue(lit *Literal) (sqltypes.Value, error) { buf := datetime.Date_YYYY_MM_DD.Format(datetime.DateTime{Date: d}, 0) return sqltypes.NewDate(hack.String(buf)), nil case TimeVal: - t, l, ok := datetime.ParseTime(lit.Val, -1) - if !ok { + t, l, state := datetime.ParseTime(lit.Val, -1) + if state != datetime.TimeOK { return sqltypes.Value{}, fmt.Errorf("invalid time literal: %v", lit.Val) } buf := datetime.Time_hh_mm_ss.Format(datetime.DateTime{Time: t}, uint8(l)) diff --git a/go/vt/sqlparser/normalizer.go b/go/vt/sqlparser/normalizer.go index 3cc5fc4cb60..0254dccdfb2 100644 --- a/go/vt/sqlparser/normalizer.go +++ b/go/vt/sqlparser/normalizer.go @@ -149,7 +149,7 @@ func (nz *normalizer) walkUpSelect(cursor *Cursor) bool { parent := cursor.Parent() switch parent.(type) { case *Order, GroupBy: - return false + return true case *Limit: nz.convertLiteral(node, cursor) default: @@ -165,7 +165,7 @@ func validateLiteral(node *Literal) error { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Incorrect DATE value: '%s'", node.Val) } case TimeVal: - if _, _, ok := datetime.ParseTime(node.Val, -1); !ok { + if _, _, state := datetime.ParseTime(node.Val, -1); state != datetime.TimeOK { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Incorrect TIME value: '%s'", node.Val) } case TimestampVal: diff --git a/go/vt/sqlparser/normalizer_test.go b/go/vt/sqlparser/normalizer_test.go index 7c8c5e7a963..18f2ad44a7f 100644 --- a/go/vt/sqlparser/normalizer_test.go +++ b/go/vt/sqlparser/normalizer_test.go @@ -17,12 +17,12 @@ limitations under the License. package sqlparser import ( - "bytes" "fmt" "math/rand" "reflect" "regexp" "strconv" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -379,10 +379,20 @@ func TestNormalize(t *testing.T) { "v1": sqltypes.HexValBindVariable([]byte("x'31'")), "v2": sqltypes.Int64BindVariable(31), }, + }, { + // ORDER BY and GROUP BY variable + in: "select a, b from t group by 1, field(a,1,2,3) order by 1 asc, field(a,1,2,3)", + outstmt: "select a, b from t group by 1, field(a, :bv1 /* INT64 */, :bv2 /* INT64 */, :bv3 /* INT64 */) order by 1 asc, field(a, :bv1 /* INT64 */, :bv2 /* INT64 */, :bv3 /* INT64 */) asc", + outbv: map[string]*querypb.BindVariable{ + "bv1": sqltypes.Int64BindVariable(1), + "bv2": sqltypes.Int64BindVariable(2), + "bv3": sqltypes.Int64BindVariable(3), + }, }} + parser := NewTestParser() for _, tc := range testcases { t.Run(tc.in, func(t *testing.T) { - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(t, err) known := GetBindvars(stmt) bv := make(map[string]*querypb.BindVariable) @@ -407,9 +417,10 @@ func TestNormalizeInvalidDates(t *testing.T) { in: "select timestamp'foo'", err: vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "Incorrect DATETIME value: '%s'", "foo"), }} + parser := NewTestParser() for _, tc := range testcases { t.Run(tc.in, func(t *testing.T) { - stmt, err := Parse(tc.in) + stmt, err := parser.Parse(tc.in) require.NoError(t, err) known := GetBindvars(stmt) bv := make(map[string]*querypb.BindVariable) @@ -419,12 +430,13 @@ func TestNormalizeInvalidDates(t *testing.T) { } func TestNormalizeValidSQL(t *testing.T) { + parser := NewTestParser() for _, tcase := range validSQL { t.Run(tcase.input, func(t *testing.T) { if tcase.partialDDL || tcase.ignoreNormalizerTest { return } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) require.NoError(t, err, tcase.input) // Skip the test for the queries that do not run the normalizer if !CanNormalize(tree) { @@ -438,7 +450,7 @@ func TestNormalizeValidSQL(t *testing.T) { if normalizerOutput == "otheradmin" || normalizerOutput == "otherread" { return } - _, err = Parse(normalizerOutput) + _, err = parser.Parse(normalizerOutput) require.NoError(t, err, normalizerOutput) }) } @@ -454,7 +466,8 @@ func TestNormalizeOneCasae(t *testing.T) { if testOne.input == "" { t.Skip("empty test case") } - tree, err := Parse(testOne.input) + parser := NewTestParser() + tree, err := parser.Parse(testOne.input) require.NoError(t, err, testOne.input) // Skip the test for the queries that do not run the normalizer if !CanNormalize(tree) { @@ -468,12 +481,13 @@ func TestNormalizeOneCasae(t *testing.T) { if normalizerOutput == "otheradmin" || normalizerOutput == "otherread" { return } - _, err = Parse(normalizerOutput) + _, err = parser.Parse(normalizerOutput) require.NoError(t, err, normalizerOutput) } func TestGetBindVars(t *testing.T) { - stmt, err := Parse("select * from t where :v1 = :v2 and :v2 = :v3 and :v4 in ::v5") + parser := NewTestParser() + stmt, err := parser.Parse("select * from t where :v1 = :v2 and :v2 = :v3 and :v4 in ::v5") if err != nil { t.Fatal(err) } @@ -497,8 +511,9 @@ Prior to skip: BenchmarkNormalize-8 500000 3620 ns/op 1461 B/op 55 allocs/op */ func BenchmarkNormalize(b *testing.B) { + parser := NewTestParser() sql := "select 'abcd', 20, 30.0, eid from a where 1=eid and name='3'" - ast, reservedVars, err := Parse2(sql) + ast, reservedVars, err := parser.Parse2(sql) if err != nil { b.Fatal(err) } @@ -508,6 +523,7 @@ func BenchmarkNormalize(b *testing.B) { } func BenchmarkNormalizeTraces(b *testing.B) { + parser := NewTestParser() for _, trace := range []string{"django_queries.txt", "lobsters.sql.gz"} { b.Run(trace, func(b *testing.B) { queries := loadQueries(b, trace) @@ -518,7 +534,7 @@ func BenchmarkNormalizeTraces(b *testing.B) { parsed := make([]Statement, 0, len(queries)) reservedVars := make([]BindVars, 0, len(queries)) for _, q := range queries { - pp, kb, err := Parse2(q) + pp, kb, err := parser.Parse2(q) if err != nil { b.Fatal(err) } @@ -540,6 +556,7 @@ func BenchmarkNormalizeTraces(b *testing.B) { func BenchmarkNormalizeVTGate(b *testing.B) { const keyspace = "main_keyspace" + parser := NewTestParser() queries := loadQueries(b, "lobsters.sql.gz") if len(queries) > 10000 { @@ -551,7 +568,7 @@ func BenchmarkNormalizeVTGate(b *testing.B) { for i := 0; i < b.N; i++ { for _, sql := range queries { - stmt, reservedVars, err := Parse2(sql) + stmt, reservedVars, err := parser.Parse2(sql) if err != nil { b.Fatal(err) } @@ -573,6 +590,7 @@ func BenchmarkNormalizeVTGate(b *testing.B) { SQLSelectLimitUnset, "", nil, /*sysvars*/ + nil, nil, /*views*/ ) if err != nil { @@ -625,7 +643,7 @@ values func BenchmarkNormalizeTPCCInsert(b *testing.B) { generateInsert := func(rows int) string { - var query bytes.Buffer + var query strings.Builder query.WriteString("INSERT IGNORE INTO customer0 (c_id, c_d_id, c_w_id, c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_since, c_credit, c_credit_lim, c_discount, c_balance, c_ytd_payment, c_payment_cnt, c_delivery_cnt, c_data) values ") for i := 0; i < rows; i++ { fmt.Fprintf(&query, "(%d, %d, %d, '%s','OE','%s','%s', '%s', '%s', '%s', '%s','%s',NOW(),'%s',50000,%f,-10,10,1,0,'%s' )", @@ -846,9 +864,10 @@ func benchmarkNormalization(b *testing.B, sqls []string) { b.Helper() b.ReportAllocs() b.ResetTimer() + parser := NewTestParser() for i := 0; i < b.N; i++ { for _, sql := range sqls { - stmt, reserved, err := Parse2(sql) + stmt, reserved, err := parser.Parse2(sql) if err != nil { b.Fatalf("%v: %q", err, sql) } @@ -864,6 +883,7 @@ func benchmarkNormalization(b *testing.B, sqls []string) { "", nil, nil, + nil, ) if err != nil { b.Fatal(err) diff --git a/go/vt/sqlparser/parse_next_test.go b/go/vt/sqlparser/parse_next_test.go index 2e55fbb8a9a..687bb7fbb51 100644 --- a/go/vt/sqlparser/parse_next_test.go +++ b/go/vt/sqlparser/parse_next_test.go @@ -17,7 +17,6 @@ limitations under the License. package sqlparser import ( - "bytes" "io" "strings" "testing" @@ -29,13 +28,14 @@ import ( // TestParseNextValid concatenates all the valid SQL test cases and check it can read // them as one long string. func TestParseNextValid(t *testing.T) { - var sql bytes.Buffer + var sql strings.Builder for _, tcase := range validSQL { sql.WriteString(strings.TrimSuffix(tcase.input, ";")) sql.WriteRune(';') } - tokens := NewStringTokenizer(sql.String()) + parser := NewTestParser() + tokens := parser.NewStringTokenizer(sql.String()) for _, tcase := range validSQL { want := tcase.output if want == "" { @@ -55,7 +55,8 @@ func TestParseNextValid(t *testing.T) { func TestIgnoreSpecialComments(t *testing.T) { input := `SELECT 1;/*! ALTER TABLE foo DISABLE KEYS */;SELECT 2;` - tokenizer := NewStringTokenizer(input) + parser := NewTestParser() + tokenizer := parser.NewStringTokenizer(input) tokenizer.SkipSpecialComments = true one, err := ParseNextStrictDDL(tokenizer) require.NoError(t, err) @@ -68,6 +69,7 @@ func TestIgnoreSpecialComments(t *testing.T) { // TestParseNextErrors tests all the error cases, and ensures a valid // SQL statement can be passed afterwards. func TestParseNextErrors(t *testing.T) { + parser := NewTestParser() for _, tcase := range invalidSQL { if tcase.excludeMulti { // Skip tests which leave unclosed strings, or comments. @@ -75,7 +77,7 @@ func TestParseNextErrors(t *testing.T) { } t.Run(tcase.input, func(t *testing.T) { sql := tcase.input + "; select 1 from t" - tokens := NewStringTokenizer(sql) + tokens := parser.NewStringTokenizer(sql) // The first statement should be an error _, err := ParseNextStrictDDL(tokens) @@ -134,9 +136,9 @@ func TestParseNextEdgeCases(t *testing.T) { input: "create table a ignore me this is garbage; select 1 from a", want: []string{"create table a", "select 1 from a"}, }} - + parser := NewTestParser() for _, test := range tests { - tokens := NewStringTokenizer(test.input) + tokens := parser.NewStringTokenizer(test.input) for i, want := range test.want { tree, err := ParseNext(tokens) @@ -166,7 +168,8 @@ func TestParseNextStrictNonStrict(t *testing.T) { want := []string{"create table a", "select 1 from a"} // First go through as expected with non-strict DDL parsing. - tokens := NewStringTokenizer(input) + parser := NewTestParser() + tokens := parser.NewStringTokenizer(input) for i, want := range want { tree, err := ParseNext(tokens) if err != nil { @@ -178,7 +181,7 @@ func TestParseNextStrictNonStrict(t *testing.T) { } // Now try again with strict parsing and observe the expected error. - tokens = NewStringTokenizer(input) + tokens = parser.NewStringTokenizer(input) _, err := ParseNextStrictDDL(tokens) if err == nil || !strings.Contains(err.Error(), "ignore") { t.Fatalf("ParseNext(%q) err = %q, want ignore", input, err) diff --git a/go/vt/sqlparser/parse_table.go b/go/vt/sqlparser/parse_table.go index 8766994ecfd..d522a855054 100644 --- a/go/vt/sqlparser/parse_table.go +++ b/go/vt/sqlparser/parse_table.go @@ -23,8 +23,8 @@ import ( // ParseTable parses the input as a qualified table name. // It handles all valid literal escaping. -func ParseTable(input string) (keyspace, table string, err error) { - tokenizer := NewStringTokenizer(input) +func (p *Parser) ParseTable(input string) (keyspace, table string, err error) { + tokenizer := p.NewStringTokenizer(input) // Start, want ID token, value := tokenizer.Scan() diff --git a/go/vt/sqlparser/parse_table_test.go b/go/vt/sqlparser/parse_table_test.go index 09e7ea44177..5f187cbc6d0 100644 --- a/go/vt/sqlparser/parse_table_test.go +++ b/go/vt/sqlparser/parse_table_test.go @@ -56,8 +56,9 @@ func TestParseTable(t *testing.T) { input: "k.t.", err: true, }} + parser := NewTestParser() for _, tcase := range testcases { - keyspace, table, err := ParseTable(tcase.input) + keyspace, table, err := parser.ParseTable(tcase.input) assert.Equal(t, tcase.keyspace, keyspace) assert.Equal(t, tcase.table, table) if tcase.err { diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 1837a104e4c..f90ae16606e 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -18,7 +18,6 @@ package sqlparser import ( "bufio" - "bytes" "compress/gzip" "fmt" "io" @@ -29,12 +28,11 @@ import ( "sync" "testing" - "vitess.io/vitess/go/test/utils" - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/utils" ) var ( @@ -693,8 +691,18 @@ var ( input: "select /* distinct */ distinct 1 from t", }, { input: "select /* straight_join */ straight_join 1 from t", + }, { + input: "select /* for share */ 1 from t for share", + }, { + input: "select /* for share */ 1 from t for share nowait", + }, { + input: "select /* for share */ 1 from t for share skip locked", }, { input: "select /* for update */ 1 from t for update", + }, { + input: "select /* for update */ 1 from t for update nowait", + }, { + input: "select /* for update */ 1 from t for update skip locked", }, { input: "select /* lock in share mode */ 1 from t lock in share mode", }, { @@ -1351,7 +1359,7 @@ var ( input: "delete /* limit */ from a limit b", }, { input: "delete /* alias where */ t.* from a as t where t.id = 2", - output: "delete /* alias where */ t from a as t where t.id = 2", + output: "delete /* alias where */ from a as t where t.id = 2", }, { input: "delete t.* from t, t1", output: "delete t from t, t1", @@ -2369,6 +2377,8 @@ var ( input: "show vitess_targets", }, { input: "show vschema tables", + }, { + input: "show vschema keyspaces", }, { input: "show vschema vindexes", }, { @@ -2410,6 +2420,13 @@ var ( input: "alter vitess_migration complete all", }, { input: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' cancel", + }, { + input: "alter vitess_migration force_cutover all", + }, { + input: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' force_cutover", + }, { + input: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' FORCE_CUTOVER", + output: "alter vitess_migration '9748c3b7_7fdb_11eb_ac2c_f875a4d24e90' force_cutover", }, { input: "alter vitess_migration cancel all", }, { @@ -2447,6 +2464,10 @@ var ( }, { input: "show foobar like select * from table where syntax is 'ignored'", output: "show foobar", + }, { + // Making sure "force_cutover" is not a keyword + input: "select force_cutover from t", + output: "select `force_cutover` from t", }, { input: "use db", output: "use db", @@ -2502,16 +2523,6 @@ var ( input: "explain format = tree select * from t", }, { input: "explain format = json select * from t", - }, { - input: "explain format = vtexplain select * from t", - }, { - input: "explain format = vitess select * from t", - }, { - input: "describe format = vitess select * from t", - output: "explain format = vitess select * from t", - }, { - input: "describe format = vtexplain select * from t", - output: "explain format = vtexplain select * from t", }, { input: "explain delete from t", }, { @@ -2648,6 +2659,20 @@ var ( }, { input: "SELECT id FROM blog_posts USE INDEX (PRIMARY) WHERE id = 10", output: "select id from blog_posts use index (`PRIMARY`) where id = 10", + }, { + input: "select * from payment_pulls ignore vindex (lookup_vindex_name) where customer_id in (1, 10) and payment_id = 5", + }, { + input: "select * from payment_pulls ignore vindex (lookup_vindex_name, x, t) order by id", + output: "select * from payment_pulls ignore vindex (lookup_vindex_name, x, t) order by id asc", + }, { + input: "select * from payment_pulls use vindex (lookup_vindex_name) where customer_id in (1, 10) and payment_id = 5", + }, { + input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) order by id", + output: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) order by id asc", + }, { + input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) ignore vindex (x, t)", + }, { + input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) ignore vindex (x, t) join tab ignore vindex (y)", }, { input: "select name, group_concat(score) from t group by name", output: "select `name`, group_concat(score) from t group by `name`", @@ -3668,6 +3693,21 @@ var ( }, { input: `select * from t1 where col1 like 'ks\_' and col2 = 'ks\_' and col1 like 'ks_' and col2 = 'ks_'`, output: `select * from t1 where col1 like 'ks\_' and col2 = 'ks\_' and col1 like 'ks_' and col2 = 'ks_'`, + }, { + input: "select 1 from dual where 'bac' = 'b' 'a' 'c'", + output: "select 1 from dual where 'bac' = 'bac'", + }, { + input: "select 'b' 'a' 'c'", + output: "select 'bac' from dual", + }, { + input: "select 1 where 'bac' = N'b' 'a' 'c'", + output: "select 1 from dual where 'bac' = N'bac'", + /*We need to ignore this test because, after the normalizer, we change the produced NChar + string into an introducer expression, so the vttablet will never see a NChar string */ + ignoreNormalizerTest: true, + }, { + input: "select _ascii 'b' 'a' 'c'", + output: "select _ascii 'bac' from dual", }, { input: `kill connection 18446744073709551615`, }, { @@ -3679,12 +3719,13 @@ var ( ) func TestValid(t *testing.T) { + parser := NewTestParser() for _, tcase := range validSQL { t.Run(tcase.input, func(t *testing.T) { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) require.NoError(t, err, tcase.input) out := String(tree) assert.Equal(t, tcase.output, out) @@ -3716,6 +3757,7 @@ func TestParallelValid(t *testing.T) { wg := sync.WaitGroup{} wg.Add(parallelism) + parser := NewTestParser() for i := 0; i < parallelism; i++ { go func() { defer wg.Done() @@ -3724,7 +3766,7 @@ func TestParallelValid(t *testing.T) { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Errorf("Parse(%q) err: %v, want nil", tcase.input, err) continue @@ -3923,9 +3965,10 @@ func TestInvalid(t *testing.T) { }, } + parser := NewTestParser() for _, tcase := range invalidSQL { t.Run(tcase.input, func(t *testing.T) { - _, err := Parse(tcase.input) + _, err := parser.Parse(tcase.input) require.Error(t, err) require.Contains(t, err.Error(), tcase.err) }) @@ -4063,12 +4106,13 @@ func TestIntroducers(t *testing.T) { input: "select _utf8mb3 'x'", output: "select _utf8mb3 'x' from dual", }} + parser := NewTestParser() for _, tcase := range validSQL { t.Run(tcase.input, func(t *testing.T) { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) assert.NoError(t, err) out := String(tree) assert.Equal(t, tcase.output, out) @@ -4157,11 +4201,12 @@ func TestCaseSensitivity(t *testing.T) { }, { input: "select /* use */ 1 from t1 use index (A) where b = 1", }} + parser := NewTestParser() for _, tcase := range validSQL { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -4256,11 +4301,12 @@ func TestKeywords(t *testing.T) { output: "select current_user(), current_user() from dual", }} + parser := NewTestParser() for _, tcase := range validSQL { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -4333,11 +4379,12 @@ func TestConvert(t *testing.T) { input: "select cast(json_keys(c) as char(64) array) from t", }} + parser := NewTestParser() for _, tcase := range validSQL { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -4381,7 +4428,7 @@ func TestConvert(t *testing.T) { }} for _, tcase := range invalidSQL { - _, err := Parse(tcase.input) + _, err := parser.Parse(tcase.input) if err == nil || err.Error() != tcase.output { t.Errorf("%s: %v, want %s", tcase.input, err, tcase.output) } @@ -4419,12 +4466,13 @@ func TestSelectInto(t *testing.T) { output: "alter vschema create vindex my_vdx using `hash`", }} + parser := NewTestParser() for _, tcase := range validSQL { t.Run(tcase.input, func(t *testing.T) { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) require.NoError(t, err) out := String(tree) assert.Equal(t, tcase.output, out) @@ -4443,7 +4491,7 @@ func TestSelectInto(t *testing.T) { }} for _, tcase := range invalidSQL { - _, err := Parse(tcase.input) + _, err := parser.Parse(tcase.input) if err == nil || err.Error() != tcase.output { t.Errorf("%s: %v, want %s", tcase.input, err, tcase.output) } @@ -4480,8 +4528,9 @@ func TestPositionedErr(t *testing.T) { output: PositionedErr{"syntax error", 34, ""}, }} + parser := NewTestParser() for _, tcase := range invalidSQL { - tkn := NewStringTokenizer(tcase.input) + tkn := parser.NewStringTokenizer(tcase.input) _, err := ParseNext(tkn) if posErr, ok := err.(PositionedErr); !ok { @@ -4530,11 +4579,12 @@ func TestSubStr(t *testing.T) { output: `select substr(substr('foo', 1), 2) from t`, }} + parser := NewTestParser() for _, tcase := range validSQL { if tcase.output == "" { tcase.output = tcase.input } - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -4554,8 +4604,9 @@ func TestLoadData(t *testing.T) { "load data infile 'x.txt' into table 'c'", "load data from s3 'x.txt' into table x"} + parser := NewTestParser() for _, tcase := range validSQL { - _, err := Parse(tcase) + _, err := parser.Parse(tcase) require.NoError(t, err) } } @@ -5732,10 +5783,11 @@ partition by range (YEAR(purchased)) subpartition by hash (TO_DAYS(purchased)) output: "create table t (\n\tid int,\n\tinfo JSON,\n\tkey zips ((cast(info -> '$.field' as unsigned array)))\n)", }, } + parser := NewTestParser() for _, test := range createTableQueries { sql := strings.TrimSpace(test.input) t.Run(sql, func(t *testing.T) { - tree, err := ParseStrictDDL(sql) + tree, err := parser.ParseStrictDDL(sql) require.NoError(t, err) got := String(tree) expected := test.output @@ -5758,7 +5810,8 @@ func TestOne(t *testing.T) { return } sql := strings.TrimSpace(testOne.input) - tree, err := Parse(sql) + parser := NewTestParser() + tree, err := parser.Parse(sql) require.NoError(t, err) got := String(tree) expected := testOne.output @@ -5787,8 +5840,9 @@ func TestCreateTableLike(t *testing.T) { "create table ks.a like unsharded_ks.b", }, } + parser := NewTestParser() for _, tcase := range testCases { - tree, err := ParseStrictDDL(tcase.input) + tree, err := parser.ParseStrictDDL(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -5817,8 +5871,9 @@ func TestCreateTableEscaped(t *testing.T) { "\tprimary key (`delete`)\n" + ")", }} + parser := NewTestParser() for _, tcase := range testCases { - tree, err := ParseStrictDDL(tcase.input) + tree, err := parser.ParseStrictDDL(tcase.input) if err != nil { t.Errorf("input: %s, err: %v", tcase.input, err) continue @@ -5963,9 +6018,10 @@ var ( ) func TestErrors(t *testing.T) { + parser := NewTestParser() for _, tcase := range invalidSQL { t.Run(tcase.input, func(t *testing.T) { - _, err := ParseStrictDDL(tcase.input) + _, err := parser.ParseStrictDDL(tcase.input) require.Error(t, err, tcase.output) require.Equal(t, tcase.output, err.Error()) }) @@ -5998,8 +6054,9 @@ func TestSkipToEnd(t *testing.T) { input: "create table a bb 'a;'; select * from t", output: "extra characters encountered after end of DDL: 'select'", }} + parser := NewTestParser() for _, tcase := range testcases { - _, err := Parse(tcase.input) + _, err := parser.Parse(tcase.input) if err == nil || err.Error() != tcase.output { t.Errorf("%s: %v, want %s", tcase.input, err, tcase.output) } @@ -6031,8 +6088,9 @@ func loadQueries(t testing.TB, filename string) (queries []string) { } func TestParseDjangoQueries(t *testing.T) { + parser := NewTestParser() for _, query := range loadQueries(t, "django_queries.txt") { - _, err := Parse(query) + _, err := parser.Parse(query) if err != nil { t.Errorf("failed to parse %q: %v", query, err) } @@ -6040,8 +6098,9 @@ func TestParseDjangoQueries(t *testing.T) { } func TestParseLobstersQueries(t *testing.T) { + parser := NewTestParser() for _, query := range loadQueries(t, "lobsters.sql.gz") { - _, err := Parse(query) + _, err := parser.Parse(query) if err != nil { t.Errorf("failed to parse %q: %v", query, err) } @@ -6056,14 +6115,14 @@ func TestParseVersionedComments(t *testing.T) { }{ { input: `CREATE TABLE table1 (id int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 /*!50900 PARTITION BY RANGE (id) (PARTITION x VALUES LESS THAN (5) ENGINE = InnoDB, PARTITION t VALUES LESS THAN (20) ENGINE = InnoDB) */`, - mysqlVersion: "50401", + mysqlVersion: "5.4.1", output: `create table table1 ( id int ) ENGINE InnoDB, CHARSET utf8mb4`, }, { input: `CREATE TABLE table1 (id int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 /*!50900 PARTITION BY RANGE (id) (PARTITION x VALUES LESS THAN (5) ENGINE = InnoDB, PARTITION t VALUES LESS THAN (20) ENGINE = InnoDB) */`, - mysqlVersion: "80001", + mysqlVersion: "8.0.1", output: `create table table1 ( id int ) ENGINE InnoDB, @@ -6076,10 +6135,9 @@ partition by range (id) for _, testcase := range testcases { t.Run(testcase.input+":"+testcase.mysqlVersion, func(t *testing.T) { - oldMySQLVersion := mySQLParserVersion - defer func() { mySQLParserVersion = oldMySQLVersion }() - mySQLParserVersion = testcase.mysqlVersion - tree, err := Parse(testcase.input) + parser, err := New(Options{MySQLServerVersion: testcase.mysqlVersion}) + require.NoError(t, err) + tree, err := parser.Parse(testcase.input) require.NoError(t, err, testcase.input) out := String(tree) require.Equal(t, testcase.output, out) @@ -6088,6 +6146,7 @@ partition by range (id) } func BenchmarkParseTraces(b *testing.B) { + parser := NewTestParser() for _, trace := range []string{"django_queries.txt", "lobsters.sql.gz"} { b.Run(trace, func(b *testing.B) { queries := loadQueries(b, trace) @@ -6099,7 +6158,7 @@ func BenchmarkParseTraces(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - _, err := Parse(query) + _, err := parser.Parse(query) if err != nil { b.Fatal(err) } @@ -6116,16 +6175,17 @@ func BenchmarkParseStress(b *testing.B) { sql2 = "select aaaa, bbb, ccc, ddd, eeee, ffff, gggg, hhhh, iiii from tttt, ttt1, ttt3 where aaaa = bbbb and bbbb = cccc and dddd+1 = eeee group by fff, gggg having hhhh = iiii and iiii = jjjj order by kkkk, llll limit 3, 4" ) + parser := NewTestParser() for i, sql := range []string{sql1, sql2} { b.Run(fmt.Sprintf("sql%d", i), func(b *testing.B) { - var buf bytes.Buffer + var buf strings.Builder buf.WriteString(sql) querySQL := buf.String() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := Parse(querySQL) + _, err := parser.Parse(querySQL) if err != nil { b.Fatal(err) } @@ -6143,7 +6203,7 @@ func BenchmarkParse3(b *testing.B) { // Size of value is 1/10 size of query. Then we add // 10 such values to the where clause. - var baseval bytes.Buffer + var baseval strings.Builder for i := 0; i < benchQuerySize/100; i++ { // Add an escape character: This will force the upcoming // tokenizer improvement to still create a copy of the string. @@ -6155,7 +6215,7 @@ func BenchmarkParse3(b *testing.B) { } } - var buf bytes.Buffer + var buf strings.Builder buf.WriteString("select a from t1 where v = 1") for i := 0; i < 10; i++ { fmt.Fprintf(&buf, " and v%d = \"%d%s\"", i, i, baseval.String()) @@ -6164,8 +6224,9 @@ func BenchmarkParse3(b *testing.B) { b.ResetTimer() b.ReportAllocs() + parser := NewTestParser() for i := 0; i < b.N; i++ { - if _, err := Parse(benchQuery); err != nil { + if _, err := parser.Parse(benchQuery); err != nil { b.Fatal(err) } } @@ -6216,6 +6277,7 @@ func escapeNewLines(in string) string { } func testFile(t *testing.T, filename, tempDir string) { + parser := NewTestParser() t.Run(filename, func(t *testing.T) { fail := false expected := strings.Builder{} @@ -6225,7 +6287,7 @@ func testFile(t *testing.T, filename, tempDir string) { tcase.output = tcase.input } expected.WriteString(fmt.Sprintf("%sINPUT\n%s\nEND\n", tcase.comments, escapeNewLines(tcase.input))) - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if tcase.errStr != "" { errPresent := "" if err != nil { @@ -6328,7 +6390,7 @@ func parsePartial(r *bufio.Reader, readType []string, lineno int, fileName strin if returnTypeNumber != -1 { break } - panic(fmt.Errorf("error reading file %s: line %d: %s - Expected keyword", fileName, lineno, err.Error())) + panic(fmt.Errorf("error reading file %s: line %d: Expected keyword", fileName, lineno)) } input := "" for { diff --git a/go/vt/sqlparser/parsed_query.go b/go/vt/sqlparser/parsed_query.go index b6b03a1901a..a612e555ee8 100644 --- a/go/vt/sqlparser/parsed_query.go +++ b/go/vt/sqlparser/parsed_query.go @@ -21,12 +21,7 @@ import ( "fmt" "strings" - "vitess.io/vitess/go/bytes2" - vjson "vitess.io/vitess/go/mysql/json" "vitess.io/vitess/go/sqltypes" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -34,11 +29,12 @@ import ( // bind locations are precomputed for fast substitutions. type ParsedQuery struct { Query string - bindLocations []bindLocation + bindLocations []BindLocation + truncateUILen int } -type bindLocation struct { - offset, length int +type BindLocation struct { + Offset, Length int } // NewParsedQuery returns a ParsedQuery of the ast. @@ -67,8 +63,8 @@ func (pq *ParsedQuery) GenerateQuery(bindVariables map[string]*querypb.BindVaria func (pq *ParsedQuery) Append(buf *strings.Builder, bindVariables map[string]*querypb.BindVariable, extras map[string]Encodable) error { current := 0 for _, loc := range pq.bindLocations { - buf.WriteString(pq.Query[current:loc.offset]) - name := pq.Query[loc.offset : loc.offset+loc.length] + buf.WriteString(pq.Query[current:loc.Offset]) + name := pq.Query[loc.Offset : loc.Offset+loc.Length] if encodable, ok := extras[name[1:]]; ok { encodable.EncodeSQL(buf) } else { @@ -78,86 +74,19 @@ func (pq *ParsedQuery) Append(buf *strings.Builder, bindVariables map[string]*qu } EncodeValue(buf, supplied) } - current = loc.offset + loc.length + current = loc.Offset + loc.Length } buf.WriteString(pq.Query[current:]) return nil } -// AppendFromRow behaves like Append but takes a querypb.Row directly, assuming that -// the fields in the row are in the same order as the placeholders in this query. The fields might include generated -// columns which are dropped, by checking against skipFields, before binding the variables -// note: there can be more fields than bind locations since extra columns might be requested from the source if not all -// primary keys columns are present in the target table, for example. Also some values in the row may not correspond for -// values from the database on the source: sum/count for aggregation queries, for example -func (pq *ParsedQuery) AppendFromRow(buf *bytes2.Buffer, fields []*querypb.Field, row *querypb.Row, skipFields map[string]bool) error { - if len(fields) < len(pq.bindLocations) { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations ", - len(fields), len(pq.bindLocations)) - } - - type colInfo struct { - typ querypb.Type - length int64 - offset int64 - } - rowInfo := make([]*colInfo, 0) - - offset := int64(0) - for i, field := range fields { // collect info required for fields to be bound - length := row.Lengths[i] - if !skipFields[strings.ToLower(field.Name)] { - rowInfo = append(rowInfo, &colInfo{ - typ: field.Type, - length: length, - offset: offset, - }) - } - if length > 0 { - offset += row.Lengths[i] - } - } - - // bind field values to locations - var offsetQuery int - for i, loc := range pq.bindLocations { - col := rowInfo[i] - buf.WriteString(pq.Query[offsetQuery:loc.offset]) - typ := col.typ - - switch typ { - case querypb.Type_TUPLE: - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected Type_TUPLE for value %d", i) - case querypb.Type_JSON: - if col.length < 0 { // An SQL NULL and not an actual JSON value - buf.WriteString(sqltypes.NullStr) - } else { // A JSON value (which may be a JSON null literal value) - buf2 := row.Values[col.offset : col.offset+col.length] - vv, err := vjson.MarshalSQLValue(buf2) - if err != nil { - return err - } - buf.WriteString(vv.RawStr()) - } - default: - if col.length < 0 { - // -1 means a null variable; serialize it directly - buf.WriteString(sqltypes.NullStr) - } else { - vv := sqltypes.MakeTrusted(typ, row.Values[col.offset:col.offset+col.length]) - vv.EncodeSQLBytes2(buf) - } - } - offsetQuery = loc.offset + loc.length - } - buf.WriteString(pq.Query[offsetQuery:]) - return nil +func (pq *ParsedQuery) BindLocations() []BindLocation { + return pq.bindLocations } // MarshalJSON is a custom JSON marshaler for ParsedQuery. -// Note that any queries longer that 512 bytes will be truncated. func (pq *ParsedQuery) MarshalJSON() ([]byte, error) { - return json.Marshal(TruncateForUI(pq.Query)) + return json.Marshal(pq.Query) } // EncodeValue encodes one bind variable value into the query. diff --git a/go/vt/sqlparser/parsed_query_test.go b/go/vt/sqlparser/parsed_query_test.go index 8c89a51984d..ef59676883f 100644 --- a/go/vt/sqlparser/parsed_query_test.go +++ b/go/vt/sqlparser/parsed_query_test.go @@ -27,7 +27,8 @@ import ( ) func TestNewParsedQuery(t *testing.T) { - stmt, err := Parse("select * from a where id =:id") + parser := NewTestParser() + stmt, err := parser.Parse("select * from a where id =:id") if err != nil { t.Error(err) return @@ -35,7 +36,7 @@ func TestNewParsedQuery(t *testing.T) { pq := NewParsedQuery(stmt) want := &ParsedQuery{ Query: "select * from a where id = :id", - bindLocations: []bindLocation{{offset: 27, length: 3}}, + bindLocations: []BindLocation{{Offset: 27, Length: 3}}, } if !reflect.DeepEqual(pq, want) { t.Errorf("GenerateParsedQuery: %+v, want %+v", pq, want) @@ -135,8 +136,9 @@ func TestGenerateQuery(t *testing.T) { }, } + parser := NewTestParser() for _, tcase := range tcases { - tree, err := Parse(tcase.query) + tree, err := parser.Parse(tcase.query) if err != nil { t.Errorf("parse failed for %s: %v", tcase.desc, err) continue diff --git a/go/vt/sqlparser/parser.go b/go/vt/sqlparser/parser.go index ae630ce3dea..d4948396ae5 100644 --- a/go/vt/sqlparser/parser.go +++ b/go/vt/sqlparser/parser.go @@ -17,22 +17,20 @@ limitations under the License. package sqlparser import ( + "errors" "fmt" "io" "strconv" "strings" "sync" - "vitess.io/vitess/go/internal/flag" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -var versionFlagSync sync.Once - // parserPool is a pool for parser objects. var parserPool = sync.Pool{ New: func() any { @@ -43,9 +41,6 @@ var parserPool = sync.Pool{ // zeroParser is a zero-initialized parser to help reinitialize the parser for pooling. var zeroParser yyParserImpl -// mySQLParserVersion is the version of MySQL that the parser would emulate -var mySQLParserVersion string - // yyParsePooled is a wrapper around yyParse that pools the parser objects. There isn't a // particularly good reason to use yyParse directly, since it immediately discards its parser. // @@ -80,12 +75,12 @@ func yyParsePooled(yylex yyLexer) int { // bind variables that were found in the original SQL query. If a DDL statement // is partially parsed but still contains a syntax error, the // error is ignored and the DDL is returned anyway. -func Parse2(sql string) (Statement, BindVars, error) { - tokenizer := NewStringTokenizer(sql) +func (p *Parser) Parse2(sql string) (Statement, BindVars, error) { + tokenizer := p.NewStringTokenizer(sql) if yyParsePooled(tokenizer) != 0 { if tokenizer.partialDDL != nil { if typ, val := tokenizer.Scan(); typ != 0 { - return nil, nil, fmt.Errorf("extra characters encountered after end of DDL: '%s'", string(val)) + return nil, nil, fmt.Errorf("extra characters encountered after end of DDL: '%s'", val) } log.Warningf("ignoring error parsing DDL '%s': %v", sql, tokenizer.LastError) switch x := tokenizer.partialDDL.(type) { @@ -105,30 +100,8 @@ func Parse2(sql string) (Statement, BindVars, error) { return tokenizer.ParseTree, tokenizer.BindVars, nil } -func checkParserVersionFlag() { - if flag.Parsed() { - versionFlagSync.Do(func() { - convVersion, err := convertMySQLVersionToCommentVersion(servenv.MySQLServerVersion()) - if err != nil { - log.Fatalf("unable to parse mysql version: %v", err) - } - mySQLParserVersion = convVersion - }) - } -} - -// SetParserVersion sets the mysql parser version -func SetParserVersion(version string) { - mySQLParserVersion = version -} - -// GetParserVersion returns the version of the mysql parser -func GetParserVersion() string { - return mySQLParserVersion -} - -// convertMySQLVersionToCommentVersion converts the MySQL version into comment version format. -func convertMySQLVersionToCommentVersion(version string) (string, error) { +// ConvertMySQLVersionToCommentVersion converts the MySQL version into comment version format. +func ConvertMySQLVersionToCommentVersion(version string) (string, error) { var res = make([]int, 3) idx := 0 val := "" @@ -166,8 +139,8 @@ func convertMySQLVersionToCommentVersion(version string) (string, error) { } // ParseExpr parses an expression and transforms it to an AST -func ParseExpr(sql string) (Expr, error) { - stmt, err := Parse("select " + sql) +func (p *Parser) ParseExpr(sql string) (Expr, error) { + stmt, err := p.Parse("select " + sql) if err != nil { return nil, err } @@ -176,15 +149,15 @@ func ParseExpr(sql string) (Expr, error) { } // Parse behaves like Parse2 but does not return a set of bind variables -func Parse(sql string) (Statement, error) { - stmt, _, err := Parse2(sql) +func (p *Parser) Parse(sql string) (Statement, error) { + stmt, _, err := p.Parse2(sql) return stmt, err } // ParseStrictDDL is the same as Parse except it errors on // partially parsed DDL statements. -func ParseStrictDDL(sql string) (Statement, error) { - tokenizer := NewStringTokenizer(sql) +func (p *Parser) ParseStrictDDL(sql string) (Statement, error) { + tokenizer := p.NewStringTokenizer(sql) if yyParsePooled(tokenizer) != 0 { return nil, tokenizer.LastError } @@ -194,17 +167,11 @@ func ParseStrictDDL(sql string) (Statement, error) { return tokenizer.ParseTree, nil } -// ParseTokenizer is a raw interface to parse from the given tokenizer. -// This does not used pooled parsers, and should not be used in general. -func ParseTokenizer(tokenizer *Tokenizer) int { - return yyParse(tokenizer) -} - // ParseNext parses a single SQL statement from the tokenizer // returning a Statement which is the AST representation of the query. // The tokenizer will always read up to the end of the statement, allowing for // the next call to ParseNext to parse any subsequent SQL statements. When -// there are no more statements to parse, a error of io.EOF is returned. +// there are no more statements to parse, an error of io.EOF is returned. func ParseNext(tokenizer *Tokenizer) (Statement, error) { return parseNext(tokenizer, false) } @@ -243,10 +210,10 @@ func parseNext(tokenizer *Tokenizer, strict bool) (Statement, error) { // ErrEmpty is a sentinel error returned when parsing empty statements. var ErrEmpty = vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.EmptyQuery, "Query was empty") -// SplitStatement returns the first sql statement up to either a ; or EOF +// SplitStatement returns the first sql statement up to either a ';' or EOF // and the remainder from the given buffer -func SplitStatement(blob string) (string, string, error) { - tokenizer := NewStringTokenizer(blob) +func (p *Parser) SplitStatement(blob string) (string, string, error) { + tokenizer := p.NewStringTokenizer(blob) tkn := 0 for { tkn, _ = tokenizer.Scan() @@ -263,9 +230,25 @@ func SplitStatement(blob string) (string, string, error) { return blob, "", nil } +// SplitStatements splits a given blob into multiple SQL statements. +func (p *Parser) SplitStatements(blob string) (statements []Statement, err error) { + tokenizer := p.NewStringTokenizer(blob) + for { + stmt, err := ParseNext(tokenizer) + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, err + } + statements = append(statements, stmt) + } + return statements, nil +} + // SplitStatementToPieces split raw sql statement that may have multi sql pieces to sql pieces // returns the sql pieces blob contains; or error if sql cannot be parsed -func SplitStatementToPieces(blob string) (pieces []string, err error) { +func (p *Parser) SplitStatementToPieces(blob string) (pieces []string, err error) { // fast path: the vast majority of SQL statements do not have semicolons in them if blob == "" { return nil, nil @@ -273,12 +256,15 @@ func SplitStatementToPieces(blob string) (pieces []string, err error) { switch strings.IndexByte(blob, ';') { case -1: // if there is no semicolon, return blob as a whole return []string{blob}, nil - case len(blob) - 1: // if there's a single semicolon and it's the last character, return blob without it + case len(blob) - 1: // if there's a single semicolon, and it's the last character, return blob without it return []string{blob[:len(blob)-1]}, nil } pieces = make([]string, 0, 16) - tokenizer := NewStringTokenizer(blob) + // It's safe here to not case about version specific tokenization + // because we are only interested in semicolons and splitting + // statements. + tokenizer := p.NewStringTokenizer(blob) tkn := 0 var stmt string @@ -313,6 +299,49 @@ loop: return } -func IsMySQL80AndAbove() bool { - return mySQLParserVersion >= "80000" +func (p *Parser) IsMySQL80AndAbove() bool { + return p.version >= "80000" +} + +func (p *Parser) SetTruncateErrLen(l int) { + p.truncateErrLen = l +} + +type Options struct { + MySQLServerVersion string + TruncateUILen int + TruncateErrLen int +} + +type Parser struct { + version string + truncateUILen int + truncateErrLen int +} + +func New(opts Options) (*Parser, error) { + if opts.MySQLServerVersion == "" { + opts.MySQLServerVersion = config.DefaultMySQLVersion + } + convVersion, err := ConvertMySQLVersionToCommentVersion(opts.MySQLServerVersion) + if err != nil { + return nil, err + } + return &Parser{ + version: convVersion, + truncateUILen: opts.TruncateUILen, + truncateErrLen: opts.TruncateErrLen, + }, nil +} + +func NewTestParser() *Parser { + convVersion, err := ConvertMySQLVersionToCommentVersion(config.DefaultMySQLVersion) + if err != nil { + panic(err) + } + return &Parser{ + version: convVersion, + truncateUILen: 512, + truncateErrLen: 0, + } } diff --git a/go/vt/sqlparser/parser_test.go b/go/vt/sqlparser/parser_test.go index 537cc598da7..5cb15317f29 100644 --- a/go/vt/sqlparser/parser_test.go +++ b/go/vt/sqlparser/parser_test.go @@ -51,9 +51,10 @@ func TestEmptyErrorAndComments(t *testing.T) { output: "select 1 from dual", }, } + parser := NewTestParser() for _, testcase := range testcases { t.Run(testcase.input, func(t *testing.T) { - res, err := Parse(testcase.input) + res, err := parser.Parse(testcase.input) if testcase.err != nil { require.Equal(t, testcase.err, err) } else { @@ -63,7 +64,7 @@ func TestEmptyErrorAndComments(t *testing.T) { }) t.Run(testcase.input+"-Strict DDL", func(t *testing.T) { - res, err := ParseStrictDDL(testcase.input) + res, err := parser.ParseStrictDDL(testcase.input) if testcase.err != nil { require.Equal(t, testcase.err, err) } else { diff --git a/go/vt/sqlparser/precedence_test.go b/go/vt/sqlparser/precedence_test.go index a6cbffee351..774ada31dbd 100644 --- a/go/vt/sqlparser/precedence_test.go +++ b/go/vt/sqlparser/precedence_test.go @@ -53,8 +53,9 @@ func TestAndOrPrecedence(t *testing.T) { input: "select * from a where a=b or c=d and e=f", output: "(a = b or (c = d and e = f))", }} + parser := NewTestParser() for _, tcase := range validSQL { - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Error(err) continue @@ -77,8 +78,9 @@ func TestPlusStarPrecedence(t *testing.T) { input: "select 1*2+3 from a", output: "((1 * 2) + 3)", }} + parser := NewTestParser() for _, tcase := range validSQL { - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Error(err) continue @@ -104,8 +106,9 @@ func TestIsPrecedence(t *testing.T) { input: "select * from a where (a=1 and b=2) is true", output: "((a = 1 and b = 2) is true)", }} + parser := NewTestParser() for _, tcase := range validSQL { - tree, err := Parse(tcase.input) + tree, err := parser.Parse(tcase.input) if err != nil { t.Error(err) continue @@ -158,9 +161,10 @@ func TestParens(t *testing.T) { {in: "0 <=> (1 and 0)", expected: "0 <=> (1 and 0)"}, } + parser := NewTestParser() for _, tc := range tests { t.Run(tc.in, func(t *testing.T) { - stmt, err := Parse("select " + tc.in) + stmt, err := parser.Parse("select " + tc.in) require.NoError(t, err) out := String(stmt) require.Equal(t, "select "+tc.expected+" from dual", out) @@ -177,6 +181,7 @@ func TestRandom(t *testing.T) { g := NewGenerator(r, 5) endBy := time.Now().Add(1 * time.Second) + parser := NewTestParser() for { if time.Now().After(endBy) { break @@ -186,7 +191,7 @@ func TestRandom(t *testing.T) { inputQ := "select " + String(randomExpr) + " from t" // When it's parsed and unparsed - parsedInput, err := Parse(inputQ) + parsedInput, err := parser.Parse(inputQ) require.NoError(t, err, inputQ) // Then the unparsing should be the same as the input query diff --git a/go/vt/sqlparser/predicate_rewriting.go b/go/vt/sqlparser/predicate_rewriting.go index 40e9a953f57..234a2f4acd5 100644 --- a/go/vt/sqlparser/predicate_rewriting.go +++ b/go/vt/sqlparser/predicate_rewriting.go @@ -16,17 +16,16 @@ limitations under the License. package sqlparser -import ( - "vitess.io/vitess/go/vt/log" -) +import "slices" // RewritePredicate walks the input AST and rewrites any boolean logic into a simpler form // This simpler form is CNF plus logic for extracting predicates from OR, plus logic for turning ORs into IN -// Note: In order to re-plan, we need to empty the accumulated metadata in the AST, -// so ColName.Metadata will be nil:ed out as part of this rewrite func RewritePredicate(ast SQLNode) SQLNode { - for { - printExpr(ast) + original := CloneSQLNode(ast) + + // Beware: converting to CNF in this loop might cause exponential formula growth. + // We bail out early to prevent going overboard. + for loop := 0; loop < 15; loop++ { exprChanged := false stopOnChange := func(SQLNode, SQLNode) bool { return !exprChanged @@ -37,9 +36,8 @@ func RewritePredicate(ast SQLNode) SQLNode { return true } - rewritten, state := simplifyExpression(e) - if ch, isChange := state.(changed); isChange { - printRule(ch.rule, ch.exprMatched) + rewritten, changed := simplifyExpression(e) + if changed { exprChanged = true cursor.Replace(rewritten) } @@ -50,9 +48,11 @@ func RewritePredicate(ast SQLNode) SQLNode { return ast } } + + return original } -func simplifyExpression(expr Expr) (Expr, rewriteState) { +func simplifyExpression(expr Expr) (Expr, bool) { switch expr := expr.(type) { case *NotExpr: return simplifyNot(expr) @@ -63,105 +63,91 @@ func simplifyExpression(expr Expr) (Expr, rewriteState) { case *AndExpr: return simplifyAnd(expr) } - return expr, noChange{} + return expr, false } -func simplifyNot(expr *NotExpr) (Expr, rewriteState) { +func simplifyNot(expr *NotExpr) (Expr, bool) { switch child := expr.Expr.(type) { case *NotExpr: - return child.Expr, - newChange("NOT NOT A => A", f(expr)) + return child.Expr, true case *OrExpr: - return &AndExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, - newChange("NOT (A OR B) => NOT A AND NOT B", f(expr)) + // not(or(a,b)) => and(not(a),not(b)) + return &AndExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, true case *AndExpr: - return &OrExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, - newChange("NOT (A AND B) => NOT A OR NOT B", f(expr)) + // not(and(a,b)) => or(not(a), not(b)) + return &OrExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, true } - return expr, noChange{} + return expr, false } -// ExtractINFromOR will add additional predicated to an OR. -// this rewriter should not be used in a fixed point way, since it returns the original expression with additions, -// and it will therefor OOM before it stops rewriting -func ExtractINFromOR(expr *OrExpr) []Expr { - // we check if we have two comparisons on either side of the OR - // that we can add as an ANDed comparison. - // WHERE (a = 5 and B) or (a = 6 AND C) => - // WHERE (a = 5 AND B) OR (a = 6 AND C) AND a IN (5,6) - // This rewrite makes it possible to find a better route than Scatter if the `a` column has a helpful vindex - lftPredicates := SplitAndExpression(nil, expr.Left) - rgtPredicates := SplitAndExpression(nil, expr.Right) - var ins []Expr - for _, lft := range lftPredicates { - l, ok := lft.(*ComparisonExpr) - if !ok { - continue - } - for _, rgt := range rgtPredicates { - r, ok := rgt.(*ComparisonExpr) - if !ok { - continue - } - in, state := tryTurningOrIntoIn(l, r) - if state.changed() { - ins = append(ins, in) - } - } +func simplifyOr(expr *OrExpr) (Expr, bool) { + res, rewritten := distinctOr(expr) + if rewritten { + return res, true } - return uniquefy(ins) -} - -func simplifyOr(expr *OrExpr) (Expr, rewriteState) { or := expr // first we search for ANDs and see how they can be simplified land, lok := or.Left.(*AndExpr) rand, rok := or.Right.(*AndExpr) - switch { - case lok && rok: + + if lok && rok { // (<> AND <>) OR (<> AND <>) + // or(and(T1,T2), and(T2, T3)) => and(T1, or(T2, T2)) var a, b, c Expr - var change changed switch { case Equals.Expr(land.Left, rand.Left): - change = newChange("(A and B) or (A and C) => A AND (B OR C)", f(expr)) a, b, c = land.Left, land.Right, rand.Right + return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, true case Equals.Expr(land.Left, rand.Right): - change = newChange("(A and B) or (C and A) => A AND (B OR C)", f(expr)) a, b, c = land.Left, land.Right, rand.Left + return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, true case Equals.Expr(land.Right, rand.Left): - change = newChange("(B and A) or (A and C) => A AND (B OR C)", f(expr)) a, b, c = land.Right, land.Left, rand.Right + return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, true case Equals.Expr(land.Right, rand.Right): - change = newChange("(B and A) or (C and A) => A AND (B OR C)", f(expr)) a, b, c = land.Right, land.Left, rand.Left - default: - return expr, noChange{} + return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, true } - return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, change - case lok: - // (<> AND <>) OR <> + } + + // (<> AND <>) OR <> + if lok { // Simplification if Equals.Expr(or.Right, land.Left) || Equals.Expr(or.Right, land.Right) { - return or.Right, newChange("(A AND B) OR A => A", f(expr)) + // or(and(a,b), c) => c where c=a or c=b + return or.Right, true } + // Distribution Law - return &AndExpr{Left: &OrExpr{Left: land.Left, Right: or.Right}, Right: &OrExpr{Left: land.Right, Right: or.Right}}, - newChange("(A AND B) OR C => (A OR C) AND (B OR C)", f(expr)) - case rok: - // <> OR (<> AND <>) + // or(c, and(a,b)) => and(or(c,a), or(c,b)) + return &AndExpr{ + Left: &OrExpr{ + Left: land.Left, + Right: or.Right, + }, + Right: &OrExpr{ + Left: land.Right, + Right: or.Right, + }, + }, true + } + + // <> OR (<> AND <>) + if rok { // Simplification if Equals.Expr(or.Left, rand.Left) || Equals.Expr(or.Left, rand.Right) { - return or.Left, newChange("A OR (A AND B) => A", f(expr)) + // or(a,and(b,c)) => a + return or.Left, true } + // Distribution Law + // or(and(a,b), c) => and(or(c,a), or(c,b)) return &AndExpr{ - Left: &OrExpr{Left: or.Left, Right: rand.Left}, - Right: &OrExpr{Left: or.Left, Right: rand.Right}, - }, - newChange("C OR (A AND B) => (C OR A) AND (C OR B)", f(expr)) + Left: &OrExpr{Left: or.Left, Right: rand.Left}, + Right: &OrExpr{Left: or.Left, Right: rand.Right}, + }, true } // next, we want to try to turn multiple ORs into an IN when possible @@ -169,63 +155,223 @@ func simplifyOr(expr *OrExpr) (Expr, rewriteState) { rgtCmp, rok := or.Right.(*ComparisonExpr) if lok && rok { newExpr, rewritten := tryTurningOrIntoIn(lftCmp, rgtCmp) - if rewritten.changed() { - return newExpr, rewritten + if rewritten { + // or(a=x,a=y) => in(a,[x,y]) + return newExpr, true } } // Try to make distinct - return distinctOr(expr) + result, changed := distinctOr(expr) + if changed { + return result, true + } + return result, false +} + +func simplifyXor(expr *XorExpr) (Expr, bool) { + // xor(a,b) => and(or(a,b), not(and(a,b)) + return &AndExpr{ + Left: &OrExpr{Left: expr.Left, Right: expr.Right}, + Right: &NotExpr{Expr: &AndExpr{Left: expr.Left, Right: expr.Right}}, + }, true } -func tryTurningOrIntoIn(l, r *ComparisonExpr) (Expr, rewriteState) { +func simplifyAnd(expr *AndExpr) (Expr, bool) { + res, rewritten := distinctAnd(expr) + if rewritten { + return res, true + } + and := expr + if or, ok := and.Left.(*OrExpr); ok { + // Simplification + // and(or(a,b),c) => c when c=a or c=b + if Equals.Expr(or.Left, and.Right) { + return and.Right, true + } + if Equals.Expr(or.Right, and.Right) { + return and.Right, true + } + } + if or, ok := and.Right.(*OrExpr); ok { + // Simplification + if Equals.Expr(or.Left, and.Left) { + return and.Left, true + } + if Equals.Expr(or.Right, and.Left) { + return and.Left, true + } + } + + return expr, false +} + +// ExtractINFromOR rewrites the OR expression into an IN clause. +// Each side of each ORs has to be an equality comparison expression and the column names have to +// match for all sides of each comparison. +// This rewriter takes a query that looks like this WHERE a = 1 and b = 11 or a = 2 and b = 12 or a = 3 and b = 13 +// And rewrite that to WHERE (a, b) IN ((1,11), (2,12), (3,13)) +func ExtractINFromOR(expr *OrExpr) []Expr { + var varNames []*ColName + var values []Exprs + orSlice := orToSlice(expr) + for _, expr := range orSlice { + andSlice := andToSlice(expr) + if len(andSlice) == 0 { + return nil + } + + var currentVarNames []*ColName + var currentValues []Expr + for _, comparisonExpr := range andSlice { + if comparisonExpr.Operator != EqualOp { + return nil + } + + var colName *ColName + if left, ok := comparisonExpr.Left.(*ColName); ok { + colName = left + currentValues = append(currentValues, comparisonExpr.Right) + } + + if right, ok := comparisonExpr.Right.(*ColName); ok { + if colName != nil { + return nil + } + colName = right + currentValues = append(currentValues, comparisonExpr.Left) + } + + if colName == nil { + return nil + } + + currentVarNames = append(currentVarNames, colName) + } + + if len(varNames) == 0 { + varNames = currentVarNames + } else if !slices.EqualFunc(varNames, currentVarNames, func(col1, col2 *ColName) bool { return col1.Equal(col2) }) { + return nil + } + + values = append(values, currentValues) + } + + var nameTuple ValTuple + for _, name := range varNames { + nameTuple = append(nameTuple, name) + } + + var valueTuple ValTuple + for _, value := range values { + valueTuple = append(valueTuple, ValTuple(value)) + } + + return []Expr{&ComparisonExpr{ + Operator: InOp, + Left: nameTuple, + Right: valueTuple, + }} +} + +func orToSlice(expr *OrExpr) []Expr { + var exprs []Expr + + handleOrSide := func(e Expr) { + switch e := e.(type) { + case *OrExpr: + exprs = append(exprs, orToSlice(e)...) + default: + exprs = append(exprs, e) + } + } + + handleOrSide(expr.Left) + handleOrSide(expr.Right) + return exprs +} + +func andToSlice(expr Expr) []*ComparisonExpr { + var andExpr *AndExpr + switch expr := expr.(type) { + case *AndExpr: + andExpr = expr + case *ComparisonExpr: + return []*ComparisonExpr{expr} + default: + return nil + } + + var exprs []*ComparisonExpr + handleAndSide := func(e Expr) bool { + switch e := e.(type) { + case *AndExpr: + slice := andToSlice(e) + if slice == nil { + return false + } + exprs = append(exprs, slice...) + case *ComparisonExpr: + exprs = append(exprs, e) + default: + return false + } + return true + } + + if !handleAndSide(andExpr.Left) { + return nil + } + if !handleAndSide(andExpr.Right) { + return nil + } + + return exprs +} + +func tryTurningOrIntoIn(l, r *ComparisonExpr) (Expr, bool) { // looks for A = X OR A = Y and turns them into A IN (X, Y) col, ok := l.Left.(*ColName) if !ok || !Equals.Expr(col, r.Left) { - return nil, noChange{} + return nil, false } var tuple ValTuple - var ruleStr string + switch l.Operator { case EqualOp: tuple = ValTuple{l.Right} - ruleStr = "A = <>" case InOp: lft, ok := l.Right.(ValTuple) if !ok { - return nil, noChange{} + return nil, false } tuple = lft - ruleStr = "A IN (<>, <>)" default: - return nil, noChange{} + return nil, false } - ruleStr += " OR " - switch r.Operator { case EqualOp: tuple = append(tuple, r.Right) - ruleStr += "A = <>" + case InOp: lft, ok := r.Right.(ValTuple) if !ok { - return nil, noChange{} + return nil, false } tuple = append(tuple, lft...) - ruleStr += "A IN (<>, <>)" + default: - return nil, noChange{} + return nil, false } - ruleStr += " => A IN (<>, <>)" - return &ComparisonExpr{ Operator: InOp, Left: col, Right: uniquefy(tuple), - }, newChange(ruleStr, f(&OrExpr{Left: l, Right: r})) + }, true } func uniquefy(tuple ValTuple) (output ValTuple) { @@ -241,45 +387,7 @@ outer: return } -func simplifyXor(expr *XorExpr) (Expr, rewriteState) { - // DeMorgan Rewriter - return &AndExpr{ - Left: &OrExpr{Left: expr.Left, Right: expr.Right}, - Right: &NotExpr{Expr: &AndExpr{Left: expr.Left, Right: expr.Right}}, - }, newChange("(A XOR B) => (A OR B) AND NOT (A AND B)", f(expr)) -} - -func simplifyAnd(expr *AndExpr) (Expr, rewriteState) { - res, rewritten := distinctAnd(expr) - if rewritten.changed() { - return res, rewritten - } - and := expr - if or, ok := and.Left.(*OrExpr); ok { - // Simplification - - if Equals.Expr(or.Left, and.Right) { - return and.Right, newChange("(A OR B) AND A => A", f(expr)) - } - if Equals.Expr(or.Right, and.Right) { - return and.Right, newChange("(A OR B) AND B => B", f(expr)) - } - } - if or, ok := and.Right.(*OrExpr); ok { - // Simplification - if Equals.Expr(or.Left, and.Left) { - return and.Left, newChange("A AND (A OR B) => A", f(expr)) - } - if Equals.Expr(or.Right, and.Left) { - return and.Left, newChange("A AND (B OR A) => A", f(expr)) - } - } - - return expr, noChange{} -} - -func distinctOr(in *OrExpr) (Expr, rewriteState) { - var skipped []*OrExpr +func distinctOr(in *OrExpr) (result Expr, changed bool) { todo := []*OrExpr{in} var leaves []Expr for len(todo) > 0 { @@ -296,27 +404,23 @@ func distinctOr(in *OrExpr) (Expr, rewriteState) { addAnd(curr.Left) addAnd(curr.Right) } - original := len(leaves) + var predicates []Expr outer1: - for len(leaves) > 0 { - curr := leaves[0] - leaves = leaves[1:] + for _, curr := range leaves { for _, alreadyIn := range predicates { if Equals.Expr(alreadyIn, curr) { - if log.V(0) { - skipped = append(skipped, &OrExpr{Left: alreadyIn, Right: curr}) - } + changed = true continue outer1 } } predicates = append(predicates, curr) } - if original == len(predicates) { - return in, noChange{} + if !changed { + return in, false } - var result Expr + for i, curr := range predicates { if i == 0 { result = curr @@ -325,25 +429,10 @@ outer1: result = &OrExpr{Left: result, Right: curr} } - return result, newChange("A OR A => A", func() Expr { - var result Expr - for _, orExpr := range skipped { - if result == nil { - result = orExpr - continue - } - - result = &OrExpr{ - Left: result, - Right: orExpr, - } - } - return result - }) + return } -func distinctAnd(in *AndExpr) (Expr, rewriteState) { - var skipped []*AndExpr +func distinctAnd(in *AndExpr) (result Expr, changed bool) { todo := []*AndExpr{in} var leaves []Expr for len(todo) > 0 { @@ -359,25 +448,23 @@ func distinctAnd(in *AndExpr) (Expr, rewriteState) { addExpr(curr.Left) addExpr(curr.Right) } - original := len(leaves) var predicates []Expr outer1: for _, curr := range leaves { for _, alreadyIn := range predicates { if Equals.Expr(alreadyIn, curr) { - if log.V(0) { - skipped = append(skipped, &AndExpr{Left: alreadyIn, Right: curr}) - } + changed = true continue outer1 } } predicates = append(predicates, curr) } - if original == len(predicates) { - return in, noChange{} + + if !changed { + return in, false } - var result Expr + for i, curr := range predicates { if i == 0 { result = curr @@ -385,62 +472,5 @@ outer1: } result = &AndExpr{Left: result, Right: curr} } - return AndExpressions(leaves...), newChange("A AND A => A", func() Expr { - var result Expr - for _, andExpr := range skipped { - if result == nil { - result = andExpr - continue - } - - result = &AndExpr{ - Left: result, - Right: andExpr, - } - } - return result - }) -} - -type ( - rewriteState interface { - changed() bool - } - noChange struct{} - - // changed makes it possible to make sure we have a rule string for each change we do in the expression tree - changed struct { - rule string - - // ExprMatched is a function here so building of this expression can be paid only when we are debug logging - exprMatched func() Expr - } -) - -func (noChange) changed() bool { return false } -func (changed) changed() bool { return true } - -// f returns a function that returns the expression. It's short by design, so it interferes minimally -// used for logging -func f(e Expr) func() Expr { - return func() Expr { return e } -} - -func printRule(rule string, expr func() Expr) { - if log.V(10) { - log.Infof("Rule: %s ON %s", rule, String(expr())) - } -} - -func printExpr(expr SQLNode) { - if log.V(10) { - log.Infof("Current: %s", String(expr)) - } -} - -func newChange(rule string, exprMatched func() Expr) changed { - return changed{ - rule: rule, - exprMatched: exprMatched, - } + return AndExpressions(leaves...), true } diff --git a/go/vt/sqlparser/predicate_rewriting_test.go b/go/vt/sqlparser/predicate_rewriting_test.go index 34e23597894..ceb4b276017 100644 --- a/go/vt/sqlparser/predicate_rewriting_test.go +++ b/go/vt/sqlparser/predicate_rewriting_test.go @@ -86,13 +86,14 @@ func TestSimplifyExpression(in *testing.T) { expected: "A and (B or C)", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { - expr, err := ParseExpr(tc.in) + expr, err := parser.ParseExpr(tc.in) require.NoError(t, err) - expr, didRewrite := simplifyExpression(expr) - assert.True(t, didRewrite.changed()) + expr, changed := simplifyExpression(expr) + assert.True(t, changed) assert.Equal(t, tc.expected, String(expr)) }) } @@ -129,11 +130,38 @@ func TestRewritePredicate(in *testing.T) { }, { in: "A and (B or A)", expected: "A", + }, { + in: "(a = 1 and b = 41) or (a = 2 and b = 42)", + // this might look weird, but it allows the planner to either a or b in a vindex operation + expected: "a in (1, 2) and (a = 1 or b = 42) and ((b = 41 or a = 2) and b in (41, 42))", + }, { + in: "(a = 1 and b = 41) or (a = 2 and b = 42) or (a = 3 and b = 43)", + expected: "a in (1, 2, 3) and (a in (1, 2) or b = 43) and ((a = 1 or b = 42 or a = 3) and (a = 1 or b = 42 or b = 43)) and ((b = 41 or a = 2 or a = 3) and (b = 41 or a = 2 or b = 43) and ((b in (41, 42) or a = 3) and b in (41, 42, 43)))", + }, { + // the following two tests show some pathological cases that would grow too much, and so we abort the rewriting + in: "a = 1 and b = 41 or a = 2 and b = 42 or a = 3 and b = 43 or a = 4 and b = 44 or a = 5 and b = 45 or a = 6 and b = 46", + expected: "a = 1 and b = 41 or a = 2 and b = 42 or a = 3 and b = 43 or a = 4 and b = 44 or a = 5 and b = 45 or a = 6 and b = 46", + }, { + in: "a = 5 and B or a = 6 and C", + expected: "a in (5, 6) and (a = 5 or C) and ((B or a = 6) and (B or C))", + }, { + in: "(a = 5 and b = 1 or b = 2 and a = 6)", + expected: "(a = 5 or b = 2) and a in (5, 6) and (b in (1, 2) and (b = 1 or a = 6))", + }, { + in: "(a in (1,5) and B or C and a = 6)", + expected: "(a in (1, 5) or C) and a in (1, 5, 6) and ((B or C) and (B or a = 6))", + }, { + in: "(a in (1, 5) and B or C and a in (5, 7))", + expected: "(a in (1, 5) or C) and a in (1, 5, 7) and ((B or C) and (B or a in (5, 7)))", + }, { + in: "not n0 xor not (n2 and n3) xor (not n2 and (n1 xor n1) xor (n0 xor n0 xor n2))", + expected: "not n0 xor not (n2 and n3) xor (not n2 and (n1 xor n1) xor (n0 xor n0 xor n2))", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { - expr, err := ParseExpr(tc.in) + expr, err := parser.ParseExpr(tc.in) require.NoError(t, err) output := RewritePredicate(expr) @@ -147,28 +175,17 @@ func TestExtractINFromOR(in *testing.T) { in string expected string }{{ - in: "(A and B) or (B and A)", - expected: "", - }, { - in: "(a = 5 and B) or A", - expected: "", - }, { - in: "a = 5 and B or a = 6 and C", - expected: "a in (5, 6)", + in: "a = 1 and b = 41 or a = 2 and b = 42 or a = 3 and b = 43 or a = 4 and b = 44 or a = 5 and b = 45 or a = 6 and b = 46", + expected: "(a, b) in ((1, 41), (2, 42), (3, 43), (4, 44), (5, 45), (6, 46))", }, { - in: "(a = 5 and b = 1 or b = 2 and a = 6)", - expected: "a in (5, 6) and b in (1, 2)", - }, { - in: "(a in (1,5) and B or C and a = 6)", - expected: "a in (1, 5, 6)", - }, { - in: "(a in (1, 5) and B or C and a in (5, 7))", - expected: "a in (1, 5, 7)", + in: "a = 1 or a = 2 or a = 3 or a = 4 or a = 5 or a = 6", + expected: "(a) in ((1), (2), (3), (4), (5), (6))", }} + parser := NewTestParser() for _, tc := range tests { in.Run(tc.in, func(t *testing.T) { - expr, err := ParseExpr(tc.in) + expr, err := parser.ParseExpr(tc.in) require.NoError(t, err) output := ExtractINFromOR(expr.(*OrExpr)) diff --git a/go/vt/sqlparser/redact_query.go b/go/vt/sqlparser/redact_query.go index 194ad1ca64d..e6b8c009c68 100644 --- a/go/vt/sqlparser/redact_query.go +++ b/go/vt/sqlparser/redact_query.go @@ -19,11 +19,11 @@ package sqlparser import querypb "vitess.io/vitess/go/vt/proto/query" // RedactSQLQuery returns a sql string with the params stripped out for display -func RedactSQLQuery(sql string) (string, error) { +func (p *Parser) RedactSQLQuery(sql string) (string, error) { bv := map[string]*querypb.BindVariable{} sqlStripped, comments := SplitMarginComments(sql) - stmt, reservedVars, err := Parse2(sqlStripped) + stmt, reservedVars, err := p.Parse2(sqlStripped) if err != nil { return "", err } diff --git a/go/vt/sqlparser/redact_query_test.go b/go/vt/sqlparser/redact_query_test.go index 1cfd6d83af3..042f0f5b5f2 100644 --- a/go/vt/sqlparser/redact_query_test.go +++ b/go/vt/sqlparser/redact_query_test.go @@ -23,8 +23,9 @@ import ( ) func TestRedactSQLStatements(t *testing.T) { + parser := NewTestParser() sql := "select a,b,c from t where x = 1234 and y = 1234 and z = 'apple'" - redactedSQL, err := RedactSQLQuery(sql) + redactedSQL, err := parser.RedactSQLQuery(sql) if err != nil { t.Fatalf("redacting sql failed: %v", err) } diff --git a/go/vt/sqlparser/rewriter_test.go b/go/vt/sqlparser/rewriter_test.go index 3044e04f8b0..91c925d672f 100644 --- a/go/vt/sqlparser/rewriter_test.go +++ b/go/vt/sqlparser/rewriter_test.go @@ -43,7 +43,8 @@ func BenchmarkVisitLargeExpression(b *testing.B) { func TestReplaceWorksInLaterCalls(t *testing.T) { q := "select * from tbl1" - stmt, err := Parse(q) + parser := NewTestParser() + stmt, err := parser.Parse(q) require.NoError(t, err) count := 0 Rewrite(stmt, func(cursor *Cursor) bool { @@ -67,7 +68,8 @@ func TestReplaceWorksInLaterCalls(t *testing.T) { func TestReplaceAndRevisitWorksInLaterCalls(t *testing.T) { q := "select * from tbl1" - stmt, err := Parse(q) + parser := NewTestParser() + stmt, err := parser.Parse(q) require.NoError(t, err) count := 0 Rewrite(stmt, func(cursor *Cursor) bool { @@ -94,7 +96,8 @@ func TestReplaceAndRevisitWorksInLaterCalls(t *testing.T) { } func TestChangeValueTypeGivesError(t *testing.T) { - parse, err := Parse("select * from a join b on a.id = b.id") + parser := NewTestParser() + parse, err := parser.Parse("select * from a join b on a.id = b.id") require.NoError(t, err) defer func() { diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index d837b38da7a..baa8e713f94 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -6,6 +6,8 @@ package sqlparser import ( __yyfmt__ "fmt" __yyunsafe__ "unsafe" + + "vitess.io/vitess/go/ptr" ) //line sql.y:17 @@ -34,722 +36,726 @@ func markBindVariable(yylex yyLexer, bvar string) { } const MEMBER = 57346 -const FUNCTION_CALL_NON_KEYWORD = 57347 -const STRING_TYPE_PREFIX_NON_KEYWORD = 57348 -const LEX_ERROR = 57349 -const UNION = 57350 -const SELECT = 57351 -const STREAM = 57352 -const VSTREAM = 57353 -const INSERT = 57354 -const UPDATE = 57355 -const DELETE = 57356 -const FROM = 57357 -const WHERE = 57358 -const GROUP = 57359 -const HAVING = 57360 -const ORDER = 57361 -const BY = 57362 -const LIMIT = 57363 -const OFFSET = 57364 -const FOR = 57365 -const ALL = 57366 -const DISTINCT = 57367 -const AS = 57368 -const EXISTS = 57369 -const ASC = 57370 -const DESC = 57371 -const INTO = 57372 -const DUPLICATE = 57373 -const DEFAULT = 57374 -const SET = 57375 -const LOCK = 57376 -const UNLOCK = 57377 -const KEYS = 57378 -const DO = 57379 -const CALL = 57380 -const DISTINCTROW = 57381 -const PARSER = 57382 -const GENERATED = 57383 -const ALWAYS = 57384 -const OUTFILE = 57385 -const S3 = 57386 -const DATA = 57387 -const LOAD = 57388 -const LINES = 57389 -const TERMINATED = 57390 -const ESCAPED = 57391 -const ENCLOSED = 57392 -const DUMPFILE = 57393 -const CSV = 57394 -const HEADER = 57395 -const MANIFEST = 57396 -const OVERWRITE = 57397 -const STARTING = 57398 -const OPTIONALLY = 57399 -const VALUES = 57400 -const LAST_INSERT_ID = 57401 -const NEXT = 57402 -const VALUE = 57403 -const SHARE = 57404 -const MODE = 57405 -const SQL_NO_CACHE = 57406 -const SQL_CACHE = 57407 -const SQL_CALC_FOUND_ROWS = 57408 -const JOIN = 57409 -const STRAIGHT_JOIN = 57410 -const LEFT = 57411 -const RIGHT = 57412 -const INNER = 57413 -const OUTER = 57414 -const CROSS = 57415 -const NATURAL = 57416 -const USE = 57417 -const FORCE = 57418 -const ON = 57419 -const USING = 57420 -const INPLACE = 57421 -const COPY = 57422 -const INSTANT = 57423 -const ALGORITHM = 57424 -const NONE = 57425 -const SHARED = 57426 -const EXCLUSIVE = 57427 -const SUBQUERY_AS_EXPR = 57428 -const STRING = 57429 -const ID = 57430 -const AT_ID = 57431 -const AT_AT_ID = 57432 -const HEX = 57433 -const NCHAR_STRING = 57434 -const INTEGRAL = 57435 -const FLOAT = 57436 -const DECIMAL = 57437 -const HEXNUM = 57438 -const COMMENT = 57439 -const COMMENT_KEYWORD = 57440 -const BITNUM = 57441 -const BIT_LITERAL = 57442 -const COMPRESSION = 57443 -const VALUE_ARG = 57444 -const LIST_ARG = 57445 -const OFFSET_ARG = 57446 -const JSON_PRETTY = 57447 -const JSON_STORAGE_SIZE = 57448 -const JSON_STORAGE_FREE = 57449 -const JSON_CONTAINS = 57450 -const JSON_CONTAINS_PATH = 57451 -const JSON_EXTRACT = 57452 -const JSON_KEYS = 57453 -const JSON_OVERLAPS = 57454 -const JSON_SEARCH = 57455 -const JSON_VALUE = 57456 -const EXTRACT = 57457 -const NULL = 57458 -const TRUE = 57459 -const FALSE = 57460 -const OFF = 57461 -const DISCARD = 57462 -const IMPORT = 57463 -const ENABLE = 57464 -const DISABLE = 57465 -const TABLESPACE = 57466 -const VIRTUAL = 57467 -const STORED = 57468 -const BOTH = 57469 -const LEADING = 57470 -const TRAILING = 57471 -const KILL = 57472 -const EMPTY_FROM_CLAUSE = 57473 -const LOWER_THAN_CHARSET = 57474 -const CHARSET = 57475 -const UNIQUE = 57476 -const KEY = 57477 -const EXPRESSION_PREC_SETTER = 57478 -const OR = 57479 -const XOR = 57480 -const AND = 57481 -const NOT = 57482 -const BETWEEN = 57483 -const CASE = 57484 -const WHEN = 57485 -const THEN = 57486 -const ELSE = 57487 -const END = 57488 -const LE = 57489 -const GE = 57490 -const NE = 57491 -const NULL_SAFE_EQUAL = 57492 -const IS = 57493 -const LIKE = 57494 -const REGEXP = 57495 -const RLIKE = 57496 -const IN = 57497 -const ASSIGNMENT_OPT = 57498 -const SHIFT_LEFT = 57499 -const SHIFT_RIGHT = 57500 -const DIV = 57501 -const MOD = 57502 -const UNARY = 57503 -const COLLATE = 57504 -const BINARY = 57505 -const UNDERSCORE_ARMSCII8 = 57506 -const UNDERSCORE_ASCII = 57507 -const UNDERSCORE_BIG5 = 57508 -const UNDERSCORE_BINARY = 57509 -const UNDERSCORE_CP1250 = 57510 -const UNDERSCORE_CP1251 = 57511 -const UNDERSCORE_CP1256 = 57512 -const UNDERSCORE_CP1257 = 57513 -const UNDERSCORE_CP850 = 57514 -const UNDERSCORE_CP852 = 57515 -const UNDERSCORE_CP866 = 57516 -const UNDERSCORE_CP932 = 57517 -const UNDERSCORE_DEC8 = 57518 -const UNDERSCORE_EUCJPMS = 57519 -const UNDERSCORE_EUCKR = 57520 -const UNDERSCORE_GB18030 = 57521 -const UNDERSCORE_GB2312 = 57522 -const UNDERSCORE_GBK = 57523 -const UNDERSCORE_GEOSTD8 = 57524 -const UNDERSCORE_GREEK = 57525 -const UNDERSCORE_HEBREW = 57526 -const UNDERSCORE_HP8 = 57527 -const UNDERSCORE_KEYBCS2 = 57528 -const UNDERSCORE_KOI8R = 57529 -const UNDERSCORE_KOI8U = 57530 -const UNDERSCORE_LATIN1 = 57531 -const UNDERSCORE_LATIN2 = 57532 -const UNDERSCORE_LATIN5 = 57533 -const UNDERSCORE_LATIN7 = 57534 -const UNDERSCORE_MACCE = 57535 -const UNDERSCORE_MACROMAN = 57536 -const UNDERSCORE_SJIS = 57537 -const UNDERSCORE_SWE7 = 57538 -const UNDERSCORE_TIS620 = 57539 -const UNDERSCORE_UCS2 = 57540 -const UNDERSCORE_UJIS = 57541 -const UNDERSCORE_UTF16 = 57542 -const UNDERSCORE_UTF16LE = 57543 -const UNDERSCORE_UTF32 = 57544 -const UNDERSCORE_UTF8 = 57545 -const UNDERSCORE_UTF8MB4 = 57546 -const UNDERSCORE_UTF8MB3 = 57547 -const INTERVAL = 57548 -const WINDOW_EXPR = 57549 -const JSON_EXTRACT_OP = 57550 -const JSON_UNQUOTE_EXTRACT_OP = 57551 -const CREATE = 57552 -const ALTER = 57553 -const DROP = 57554 -const RENAME = 57555 -const ANALYZE = 57556 -const ADD = 57557 -const FLUSH = 57558 -const CHANGE = 57559 -const MODIFY = 57560 -const DEALLOCATE = 57561 -const REVERT = 57562 -const QUERIES = 57563 -const SCHEMA = 57564 -const TABLE = 57565 -const INDEX = 57566 -const VIEW = 57567 -const TO = 57568 -const IGNORE = 57569 -const IF = 57570 -const PRIMARY = 57571 -const COLUMN = 57572 -const SPATIAL = 57573 -const FULLTEXT = 57574 -const KEY_BLOCK_SIZE = 57575 -const CHECK = 57576 -const INDEXES = 57577 -const ACTION = 57578 -const CASCADE = 57579 -const CONSTRAINT = 57580 -const FOREIGN = 57581 -const NO = 57582 -const REFERENCES = 57583 -const RESTRICT = 57584 -const SHOW = 57585 -const DESCRIBE = 57586 -const EXPLAIN = 57587 -const DATE = 57588 -const ESCAPE = 57589 -const REPAIR = 57590 -const OPTIMIZE = 57591 -const TRUNCATE = 57592 -const COALESCE = 57593 -const EXCHANGE = 57594 -const REBUILD = 57595 -const PARTITIONING = 57596 -const REMOVE = 57597 -const PREPARE = 57598 -const EXECUTE = 57599 -const MAXVALUE = 57600 -const PARTITION = 57601 -const REORGANIZE = 57602 -const LESS = 57603 -const THAN = 57604 -const PROCEDURE = 57605 -const TRIGGER = 57606 -const VINDEX = 57607 -const VINDEXES = 57608 -const DIRECTORY = 57609 -const NAME = 57610 -const UPGRADE = 57611 -const STATUS = 57612 -const VARIABLES = 57613 -const WARNINGS = 57614 -const CASCADED = 57615 -const DEFINER = 57616 -const OPTION = 57617 -const SQL = 57618 -const UNDEFINED = 57619 -const SEQUENCE = 57620 -const MERGE = 57621 -const TEMPORARY = 57622 -const TEMPTABLE = 57623 -const INVOKER = 57624 -const SECURITY = 57625 -const FIRST = 57626 -const AFTER = 57627 -const LAST = 57628 -const VITESS_MIGRATION = 57629 -const CANCEL = 57630 -const RETRY = 57631 -const LAUNCH = 57632 -const COMPLETE = 57633 -const CLEANUP = 57634 -const THROTTLE = 57635 -const UNTHROTTLE = 57636 -const EXPIRE = 57637 -const RATIO = 57638 -const VITESS_THROTTLER = 57639 -const BEGIN = 57640 -const START = 57641 -const TRANSACTION = 57642 -const COMMIT = 57643 -const ROLLBACK = 57644 -const SAVEPOINT = 57645 -const RELEASE = 57646 -const WORK = 57647 -const CONSISTENT = 57648 -const SNAPSHOT = 57649 -const BIT = 57650 -const TINYINT = 57651 -const SMALLINT = 57652 -const MEDIUMINT = 57653 -const INT = 57654 -const INTEGER = 57655 -const BIGINT = 57656 -const INTNUM = 57657 -const REAL = 57658 -const DOUBLE = 57659 -const FLOAT_TYPE = 57660 -const FLOAT4_TYPE = 57661 -const FLOAT8_TYPE = 57662 -const DECIMAL_TYPE = 57663 -const NUMERIC = 57664 -const TIME = 57665 -const TIMESTAMP = 57666 -const DATETIME = 57667 -const YEAR = 57668 -const CHAR = 57669 -const VARCHAR = 57670 -const BOOL = 57671 -const CHARACTER = 57672 -const VARBINARY = 57673 -const NCHAR = 57674 -const TEXT = 57675 -const TINYTEXT = 57676 -const MEDIUMTEXT = 57677 -const LONGTEXT = 57678 -const BLOB = 57679 -const TINYBLOB = 57680 -const MEDIUMBLOB = 57681 -const LONGBLOB = 57682 -const JSON = 57683 -const JSON_SCHEMA_VALID = 57684 -const JSON_SCHEMA_VALIDATION_REPORT = 57685 -const ENUM = 57686 -const GEOMETRY = 57687 -const POINT = 57688 -const LINESTRING = 57689 -const POLYGON = 57690 -const GEOMCOLLECTION = 57691 -const GEOMETRYCOLLECTION = 57692 -const MULTIPOINT = 57693 -const MULTILINESTRING = 57694 -const MULTIPOLYGON = 57695 -const ASCII = 57696 -const UNICODE = 57697 -const NULLX = 57698 -const AUTO_INCREMENT = 57699 -const APPROXNUM = 57700 -const SIGNED = 57701 -const UNSIGNED = 57702 -const ZEROFILL = 57703 -const PURGE = 57704 -const BEFORE = 57705 -const CODE = 57706 -const COLLATION = 57707 -const COLUMNS = 57708 -const DATABASES = 57709 -const ENGINES = 57710 -const EVENT = 57711 -const EXTENDED = 57712 -const FIELDS = 57713 -const FULL = 57714 -const FUNCTION = 57715 -const GTID_EXECUTED = 57716 -const KEYSPACES = 57717 -const OPEN = 57718 -const PLUGINS = 57719 -const PRIVILEGES = 57720 -const PROCESSLIST = 57721 -const SCHEMAS = 57722 -const TABLES = 57723 -const TRIGGERS = 57724 -const USER = 57725 -const VGTID_EXECUTED = 57726 -const VITESS_KEYSPACES = 57727 -const VITESS_METADATA = 57728 -const VITESS_MIGRATIONS = 57729 -const VITESS_REPLICATION_STATUS = 57730 -const VITESS_SHARDS = 57731 -const VITESS_TABLETS = 57732 -const VITESS_TARGET = 57733 -const VSCHEMA = 57734 -const VITESS_THROTTLED_APPS = 57735 -const NAMES = 57736 -const GLOBAL = 57737 -const SESSION = 57738 -const ISOLATION = 57739 -const LEVEL = 57740 -const READ = 57741 -const WRITE = 57742 -const ONLY = 57743 -const REPEATABLE = 57744 -const COMMITTED = 57745 -const UNCOMMITTED = 57746 -const SERIALIZABLE = 57747 -const ADDDATE = 57748 -const CURRENT_TIMESTAMP = 57749 -const DATABASE = 57750 -const CURRENT_DATE = 57751 -const CURDATE = 57752 -const DATE_ADD = 57753 -const DATE_SUB = 57754 -const NOW = 57755 -const SUBDATE = 57756 -const CURTIME = 57757 -const CURRENT_TIME = 57758 -const LOCALTIME = 57759 -const LOCALTIMESTAMP = 57760 -const CURRENT_USER = 57761 -const UTC_DATE = 57762 -const UTC_TIME = 57763 -const UTC_TIMESTAMP = 57764 -const SYSDATE = 57765 -const DAY = 57766 -const DAY_HOUR = 57767 -const DAY_MICROSECOND = 57768 -const DAY_MINUTE = 57769 -const DAY_SECOND = 57770 -const HOUR = 57771 -const HOUR_MICROSECOND = 57772 -const HOUR_MINUTE = 57773 -const HOUR_SECOND = 57774 -const MICROSECOND = 57775 -const MINUTE = 57776 -const MINUTE_MICROSECOND = 57777 -const MINUTE_SECOND = 57778 -const MONTH = 57779 -const QUARTER = 57780 -const SECOND = 57781 -const SECOND_MICROSECOND = 57782 -const YEAR_MONTH = 57783 -const WEEK = 57784 -const SQL_TSI_DAY = 57785 -const SQL_TSI_WEEK = 57786 -const SQL_TSI_HOUR = 57787 -const SQL_TSI_MINUTE = 57788 -const SQL_TSI_MONTH = 57789 -const SQL_TSI_QUARTER = 57790 -const SQL_TSI_SECOND = 57791 -const SQL_TSI_MICROSECOND = 57792 -const SQL_TSI_YEAR = 57793 -const REPLACE = 57794 -const CONVERT = 57795 -const CAST = 57796 -const SUBSTR = 57797 -const SUBSTRING = 57798 -const SEPARATOR = 57799 -const TIMESTAMPADD = 57800 -const TIMESTAMPDIFF = 57801 -const WEIGHT_STRING = 57802 -const LTRIM = 57803 -const RTRIM = 57804 -const TRIM = 57805 -const JSON_ARRAY = 57806 -const JSON_OBJECT = 57807 -const JSON_QUOTE = 57808 -const JSON_DEPTH = 57809 -const JSON_TYPE = 57810 -const JSON_LENGTH = 57811 -const JSON_VALID = 57812 -const JSON_ARRAY_APPEND = 57813 -const JSON_ARRAY_INSERT = 57814 -const JSON_INSERT = 57815 -const JSON_MERGE = 57816 -const JSON_MERGE_PATCH = 57817 -const JSON_MERGE_PRESERVE = 57818 -const JSON_REMOVE = 57819 -const JSON_REPLACE = 57820 -const JSON_SET = 57821 -const JSON_UNQUOTE = 57822 -const COUNT = 57823 -const AVG = 57824 -const MAX = 57825 -const MIN = 57826 -const SUM = 57827 -const GROUP_CONCAT = 57828 -const BIT_AND = 57829 -const BIT_OR = 57830 -const BIT_XOR = 57831 -const STD = 57832 -const STDDEV = 57833 -const STDDEV_POP = 57834 -const STDDEV_SAMP = 57835 -const VAR_POP = 57836 -const VAR_SAMP = 57837 -const VARIANCE = 57838 -const ANY_VALUE = 57839 -const REGEXP_INSTR = 57840 -const REGEXP_LIKE = 57841 -const REGEXP_REPLACE = 57842 -const REGEXP_SUBSTR = 57843 -const ExtractValue = 57844 -const UpdateXML = 57845 -const GET_LOCK = 57846 -const RELEASE_LOCK = 57847 -const RELEASE_ALL_LOCKS = 57848 -const IS_FREE_LOCK = 57849 -const IS_USED_LOCK = 57850 -const LOCATE = 57851 -const POSITION = 57852 -const ST_GeometryCollectionFromText = 57853 -const ST_GeometryFromText = 57854 -const ST_LineStringFromText = 57855 -const ST_MultiLineStringFromText = 57856 -const ST_MultiPointFromText = 57857 -const ST_MultiPolygonFromText = 57858 -const ST_PointFromText = 57859 -const ST_PolygonFromText = 57860 -const ST_GeometryCollectionFromWKB = 57861 -const ST_GeometryFromWKB = 57862 -const ST_LineStringFromWKB = 57863 -const ST_MultiLineStringFromWKB = 57864 -const ST_MultiPointFromWKB = 57865 -const ST_MultiPolygonFromWKB = 57866 -const ST_PointFromWKB = 57867 -const ST_PolygonFromWKB = 57868 -const ST_AsBinary = 57869 -const ST_AsText = 57870 -const ST_Dimension = 57871 -const ST_Envelope = 57872 -const ST_IsSimple = 57873 -const ST_IsEmpty = 57874 -const ST_GeometryType = 57875 -const ST_X = 57876 -const ST_Y = 57877 -const ST_Latitude = 57878 -const ST_Longitude = 57879 -const ST_EndPoint = 57880 -const ST_IsClosed = 57881 -const ST_Length = 57882 -const ST_NumPoints = 57883 -const ST_StartPoint = 57884 -const ST_PointN = 57885 -const ST_Area = 57886 -const ST_Centroid = 57887 -const ST_ExteriorRing = 57888 -const ST_InteriorRingN = 57889 -const ST_NumInteriorRings = 57890 -const ST_NumGeometries = 57891 -const ST_GeometryN = 57892 -const ST_LongFromGeoHash = 57893 -const ST_PointFromGeoHash = 57894 -const ST_LatFromGeoHash = 57895 -const ST_GeoHash = 57896 -const ST_AsGeoJSON = 57897 -const ST_GeomFromGeoJSON = 57898 -const MATCH = 57899 -const AGAINST = 57900 -const BOOLEAN = 57901 -const LANGUAGE = 57902 -const WITH = 57903 -const QUERY = 57904 -const EXPANSION = 57905 -const WITHOUT = 57906 -const VALIDATION = 57907 -const UNUSED = 57908 -const ARRAY = 57909 -const BYTE = 57910 -const CUME_DIST = 57911 -const DESCRIPTION = 57912 -const DENSE_RANK = 57913 -const EMPTY = 57914 -const EXCEPT = 57915 -const FIRST_VALUE = 57916 -const GROUPING = 57917 -const GROUPS = 57918 -const JSON_TABLE = 57919 -const LAG = 57920 -const LAST_VALUE = 57921 -const LATERAL = 57922 -const LEAD = 57923 -const NTH_VALUE = 57924 -const NTILE = 57925 -const OF = 57926 -const OVER = 57927 -const PERCENT_RANK = 57928 -const RANK = 57929 -const RECURSIVE = 57930 -const ROW_NUMBER = 57931 -const SYSTEM = 57932 -const WINDOW = 57933 -const ACTIVE = 57934 -const ADMIN = 57935 -const AUTOEXTEND_SIZE = 57936 -const BUCKETS = 57937 -const CLONE = 57938 -const COLUMN_FORMAT = 57939 -const COMPONENT = 57940 -const DEFINITION = 57941 -const ENFORCED = 57942 -const ENGINE_ATTRIBUTE = 57943 -const EXCLUDE = 57944 -const FOLLOWING = 57945 -const GET_MASTER_PUBLIC_KEY = 57946 -const HISTOGRAM = 57947 -const HISTORY = 57948 -const INACTIVE = 57949 -const INVISIBLE = 57950 -const LOCKED = 57951 -const MASTER_COMPRESSION_ALGORITHMS = 57952 -const MASTER_PUBLIC_KEY_PATH = 57953 -const MASTER_TLS_CIPHERSUITES = 57954 -const MASTER_ZSTD_COMPRESSION_LEVEL = 57955 -const NESTED = 57956 -const NETWORK_NAMESPACE = 57957 -const NOWAIT = 57958 -const NULLS = 57959 -const OJ = 57960 -const OLD = 57961 -const OPTIONAL = 57962 -const ORDINALITY = 57963 -const ORGANIZATION = 57964 -const OTHERS = 57965 -const PARTIAL = 57966 -const PATH = 57967 -const PERSIST = 57968 -const PERSIST_ONLY = 57969 -const PRECEDING = 57970 -const PRIVILEGE_CHECKS_USER = 57971 -const PROCESS = 57972 -const RANDOM = 57973 -const REFERENCE = 57974 -const REQUIRE_ROW_FORMAT = 57975 -const RESOURCE = 57976 -const RESPECT = 57977 -const RESTART = 57978 -const RETAIN = 57979 -const REUSE = 57980 -const ROLE = 57981 -const SECONDARY = 57982 -const SECONDARY_ENGINE = 57983 -const SECONDARY_ENGINE_ATTRIBUTE = 57984 -const SECONDARY_LOAD = 57985 -const SECONDARY_UNLOAD = 57986 -const SIMPLE = 57987 -const SKIP = 57988 -const SRID = 57989 -const THREAD_PRIORITY = 57990 -const TIES = 57991 -const UNBOUNDED = 57992 -const VCPU = 57993 -const VISIBLE = 57994 -const RETURNING = 57995 -const FORMAT_BYTES = 57996 -const FORMAT_PICO_TIME = 57997 -const PS_CURRENT_THREAD_ID = 57998 -const PS_THREAD_ID = 57999 -const GTID_SUBSET = 58000 -const GTID_SUBTRACT = 58001 -const WAIT_FOR_EXECUTED_GTID_SET = 58002 -const WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS = 58003 -const FORMAT = 58004 -const TREE = 58005 -const VITESS = 58006 -const TRADITIONAL = 58007 -const VTEXPLAIN = 58008 -const VEXPLAIN = 58009 -const PLAN = 58010 -const LOCAL = 58011 -const LOW_PRIORITY = 58012 -const NO_WRITE_TO_BINLOG = 58013 -const LOGS = 58014 -const ERROR = 58015 -const GENERAL = 58016 -const HOSTS = 58017 -const OPTIMIZER_COSTS = 58018 -const USER_RESOURCES = 58019 -const SLOW = 58020 -const CHANNEL = 58021 -const RELAY = 58022 -const EXPORT = 58023 -const CURRENT = 58024 -const ROW = 58025 -const ROWS = 58026 -const AVG_ROW_LENGTH = 58027 -const CONNECTION = 58028 -const CHECKSUM = 58029 -const DELAY_KEY_WRITE = 58030 -const ENCRYPTION = 58031 -const ENGINE = 58032 -const INSERT_METHOD = 58033 -const MAX_ROWS = 58034 -const MIN_ROWS = 58035 -const PACK_KEYS = 58036 -const PASSWORD = 58037 -const FIXED = 58038 -const DYNAMIC = 58039 -const COMPRESSED = 58040 -const REDUNDANT = 58041 -const COMPACT = 58042 -const ROW_FORMAT = 58043 -const STATS_AUTO_RECALC = 58044 -const STATS_PERSISTENT = 58045 -const STATS_SAMPLE_PAGES = 58046 -const STORAGE = 58047 -const MEMORY = 58048 -const DISK = 58049 -const PARTITIONS = 58050 -const LINEAR = 58051 -const RANGE = 58052 -const LIST = 58053 -const SUBPARTITION = 58054 -const SUBPARTITIONS = 58055 -const HASH = 58056 +const MULTIPLE_TEXT_LITERAL = 57347 +const FUNCTION_CALL_NON_KEYWORD = 57348 +const STRING_TYPE_PREFIX_NON_KEYWORD = 57349 +const LEX_ERROR = 57350 +const UNION = 57351 +const SELECT = 57352 +const STREAM = 57353 +const VSTREAM = 57354 +const INSERT = 57355 +const UPDATE = 57356 +const DELETE = 57357 +const FROM = 57358 +const WHERE = 57359 +const GROUP = 57360 +const HAVING = 57361 +const ORDER = 57362 +const BY = 57363 +const LIMIT = 57364 +const OFFSET = 57365 +const FOR = 57366 +const ALL = 57367 +const DISTINCT = 57368 +const AS = 57369 +const EXISTS = 57370 +const ASC = 57371 +const DESC = 57372 +const INTO = 57373 +const DUPLICATE = 57374 +const DEFAULT = 57375 +const SET = 57376 +const LOCK = 57377 +const UNLOCK = 57378 +const KEYS = 57379 +const DO = 57380 +const CALL = 57381 +const DISTINCTROW = 57382 +const PARSER = 57383 +const GENERATED = 57384 +const ALWAYS = 57385 +const OUTFILE = 57386 +const S3 = 57387 +const DATA = 57388 +const LOAD = 57389 +const LINES = 57390 +const TERMINATED = 57391 +const ESCAPED = 57392 +const ENCLOSED = 57393 +const DUMPFILE = 57394 +const CSV = 57395 +const HEADER = 57396 +const MANIFEST = 57397 +const OVERWRITE = 57398 +const STARTING = 57399 +const OPTIONALLY = 57400 +const VALUES = 57401 +const LAST_INSERT_ID = 57402 +const NEXT = 57403 +const VALUE = 57404 +const SHARE = 57405 +const MODE = 57406 +const SQL_NO_CACHE = 57407 +const SQL_CACHE = 57408 +const SQL_CALC_FOUND_ROWS = 57409 +const JOIN = 57410 +const STRAIGHT_JOIN = 57411 +const LEFT = 57412 +const RIGHT = 57413 +const INNER = 57414 +const OUTER = 57415 +const CROSS = 57416 +const NATURAL = 57417 +const USE = 57418 +const FORCE = 57419 +const ON = 57420 +const USING = 57421 +const INPLACE = 57422 +const COPY = 57423 +const INSTANT = 57424 +const ALGORITHM = 57425 +const NONE = 57426 +const SHARED = 57427 +const EXCLUSIVE = 57428 +const SUBQUERY_AS_EXPR = 57429 +const STRING = 57430 +const ID = 57431 +const AT_ID = 57432 +const AT_AT_ID = 57433 +const HEX = 57434 +const NCHAR_STRING = 57435 +const INTEGRAL = 57436 +const FLOAT = 57437 +const DECIMAL = 57438 +const HEXNUM = 57439 +const COMMENT = 57440 +const COMMENT_KEYWORD = 57441 +const BITNUM = 57442 +const BIT_LITERAL = 57443 +const COMPRESSION = 57444 +const VALUE_ARG = 57445 +const LIST_ARG = 57446 +const OFFSET_ARG = 57447 +const JSON_PRETTY = 57448 +const JSON_STORAGE_SIZE = 57449 +const JSON_STORAGE_FREE = 57450 +const JSON_CONTAINS = 57451 +const JSON_CONTAINS_PATH = 57452 +const JSON_EXTRACT = 57453 +const JSON_KEYS = 57454 +const JSON_OVERLAPS = 57455 +const JSON_SEARCH = 57456 +const JSON_VALUE = 57457 +const EXTRACT = 57458 +const NULL = 57459 +const TRUE = 57460 +const FALSE = 57461 +const OFF = 57462 +const DISCARD = 57463 +const IMPORT = 57464 +const ENABLE = 57465 +const DISABLE = 57466 +const TABLESPACE = 57467 +const VIRTUAL = 57468 +const STORED = 57469 +const BOTH = 57470 +const LEADING = 57471 +const TRAILING = 57472 +const KILL = 57473 +const EMPTY_FROM_CLAUSE = 57474 +const LOWER_THAN_CHARSET = 57475 +const CHARSET = 57476 +const UNIQUE = 57477 +const KEY = 57478 +const EXPRESSION_PREC_SETTER = 57479 +const OR = 57480 +const XOR = 57481 +const AND = 57482 +const NOT = 57483 +const BETWEEN = 57484 +const CASE = 57485 +const WHEN = 57486 +const THEN = 57487 +const ELSE = 57488 +const END = 57489 +const LE = 57490 +const GE = 57491 +const NE = 57492 +const NULL_SAFE_EQUAL = 57493 +const IS = 57494 +const LIKE = 57495 +const REGEXP = 57496 +const RLIKE = 57497 +const IN = 57498 +const ASSIGNMENT_OPT = 57499 +const SHIFT_LEFT = 57500 +const SHIFT_RIGHT = 57501 +const DIV = 57502 +const MOD = 57503 +const UNARY = 57504 +const COLLATE = 57505 +const BINARY = 57506 +const UNDERSCORE_ARMSCII8 = 57507 +const UNDERSCORE_ASCII = 57508 +const UNDERSCORE_BIG5 = 57509 +const UNDERSCORE_BINARY = 57510 +const UNDERSCORE_CP1250 = 57511 +const UNDERSCORE_CP1251 = 57512 +const UNDERSCORE_CP1256 = 57513 +const UNDERSCORE_CP1257 = 57514 +const UNDERSCORE_CP850 = 57515 +const UNDERSCORE_CP852 = 57516 +const UNDERSCORE_CP866 = 57517 +const UNDERSCORE_CP932 = 57518 +const UNDERSCORE_DEC8 = 57519 +const UNDERSCORE_EUCJPMS = 57520 +const UNDERSCORE_EUCKR = 57521 +const UNDERSCORE_GB18030 = 57522 +const UNDERSCORE_GB2312 = 57523 +const UNDERSCORE_GBK = 57524 +const UNDERSCORE_GEOSTD8 = 57525 +const UNDERSCORE_GREEK = 57526 +const UNDERSCORE_HEBREW = 57527 +const UNDERSCORE_HP8 = 57528 +const UNDERSCORE_KEYBCS2 = 57529 +const UNDERSCORE_KOI8R = 57530 +const UNDERSCORE_KOI8U = 57531 +const UNDERSCORE_LATIN1 = 57532 +const UNDERSCORE_LATIN2 = 57533 +const UNDERSCORE_LATIN5 = 57534 +const UNDERSCORE_LATIN7 = 57535 +const UNDERSCORE_MACCE = 57536 +const UNDERSCORE_MACROMAN = 57537 +const UNDERSCORE_SJIS = 57538 +const UNDERSCORE_SWE7 = 57539 +const UNDERSCORE_TIS620 = 57540 +const UNDERSCORE_UCS2 = 57541 +const UNDERSCORE_UJIS = 57542 +const UNDERSCORE_UTF16 = 57543 +const UNDERSCORE_UTF16LE = 57544 +const UNDERSCORE_UTF32 = 57545 +const UNDERSCORE_UTF8 = 57546 +const UNDERSCORE_UTF8MB4 = 57547 +const UNDERSCORE_UTF8MB3 = 57548 +const INTERVAL = 57549 +const WINDOW_EXPR = 57550 +const JSON_EXTRACT_OP = 57551 +const JSON_UNQUOTE_EXTRACT_OP = 57552 +const CREATE = 57553 +const ALTER = 57554 +const DROP = 57555 +const RENAME = 57556 +const ANALYZE = 57557 +const ADD = 57558 +const FLUSH = 57559 +const CHANGE = 57560 +const MODIFY = 57561 +const DEALLOCATE = 57562 +const REVERT = 57563 +const QUERIES = 57564 +const SCHEMA = 57565 +const TABLE = 57566 +const INDEX = 57567 +const VIEW = 57568 +const TO = 57569 +const IGNORE = 57570 +const IF = 57571 +const PRIMARY = 57572 +const COLUMN = 57573 +const SPATIAL = 57574 +const FULLTEXT = 57575 +const KEY_BLOCK_SIZE = 57576 +const CHECK = 57577 +const INDEXES = 57578 +const ACTION = 57579 +const CASCADE = 57580 +const CONSTRAINT = 57581 +const FOREIGN = 57582 +const NO = 57583 +const REFERENCES = 57584 +const RESTRICT = 57585 +const SHOW = 57586 +const DESCRIBE = 57587 +const EXPLAIN = 57588 +const DATE = 57589 +const ESCAPE = 57590 +const REPAIR = 57591 +const OPTIMIZE = 57592 +const TRUNCATE = 57593 +const COALESCE = 57594 +const EXCHANGE = 57595 +const REBUILD = 57596 +const PARTITIONING = 57597 +const REMOVE = 57598 +const PREPARE = 57599 +const EXECUTE = 57600 +const MAXVALUE = 57601 +const PARTITION = 57602 +const REORGANIZE = 57603 +const LESS = 57604 +const THAN = 57605 +const PROCEDURE = 57606 +const TRIGGER = 57607 +const VINDEX = 57608 +const VINDEXES = 57609 +const DIRECTORY = 57610 +const NAME = 57611 +const UPGRADE = 57612 +const STATUS = 57613 +const VARIABLES = 57614 +const WARNINGS = 57615 +const CASCADED = 57616 +const DEFINER = 57617 +const OPTION = 57618 +const SQL = 57619 +const UNDEFINED = 57620 +const SEQUENCE = 57621 +const MERGE = 57622 +const TEMPORARY = 57623 +const TEMPTABLE = 57624 +const INVOKER = 57625 +const SECURITY = 57626 +const FIRST = 57627 +const AFTER = 57628 +const LAST = 57629 +const VITESS_MIGRATION = 57630 +const CANCEL = 57631 +const RETRY = 57632 +const LAUNCH = 57633 +const COMPLETE = 57634 +const CLEANUP = 57635 +const THROTTLE = 57636 +const UNTHROTTLE = 57637 +const FORCE_CUTOVER = 57638 +const EXPIRE = 57639 +const RATIO = 57640 +const VITESS_THROTTLER = 57641 +const BEGIN = 57642 +const START = 57643 +const TRANSACTION = 57644 +const COMMIT = 57645 +const ROLLBACK = 57646 +const SAVEPOINT = 57647 +const RELEASE = 57648 +const WORK = 57649 +const CONSISTENT = 57650 +const SNAPSHOT = 57651 +const BIT = 57652 +const TINYINT = 57653 +const SMALLINT = 57654 +const MEDIUMINT = 57655 +const INT = 57656 +const INTEGER = 57657 +const BIGINT = 57658 +const INTNUM = 57659 +const REAL = 57660 +const DOUBLE = 57661 +const FLOAT_TYPE = 57662 +const FLOAT4_TYPE = 57663 +const FLOAT8_TYPE = 57664 +const DECIMAL_TYPE = 57665 +const NUMERIC = 57666 +const TIME = 57667 +const TIMESTAMP = 57668 +const DATETIME = 57669 +const YEAR = 57670 +const CHAR = 57671 +const VARCHAR = 57672 +const BOOL = 57673 +const CHARACTER = 57674 +const VARBINARY = 57675 +const NCHAR = 57676 +const TEXT = 57677 +const TINYTEXT = 57678 +const MEDIUMTEXT = 57679 +const LONGTEXT = 57680 +const BLOB = 57681 +const TINYBLOB = 57682 +const MEDIUMBLOB = 57683 +const LONGBLOB = 57684 +const JSON = 57685 +const JSON_SCHEMA_VALID = 57686 +const JSON_SCHEMA_VALIDATION_REPORT = 57687 +const ENUM = 57688 +const GEOMETRY = 57689 +const POINT = 57690 +const LINESTRING = 57691 +const POLYGON = 57692 +const GEOMCOLLECTION = 57693 +const GEOMETRYCOLLECTION = 57694 +const MULTIPOINT = 57695 +const MULTILINESTRING = 57696 +const MULTIPOLYGON = 57697 +const ASCII = 57698 +const UNICODE = 57699 +const NULLX = 57700 +const AUTO_INCREMENT = 57701 +const APPROXNUM = 57702 +const SIGNED = 57703 +const UNSIGNED = 57704 +const ZEROFILL = 57705 +const PURGE = 57706 +const BEFORE = 57707 +const CODE = 57708 +const COLLATION = 57709 +const COLUMNS = 57710 +const DATABASES = 57711 +const ENGINES = 57712 +const EVENT = 57713 +const EXTENDED = 57714 +const FIELDS = 57715 +const FULL = 57716 +const FUNCTION = 57717 +const GTID_EXECUTED = 57718 +const KEYSPACES = 57719 +const OPEN = 57720 +const PLUGINS = 57721 +const PRIVILEGES = 57722 +const PROCESSLIST = 57723 +const SCHEMAS = 57724 +const TABLES = 57725 +const TRIGGERS = 57726 +const USER = 57727 +const VGTID_EXECUTED = 57728 +const VITESS_KEYSPACES = 57729 +const VITESS_METADATA = 57730 +const VITESS_MIGRATIONS = 57731 +const VITESS_REPLICATION_STATUS = 57732 +const VITESS_SHARDS = 57733 +const VITESS_TABLETS = 57734 +const VITESS_TARGET = 57735 +const VSCHEMA = 57736 +const VITESS_THROTTLED_APPS = 57737 +const NAMES = 57738 +const GLOBAL = 57739 +const SESSION = 57740 +const ISOLATION = 57741 +const LEVEL = 57742 +const READ = 57743 +const WRITE = 57744 +const ONLY = 57745 +const REPEATABLE = 57746 +const COMMITTED = 57747 +const UNCOMMITTED = 57748 +const SERIALIZABLE = 57749 +const ADDDATE = 57750 +const CURRENT_TIMESTAMP = 57751 +const DATABASE = 57752 +const CURRENT_DATE = 57753 +const CURDATE = 57754 +const DATE_ADD = 57755 +const DATE_SUB = 57756 +const NOW = 57757 +const SUBDATE = 57758 +const CURTIME = 57759 +const CURRENT_TIME = 57760 +const LOCALTIME = 57761 +const LOCALTIMESTAMP = 57762 +const CURRENT_USER = 57763 +const UTC_DATE = 57764 +const UTC_TIME = 57765 +const UTC_TIMESTAMP = 57766 +const SYSDATE = 57767 +const DAY = 57768 +const DAY_HOUR = 57769 +const DAY_MICROSECOND = 57770 +const DAY_MINUTE = 57771 +const DAY_SECOND = 57772 +const HOUR = 57773 +const HOUR_MICROSECOND = 57774 +const HOUR_MINUTE = 57775 +const HOUR_SECOND = 57776 +const MICROSECOND = 57777 +const MINUTE = 57778 +const MINUTE_MICROSECOND = 57779 +const MINUTE_SECOND = 57780 +const MONTH = 57781 +const QUARTER = 57782 +const SECOND = 57783 +const SECOND_MICROSECOND = 57784 +const YEAR_MONTH = 57785 +const WEEK = 57786 +const SQL_TSI_DAY = 57787 +const SQL_TSI_WEEK = 57788 +const SQL_TSI_HOUR = 57789 +const SQL_TSI_MINUTE = 57790 +const SQL_TSI_MONTH = 57791 +const SQL_TSI_QUARTER = 57792 +const SQL_TSI_SECOND = 57793 +const SQL_TSI_MICROSECOND = 57794 +const SQL_TSI_YEAR = 57795 +const REPLACE = 57796 +const CONVERT = 57797 +const CAST = 57798 +const SUBSTR = 57799 +const SUBSTRING = 57800 +const MID = 57801 +const SEPARATOR = 57802 +const TIMESTAMPADD = 57803 +const TIMESTAMPDIFF = 57804 +const WEIGHT_STRING = 57805 +const LTRIM = 57806 +const RTRIM = 57807 +const TRIM = 57808 +const JSON_ARRAY = 57809 +const JSON_OBJECT = 57810 +const JSON_QUOTE = 57811 +const JSON_DEPTH = 57812 +const JSON_TYPE = 57813 +const JSON_LENGTH = 57814 +const JSON_VALID = 57815 +const JSON_ARRAY_APPEND = 57816 +const JSON_ARRAY_INSERT = 57817 +const JSON_INSERT = 57818 +const JSON_MERGE = 57819 +const JSON_MERGE_PATCH = 57820 +const JSON_MERGE_PRESERVE = 57821 +const JSON_REMOVE = 57822 +const JSON_REPLACE = 57823 +const JSON_SET = 57824 +const JSON_UNQUOTE = 57825 +const COUNT = 57826 +const AVG = 57827 +const MAX = 57828 +const MIN = 57829 +const SUM = 57830 +const GROUP_CONCAT = 57831 +const BIT_AND = 57832 +const BIT_OR = 57833 +const BIT_XOR = 57834 +const STD = 57835 +const STDDEV = 57836 +const STDDEV_POP = 57837 +const STDDEV_SAMP = 57838 +const VAR_POP = 57839 +const VAR_SAMP = 57840 +const VARIANCE = 57841 +const ANY_VALUE = 57842 +const REGEXP_INSTR = 57843 +const REGEXP_LIKE = 57844 +const REGEXP_REPLACE = 57845 +const REGEXP_SUBSTR = 57846 +const ExtractValue = 57847 +const UpdateXML = 57848 +const GET_LOCK = 57849 +const RELEASE_LOCK = 57850 +const RELEASE_ALL_LOCKS = 57851 +const IS_FREE_LOCK = 57852 +const IS_USED_LOCK = 57853 +const LOCATE = 57854 +const POSITION = 57855 +const ST_GeometryCollectionFromText = 57856 +const ST_GeometryFromText = 57857 +const ST_LineStringFromText = 57858 +const ST_MultiLineStringFromText = 57859 +const ST_MultiPointFromText = 57860 +const ST_MultiPolygonFromText = 57861 +const ST_PointFromText = 57862 +const ST_PolygonFromText = 57863 +const ST_GeometryCollectionFromWKB = 57864 +const ST_GeometryFromWKB = 57865 +const ST_LineStringFromWKB = 57866 +const ST_MultiLineStringFromWKB = 57867 +const ST_MultiPointFromWKB = 57868 +const ST_MultiPolygonFromWKB = 57869 +const ST_PointFromWKB = 57870 +const ST_PolygonFromWKB = 57871 +const ST_AsBinary = 57872 +const ST_AsText = 57873 +const ST_Dimension = 57874 +const ST_Envelope = 57875 +const ST_IsSimple = 57876 +const ST_IsEmpty = 57877 +const ST_GeometryType = 57878 +const ST_X = 57879 +const ST_Y = 57880 +const ST_Latitude = 57881 +const ST_Longitude = 57882 +const ST_EndPoint = 57883 +const ST_IsClosed = 57884 +const ST_Length = 57885 +const ST_NumPoints = 57886 +const ST_StartPoint = 57887 +const ST_PointN = 57888 +const ST_Area = 57889 +const ST_Centroid = 57890 +const ST_ExteriorRing = 57891 +const ST_InteriorRingN = 57892 +const ST_NumInteriorRings = 57893 +const ST_NumGeometries = 57894 +const ST_GeometryN = 57895 +const ST_LongFromGeoHash = 57896 +const ST_PointFromGeoHash = 57897 +const ST_LatFromGeoHash = 57898 +const ST_GeoHash = 57899 +const ST_AsGeoJSON = 57900 +const ST_GeomFromGeoJSON = 57901 +const MATCH = 57902 +const AGAINST = 57903 +const BOOLEAN = 57904 +const LANGUAGE = 57905 +const WITH = 57906 +const QUERY = 57907 +const EXPANSION = 57908 +const WITHOUT = 57909 +const VALIDATION = 57910 +const UNUSED = 57911 +const ARRAY = 57912 +const BYTE = 57913 +const CUME_DIST = 57914 +const DESCRIPTION = 57915 +const DENSE_RANK = 57916 +const EMPTY = 57917 +const EXCEPT = 57918 +const FIRST_VALUE = 57919 +const GROUPING = 57920 +const GROUPS = 57921 +const JSON_TABLE = 57922 +const LAG = 57923 +const LAST_VALUE = 57924 +const LATERAL = 57925 +const LEAD = 57926 +const NTH_VALUE = 57927 +const NTILE = 57928 +const OF = 57929 +const OVER = 57930 +const PERCENT_RANK = 57931 +const RANK = 57932 +const RECURSIVE = 57933 +const ROW_NUMBER = 57934 +const SYSTEM = 57935 +const WINDOW = 57936 +const ACTIVE = 57937 +const ADMIN = 57938 +const AUTOEXTEND_SIZE = 57939 +const BUCKETS = 57940 +const CLONE = 57941 +const COLUMN_FORMAT = 57942 +const COMPONENT = 57943 +const DEFINITION = 57944 +const ENFORCED = 57945 +const ENGINE_ATTRIBUTE = 57946 +const EXCLUDE = 57947 +const FOLLOWING = 57948 +const GET_MASTER_PUBLIC_KEY = 57949 +const HISTOGRAM = 57950 +const HISTORY = 57951 +const INACTIVE = 57952 +const INVISIBLE = 57953 +const LOCKED = 57954 +const MASTER_COMPRESSION_ALGORITHMS = 57955 +const MASTER_PUBLIC_KEY_PATH = 57956 +const MASTER_TLS_CIPHERSUITES = 57957 +const MASTER_ZSTD_COMPRESSION_LEVEL = 57958 +const NESTED = 57959 +const NETWORK_NAMESPACE = 57960 +const NOWAIT = 57961 +const NULLS = 57962 +const OJ = 57963 +const OLD = 57964 +const OPTIONAL = 57965 +const ORDINALITY = 57966 +const ORGANIZATION = 57967 +const OTHERS = 57968 +const PARTIAL = 57969 +const PATH = 57970 +const PERSIST = 57971 +const PERSIST_ONLY = 57972 +const PRECEDING = 57973 +const PRIVILEGE_CHECKS_USER = 57974 +const PROCESS = 57975 +const RANDOM = 57976 +const REFERENCE = 57977 +const REQUIRE_ROW_FORMAT = 57978 +const RESOURCE = 57979 +const RESPECT = 57980 +const RESTART = 57981 +const RETAIN = 57982 +const REUSE = 57983 +const ROLE = 57984 +const SECONDARY = 57985 +const SECONDARY_ENGINE = 57986 +const SECONDARY_ENGINE_ATTRIBUTE = 57987 +const SECONDARY_LOAD = 57988 +const SECONDARY_UNLOAD = 57989 +const SIMPLE = 57990 +const SKIP = 57991 +const SRID = 57992 +const THREAD_PRIORITY = 57993 +const TIES = 57994 +const UNBOUNDED = 57995 +const VCPU = 57996 +const VISIBLE = 57997 +const RETURNING = 57998 +const FORMAT_BYTES = 57999 +const FORMAT_PICO_TIME = 58000 +const PS_CURRENT_THREAD_ID = 58001 +const PS_THREAD_ID = 58002 +const GTID_SUBSET = 58003 +const GTID_SUBTRACT = 58004 +const WAIT_FOR_EXECUTED_GTID_SET = 58005 +const WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS = 58006 +const FORMAT = 58007 +const TREE = 58008 +const VITESS = 58009 +const TRADITIONAL = 58010 +const VTEXPLAIN = 58011 +const VEXPLAIN = 58012 +const PLAN = 58013 +const LOCAL = 58014 +const LOW_PRIORITY = 58015 +const NO_WRITE_TO_BINLOG = 58016 +const LOGS = 58017 +const ERROR = 58018 +const GENERAL = 58019 +const HOSTS = 58020 +const OPTIMIZER_COSTS = 58021 +const USER_RESOURCES = 58022 +const SLOW = 58023 +const CHANNEL = 58024 +const RELAY = 58025 +const EXPORT = 58026 +const CURRENT = 58027 +const ROW = 58028 +const ROWS = 58029 +const AVG_ROW_LENGTH = 58030 +const CONNECTION = 58031 +const CHECKSUM = 58032 +const DELAY_KEY_WRITE = 58033 +const ENCRYPTION = 58034 +const ENGINE = 58035 +const INSERT_METHOD = 58036 +const MAX_ROWS = 58037 +const MIN_ROWS = 58038 +const PACK_KEYS = 58039 +const PASSWORD = 58040 +const FIXED = 58041 +const DYNAMIC = 58042 +const COMPRESSED = 58043 +const REDUNDANT = 58044 +const COMPACT = 58045 +const ROW_FORMAT = 58046 +const STATS_AUTO_RECALC = 58047 +const STATS_PERSISTENT = 58048 +const STATS_SAMPLE_PAGES = 58049 +const STORAGE = 58050 +const MEMORY = 58051 +const DISK = 58052 +const PARTITIONS = 58053 +const LINEAR = 58054 +const RANGE = 58055 +const LIST = 58056 +const SUBPARTITION = 58057 +const SUBPARTITIONS = 58058 +const HASH = 58059 var yyToknames = [...]string{ "$end", "error", "$unk", "MEMBER", + "MULTIPLE_TEXT_LITERAL", "FUNCTION_CALL_NON_KEYWORD", "STRING_TYPE_PREFIX_NON_KEYWORD", "LEX_ERROR", @@ -1057,6 +1063,7 @@ var yyToknames = [...]string{ "CLEANUP", "THROTTLE", "UNTHROTTLE", + "FORCE_CUTOVER", "EXPIRE", "RATIO", "VITESS_THROTTLER", @@ -1219,6 +1226,7 @@ var yyToknames = [...]string{ "CAST", "SUBSTR", "SUBSTRING", + "MID", "SEPARATOR", "TIMESTAMPADD", "TIMESTAMPDIFF", @@ -1492,4309 +1500,2896 @@ var yyExca = [...]int{ 1, -1, -2, 0, -1, 2, - 13, 51, 14, 51, + 15, 51, -2, 40, -1, 52, 1, 159, - 732, 159, + 735, 159, -2, 167, -1, 53, - 136, 167, - 178, 167, - 347, 167, - -2, 523, + 137, 167, + 179, 167, + 349, 167, + -2, 525, -1, 61, - 36, 774, - 241, 774, - 252, 774, - 287, 788, - 288, 788, - -2, 776, + 37, 779, + 242, 779, + 253, 779, + 288, 793, + 289, 793, + -2, 781, -1, 66, - 243, 812, - -2, 810, + 244, 817, + -2, 815, -1, 122, - 240, 1587, + 241, 1597, -2, 133, -1, 124, 1, 160, - 732, 160, + 735, 160, -2, 167, -1, 135, - 137, 408, - 246, 408, - -2, 512, + 138, 410, + 247, 410, + -2, 514, -1, 154, - 136, 167, - 178, 167, - 347, 167, - -2, 532, - -1, 733, - 164, 41, + 137, 167, + 179, 167, + 349, 167, + -2, 534, + -1, 735, + 165, 41, -2, 45, - -1, 939, - 87, 1604, - -2, 1458, - -1, 940, - 87, 1605, - 223, 1609, - -2, 1459, - -1, 941, - 223, 1608, + -1, 942, + 88, 1614, + -2, 1463, + -1, 943, + 88, 1615, + 224, 1619, + -2, 1464, + -1, 944, + 224, 1618, -2, 42, - -1, 1024, - 60, 886, - -2, 901, - -1, 1111, - 251, 43, - 256, 43, - -2, 419, - -1, 1196, - 1, 580, - 732, 580, + -1, 1028, + 61, 889, + -2, 904, + -1, 1116, + 252, 43, + 257, 43, + -2, 421, + -1, 1201, + 1, 582, + 735, 582, -2, 167, - -1, 1498, - 223, 1609, - -2, 1459, - -1, 1707, - 60, 887, - -2, 906, - -1, 1708, - 60, 888, - -2, 907, - -1, 1759, - 136, 167, - 178, 167, - 347, 167, - -2, 458, - -1, 1840, - 137, 408, - 246, 408, - -2, 512, - -1, 1849, - 251, 44, - 256, 44, - -2, 420, - -1, 2287, - 223, 1613, - -2, 1607, - -1, 2288, - 223, 1609, - -2, 1605, - -1, 2388, - 136, 167, - 178, 167, - 347, 167, - -2, 459, - -1, 2395, - 26, 188, + -1, 1504, + 224, 1619, + -2, 1464, + -1, 1715, + 61, 890, + -2, 908, + -1, 1716, + 61, 891, + -2, 909, + -1, 1772, + 137, 167, + 179, 167, + 349, 167, + -2, 460, + -1, 1853, + 138, 410, + 247, 410, + -2, 514, + -1, 1862, + 252, 44, + 257, 44, + -2, 422, + -1, 2302, + 224, 1623, + -2, 1617, + -1, 2303, + 224, 1619, + -2, 1615, + -1, 2405, + 137, 167, + 179, 167, + 349, 167, + -2, 461, + -1, 2412, + 27, 188, -2, 190, - -1, 2849, - 78, 98, - 88, 98, - -2, 965, - -1, 2918, - 707, 698, - -2, 672, - -1, 3126, - 50, 1555, - -2, 1549, - -1, 3941, - 707, 698, - -2, 686, - -1, 4028, - 90, 630, - 95, 630, - 105, 630, - 180, 630, - 181, 630, - 182, 630, - 183, 630, - 184, 630, - 185, 630, - 186, 630, - 187, 630, - 188, 630, - 189, 630, - 190, 630, - 191, 630, - 192, 630, - 193, 630, - 194, 630, - 195, 630, - 196, 630, - 197, 630, - 198, 630, - 199, 630, - 200, 630, - 201, 630, - 202, 630, - 203, 630, - 204, 630, - 205, 630, - 206, 630, - 207, 630, - 208, 630, - 209, 630, - 210, 630, - 211, 630, - 212, 630, - 213, 630, - 214, 630, - 215, 630, - 216, 630, - 217, 630, - 218, 630, - 219, 630, - 220, 630, - 221, 630, - -2, 1976, + -1, 2866, + 79, 98, + 89, 98, + -2, 967, + -1, 2935, + 710, 702, + -2, 676, + -1, 3145, + 51, 1565, + -2, 1559, + -1, 3970, + 710, 702, + -2, 690, + -1, 4056, + 91, 634, + 96, 634, + 106, 634, + 181, 634, + 182, 634, + 183, 634, + 184, 634, + 185, 634, + 186, 634, + 187, 634, + 188, 634, + 189, 634, + 190, 634, + 191, 634, + 192, 634, + 193, 634, + 194, 634, + 195, 634, + 196, 634, + 197, 634, + 198, 634, + 199, 634, + 200, 634, + 201, 634, + 202, 634, + 203, 634, + 204, 634, + 205, 634, + 206, 634, + 207, 634, + 208, 634, + 209, 634, + 210, 634, + 211, 634, + 212, 634, + 213, 634, + 214, 634, + 215, 634, + 216, 634, + 217, 634, + 218, 634, + 219, 634, + 220, 634, + 221, 634, + 222, 634, + -2, 1988, } const yyPrivate = 57344 -const yyLast = 54976 +const yyLast = 56127 var yyAct = [...]int{ - 955, 3603, 3604, 87, 3602, 4026, 4103, 3922, 943, 3278, - 4116, 4007, 4070, 1263, 950, 3554, 942, 2081, 4071, 2385, - 1968, 3995, 3906, 3831, 2316, 3178, 3407, 3185, 3227, 2093, - 3236, 3241, 3238, 3237, 3235, 3240, 1762, 1261, 3139, 2024, - 3904, 3239, 5, 3541, 2745, 2318, 3256, 3079, 2459, 737, - 3193, 3255, 3143, 3140, 3452, 3446, 3641, 2982, 2340, 3127, - 2809, 731, 3438, 764, 904, 2356, 903, 2422, 908, 3972, - 3258, 42, 1818, 732, 2883, 3285, 2964, 2915, 2447, 1722, - 3472, 2427, 2885, 2884, 2359, 2373, 1022, 1073, 87, 2490, - 2360, 1041, 163, 1143, 1019, 2834, 1865, 2815, 2361, 41, - 3137, 2271, 2785, 1709, 2801, 2239, 43, 1022, 2283, 2238, - 2077, 2116, 2468, 2956, 2446, 2032, 149, 2348, 2429, 1847, - 1106, 1101, 1083, 2507, 2876, 1751, 2851, 1731, 2363, 1119, - 100, 2822, 1688, 1510, 104, 2120, 2336, 105, 2052, 1437, - 1422, 1964, 1854, 747, 3142, 1077, 1080, 2444, 1109, 1112, - 1946, 1081, 2418, 2419, 1021, 1107, 1025, 1108, 1750, 1058, - 1736, 1060, 2189, 735, 3636, 2128, 1031, 2147, 1040, 742, - 3894, 2783, 2341, 107, 1470, 1043, 1028, 2023, 1252, 85, - 1976, 1813, 167, 127, 125, 126, 99, 1026, 1192, 905, - 1494, 1017, 1839, 132, 1027, 133, 1053, 741, 1029, 734, - 98, 4104, 1259, 3542, 1238, 2284, 106, 1514, 2461, 2462, - 2463, 84, 1519, 93, 3224, 3957, 2461, 724, 2938, 2937, - 2505, 2906, 3534, 1048, 1052, 4053, 1016, 2972, 2973, 3953, - 1034, 3952, 2313, 2314, 669, 128, 2039, 3497, 2038, 2037, - 1074, 3958, 2036, 2035, 2034, 1145, 134, 1148, 2007, 1208, - 666, 1684, 667, 4047, 2553, 2781, 3123, 4074, 1162, 1163, - 1164, 1931, 1167, 1168, 1169, 1170, 1123, 3083, 1173, 1174, - 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, - 1185, 1186, 1187, 1188, 1189, 1122, 1067, 1035, 1156, 1068, - 725, 1018, 2811, 95, 1209, 3607, 2, 1020, 1090, 1085, - 709, 1098, 3931, 2908, 1149, 1152, 1153, 128, 1097, 1096, - 1095, 95, 3246, 4126, 1433, 4069, 4094, 1454, 2494, 4109, - 1042, 4057, 4055, 3412, 3411, 709, 727, 2931, 3907, 2928, - 2055, 2746, 3953, 2044, 95, 3607, 3304, 909, 3827, 3246, - 1165, 111, 112, 113, 4108, 116, 4056, 4054, 122, 190, - 1015, 191, 3243, 3826, 661, 703, 1066, 1070, 907, 1066, - 1070, 907, 2493, 1147, 1146, 4084, 722, 723, 3244, 190, - 703, 3547, 3837, 129, 3548, 128, 1010, 1011, 1012, 1013, - 4051, 95, 3606, 1024, 3566, 3555, 172, 959, 960, 961, - 3996, 4004, 1716, 129, 3250, 3244, 2487, 1099, 1424, 3836, - 2086, 703, 4031, 3324, 703, 1828, 172, 3175, 3176, 86, - 2782, 1055, 1056, 700, 959, 960, 961, 86, 3174, 2945, - 2946, 3250, 3606, 1245, 2860, 1247, 2971, 2859, 86, 2865, - 2861, 2559, 2562, 2825, 2380, 2381, 1752, 4008, 1753, 4036, - 2379, 2955, 169, 2016, 2017, 170, 1256, 1228, 2492, 1094, - 1008, 1201, 1202, 1451, 1007, 1452, 1453, 4034, 2826, 3923, - 1972, 685, 169, 1244, 1246, 170, 4040, 4041, 189, 1229, - 2872, 1222, 3282, 1216, 683, 3195, 3196, 3654, 1217, 2398, - 2397, 3936, 4035, 1204, 1233, 1234, 3565, 95, 189, 86, - 1191, 3017, 88, 703, 2438, 95, 703, 2560, 3280, 2818, - 2819, 703, 3312, 2315, 3310, 1216, 95, 1092, 3247, 4075, - 1217, 2551, 2015, 4012, 680, 717, 2019, 2432, 1215, 721, - 1214, 1748, 1692, 695, 715, 1471, 3286, 2957, 1434, 2983, - 4076, 3878, 703, 3879, 2916, 3247, 2344, 2469, 690, 2941, - 4012, 2508, 4106, 1921, 2344, 1947, 1249, 3301, 693, 1472, - 1473, 1474, 1475, 1476, 1477, 1478, 1480, 1479, 1481, 1482, - 1423, 1255, 3273, 1230, 3283, 1223, 704, 95, 1254, 1242, - 3274, 1231, 1232, 1243, 1166, 2512, 2554, 2555, 2557, 2556, - 1237, 704, 173, 1248, 3194, 1059, 1197, 1922, 1235, 1923, - 3281, 179, 2959, 2514, 3536, 3535, 3197, 1973, 1236, 2529, - 2532, 2530, 173, 2531, 1172, 1171, 2510, 1121, 1241, 3811, - 2472, 179, 704, 2985, 1832, 704, 670, 2511, 672, 686, - 3611, 706, 2357, 705, 676, 3449, 674, 678, 687, 679, - 2513, 673, 1103, 684, 3532, 1093, 675, 688, 689, 692, - 696, 697, 698, 694, 691, 1141, 682, 707, 2515, 4048, - 1102, 1140, 1139, 1138, 1103, 1137, 3018, 2521, 2517, 2519, - 2520, 2518, 2522, 2523, 1136, 1135, 2431, 1134, 1129, 1142, - 1695, 3197, 4081, 4127, 1485, 3082, 1069, 1063, 1061, 1069, - 1063, 1061, 1078, 2995, 2994, 2993, 1078, 1115, 2987, 1078, - 2991, 1151, 2986, 1076, 2984, 1114, 1965, 1114, 2445, 2989, - 1260, 1150, 1260, 1260, 704, 2909, 1054, 704, 2988, 2960, - 1120, 2498, 704, 2497, 3217, 164, 1114, 1117, 1118, 1961, - 1078, 1425, 1159, 2940, 1111, 1115, 2990, 2992, 2342, 2343, - 1826, 1825, 1824, 2926, 1962, 164, 2342, 2343, 1822, 1207, - 660, 2954, 4049, 704, 2953, 1110, 3919, 1749, 3486, 3531, - 1022, 1495, 1500, 1501, 3468, 1504, 1506, 1507, 1508, 1509, - 2976, 1512, 1513, 1515, 1515, 2943, 1515, 1515, 1520, 1520, - 1520, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, - 1532, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, - 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, - 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, - 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, - 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, - 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, - 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, - 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, - 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621, - 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, - 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, - 1642, 1643, 1644, 1492, 1250, 3930, 2907, 1645, 1415, 1647, - 1648, 1649, 1650, 1651, 1416, 1417, 956, 1488, 1489, 1490, - 1491, 1520, 1520, 1520, 1520, 1520, 1520, 1502, 3495, 3496, - 708, 2491, 2930, 3450, 956, 1100, 1658, 1659, 1660, 1661, - 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, - 1496, 701, 1933, 1932, 1934, 1935, 1936, 956, 1438, 89, - 2874, 1062, 165, 3605, 1062, 1505, 702, 1685, 4010, 177, - 1203, 1200, 3302, 4039, 1213, 1212, 2929, 1218, 1219, 1220, - 1221, 1516, 165, 1517, 1518, 1432, 3248, 3249, 94, 177, - 1438, 1521, 1522, 3395, 1195, 4010, 94, 2561, 3564, 3252, - 4009, 1257, 1258, 3605, 2435, 1132, 1853, 94, 1130, 2560, - 185, 2910, 2963, 3248, 3249, 1715, 1485, 4038, 1121, 1226, - 1691, 2786, 2788, 2856, 2489, 2821, 3252, 4009, 2758, 1022, - 185, 2089, 1951, 1022, 1682, 1486, 1487, 1089, 1740, 1022, - 1091, 1646, 1206, 3091, 2436, 3090, 2816, 1121, 1482, 668, - 124, 2434, 2386, 166, 171, 168, 174, 175, 176, 178, - 180, 181, 182, 183, 1485, 1683, 1448, 3173, 94, 184, - 186, 187, 188, 166, 171, 168, 174, 175, 176, 178, - 180, 181, 182, 183, 1716, 2437, 2585, 1465, 1037, 184, - 186, 187, 188, 1253, 1952, 2433, 3944, 1699, 1448, 119, - 1144, 1703, 2574, 4120, 1239, 1977, 3527, 1021, 1121, 1477, - 1478, 1480, 1479, 1481, 1482, 1852, 1211, 3462, 2509, 2028, - 1958, 1120, 3003, 1701, 1754, 2121, 1702, 104, 2129, 2121, - 105, 2594, 1683, 1652, 1653, 1654, 1655, 1656, 1657, 1094, - 2899, 1086, 2130, 1454, 1158, 4085, 1689, 3650, 1088, 1087, - 1120, 1094, 1190, 2057, 1133, 1124, 1114, 1131, 1676, 1453, - 1126, 1452, 1453, 2585, 1127, 1125, 107, 2058, 1483, 1484, - 2056, 3502, 120, 3501, 1444, 2966, 2476, 1436, 2966, 1862, - 2965, 1454, 1948, 2965, 1949, 1128, 2113, 1950, 1861, 1851, - 2787, 2486, 2484, 1132, 1130, 1454, 1697, 1092, 2566, 2567, - 2568, 4077, 1829, 1830, 1831, 3487, 1444, 1845, 1033, 3974, - 3912, 1120, 4122, 3561, 3319, 3562, 1194, 1114, 1117, 1118, - 2488, 1078, 1700, 1718, 1225, 1111, 1115, 4090, 1716, 2481, - 1970, 1916, 1838, 1454, 1686, 1227, 1867, 1018, 1868, 1721, - 1870, 1872, 1698, 1196, 1876, 1878, 1880, 1882, 1884, 1857, - 1020, 1898, 1240, 1978, 3975, 3913, 1121, 1855, 1855, 1260, - 1210, 3819, 1745, 1746, 1454, 1121, 2481, 3818, 2485, 1906, - 1907, 1856, 1941, 2127, 709, 1912, 1913, 3809, 3577, 1451, - 1716, 1452, 1453, 1821, 2105, 2094, 2095, 2096, 2097, 2107, - 2098, 2099, 2100, 2112, 2108, 2101, 2102, 2109, 2110, 2111, - 2103, 2104, 2106, 1848, 3576, 2483, 1716, 1835, 1836, 1955, - 1834, 1953, 1954, 2126, 1956, 1957, 3509, 1451, 1716, 1452, - 1453, 1193, 1859, 1939, 4118, 1093, 3005, 4119, 1454, 4117, - 1704, 1451, 1928, 1452, 1453, 2276, 1940, 1093, 86, 44, - 45, 88, 1902, 1454, 4128, 2046, 2048, 2049, 3840, 1894, - 2621, 3508, 1897, 3498, 1899, 1966, 1748, 3225, 92, 1120, - 3213, 1157, 48, 76, 77, 1154, 74, 78, 1120, 1451, - 2047, 1452, 1453, 1124, 1114, 75, 3277, 2881, 1126, 959, - 960, 961, 1127, 1125, 2880, 1454, 2879, 1938, 2441, 128, - 1097, 1096, 1095, 1454, 1942, 1926, 1927, 1827, 1925, 1924, - 1451, 1914, 1452, 1453, 62, 1473, 1474, 1475, 1476, 1477, - 1478, 1480, 1479, 1481, 1482, 1983, 95, 1475, 1476, 1477, - 1478, 1480, 1479, 1481, 1482, 1260, 1260, 4088, 1716, 1979, - 1980, 4129, 1908, 1905, 1904, 1419, 1903, 1874, 2005, 87, - 1696, 4078, 87, 1984, 3939, 1454, 3492, 709, 2863, 709, - 1991, 1992, 1993, 1458, 1459, 1460, 1461, 1462, 1463, 1464, - 1456, 2004, 83, 1471, 1451, 2975, 1452, 1453, 1443, 1440, - 1441, 1442, 1447, 1449, 1446, 3938, 1445, 4018, 1716, 1451, - 3916, 1452, 1453, 2457, 2456, 3915, 1439, 1472, 1473, 1474, - 1475, 1476, 1477, 1478, 1480, 1479, 1481, 1482, 2455, 2454, - 1443, 1440, 1441, 1442, 1447, 1449, 1446, 42, 1445, 3914, - 42, 2084, 2084, 2082, 2082, 2085, 3814, 1454, 1439, 3798, - 1471, 1451, 3797, 1452, 1453, 2453, 2452, 1981, 2591, 1451, - 4079, 1452, 1453, 1454, 1985, 3649, 1987, 1988, 1989, 1990, - 3647, 2050, 2633, 1994, 1472, 1473, 1474, 1475, 1476, 1477, - 1478, 1480, 1479, 1481, 1482, 2006, 51, 54, 57, 56, - 59, 3573, 73, 2807, 4105, 82, 79, 1472, 1473, 1474, - 1475, 1476, 1477, 1478, 1480, 1479, 1481, 1482, 1725, 1454, - 1471, 1451, 2167, 1452, 1453, 1682, 110, 4065, 1716, 61, - 91, 90, 3181, 1681, 71, 72, 58, 109, 1680, 108, - 1679, 2590, 80, 81, 1472, 1473, 1474, 1475, 1476, 1477, - 1478, 1480, 1479, 1481, 1482, 85, 1683, 3481, 85, 2029, - 2054, 1454, 2631, 2156, 1726, 1450, 1716, 2012, 2013, 2807, - 4003, 1716, 1450, 1716, 3932, 2807, 3982, 3182, 2276, 2807, - 3978, 3845, 2273, 2061, 63, 64, 110, 65, 66, 67, - 68, 2275, 2059, 1451, 3506, 1452, 1453, 109, 3491, 108, - 3287, 3184, 101, 4016, 1716, 954, 3284, 1716, 103, 1451, - 2287, 1452, 1453, 102, 3965, 1716, 3844, 2286, 2060, 3179, - 2062, 2063, 2064, 2065, 2066, 2067, 2069, 2071, 2072, 2073, - 2074, 2075, 2076, 3216, 2285, 1496, 2088, 3195, 3196, 3215, - 2131, 2132, 2133, 2134, 3180, 4014, 1716, 103, 60, 2890, - 2274, 2877, 2122, 2272, 2145, 1451, 1678, 1452, 1453, 2166, - 2542, 2148, 2541, 2115, 2117, 2503, 2150, 1716, 1716, 3802, - 2155, 2151, 3545, 3929, 2152, 2153, 2154, 2502, 3186, 2149, - 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 1471, - 1454, 2573, 3822, 1716, 101, 2339, 2365, 1451, 2321, 1452, - 1453, 103, 2181, 1454, 2287, 102, 1716, 2290, 2291, 2807, - 3810, 2354, 2008, 1472, 1473, 1474, 1475, 1476, 1477, 1478, - 1480, 1479, 1481, 1482, 104, 3545, 1716, 105, 2285, 2807, - 3543, 3801, 2395, 1454, 2481, 1716, 3466, 1716, 89, 1454, - 2713, 1716, 3206, 3205, 2823, 104, 3194, 1974, 105, 3203, - 3204, 3553, 2179, 1454, 2053, 3201, 3202, 2917, 3197, 1937, - 1716, 2895, 2190, 2332, 2367, 1454, 3201, 3200, 1083, 1454, - 2831, 1716, 2560, 2939, 3891, 1716, 2394, 2349, 2350, 1454, - 1817, 2920, 2404, 2405, 2406, 2407, 1929, 3889, 1716, 3461, - 2399, 1454, 2400, 2401, 2402, 2403, 2913, 2914, 2390, 1034, - 1454, 1083, 2389, 2289, 2320, 1919, 2292, 2293, 2410, 2411, - 2412, 2413, 2371, 1454, 2807, 2806, 2831, 3886, 1716, 2326, - 2308, 2327, 1915, 3868, 1716, 2482, 1451, 1911, 1452, 1453, - 2424, 2393, 2263, 2264, 2265, 2266, 2267, 2334, 1910, 1451, - 1909, 1452, 1453, 2470, 1727, 2430, 1251, 94, 2352, 3437, - 1716, 2587, 1716, 3430, 1716, 2087, 1716, 2376, 2377, 2375, - 2823, 1454, 2331, 3427, 1716, 109, 2392, 1067, 2391, 1451, - 1068, 1452, 1453, 1454, 3183, 1451, 3927, 1452, 1453, 1729, - 1817, 1816, 2467, 2481, 3425, 1716, 2440, 2310, 1454, 1451, - 1450, 1452, 1453, 2190, 1454, 3463, 2852, 3387, 1716, 3970, - 1454, 1451, 2852, 1452, 1453, 1451, 1454, 1452, 1453, 2425, - 2414, 2416, 2417, 2421, 3943, 1451, 2803, 1452, 1453, 2439, - 2807, 2475, 2443, 1123, 2478, 2451, 2479, 1451, 2581, 1452, - 1453, 103, 3461, 1855, 1450, 2495, 1451, 2831, 1452, 1453, - 1760, 1759, 1122, 3138, 2425, 1728, 2477, 2474, 2473, 1451, - 3168, 1452, 1453, 70, 3461, 3510, 3416, 3385, 1716, 2853, - 2560, 2496, 2830, 2499, 3203, 2853, 190, 2500, 2501, 2855, - 3111, 1454, 3381, 1716, 2378, 2560, 2587, 2911, 3378, 1716, - 2713, 2618, 1717, 1719, 3376, 1716, 2617, 2481, 2464, 2587, - 129, 1454, 151, 2347, 1720, 2565, 1454, 1451, 2311, 1452, - 1453, 2087, 2030, 172, 2014, 1960, 3511, 3512, 3513, 1451, - 2506, 1452, 1453, 1747, 3228, 1105, 1104, 2831, 1454, 1506, - 95, 1506, 4044, 3985, 1451, 3833, 1452, 1453, 1023, 1723, - 1451, 3799, 1452, 1453, 162, 1454, 1451, 2577, 1452, 1453, - 150, 3661, 1451, 1454, 1452, 1453, 3526, 3523, 3504, 3329, - 3328, 1819, 2423, 2287, 2535, 3374, 1716, 3275, 1454, 169, - 2286, 3230, 170, 3226, 1454, 2921, 2420, 1471, 2415, 2409, - 1467, 2408, 1468, 1944, 3514, 3372, 1716, 2580, 1454, 2887, - 3432, 1841, 1842, 161, 160, 189, 1469, 1483, 1484, 1466, - 1850, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1480, 1479, - 1481, 1482, 3370, 1716, 1846, 2550, 95, 1451, 1815, 1452, - 1453, 121, 1195, 2886, 3187, 1890, 3279, 1454, 3191, 3428, - 2558, 3515, 3516, 3517, 3834, 3190, 1454, 1451, 3806, 1452, - 1453, 2438, 1451, 2324, 1452, 1453, 2010, 1454, 3473, 3474, - 4100, 1454, 3368, 1716, 4098, 2569, 1454, 4072, 3366, 1716, - 2054, 3479, 2629, 3951, 1451, 1454, 1452, 1453, 3873, 3192, - 1454, 2887, 3364, 1716, 3188, 1454, 1891, 1892, 1893, 3189, - 3476, 1451, 2571, 1452, 1453, 1454, 3222, 3221, 3220, 1451, - 3138, 1452, 1453, 2900, 2536, 1454, 155, 1843, 158, 665, - 1840, 2583, 156, 157, 1451, 3478, 1452, 1453, 2011, 173, - 1451, 2582, 1452, 1453, 3631, 2593, 3630, 2570, 179, 2572, - 3362, 1716, 3157, 3156, 1451, 3947, 1452, 1453, 2575, 1724, - 2576, 3360, 1716, 3835, 2544, 2545, 1716, 2578, 2330, 2547, - 3358, 1716, 3160, 1454, 3467, 2338, 2757, 3161, 2548, 3356, - 1716, 1454, 3158, 3116, 3354, 1716, 3115, 3159, 3911, 3352, - 1716, 3640, 3642, 1451, 3629, 1452, 1453, 1454, 2627, 3350, - 1716, 3457, 1451, 726, 1452, 1453, 1454, 3125, 2789, 3348, - 1716, 1959, 1006, 1451, 3199, 1452, 1453, 1451, 1454, 1452, - 1453, 2870, 1451, 1454, 1452, 1453, 1022, 2084, 1454, 2082, - 2792, 1451, 3454, 1452, 1453, 2891, 1451, 1038, 1452, 1453, - 3453, 1451, 1161, 1452, 1453, 1039, 1160, 2828, 2829, 3295, - 2886, 1451, 1886, 1452, 1453, 2790, 2365, 3334, 1716, 1022, - 2848, 1451, 101, 1452, 1453, 3317, 1716, 2969, 1418, 2129, - 2600, 3459, 164, 102, 2793, 3162, 2795, 2840, 2841, 103, - 2927, 2778, 1716, 2130, 2053, 129, 2827, 2615, 2808, 4114, - 2776, 1716, 3218, 1454, 2539, 3128, 3130, 1454, 4023, 1887, - 1888, 1889, 2751, 1716, 3131, 3928, 1454, 2728, 1716, 1451, - 3829, 1452, 1453, 3528, 101, 42, 3198, 1451, 2844, 1452, - 1453, 103, 1454, 2335, 2845, 102, 2804, 2847, 2528, 1454, - 1689, 2817, 2527, 1451, 2780, 1452, 1453, 2349, 2350, 1454, - 2846, 2526, 1451, 3114, 1452, 1453, 1046, 1047, 2525, 2873, - 2875, 3113, 2800, 2524, 1451, 1683, 1452, 1453, 3899, 1451, - 3439, 1452, 1453, 1454, 1451, 2820, 1452, 1453, 159, 2805, - 2564, 2866, 108, 2925, 3898, 110, 2850, 2720, 1716, 3637, - 3876, 2711, 1716, 3648, 3646, 1454, 109, 109, 108, 2854, - 2709, 1716, 3645, 3638, 2857, 3524, 3458, 2430, 3456, 2864, - 1454, 2867, 3231, 2465, 1454, 1833, 2696, 1716, 1045, 2936, - 1454, 2124, 3447, 2694, 1716, 2889, 2125, 110, 2823, 3615, - 2892, 2893, 1454, 3393, 2878, 4102, 4101, 1454, 109, 1451, - 108, 1452, 1453, 1451, 1454, 1452, 1453, 2888, 4101, 103, - 110, 2803, 1451, 3019, 1452, 1453, 2619, 2692, 1716, 1454, - 2896, 109, 2185, 2901, 2902, 2903, 2322, 2897, 1451, 1741, - 1452, 1453, 1733, 4102, 2933, 1451, 3917, 1452, 1453, 2690, - 1716, 1838, 114, 115, 3490, 1451, 152, 1452, 1453, 153, - 1036, 3, 97, 1, 2688, 1716, 2922, 2923, 2686, 1716, - 2027, 1454, 2912, 10, 3389, 2979, 2980, 1454, 2932, 1451, - 2025, 1452, 1453, 9, 1014, 1421, 2684, 1716, 1454, 165, - 1420, 2682, 1716, 1454, 2026, 3494, 177, 8, 2680, 1716, - 4033, 1451, 681, 1452, 1453, 2312, 1687, 1454, 4073, 4029, - 2996, 2958, 2269, 2678, 1716, 1454, 1451, 2977, 1452, 1453, - 1451, 2961, 1452, 1453, 4030, 1930, 1451, 1920, 1452, 1453, - 3556, 2237, 3830, 3234, 2471, 3522, 2428, 185, 1451, 1113, - 1452, 1453, 2302, 1451, 154, 1452, 1453, 2387, 2388, 1454, - 1451, 3998, 1452, 1453, 1454, 2676, 1716, 2934, 118, 1717, - 2309, 2674, 1716, 1071, 2997, 1451, 117, 1452, 1453, 3000, - 1116, 1224, 2672, 1716, 2466, 3546, 2871, 2670, 1716, 2396, - 166, 171, 168, 174, 175, 176, 178, 180, 181, 182, - 183, 2668, 1716, 1766, 2333, 1454, 184, 186, 187, 188, - 2882, 1764, 1765, 1763, 1768, 1767, 3303, 1451, 2978, 1452, - 1453, 2620, 3021, 1451, 3394, 1452, 1453, 3077, 2967, 2018, - 716, 2968, 2843, 710, 1451, 192, 1452, 1453, 1755, 1451, - 1454, 1452, 1453, 2666, 1716, 1734, 3408, 1155, 2664, 1716, - 671, 3207, 2504, 1451, 677, 1452, 1453, 2981, 1503, 2009, - 3112, 1451, 2858, 1452, 1453, 2998, 1065, 1057, 2323, 2794, - 1064, 3807, 3146, 3084, 3451, 3124, 3095, 3126, 3086, 2810, - 3129, 3122, 3910, 3639, 2365, 3983, 1454, 3012, 2868, 2662, - 1716, 2274, 1730, 2274, 2272, 1451, 2272, 1452, 1453, 3057, - 1451, 3415, 1452, 1453, 2592, 2119, 2442, 3145, 1493, 87, - 2364, 3610, 2365, 2365, 2365, 2365, 2365, 1454, 2999, 3067, - 3068, 3069, 3070, 3071, 2657, 1716, 1454, 2045, 739, 738, - 736, 3085, 2365, 3087, 2796, 2365, 2824, 1457, 944, 3095, - 2784, 1451, 1742, 1452, 1453, 2835, 3094, 1454, 2833, 3150, - 1970, 2832, 2367, 2537, 3167, 1454, 2372, 3475, 3471, 4025, - 1454, 2366, 2362, 2802, 895, 894, 3110, 3106, 3119, 748, - 2653, 1716, 740, 1454, 730, 893, 1451, 1025, 1452, 1453, - 2367, 2367, 2367, 2367, 2367, 3117, 1454, 892, 3120, 3261, - 3132, 3133, 1454, 3262, 2942, 3276, 3107, 3108, 3109, 3251, - 2367, 3326, 3151, 2367, 2944, 3154, 3149, 2869, 1026, 3259, - 3325, 3152, 3153, 3118, 3155, 1027, 3169, 104, 3163, 3170, - 105, 3171, 1451, 1454, 1452, 1453, 3272, 1435, 1706, 1084, - 3300, 2651, 1716, 3934, 2563, 3323, 1705, 3941, 3177, 2644, - 1716, 3242, 3540, 1454, 2642, 1716, 3223, 3209, 3059, 3210, - 3061, 2918, 3208, 1451, 2458, 1452, 1453, 3322, 69, 46, - 3211, 3212, 1451, 3905, 1452, 1453, 3072, 3073, 3074, 3075, - 2774, 3135, 3260, 3263, 3971, 3264, 2773, 887, 884, 2430, - 1454, 3232, 3253, 1451, 3612, 1452, 1453, 3613, 3614, 3080, - 3270, 1451, 3081, 1452, 1453, 3141, 1451, 3954, 1452, 1453, - 3141, 1454, 3955, 883, 3956, 2174, 1431, 2769, 1428, 1451, - 4046, 1452, 1453, 2020, 3288, 96, 36, 3291, 3290, 35, - 34, 33, 1451, 32, 1452, 1453, 26, 2768, 1451, 3298, - 1452, 1453, 25, 24, 23, 3308, 3305, 3306, 3254, 3307, - 22, 29, 3309, 19, 3311, 21, 3313, 2836, 2839, 2840, - 2841, 2837, 20, 2838, 2842, 18, 3245, 3473, 3474, 1451, - 4068, 1452, 1453, 4113, 2767, 1471, 123, 55, 52, 1506, - 50, 131, 130, 1506, 2579, 53, 49, 1198, 2584, 1451, - 47, 1452, 1453, 31, 30, 2766, 3233, 17, 16, 1472, - 1473, 1474, 1475, 1476, 1477, 1478, 1480, 1479, 1481, 1482, - 15, 2588, 14, 2589, 3410, 13, 12, 11, 2596, 7, - 6, 3414, 2598, 2599, 39, 38, 1451, 37, 1452, 1453, - 3299, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, - 2614, 28, 2616, 27, 40, 4, 2905, 1451, 2460, 1452, - 1453, 0, 0, 0, 3144, 0, 0, 0, 0, 2365, - 0, 3440, 3441, 3443, 0, 2622, 2623, 2624, 2625, 2626, - 0, 2628, 3488, 3448, 0, 2630, 3455, 0, 0, 2635, - 2636, 728, 2637, 0, 1454, 2640, 0, 2641, 2643, 2645, - 2646, 2647, 2648, 2649, 2650, 2652, 2654, 2655, 2656, 2658, - 1454, 2660, 2661, 2663, 2665, 2667, 2669, 2671, 2673, 2675, - 2677, 2679, 2681, 2683, 2685, 2687, 2689, 2691, 2693, 2695, - 2697, 2698, 2699, 3482, 2701, 3477, 2703, 2367, 2705, 2706, - 3460, 2708, 2710, 2712, 3480, 3260, 3263, 2715, 3264, 3445, - 0, 2719, 3489, 3483, 0, 2724, 2725, 2726, 2727, 3505, - 0, 3507, 3293, 3294, 0, 0, 0, 0, 2738, 2739, - 2740, 2741, 2742, 2743, 3550, 3551, 2747, 2748, 2765, 1454, - 0, 0, 3470, 0, 2750, 3499, 3500, 2113, 3417, 2756, - 3419, 3420, 3421, 1454, 2764, 2759, 2760, 2761, 2762, 2763, - 1044, 3484, 3485, 1050, 1050, 0, 2770, 2771, 0, 2772, - 0, 0, 2775, 2777, 2333, 0, 2779, 0, 0, 0, - 1454, 0, 0, 0, 1454, 0, 2791, 0, 0, 0, - 1451, 0, 1452, 1453, 0, 1454, 0, 0, 3533, 1454, - 0, 0, 3537, 3538, 3539, 0, 1451, 0, 1452, 1453, - 1454, 3552, 2836, 2839, 2840, 2841, 2837, 0, 2838, 2842, - 0, 0, 0, 2755, 1454, 3568, 0, 3529, 3530, 0, - 0, 0, 0, 1454, 0, 0, 0, 2754, 0, 0, - 0, 1454, 0, 0, 0, 2105, 2094, 2095, 2096, 2097, - 2107, 2098, 2099, 2100, 2112, 2108, 2101, 2102, 2109, 2110, - 2111, 2103, 2104, 2106, 2753, 0, 1454, 0, 2752, 0, - 1454, 0, 0, 0, 0, 1451, 0, 1452, 1453, 2749, - 1454, 0, 0, 2744, 1454, 0, 0, 0, 0, 1451, - 1454, 1452, 1453, 0, 2737, 0, 0, 0, 1454, 3628, - 0, 3632, 3633, 1454, 0, 0, 0, 3618, 2736, 3619, - 3620, 3621, 1454, 3608, 0, 0, 1451, 2735, 1452, 1453, - 1451, 0, 1452, 1453, 3145, 2734, 87, 3634, 3145, 0, - 0, 1451, 0, 1452, 1453, 1451, 0, 1452, 1453, 0, - 1454, 0, 0, 0, 0, 0, 1451, 0, 1452, 1453, - 2733, 0, 3572, 0, 2732, 0, 2084, 0, 2082, 3663, - 1451, 3635, 1452, 1453, 2731, 3655, 3644, 3643, 2730, 1451, - 0, 1452, 1453, 0, 2729, 3651, 3653, 1451, 1454, 1452, - 1453, 0, 2723, 0, 0, 0, 0, 2722, 0, 1454, - 0, 0, 0, 3813, 42, 0, 2721, 0, 0, 0, - 3667, 0, 1451, 0, 1452, 1453, 1451, 0, 1452, 1453, - 0, 0, 0, 0, 0, 0, 1451, 0, 1452, 1453, - 1451, 0, 1452, 1453, 2718, 0, 1451, 0, 1452, 1453, - 0, 0, 0, 3805, 1451, 3804, 1452, 1453, 0, 1451, - 0, 1452, 1453, 0, 0, 3820, 0, 0, 1451, 0, - 1452, 1453, 3825, 3832, 3824, 0, 1714, 1710, 3803, 0, - 0, 0, 2717, 0, 3870, 3871, 3007, 3008, 3009, 3010, - 3011, 1711, 3657, 2716, 3664, 3665, 1451, 0, 1452, 1453, - 0, 0, 2084, 0, 2082, 3874, 3016, 0, 3815, 3816, - 3817, 0, 0, 0, 0, 0, 2328, 2329, 1713, 0, - 1712, 3599, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3141, 0, 0, 1451, 3145, 1452, 1453, 0, 0, - 3877, 0, 3659, 3808, 3880, 1451, 0, 1452, 1453, 1523, - 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, - 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1543, 1544, - 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1554, + 958, 3626, 3951, 87, 3627, 946, 3625, 953, 2095, 4037, + 4144, 2107, 4131, 3297, 4025, 2331, 3197, 3204, 4098, 1269, + 945, 4099, 1982, 3935, 3858, 2402, 3246, 3158, 4054, 3255, + 3260, 3257, 3427, 3256, 3254, 3259, 3258, 911, 3933, 3563, + 42, 1267, 2763, 2333, 3097, 2038, 3275, 3576, 5, 3212, + 739, 3162, 3159, 3474, 3468, 2476, 3668, 3274, 2999, 2357, + 3156, 2826, 733, 3458, 734, 3277, 2900, 907, 3146, 2439, + 767, 1831, 1775, 2981, 906, 3304, 2444, 2932, 2901, 4002, + 1731, 2464, 2902, 2507, 2390, 163, 1026, 1078, 87, 2376, + 1046, 1023, 2851, 2373, 2378, 1878, 2832, 43, 2819, 2377, + 2298, 2803, 3494, 2286, 2254, 2130, 1148, 1026, 2091, 2046, + 41, 2973, 2253, 2485, 149, 2463, 2365, 2524, 1860, 2446, + 2893, 1106, 1088, 1025, 1111, 1029, 1764, 2868, 2380, 1744, + 2135, 1696, 100, 104, 105, 1517, 2066, 2351, 1443, 1428, + 1867, 1978, 1085, 1082, 1048, 1959, 3161, 749, 1117, 2461, + 1086, 2435, 2436, 1112, 1113, 1763, 1063, 1749, 1065, 1035, + 2299, 1114, 744, 1718, 737, 1124, 2204, 3663, 2143, 2162, + 2801, 2839, 1476, 99, 1257, 107, 2037, 1045, 1990, 127, + 125, 1032, 3655, 2358, 85, 1031, 167, 1826, 126, 1852, + 132, 908, 1500, 133, 1058, 1021, 1197, 726, 1030, 743, + 1521, 1033, 98, 736, 1265, 1243, 4132, 2478, 2479, 2480, + 3986, 3564, 3243, 2478, 93, 1526, 2923, 1053, 1057, 2522, + 84, 1444, 106, 2955, 2954, 3556, 4081, 2989, 1020, 2990, + 3981, 3982, 1038, 2328, 2329, 2053, 3987, 2052, 2051, 128, + 1153, 3519, 2050, 134, 1079, 671, 2049, 2048, 2021, 1213, + 4075, 668, 1692, 669, 2799, 2572, 3142, 4102, 1150, 3101, + 4154, 4097, 4122, 3432, 1439, 1944, 3431, 2509, 2354, 2353, + 727, 1167, 1168, 1169, 1039, 1172, 1173, 1174, 1175, 1072, + 3265, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, + 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1127, 2511, + 1073, 2, 1128, 3960, 1102, 1101, 1024, 1022, 95, 1735, + 711, 128, 1103, 1100, 1733, 2828, 2948, 1154, 1157, 1158, + 3630, 1047, 3630, 1460, 1161, 3936, 2925, 729, 2764, 1454, + 95, 3982, 2058, 3323, 1090, 4137, 3263, 1095, 95, 1736, + 4085, 912, 4083, 2510, 1734, 3265, 111, 112, 113, 2069, + 116, 1170, 1019, 122, 705, 705, 191, 711, 3262, 663, + 4136, 3854, 3269, 3853, 1152, 4084, 3569, 4082, 2945, 3570, + 4112, 724, 725, 1214, 1071, 1075, 910, 1725, 190, 128, + 1151, 1014, 1015, 1016, 1017, 3864, 4079, 95, 1028, 961, + 962, 963, 961, 962, 963, 3588, 1104, 86, 702, 1430, + 3577, 3263, 129, 1071, 1075, 910, 4026, 3629, 4034, 3629, + 2504, 3863, 2100, 4059, 3344, 172, 1060, 1061, 3587, 705, + 1841, 2962, 2963, 2800, 86, 3194, 3195, 3269, 1094, 4064, + 3193, 1096, 2397, 2398, 2877, 2988, 2579, 2876, 1450, 4038, + 2878, 1442, 2030, 2031, 86, 705, 687, 4062, 705, 1099, + 2576, 1206, 1207, 1765, 2972, 1766, 4068, 4069, 2882, 685, + 1457, 1444, 1458, 1459, 2396, 3214, 3215, 1262, 2842, 1233, + 1012, 169, 4063, 3681, 170, 95, 3266, 1221, 1440, 1238, + 1239, 1011, 1222, 1209, 1221, 3952, 1234, 1227, 2361, 1222, + 1220, 2889, 1219, 2843, 705, 86, 3965, 189, 88, 682, + 1986, 2577, 95, 2455, 1250, 2330, 1252, 1097, 697, 705, + 4103, 2415, 2414, 3301, 3035, 4042, 2835, 2836, 4042, 2570, + 3331, 3329, 95, 692, 719, 1477, 2449, 2029, 3299, 1196, + 1099, 4104, 1091, 2033, 695, 723, 1761, 2361, 717, 1093, + 1092, 3266, 3305, 2974, 1249, 1251, 1934, 3320, 1700, 1478, + 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1485, 1487, 1488, + 2933, 3906, 1429, 3907, 2486, 2958, 706, 706, 3292, 1454, + 1960, 2525, 4134, 95, 3213, 1254, 3293, 1261, 2573, 2531, + 2574, 1235, 1228, 1260, 1240, 1259, 3216, 1202, 1097, 2548, + 1935, 2549, 1936, 2550, 1241, 2529, 2976, 1236, 1237, 3558, + 1242, 1064, 672, 3557, 674, 688, 3302, 708, 2551, 707, + 678, 173, 676, 680, 689, 681, 1177, 675, 2489, 686, + 179, 3300, 677, 690, 691, 694, 698, 699, 700, 696, + 693, 706, 684, 709, 2532, 1176, 1098, 2528, 1987, 2538, + 2534, 2536, 2537, 2535, 2539, 2540, 2541, 2527, 4076, 1703, + 2530, 1247, 1107, 3838, 3634, 1248, 1108, 706, 2374, 1108, + 706, 2926, 1146, 1145, 1845, 1253, 1144, 3471, 1143, 3100, + 1142, 1141, 1140, 1139, 1134, 2448, 1147, 3216, 1450, 1083, + 3036, 2359, 2360, 1083, 1120, 4109, 4155, 1081, 1156, 1083, + 1246, 1119, 2977, 1979, 1119, 1074, 1068, 1066, 1155, 2462, + 1059, 2515, 1266, 2514, 1266, 1266, 706, 1975, 1431, 1164, + 3236, 2957, 1839, 1838, 1837, 2943, 1976, 1098, 1835, 1212, + 662, 706, 1492, 1493, 1074, 1068, 1066, 4077, 3948, 2971, + 2359, 2360, 2970, 1449, 1446, 1447, 1448, 1453, 1455, 1452, + 3508, 1451, 2508, 3490, 2873, 164, 2838, 2776, 2103, 1753, + 1653, 1445, 1026, 1501, 1506, 1507, 1211, 1510, 1512, 1513, + 1514, 1515, 1516, 1762, 1519, 1520, 1522, 1522, 2960, 1522, + 1522, 1527, 1527, 1527, 1530, 1531, 1532, 1533, 1534, 1535, + 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, + 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, + 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, + 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, + 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, + 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, + 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, + 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, + 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, 1625, + 1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, + 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 1651, 2891, 1255, 3959, 1498, + 1652, 1200, 1654, 1655, 1656, 1657, 1658, 710, 1422, 1423, + 1494, 1495, 1496, 1497, 1527, 1527, 1527, 1527, 1527, 1527, + 1508, 2924, 3586, 959, 3517, 3518, 1105, 1438, 703, 1665, + 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, + 1676, 1677, 1678, 704, 1502, 959, 3267, 3268, 1946, 1945, + 1947, 1948, 1949, 959, 1126, 4067, 89, 1421, 2927, 3271, + 1693, 1511, 4040, 2947, 3321, 4040, 3472, 1523, 94, 1524, + 1525, 1067, 3554, 1449, 1446, 1447, 1448, 1453, 1455, 1452, + 3628, 1451, 3628, 165, 705, 1231, 1137, 1528, 1529, 1218, + 177, 1445, 3415, 2578, 4039, 94, 1690, 4039, 1205, 4066, + 1067, 1208, 3200, 1135, 1866, 2452, 1126, 2946, 1491, 1126, + 2506, 3267, 3268, 1491, 1699, 94, 2577, 2980, 3109, 3108, + 124, 1724, 2833, 1026, 3271, 670, 1171, 1026, 2804, 2806, + 2403, 185, 1491, 1026, 3192, 1488, 1725, 4148, 2602, 1471, + 2144, 1042, 1216, 1964, 1244, 2453, 1217, 3201, 1223, 1224, + 1225, 1226, 2451, 2171, 2145, 1991, 1258, 1125, 1099, 1195, + 1707, 3973, 1149, 119, 1711, 3021, 94, 3549, 1698, 1691, + 1025, 3203, 1263, 1264, 166, 171, 168, 174, 175, 176, + 178, 180, 181, 182, 183, 3484, 2454, 3553, 2526, 3198, + 184, 186, 187, 188, 2993, 1965, 2450, 2042, 3128, 2591, + 1483, 1484, 1486, 1485, 1487, 1488, 1972, 3214, 3215, 1125, + 3000, 1767, 1125, 1865, 3199, 2602, 1709, 1710, 4113, 2136, + 104, 105, 2136, 1199, 2612, 2916, 1459, 665, 2071, 1659, + 1660, 1661, 1662, 1663, 1664, 1138, 120, 1691, 1458, 1459, + 1697, 3677, 2072, 1489, 1490, 2070, 3524, 1013, 3205, 3523, + 3126, 2163, 1136, 1684, 2493, 1875, 2165, 1874, 1864, 1163, + 2170, 2166, 107, 2503, 2167, 2168, 2169, 4105, 2498, 2164, + 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2498, + 2501, 1137, 2983, 2983, 1126, 2142, 1215, 2982, 2982, 1084, + 1230, 1135, 1245, 1961, 3002, 1962, 706, 2805, 1963, 1705, + 3509, 1232, 1858, 1992, 1727, 1037, 2505, 2502, 1694, 4150, + 1842, 1843, 1844, 1201, 3846, 1126, 3213, 1126, 2500, 3583, + 1708, 3584, 3845, 4004, 1929, 1851, 1725, 4156, 3216, 1198, + 1880, 1984, 1881, 2141, 1883, 1885, 1730, 1706, 1889, 1891, + 1893, 1895, 1897, 1870, 3836, 1098, 1911, 1022, 2060, 2062, + 2063, 3941, 3600, 1024, 1266, 1758, 1759, 2127, 4146, 1460, + 3599, 4147, 3531, 4145, 1869, 3012, 3011, 3010, 4005, 3530, + 3004, 1126, 3008, 2061, 3003, 3520, 3001, 1919, 1920, 3244, + 1460, 3006, 3232, 1925, 1926, 2898, 1834, 1125, 1868, 1868, + 3005, 2291, 1126, 1119, 1122, 1123, 3942, 1083, 2897, 1848, + 1849, 1116, 1120, 1847, 1861, 2366, 2367, 2896, 3007, 3009, + 1968, 1954, 1966, 1967, 4157, 1969, 1970, 1971, 1125, 1872, + 1125, 2458, 1115, 1129, 1119, 1129, 1119, 1955, 1131, 1712, + 1131, 1939, 1132, 1130, 1132, 1130, 1481, 1482, 1483, 1484, + 1486, 1485, 1487, 1488, 3868, 3202, 1907, 1952, 1915, 1910, + 1980, 1912, 1941, 1133, 3296, 2119, 2108, 2109, 2110, 2111, + 2121, 2112, 2113, 2114, 2126, 2122, 2115, 2116, 2123, 2124, + 2125, 2117, 2118, 2120, 1125, 1953, 1162, 2583, 2584, 2585, + 1159, 1102, 1101, 961, 962, 963, 1938, 1937, 128, 1927, + 1100, 190, 1921, 1425, 1918, 1125, 1457, 1840, 1458, 1459, + 2291, 1119, 1122, 1123, 2288, 1083, 1917, 1916, 1997, 1116, + 1120, 1951, 1887, 2290, 1460, 129, 1940, 1457, 1460, 1458, + 1459, 1266, 1266, 1704, 711, 1993, 1994, 957, 172, 2019, + 1460, 3514, 711, 2880, 711, 87, 1477, 1761, 87, 1998, + 2474, 2473, 2647, 2472, 2471, 1460, 2005, 2006, 2007, 1464, + 1465, 1466, 1467, 1468, 1469, 1470, 1462, 2608, 2018, 1460, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1485, 1487, + 1488, 4106, 42, 2824, 4133, 42, 2470, 2469, 4093, 1725, + 1456, 1725, 2639, 1738, 169, 2824, 4033, 170, 1478, 1479, + 1480, 1481, 1482, 1483, 1484, 1486, 1485, 1487, 1488, 3968, + 2098, 2098, 2099, 4118, 1725, 2824, 4012, 103, 1460, 3967, + 189, 3945, 1477, 2096, 2096, 1473, 1725, 1474, 1479, 1480, + 1481, 1482, 1483, 1484, 1486, 1485, 1487, 1488, 2064, 1739, + 2607, 1475, 1489, 1490, 1472, 3944, 1478, 1479, 1480, 1481, + 1482, 1483, 1484, 1486, 1485, 1487, 1488, 1477, 3943, 2992, + 1690, 1457, 101, 1458, 1459, 1457, 3841, 1458, 1459, 103, + 3023, 2182, 3825, 102, 2824, 4008, 1725, 1457, 3824, 1458, + 1459, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1485, + 1487, 1488, 1457, 3676, 1458, 1459, 3206, 3926, 1725, 1725, + 3210, 3674, 2649, 4116, 1725, 3961, 1457, 3209, 1458, 1459, + 3596, 1995, 1456, 1725, 3567, 3958, 3849, 1725, 1999, 1689, + 2001, 2002, 2003, 2004, 2043, 1688, 85, 2008, 1725, 85, + 2068, 2824, 3837, 1691, 173, 2026, 2027, 1477, 1687, 2020, + 1460, 3211, 3528, 179, 3513, 1460, 3207, 3306, 1477, 2127, + 2075, 3208, 3567, 1725, 3483, 1457, 2073, 1458, 1459, 2651, + 1460, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1485, + 1487, 1488, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, + 1485, 1487, 1488, 2302, 2129, 2131, 2301, 3303, 2074, 3235, + 2076, 2077, 2078, 2079, 2080, 2081, 2083, 2085, 2086, 2087, + 2088, 2089, 2090, 1502, 2102, 2300, 1477, 3234, 2590, 1460, + 2146, 2147, 2148, 2149, 2907, 1460, 2824, 3565, 2289, 2498, + 1725, 2137, 2894, 2287, 2160, 4048, 1725, 3873, 2181, 1686, + 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1485, 1487, + 1488, 1204, 1679, 1210, 2561, 4046, 1725, 2119, 2108, 2109, + 2110, 2111, 2121, 2112, 2113, 2114, 2126, 2122, 2115, 2116, + 2123, 2124, 2125, 2117, 2118, 2120, 2305, 2306, 164, 2382, + 101, 3488, 1725, 2196, 2560, 1460, 2520, 1457, 2821, 1458, + 1459, 102, 1457, 2302, 1458, 1459, 2371, 2731, 1725, 3225, + 3224, 104, 105, 103, 4044, 1725, 110, 1457, 2519, 1458, + 1459, 1725, 1460, 1433, 2356, 2300, 2384, 109, 110, 108, + 2336, 2412, 104, 105, 3222, 3223, 3220, 3221, 103, 109, + 2194, 108, 3220, 3219, 2067, 2304, 2848, 1725, 2307, 2308, + 2205, 2577, 2956, 1830, 2937, 3872, 1725, 1460, 2930, 2931, + 3829, 1088, 2824, 2823, 2869, 2022, 1457, 2869, 1458, 1459, + 1988, 2604, 1457, 3828, 1458, 1459, 2421, 2422, 2423, 2424, + 3919, 1725, 2416, 1950, 2417, 2418, 2419, 2420, 2406, 1942, + 1038, 2407, 1460, 1932, 1088, 2323, 2335, 1725, 2840, 2388, + 2427, 2428, 2429, 2430, 1928, 2346, 1460, 3917, 1725, 1725, + 3575, 2347, 2604, 1725, 2101, 1725, 2341, 1924, 2342, 2349, + 2278, 2279, 2280, 2281, 2282, 2441, 1923, 2870, 2410, 1460, + 2870, 1922, 1457, 1460, 1458, 1459, 2487, 2872, 2369, 1460, + 2577, 2447, 3914, 1725, 1830, 1829, 3187, 2393, 2394, 2392, + 1773, 1772, 2934, 1072, 1740, 1256, 2577, 2409, 2408, 1457, + 2912, 1458, 1459, 1460, 86, 44, 45, 88, 3157, 2840, + 2848, 1460, 2847, 2411, 1073, 2484, 2325, 3503, 2457, 3483, + 2499, 2600, 2205, 3338, 92, 1456, 3485, 4000, 48, 76, + 77, 2599, 74, 78, 1457, 3972, 1458, 1459, 109, 2824, + 2442, 75, 2431, 2433, 2434, 2438, 1460, 2848, 3436, 3222, + 2460, 2456, 3131, 2492, 3896, 1725, 2495, 2395, 2496, 4107, + 2468, 1460, 2604, 2731, 3457, 1725, 165, 2848, 2512, 1457, + 62, 1458, 1459, 177, 2636, 2442, 2491, 2494, 2498, 2635, + 2490, 3483, 95, 1457, 2498, 1458, 1459, 2481, 1127, 1725, + 2364, 1729, 1128, 2326, 2516, 2513, 3450, 1725, 2517, 2518, + 2101, 2044, 1868, 2028, 1460, 1974, 1457, 1456, 1458, 1459, + 1457, 1460, 1458, 1459, 185, 1760, 1457, 3532, 1458, 1459, + 1110, 1109, 1726, 1728, 1027, 95, 1460, 1903, 83, 2582, + 4128, 3447, 1725, 1460, 2598, 4072, 4015, 2523, 3860, 1732, + 1457, 3826, 1458, 1459, 1460, 3247, 3445, 1725, 1457, 3688, + 1458, 1459, 3548, 1512, 1460, 1512, 3545, 166, 171, 168, + 174, 175, 176, 178, 180, 181, 182, 183, 3533, 3534, + 3535, 2594, 3976, 184, 186, 187, 188, 3526, 1904, 1905, + 1906, 3349, 4126, 1457, 3348, 1458, 1459, 1832, 2554, 3407, + 1725, 2440, 1460, 2302, 3294, 2904, 2301, 1757, 1457, 1460, + 1458, 1459, 95, 3249, 1460, 3245, 2938, 2437, 2432, 2426, + 2903, 3405, 1725, 2425, 1957, 2597, 1774, 1200, 3401, 1725, + 1460, 1863, 51, 54, 57, 56, 59, 1859, 73, 3398, + 1725, 82, 79, 1460, 1828, 121, 3495, 3496, 2569, 3396, + 1725, 1457, 1460, 1458, 1459, 3298, 3861, 2455, 1457, 2339, + 1458, 1459, 4100, 2575, 3536, 61, 91, 90, 2904, 2024, + 71, 72, 58, 1457, 3980, 1458, 1459, 1460, 80, 81, + 1457, 3901, 1458, 1459, 3498, 3241, 3240, 2586, 3956, 3239, + 1460, 1457, 3157, 1458, 1459, 3833, 2917, 2555, 2068, 3394, + 1725, 1457, 1460, 1458, 1459, 3179, 3654, 3862, 3653, 1913, + 3180, 3537, 3538, 3539, 3177, 3392, 1725, 2588, 3501, 3178, + 667, 63, 64, 3500, 65, 66, 67, 68, 3390, 1725, + 3176, 2025, 3181, 1460, 2857, 2858, 3175, 3388, 1725, 1457, + 2355, 1458, 1459, 1737, 1958, 2611, 1457, 1460, 1458, 1459, + 2345, 1457, 3489, 1458, 1459, 2587, 3652, 2589, 1460, 1899, + 3136, 1985, 3386, 1725, 3135, 1460, 2592, 1457, 2593, 1458, + 1459, 1460, 1040, 2595, 3940, 3384, 1725, 1996, 3667, 1460, + 1457, 2775, 1458, 1459, 2000, 60, 1460, 3382, 1725, 1457, + 3669, 1458, 1459, 1460, 728, 2011, 2012, 2013, 2014, 2015, + 2016, 2017, 3476, 1460, 3144, 2645, 1900, 1901, 1902, 1460, + 3475, 3147, 3149, 2807, 1457, 1043, 1458, 1459, 3380, 1725, + 3150, 1041, 1973, 1044, 3479, 2098, 2810, 1457, 1460, 1458, + 1459, 1026, 3378, 1725, 1010, 3218, 2887, 2908, 2096, 1457, + 1166, 1458, 1459, 3376, 1725, 1165, 2144, 2563, 2564, 1460, + 3374, 1725, 2566, 3314, 2845, 2846, 3372, 1725, 2808, 1460, + 2145, 2567, 2903, 2382, 3370, 1725, 1026, 2865, 1742, 2986, + 1457, 3452, 1458, 1459, 101, 89, 2618, 1460, 3368, 1725, + 2811, 1460, 2813, 1424, 1457, 102, 1458, 1459, 3354, 1725, + 2944, 42, 2067, 2633, 3448, 1457, 2825, 1458, 1459, 2844, + 2862, 129, 1457, 2864, 1458, 1459, 3481, 1460, 1457, 103, + 1458, 1459, 4142, 3336, 1725, 1460, 1457, 101, 1458, 1459, + 1460, 2366, 2367, 1457, 103, 1458, 1459, 3237, 102, 2558, + 1457, 4051, 1458, 1459, 1741, 1697, 3957, 2834, 2798, 3856, + 1457, 3217, 1458, 1459, 2796, 1725, 1457, 2861, 1458, 1459, + 2350, 2547, 2863, 1051, 1052, 2546, 2545, 2890, 2892, 2818, + 2544, 3134, 2794, 1725, 1460, 1457, 3413, 1458, 1459, 3133, + 3459, 2883, 2543, 1691, 2837, 2542, 2581, 2867, 2822, 108, + 2942, 1460, 3925, 3924, 3904, 94, 1457, 1460, 1458, 1459, + 3675, 2047, 2769, 1725, 1460, 3673, 1457, 2871, 1458, 1459, + 2746, 1725, 2874, 1460, 109, 3409, 2881, 2447, 3672, 1460, + 3665, 3664, 2884, 1460, 1457, 3546, 1458, 1459, 1457, 1460, + 1458, 1459, 2953, 1460, 2139, 110, 3469, 2895, 1460, 2140, + 3638, 3480, 3478, 3250, 2482, 2906, 109, 1460, 108, 1846, + 2909, 2910, 1050, 1460, 1457, 2905, 1458, 1459, 2840, 2738, + 1725, 2821, 1457, 110, 1458, 1459, 2913, 1457, 2914, 1458, + 1459, 2918, 2919, 2920, 109, 2200, 2729, 1725, 3037, 1460, + 2637, 2950, 2727, 1725, 4130, 4129, 1851, 2337, 1754, 2714, + 1725, 4130, 1746, 4129, 2996, 2997, 1460, 3946, 2712, 1725, + 3512, 70, 2939, 2940, 2710, 1725, 3, 97, 2708, 1725, + 2929, 1457, 1460, 1458, 1459, 3550, 2949, 1, 2706, 1725, + 1460, 114, 115, 2704, 1725, 1460, 1018, 1427, 1457, 1460, + 1458, 1459, 2702, 1725, 1457, 1460, 1458, 1459, 2700, 1725, + 1460, 1457, 1426, 1458, 1459, 2994, 1460, 2975, 3013, 3516, + 1457, 4061, 1458, 1459, 2978, 2284, 1457, 683, 1458, 1459, + 1457, 1460, 1458, 1459, 2698, 1725, 1457, 2327, 1458, 1459, + 1457, 2041, 1458, 1459, 10, 1457, 2039, 1458, 1459, 9, + 2040, 2696, 1725, 8, 1457, 2317, 1458, 1459, 1695, 4101, + 1457, 4057, 1458, 1459, 4058, 1943, 2951, 2694, 1725, 3014, + 3017, 1933, 3578, 1726, 2324, 2692, 1725, 2252, 3857, 3253, + 2690, 1725, 2488, 3544, 2688, 1725, 1457, 2445, 1458, 1459, + 2686, 1725, 1118, 154, 2404, 2684, 1725, 2405, 4028, 118, + 1076, 2682, 1725, 1457, 117, 1458, 1459, 1121, 1229, 2348, + 2483, 3568, 2888, 2413, 1779, 1460, 2995, 2899, 3039, 1457, + 1460, 1458, 1459, 1777, 1778, 3095, 1776, 1457, 2984, 1458, + 1459, 2985, 1457, 1781, 1458, 1459, 1457, 1780, 1458, 1459, + 3322, 1460, 1457, 2638, 1458, 1459, 3414, 1457, 2032, 1458, + 1459, 718, 1460, 1457, 110, 1458, 1459, 2860, 2998, 712, + 1460, 192, 1768, 1747, 3428, 109, 3015, 108, 1457, 3102, + 1458, 1459, 1160, 3104, 673, 2368, 103, 3226, 3113, 2521, + 679, 1509, 2382, 2372, 2023, 2375, 3132, 1460, 2047, 3030, + 2289, 2875, 2289, 1070, 1062, 2287, 3075, 2287, 1460, 2338, + 2680, 1725, 2812, 1069, 3164, 3346, 87, 3834, 3165, 2382, + 2382, 2382, 2382, 2382, 3473, 2459, 3016, 3143, 3145, 2384, + 3085, 3086, 3087, 3088, 3089, 2827, 2675, 1725, 3148, 2382, + 3141, 3103, 2382, 3105, 3939, 3169, 3112, 2671, 1725, 3666, + 3113, 4013, 3186, 1029, 1460, 3345, 2384, 2384, 2384, 2384, + 2384, 1460, 1457, 1984, 1458, 1459, 2885, 1457, 1743, 1458, + 1459, 1460, 3435, 3130, 3124, 2610, 2384, 2134, 1499, 2384, + 2381, 3139, 2669, 1725, 3633, 2059, 1460, 3137, 1457, 741, + 1458, 1459, 740, 2662, 1725, 738, 3151, 3152, 2814, 1457, + 1460, 1458, 1459, 2841, 1463, 947, 3270, 1457, 2802, 1458, + 1459, 1755, 3168, 1031, 3171, 3172, 3278, 3174, 3170, 2852, + 1460, 3173, 3182, 104, 105, 2850, 1030, 3190, 3188, 2849, + 1460, 3189, 2556, 2389, 1457, 1460, 1458, 1459, 3196, 3342, + 3125, 3127, 3129, 1460, 3497, 1457, 3340, 1458, 1459, 1460, + 3493, 3229, 4053, 3227, 3228, 2383, 2792, 3077, 2379, 3079, + 2820, 3138, 898, 897, 1460, 750, 742, 732, 1460, 960, + 896, 2660, 1725, 895, 3280, 3090, 3091, 3092, 3093, 1460, + 3230, 3231, 3281, 3282, 3272, 2791, 3279, 3154, 3251, 2447, + 3283, 1457, 2959, 1458, 1459, 3295, 2047, 3289, 1457, 1460, + 1458, 1459, 2961, 2533, 3160, 2787, 2886, 3291, 1457, 3160, + 1458, 1459, 1441, 2552, 2553, 2786, 3310, 2557, 1714, 1717, + 2785, 3307, 3309, 1457, 1089, 1458, 1459, 3317, 2784, 2562, + 1460, 3319, 3963, 2580, 2783, 1460, 2565, 1457, 3343, 1458, + 1459, 1713, 3324, 3325, 3327, 3326, 3273, 3970, 3328, 2782, + 3330, 3261, 3332, 2773, 3562, 3242, 2935, 1457, 2475, 1458, + 1459, 69, 2568, 46, 2772, 3934, 4001, 1457, 890, 1458, + 1459, 887, 1457, 3635, 1458, 1459, 3636, 1512, 3637, 3098, + 1457, 1512, 1458, 1459, 2771, 3099, 1457, 3983, 1458, 1459, + 3984, 886, 3985, 2189, 2596, 3252, 1437, 3460, 2601, 3462, + 1434, 1457, 4074, 1458, 1459, 1457, 2034, 1458, 1459, 96, + 3430, 36, 35, 34, 33, 2770, 1457, 3434, 1458, 1459, + 2767, 2605, 32, 2606, 26, 25, 1460, 24, 3318, 2614, + 23, 22, 29, 2616, 2617, 19, 1457, 21, 1458, 1459, + 20, 18, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, + 2631, 2632, 3264, 2634, 4096, 3163, 4141, 123, 2382, 55, + 52, 3461, 50, 3463, 3465, 131, 130, 1457, 53, 1458, + 1459, 3510, 1457, 3470, 1458, 1459, 2640, 2641, 2642, 2643, + 2644, 49, 2646, 1460, 3477, 1203, 2648, 47, 31, 3482, + 2653, 2654, 30, 2655, 17, 2384, 2658, 16, 2659, 2661, + 2663, 2664, 2665, 2666, 2667, 2668, 2670, 2672, 2673, 2674, + 2676, 2762, 2678, 2679, 2681, 2683, 2685, 2687, 2689, 2691, + 2693, 2695, 2697, 2699, 2701, 2703, 2705, 2707, 2709, 2711, + 2713, 2715, 2716, 2717, 3502, 2719, 3511, 2721, 3282, 2723, + 2724, 3279, 2726, 2728, 2730, 3283, 3499, 3505, 2733, 3527, + 3504, 3529, 2737, 15, 14, 13, 2742, 2743, 2744, 2745, + 3572, 3573, 12, 1457, 11, 1458, 1459, 7, 2755, 2756, + 2757, 2758, 2759, 2760, 2761, 1460, 1008, 2765, 2766, 3467, + 3437, 1009, 3439, 3440, 3441, 2768, 6, 3521, 3522, 39, + 2774, 2097, 38, 37, 1723, 1719, 2777, 2778, 2779, 2780, + 2781, 28, 27, 40, 4, 2922, 2477, 2788, 2789, 1720, + 2790, 3492, 0, 2793, 2795, 2348, 0, 2797, 0, 0, + 1457, 0, 1458, 1459, 1460, 0, 0, 2809, 3312, 3313, + 3506, 3507, 1460, 0, 2343, 2344, 1722, 0, 1721, 1460, + 3555, 3574, 0, 0, 3559, 3560, 3561, 0, 1460, 0, + 0, 0, 0, 0, 0, 3590, 0, 3551, 3552, 0, + 2754, 0, 0, 0, 0, 0, 966, 967, 968, 969, + 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, + 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, + 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, + 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1460, 2753, + 0, 0, 1457, 1460, 1458, 1459, 0, 2752, 0, 3641, + 1460, 3642, 3643, 3644, 2751, 1460, 2866, 0, 0, 3651, + 0, 0, 3658, 2750, 3660, 0, 0, 1460, 0, 0, + 0, 1460, 0, 0, 3631, 2853, 2856, 2857, 2858, 2854, + 3661, 2855, 2859, 0, 0, 0, 3164, 0, 87, 1460, + 3164, 1457, 0, 1458, 1459, 0, 0, 0, 0, 1457, + 0, 1458, 1459, 0, 1723, 1719, 1457, 0, 1458, 1459, + 0, 2098, 3690, 0, 0, 1457, 0, 1458, 1459, 1720, + 0, 3595, 3662, 2749, 2096, 42, 0, 3671, 2748, 3670, + 2915, 1460, 0, 3682, 0, 2747, 3678, 3680, 0, 0, + 2741, 0, 0, 0, 1715, 1716, 1722, 0, 1721, 0, + 1460, 0, 2740, 0, 0, 3840, 2739, 0, 2853, 2856, + 2857, 2858, 2854, 3694, 2855, 2859, 0, 0, 3495, 3496, + 0, 0, 0, 0, 2736, 1457, 0, 1458, 1459, 0, + 1457, 0, 1458, 1459, 0, 0, 0, 1457, 0, 1458, + 1459, 0, 1457, 0, 1458, 1459, 2964, 2965, 2966, 2967, + 2968, 2969, 3832, 3831, 1457, 3847, 1458, 1459, 1457, 0, + 1458, 1459, 0, 0, 3852, 3851, 2735, 0, 0, 3859, + 0, 3830, 0, 2047, 2979, 0, 1457, 3898, 1458, 1459, + 3899, 0, 0, 0, 0, 2734, 3684, 0, 2098, 3902, + 3025, 3026, 3027, 3028, 3029, 3659, 2987, 0, 0, 0, + 0, 2096, 0, 0, 0, 0, 0, 0, 0, 0, + 3034, 0, 3622, 3842, 3843, 3844, 0, 0, 1457, 0, + 1458, 1459, 0, 0, 0, 3160, 3691, 3692, 0, 0, + 3905, 0, 3164, 0, 3908, 0, 3835, 1457, 3686, 1458, + 1459, 0, 0, 0, 0, 0, 1530, 1531, 1532, 1533, + 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, + 1544, 1545, 1546, 1547, 1548, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, - 1615, 1616, 1617, 1618, 1620, 1621, 1622, 1623, 1624, 1625, - 1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, - 1641, 1642, 1643, 1644, 1658, 1659, 1660, 1661, 1662, 1663, - 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 3921, 3918, - 3903, 3144, 3875, 3900, 3901, 3144, 3902, 1454, 0, 1714, - 1710, 1454, 3935, 0, 0, 0, 1454, 0, 0, 0, - 0, 0, 0, 1454, 1711, 0, 0, 0, 3920, 0, - 87, 0, 0, 0, 3147, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1454, 1707, - 1708, 1713, 3165, 1712, 0, 0, 3924, 0, 0, 0, - 0, 3937, 0, 0, 0, 0, 3940, 0, 0, 3942, - 3812, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1454, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3909, 2714, 0, 0, 0, 2707, 0, 0, 42, 0, - 2704, 0, 0, 0, 0, 0, 0, 2702, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1455, 3960, 0, 3980, 3961, 0, 0, 0, 87, - 0, 0, 2700, 0, 0, 0, 3926, 0, 0, 0, - 0, 0, 0, 1451, 3969, 1452, 1453, 1451, 0, 1452, - 1453, 1511, 1451, 0, 1452, 1453, 3976, 0, 0, 1451, - 3986, 1452, 1453, 0, 2659, 4011, 0, 3997, 3984, 0, - 3945, 0, 3989, 3994, 3991, 3990, 3988, 3993, 0, 0, - 3297, 3832, 4000, 3992, 1451, 0, 1452, 1453, 0, 0, - 0, 0, 3144, 4021, 0, 0, 0, 42, 0, 0, - 0, 0, 3314, 3315, 4024, 3316, 4042, 3318, 3320, 4032, - 0, 4037, 4066, 4050, 0, 0, 1451, 4011, 1452, 1453, - 4052, 3327, 1783, 0, 4063, 0, 3331, 3332, 3333, 3335, - 3336, 3337, 3338, 3339, 3340, 3341, 3342, 3343, 3344, 3345, - 3346, 3347, 3349, 3351, 3353, 3355, 3357, 3359, 3361, 3363, - 3365, 3367, 3369, 3371, 3373, 3375, 3377, 3379, 3380, 3382, - 3383, 3384, 3386, 1970, 4067, 3388, 4083, 3390, 3391, 3392, - 4082, 4093, 3396, 3397, 3398, 3399, 3400, 3401, 3402, 3403, - 3404, 3405, 3406, 2084, 4099, 2082, 4096, 4095, 4086, 4097, - 4092, 3413, 4062, 3981, 4011, 3418, 1454, 4107, 0, 3422, - 3423, 0, 3424, 3426, 4115, 3429, 3431, 3141, 3433, 3434, - 3435, 3436, 4123, 4121, 0, 1454, 3442, 0, 0, 1454, - 3949, 0, 0, 0, 1454, 0, 0, 0, 3959, 1454, - 0, 0, 4132, 4133, 3871, 4131, 0, 0, 1454, 0, - 0, 2084, 0, 2082, 4130, 0, 0, 0, 0, 3933, - 0, 3464, 3465, 0, 0, 3469, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1771, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2639, 4080, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2638, - 0, 0, 0, 2634, 0, 0, 0, 0, 2632, 0, - 0, 0, 4058, 2597, 0, 0, 0, 0, 0, 0, - 0, 0, 2586, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1451, 0, 1452, 1453, 0, 1732, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1451, 1784, 1452, 1453, 1451, 0, 1452, 1453, 0, - 1451, 3544, 1452, 1453, 0, 1451, 0, 1452, 1453, 0, - 0, 0, 0, 0, 1451, 1820, 1452, 1453, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3563, 0, 0, 3567, - 0, 0, 0, 0, 1797, 1800, 1801, 1802, 1803, 1804, - 1805, 0, 1806, 1807, 1809, 1810, 1808, 1811, 1812, 1785, - 1786, 1787, 1788, 1769, 1770, 1798, 3578, 1772, 0, 1773, - 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 0, 0, - 1782, 1789, 1790, 1791, 1792, 0, 1793, 1794, 1795, 1796, - 0, 0, 0, 1690, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 940, 0, 0, 0, 0, 0, 0, 0, 0, - 3601, 0, 0, 1975, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3609, 0, 0, 0, 0, 0, 0, - 0, 3616, 663, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1009, 0, 0, 0, 0, 195, 0, 0, - 195, 0, 0, 0, 714, 0, 0, 0, 0, 720, + 1615, 1616, 1617, 1618, 1619, 1620, 1621, 1622, 1623, 1624, + 1625, 1627, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, + 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1648, 1649, 1650, + 1651, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, + 1674, 1675, 1676, 1677, 1678, 730, 3950, 3163, 3947, 3903, + 3932, 3163, 3931, 0, 3922, 0, 1460, 0, 0, 3964, + 1460, 3928, 0, 3930, 1460, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, + 0, 0, 0, 0, 3949, 0, 0, 3166, 0, 0, + 0, 1460, 0, 0, 0, 0, 0, 0, 0, 0, + 1460, 0, 0, 0, 0, 3184, 3966, 3953, 0, 3969, + 0, 0, 0, 0, 42, 0, 3839, 0, 0, 0, + 0, 0, 3971, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1460, 0, 0, 0, 3938, 0, + 0, 2732, 0, 0, 0, 2725, 0, 0, 0, 2722, + 0, 0, 0, 0, 1049, 0, 0, 1055, 1055, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3989, 0, + 0, 3990, 0, 4010, 0, 0, 2720, 0, 87, 0, + 0, 0, 0, 0, 3955, 2718, 0, 0, 0, 0, + 0, 3999, 0, 1457, 4006, 1458, 1459, 1457, 0, 1458, + 1459, 1457, 0, 1458, 1459, 0, 3238, 4016, 0, 0, + 0, 4041, 0, 0, 0, 42, 0, 4027, 3974, 2677, + 4019, 4024, 4021, 4014, 4020, 4018, 4023, 4022, 1457, 0, + 1458, 1459, 3276, 3316, 3859, 4030, 0, 1457, 0, 1458, + 1459, 0, 0, 3163, 4049, 4052, 3290, 0, 0, 4070, + 4060, 4065, 0, 0, 0, 3333, 3334, 0, 3335, 0, + 3337, 3339, 0, 0, 4041, 0, 3308, 4078, 4080, 3311, + 0, 1457, 4091, 1458, 1459, 3347, 0, 0, 0, 0, + 3351, 3352, 3353, 3355, 3356, 3357, 3358, 3359, 3360, 3361, + 3362, 3363, 3364, 3365, 3366, 3367, 3369, 3371, 3373, 3375, + 3377, 3379, 3381, 3383, 3385, 3387, 3389, 3391, 3393, 3395, + 3397, 3399, 3400, 3402, 3403, 3404, 3406, 1984, 4114, 3408, + 4110, 3410, 3411, 3412, 2098, 4124, 3416, 3417, 3418, 3419, + 3420, 3421, 3422, 3423, 3424, 3425, 3426, 2096, 4127, 4041, + 4125, 4123, 4121, 4135, 4120, 3433, 4111, 4143, 4095, 3438, + 4090, 4011, 190, 3442, 3443, 0, 3444, 3446, 3160, 3449, + 3451, 4149, 3453, 3454, 3455, 3456, 4151, 1460, 0, 3978, + 0, 1460, 3464, 0, 0, 0, 129, 3988, 151, 0, + 4160, 4161, 2098, 4158, 3899, 4159, 0, 1460, 0, 172, + 0, 0, 4094, 0, 0, 2096, 0, 0, 0, 0, + 3962, 1460, 1796, 0, 0, 0, 3486, 3487, 0, 0, + 3491, 0, 1460, 0, 0, 0, 190, 1460, 0, 0, + 162, 3466, 0, 0, 0, 0, 150, 2928, 1460, 0, + 4108, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 129, 0, 151, 0, 0, 169, 0, 0, 170, 0, + 0, 0, 2657, 172, 0, 0, 2656, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4086, 138, 139, 161, + 160, 189, 2652, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 162, 0, 2650, 0, 0, 0, + 150, 0, 0, 0, 0, 0, 0, 2615, 0, 0, + 0, 0, 2609, 0, 1457, 3525, 1458, 1459, 1457, 169, + 1458, 1459, 170, 2603, 0, 0, 3566, 0, 0, 0, + 0, 0, 0, 0, 1457, 3540, 1458, 1459, 3541, 3542, + 3543, 1854, 1855, 161, 160, 189, 0, 0, 1457, 0, + 1458, 1459, 0, 0, 0, 0, 0, 0, 1784, 1457, + 0, 1458, 1459, 0, 1457, 0, 1458, 1459, 0, 0, + 0, 3585, 0, 0, 3589, 1457, 0, 1458, 1459, 0, + 0, 0, 155, 136, 158, 143, 135, 0, 156, 157, + 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, + 0, 0, 3601, 0, 179, 144, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, + 145, 140, 141, 142, 146, 0, 0, 0, 0, 0, + 0, 137, 0, 0, 0, 0, 0, 0, 0, 0, + 148, 0, 1797, 0, 0, 0, 155, 1856, 158, 0, + 1853, 0, 156, 157, 0, 0, 0, 0, 0, 173, + 0, 0, 0, 0, 0, 0, 3624, 0, 179, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3632, + 0, 0, 0, 0, 0, 0, 0, 3639, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1810, 1813, 1814, 1815, 1816, + 1817, 1818, 0, 1819, 1820, 1822, 1823, 1821, 1824, 1825, + 1798, 1799, 1800, 1801, 1782, 1783, 1811, 0, 1785, 164, + 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 0, + 0, 1795, 1802, 1803, 1804, 1805, 0, 1806, 1807, 1808, + 1809, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1461, 0, 0, + 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3848, 0, 0, 0, 1518, 0, + 0, 0, 0, 3855, 0, 159, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3865, 3866, 3867, 0, 3869, 0, 3870, + 3871, 0, 0, 0, 3874, 3875, 3876, 3877, 3878, 3879, + 3880, 3881, 3882, 3883, 3884, 3885, 3886, 3887, 3888, 3889, + 3890, 3891, 3892, 3893, 3894, 3895, 0, 3897, 3900, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, + 0, 0, 0, 3909, 3910, 3911, 3912, 3913, 3915, 3916, + 3918, 3920, 3921, 3923, 0, 0, 0, 3927, 0, 0, + 0, 3929, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 0, 0, 153, 0, 0, + 0, 0, 0, 0, 0, 3954, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1812, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 165, 0, 0, + 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 152, 0, + 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 165, 0, 0, 0, 0, 0, 0, 177, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 166, 171, + 168, 174, 175, 176, 178, 180, 181, 182, 183, 0, + 0, 0, 0, 0, 184, 186, 187, 188, 0, 185, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1745, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 166, 171, 168, 174, 175, 176, 178, 180, + 181, 182, 183, 0, 0, 0, 0, 0, 184, 186, + 187, 188, 0, 0, 1833, 0, 0, 0, 1796, 0, + 3979, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3975, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3994, 0, 0, 0, 0, 0, + 3997, 0, 3998, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4009, 0, 0, 0, 0, 0, 0, + 0, 0, 943, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4035, + 4036, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4043, 4045, 4047, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 195, 4073, + 0, 195, 1989, 0, 0, 716, 0, 0, 0, 0, + 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 195, 0, 0, 1784, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1008, 4092, 2291, 195, 0, 1009, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2097, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 722, 195, 722, 0, 0, 0, 0, 4115, + 4117, 4119, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4140, 0, 0, 0, 0, 0, 1797, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4152, 4153, 0, 0, 966, 967, 968, 969, 970, 971, + 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, + 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, + 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, + 1002, 1003, 1004, 1005, 1006, 1007, 0, 0, 0, 0, + 0, 1810, 1813, 1814, 1815, 1816, 1817, 1818, 0, 1819, + 1820, 1822, 1823, 1821, 1824, 1825, 1798, 1799, 1800, 1801, + 1782, 1783, 1811, 0, 1785, 0, 1786, 1787, 1788, 1789, + 1790, 1791, 1792, 1793, 1794, 0, 0, 1795, 1802, 1803, + 1804, 1805, 0, 1806, 1807, 1808, 1809, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2054, 2055, 2056, 2057, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2065, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2104, 2105, 0, 0, 0, 0, 2128, 1055, + 1055, 2132, 2133, 0, 0, 0, 2138, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, + 2159, 0, 2161, 0, 0, 0, 2183, 2184, 2185, 2186, + 2187, 2188, 2190, 0, 2195, 0, 2197, 2198, 2199, 0, + 2201, 2202, 2203, 0, 2206, 2207, 2208, 2209, 2210, 2211, + 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, + 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, 2231, + 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, + 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, + 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, + 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, + 2275, 2276, 2277, 1812, 0, 0, 0, 0, 2283, 0, + 2285, 0, 2292, 2293, 2294, 2295, 2296, 2297, 1055, 0, + 1055, 1055, 1055, 1055, 1055, 0, 0, 0, 0, 0, + 0, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 0, + 2318, 2319, 2320, 2321, 2322, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1055, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2362, 2363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 190, 0, 0, 0, 0, 0, + 0, 0, 0, 2401, 0, 1850, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, + 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 0, 195, 0, 0, + 0, 0, 162, 0, 2443, 0, 0, 95, 150, 0, + 1008, 0, 0, 0, 948, 1009, 961, 962, 963, 949, + 0, 0, 950, 951, 0, 952, 0, 169, 0, 0, + 170, 0, 0, 0, 722, 0, 722, 722, 0, 957, + 964, 965, 0, 0, 0, 0, 0, 0, 0, 1854, + 1855, 161, 160, 189, 0, 0, 722, 195, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1504, 0, 0, 3284, 3285, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, + 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, + 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, + 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, + 1006, 1007, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 155, 1856, 158, 0, 1853, 0, + 156, 157, 0, 0, 0, 0, 0, 173, 0, 0, + 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, + 0, 0, 0, 3286, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1079, 0, 195, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 720, 195, 720, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3821, 0, 0, 0, - 0, 0, 0, 0, 0, 3828, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1799, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3838, 3839, 0, 3841, 0, - 3842, 3843, 0, 0, 0, 3846, 3847, 3848, 3849, 3850, - 3851, 3852, 3853, 3854, 3855, 3856, 3857, 3858, 3859, 3860, - 3861, 3862, 3863, 3864, 3865, 3866, 3867, 0, 3869, 3872, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3881, 3882, 3883, 3884, 3885, 3887, - 3888, 3890, 3892, 3893, 3895, 0, 0, 0, 0, 0, - 0, 0, 2040, 2041, 2042, 2043, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2051, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3925, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2090, 2091, 0, 0, 0, 0, 2114, 1050, - 1050, 2118, 0, 0, 0, 2123, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, - 0, 2146, 0, 0, 0, 2168, 2169, 2170, 2171, 2172, - 2173, 2175, 0, 2180, 0, 2182, 2183, 2184, 0, 2186, - 2187, 2188, 0, 2191, 2192, 2193, 2194, 2195, 2196, 2197, - 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, - 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, - 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, - 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2240, - 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, - 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, - 2261, 2262, 0, 0, 0, 0, 1783, 2268, 0, 2270, - 0, 2277, 2278, 2279, 2280, 2281, 2282, 1050, 0, 1050, - 1050, 1050, 1050, 1050, 0, 0, 0, 0, 0, 0, - 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 0, 2303, - 2304, 2305, 2306, 2307, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3950, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1050, 0, - 3966, 0, 0, 0, 0, 0, 3967, 3968, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2345, 2346, 0, 0, 0, 0, 0, 0, 3979, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2384, 0, 0, 0, - 0, 0, 0, 0, 4005, 4006, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 190, 0, 0, 4013, 4015, - 4017, 0, 1771, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, - 0, 151, 0, 4045, 0, 0, 0, 0, 0, 0, - 0, 0, 172, 0, 0, 0, 0, 2426, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1199, 0, 1205, 0, 0, 0, - 0, 4064, 0, 162, 0, 0, 0, 0, 0, 150, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 195, 0, 195, 0, 1784, 0, 169, 0, - 0, 170, 0, 0, 0, 4087, 4089, 4091, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 138, 139, 161, 160, 189, 0, 1427, 0, 0, 0, - 0, 720, 0, 720, 720, 0, 0, 0, 4112, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 720, 195, 0, 4124, 4125, 1797, 1800, - 1801, 1802, 1803, 1804, 1805, 0, 1806, 1807, 1809, 1810, - 1808, 1811, 1812, 1785, 1786, 1787, 1788, 1769, 1770, 1798, - 0, 1772, 1498, 1773, 1774, 1775, 1776, 1777, 1778, 1779, - 1780, 1781, 0, 0, 1782, 1789, 1790, 1791, 1792, 0, - 1793, 1794, 1795, 1796, 957, 0, 2276, 0, 0, 958, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2083, - 0, 0, 0, 0, 0, 155, 136, 158, 143, 135, - 0, 156, 157, 0, 0, 0, 0, 0, 173, 0, - 0, 0, 0, 0, 0, 0, 0, 179, 144, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 147, 145, 140, 141, 142, 146, 0, 0, - 0, 0, 0, 0, 137, 0, 0, 0, 0, 0, - 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 964, 965, 966, 967, 968, 969, - 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, - 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, - 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, - 1000, 1001, 1002, 1003, 1004, 1005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2595, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2601, 2602, 2603, 2604, 0, 0, 0, + 0, 3287, 3288, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1504, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1498, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2613, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2619, 2620, 2621, 2622, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1511, 0, 0, 1799, + 0, 0, 0, 0, 195, 913, 0, 0, 722, 722, + 0, 917, 0, 0, 0, 914, 915, 0, 0, 0, + 916, 918, 0, 0, 0, 1518, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 0, + 0, 195, 0, 0, 0, 0, 0, 159, 0, 0, + 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, - 0, 0, 0, 720, 720, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1744, 0, 0, - 0, 0, 195, 0, 0, 0, 0, 159, 0, 0, - 0, 0, 0, 0, 0, 0, 1761, 0, 0, 0, - 0, 0, 720, 0, 0, 195, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 720, 0, 0, - 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 722, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1504, 0, 0, 0, 0, 0, 722, 722, 0, 722, + 0, 722, 722, 0, 722, 722, 722, 722, 722, 722, + 0, 0, 0, 0, 0, 0, 0, 1504, 0, 0, + 1504, 722, 1504, 195, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 152, 0, 0, 153, + 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 722, 0, 195, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, + 1745, 0, 722, 0, 195, 195, 177, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 195, 0, 0, 0, 0, 0, 0, 195, 0, + 0, 0, 0, 0, 0, 0, 0, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 722, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 720, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1498, 0, 0, 0, 0, 1900, - 720, 720, 0, 720, 0, 720, 720, 0, 720, 720, - 720, 720, 720, 720, 0, 152, 0, 0, 153, 1732, - 0, 1498, 0, 0, 1498, 720, 1498, 195, 0, 0, - 0, 0, 0, 0, 1945, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 195, 165, 0, - 0, 1971, 0, 0, 0, 177, 0, 0, 0, 0, - 720, 0, 195, 0, 0, 0, 0, 1982, 0, 0, - 0, 0, 0, 0, 1986, 0, 720, 0, 195, 195, - 0, 0, 0, 0, 0, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 0, 0, 0, 195, 185, 0, 0, 0, - 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, - 0, 195, 195, 195, 195, 195, 195, 195, 195, 195, - 720, 0, 0, 0, 0, 0, 0, 0, 939, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, - 171, 168, 174, 175, 176, 178, 180, 181, 182, 183, - 0, 0, 0, 0, 0, 184, 186, 187, 188, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 0, 0, 0, 0, 0, 719, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2974, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 719, 0, - 719, 0, 0, 0, 0, 0, 0, 0, 0, 1050, - 0, 0, 3001, 3002, 0, 0, 3004, 0, 0, 3006, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2033, 0, 0, 0, 0, 0, 720, 720, 0, 3013, - 3014, 3015, 0, 0, 0, 0, 0, 0, 0, 720, - 0, 3020, 0, 0, 3022, 3023, 3024, 0, 195, 0, - 3025, 3026, 0, 0, 3027, 0, 3028, 0, 0, 0, - 0, 0, 0, 3029, 0, 3030, 0, 0, 0, 3031, - 0, 3032, 0, 0, 3033, 0, 3034, 0, 3035, 0, - 3036, 0, 3037, 0, 3038, 0, 3039, 0, 3040, 0, - 3041, 0, 3042, 0, 3043, 0, 3044, 720, 3045, 0, - 3046, 0, 3047, 0, 3048, 0, 3049, 1498, 3050, 0, - 0, 0, 3051, 0, 3052, 0, 3053, 0, 0, 3054, - 0, 3055, 0, 3056, 1498, 2240, 3058, 0, 0, 3060, - 0, 0, 3062, 3063, 3064, 3065, 0, 0, 0, 0, - 3066, 2240, 2240, 2240, 2240, 2240, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3076, 0, 0, 0, - 0, 0, 0, 0, 3089, 0, 0, 3093, 0, 1050, - 0, 0, 0, 0, 0, 0, 3096, 3097, 3098, 3099, - 3100, 3101, 0, 0, 0, 3102, 3103, 0, 3104, 0, - 3105, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3136, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2288, 0, - 3166, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 957, 0, 0, 0, 0, 958, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2083, 0, - 0, 0, 195, 0, 0, 0, 0, 720, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2351, 0, 0, - 0, 3229, 0, 0, 0, 2355, 0, 2358, 0, 0, - 2033, 0, 195, 0, 0, 720, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 195, 0, 0, 0, 720, - 0, 0, 2288, 195, 0, 195, 0, 195, 195, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 720, 964, 965, 966, 967, 968, 969, 970, - 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, - 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, - 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, - 1001, 1002, 1003, 1004, 1005, 0, 0, 3321, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 720, - 0, 3330, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 720, 0, 0, 0, - 0, 0, 720, 0, 0, 190, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1837, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, - 0, 151, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 172, 0, 0, 0, 0, 0, 0, 720, - 0, 0, 0, 0, 720, 0, 0, 0, 720, 720, - 0, 0, 0, 0, 0, 0, 0, 0, 2033, 0, - 0, 0, 0, 162, 0, 2516, 0, 0, 0, 150, - 0, 0, 0, 0, 2533, 2534, 0, 0, 2538, 0, - 0, 0, 0, 0, 0, 0, 195, 0, 169, 0, - 2543, 170, 0, 195, 0, 0, 0, 2546, 719, 1414, - 719, 719, 195, 195, 0, 0, 195, 0, 195, 0, - 1841, 1842, 161, 160, 189, 0, 0, 0, 195, 0, - 719, 0, 0, 2549, 0, 195, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1497, - 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, - 720, 0, 0, 0, 0, 0, 0, 0, 3525, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3549, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 155, 1843, 158, 0, 1840, - 0, 156, 157, 0, 0, 0, 0, 0, 173, 1498, - 0, 2288, 0, 0, 0, 0, 0, 179, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3569, - 0, 3570, 0, 0, 3571, 0, 0, 3574, 3575, 0, - 0, 0, 0, 0, 0, 0, 3579, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 897, 0, 0, - 3580, 0, 3581, 0, 3582, 0, 3583, 0, 3584, 0, - 3585, 0, 3586, 0, 3587, 0, 3588, 0, 3589, 0, - 3590, 0, 3591, 0, 3592, 0, 3593, 0, 3594, 0, - 3595, 0, 0, 3596, 0, 0, 0, 3597, 0, 3598, - 0, 0, 0, 0, 0, 3600, 0, 0, 0, 0, - 0, 0, 0, 193, 0, 0, 664, 0, 0, 1497, - 0, 0, 0, 0, 0, 0, 0, 0, 3617, 0, - 0, 164, 0, 0, 0, 0, 664, 3622, 0, 3623, - 3624, 0, 3625, 0, 3626, 0, 0, 0, 0, 3627, - 0, 0, 1032, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1051, - 1051, 0, 0, 0, 3652, 0, 0, 0, 664, 0, - 719, 719, 0, 0, 0, 3660, 0, 0, 3662, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3666, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3800, 0, 0, 719, - 0, 0, 0, 195, 0, 0, 0, 159, 0, 0, - 0, 195, 0, 0, 719, 0, 0, 0, 0, 0, - 0, 0, 720, 0, 0, 1814, 0, 0, 0, 0, - 0, 0, 0, 720, 2849, 1823, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 195, 719, 0, - 1849, 0, 195, 0, 0, 0, 0, 0, 1858, 0, - 0, 1497, 1860, 0, 0, 1863, 1864, 719, 719, 0, - 719, 0, 719, 719, 0, 719, 719, 719, 719, 719, - 719, 0, 0, 0, 0, 0, 0, 0, 1497, 1895, - 1896, 1497, 719, 1497, 0, 1901, 0, 0, 2898, 0, - 0, 0, 0, 0, 896, 152, 0, 0, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3908, - 720, 0, 0, 0, 0, 0, 195, 719, 0, 0, - 0, 0, 0, 195, 0, 0, 0, 0, 165, 0, - 1963, 0, 0, 719, 0, 177, 0, 720, 0, 0, - 0, 0, 0, 0, 720, 0, 0, 0, 0, 0, - 0, 0, 0, 720, 2947, 2948, 2949, 2950, 2951, 2952, - 0, 0, 718, 0, 0, 0, 0, 0, 0, 1498, - 0, 0, 0, 0, 0, 0, 185, 719, 0, 2033, - 2962, 0, 195, 195, 195, 195, 195, 195, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2970, 0, 0, 195, 195, 0, - 0, 0, 0, 0, 1075, 0, 1082, 0, 0, 166, - 171, 168, 174, 175, 176, 178, 180, 181, 182, 183, - 0, 0, 195, 0, 0, 184, 186, 187, 188, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 720, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 720, 0, 0, 0, 0, 0, 0, - 0, 0, 3948, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 719, 719, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 719, 0, 3962, 0, - 0, 3963, 0, 3964, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 719, 0, 0, 0, 720, 0, - 0, 0, 0, 0, 1497, 0, 0, 0, 0, 0, - 720, 0, 0, 2092, 0, 0, 0, 0, 0, 0, - 0, 1497, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4043, 0, 0, - 0, 720, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 195, 0, 0, 664, 720, - 664, 0, 0, 0, 0, 0, 4059, 0, 4060, 0, - 4061, 0, 0, 720, 0, 0, 0, 1498, 0, 0, - 720, 720, 1498, 195, 195, 195, 195, 195, 0, 0, + 166, 171, 168, 174, 175, 176, 178, 180, 181, 182, + 183, 0, 0, 0, 0, 0, 184, 186, 187, 188, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 701, 0, 0, 0, 0, + 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 721, 0, 721, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 722, 0, 0, 0, 2991, 0, + 0, 0, 0, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, - 0, 195, 0, 195, 0, 0, 195, 195, 195, 0, + 1055, 0, 0, 3018, 3019, 3020, 0, 0, 3022, 0, + 0, 3024, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3031, 3032, 3033, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3038, 722, 0, 3040, 3041, 3042, 0, + 0, 0, 3043, 3044, 1504, 0, 3045, 0, 3046, 0, + 0, 0, 0, 0, 0, 3047, 0, 3048, 0, 0, + 0, 3049, 1504, 3050, 0, 0, 3051, 0, 3052, 0, + 3053, 0, 3054, 0, 3055, 0, 3056, 0, 3057, 0, + 3058, 0, 3059, 0, 3060, 0, 3061, 0, 3062, 0, + 3063, 0, 3064, 0, 3065, 0, 3066, 0, 3067, 0, + 3068, 0, 0, 0, 3069, 0, 3070, 0, 3071, 0, + 0, 3072, 0, 3073, 0, 3074, 0, 2255, 3076, 0, + 0, 3078, 0, 0, 3080, 3081, 3082, 3083, 0, 0, + 0, 0, 3084, 2255, 2255, 2255, 2255, 2255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3094, 0, + 0, 0, 0, 0, 0, 0, 3107, 0, 0, 3111, + 0, 1055, 0, 0, 0, 0, 0, 0, 3114, 3115, + 3116, 3117, 3118, 3119, 0, 0, 0, 3120, 3121, 0, + 3122, 0, 3123, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2303, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3155, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 195, 0, 0, 0, 0, 722, 0, 0, 0, + 0, 3185, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 195, 0, 0, 722, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, + 0, 0, 0, 722, 0, 0, 2303, 195, 0, 195, + 0, 195, 195, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3248, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 664, 0, 0, 0, 3219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4110, 0, 4111, 0, 0, 0, 0, 0, 1499, 0, - 3257, 0, 195, 0, 0, 719, 0, 0, 0, 0, - 0, 0, 0, 0, 3271, 720, 0, 0, 1498, 0, - 0, 0, 0, 720, 0, 0, 0, 0, 195, 0, - 0, 0, 0, 0, 3289, 0, 0, 3292, 0, 0, - 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 719, 0, 0, 0, 0, 0, - 0, 0, 195, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 719, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 719, 0, 0, 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 722, 0, 0, 0, 0, 0, 722, 0, 0, 3341, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3350, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 0, 0, 0, 0, 722, 0, + 0, 0, 722, 722, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 195, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 195, 195, 0, + 0, 195, 0, 195, 0, 721, 1420, 721, 721, 0, + 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 721, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 195, 0, 0, 0, + 0, 0, 0, 0, 0, 722, 1503, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 719, 0, 0, 720, - 0, 0, 0, 2448, 2449, 2450, 3444, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1499, 0, - 0, 0, 0, 719, 0, 0, 0, 0, 0, 719, - 1858, 0, 0, 1858, 195, 1858, 0, 0, 0, 0, - 0, 2480, 0, 0, 1262, 0, 1262, 1262, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1426, 0, 0, 0, - 0, 0, 0, 0, 0, 664, 719, 0, 0, 0, - 0, 719, 0, 0, 0, 719, 719, 0, 0, 0, - 195, 3503, 0, 0, 0, 0, 0, 0, 1032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3518, 0, 0, 3519, 3520, 3521, 0, 0, 195, - 0, 664, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, - 664, 0, 195, 195, 195, 0, 0, 0, 0, 0, - 0, 0, 720, 720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3547, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1504, 0, 2303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1499, 0, 0, 0, 0, 0, 0, 719, 0, 0, - 0, 720, 720, 720, 720, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1499, 0, 0, - 1499, 0, 1499, 664, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3571, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1917, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 0, - 0, 0, 0, 0, 0, 0, 1497, 0, 719, 0, - 0, 0, 0, 0, 1969, 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 664, 0, 0, 0, 0, 0, 0, 664, 0, - 0, 0, 0, 0, 0, 0, 0, 1995, 1996, 664, - 664, 664, 664, 664, 664, 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1693, 1694, 0, 0, + 0, 0, 3591, 0, 3592, 0, 3593, 0, 3594, 0, + 0, 3597, 3598, 0, 0, 0, 0, 0, 0, 0, + 3602, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3603, 0, 3604, 0, 3605, 0, + 3606, 0, 3607, 0, 3608, 0, 3609, 0, 3610, 0, + 3611, 0, 3612, 0, 3613, 0, 3614, 1503, 3615, 0, + 3616, 0, 3617, 0, 3618, 0, 0, 3619, 0, 0, + 0, 3620, 0, 3621, 0, 0, 0, 0, 0, 3623, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3640, 0, 0, 0, 0, 0, 0, 0, + 0, 3645, 0, 3646, 3647, 0, 3648, 0, 3649, 721, + 721, 0, 0, 3650, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, + 3679, 0, 0, 0, 0, 0, 0, 195, 0, 721, + 0, 3687, 0, 0, 3689, 0, 0, 0, 722, 0, + 0, 0, 0, 0, 721, 0, 3693, 0, 0, 0, + 0, 722, 0, 0, 0, 1827, 0, 0, 0, 0, + 0, 0, 3827, 0, 0, 1836, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 0, 721, 0, + 1862, 0, 0, 0, 0, 0, 0, 0, 1871, 0, + 0, 1503, 1873, 0, 0, 1876, 1877, 721, 721, 0, + 721, 0, 721, 721, 0, 721, 721, 721, 721, 721, + 721, 0, 0, 0, 0, 0, 0, 0, 1503, 1908, + 1909, 1503, 721, 1503, 0, 1914, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, + 0, 195, 0, 0, 0, 0, 0, 721, 0, 0, + 0, 0, 0, 0, 0, 722, 0, 0, 0, 0, + 1977, 0, 722, 721, 0, 0, 0, 0, 0, 0, + 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, + 3937, 0, 0, 0, 0, 0, 0, 1504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1498, 0, 0, 0, 0, 720, 0, - 720, 0, 0, 0, 0, 1738, 0, 0, 0, 0, + 195, 195, 195, 195, 195, 195, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1756, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 195, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 900, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 720, 0, - 0, 0, 0, 0, 1075, 0, 0, 0, 0, 0, - 0, 195, 0, 0, 720, 0, 0, 0, 0, 0, - 0, 0, 0, 1866, 1866, 0, 1866, 720, 1866, 1866, - 0, 1875, 1866, 1866, 1866, 1866, 1866, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1075, 0, - 0, 0, 0, 0, 664, 0, 0, 0, 0, 719, + 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1943, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1967, - 720, 0, 0, 0, 720, 720, 0, 0, 0, 0, - 0, 0, 0, 1499, 0, 2862, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1051, 1051, 0, 0, 0, - 1499, 0, 0, 720, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1262, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 193, 0, + 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 719, 0, 0, + 0, 666, 722, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1036, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1056, 1056, 0, 0, 0, 0, + 0, 0, 0, 666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 721, 0, 0, 0, 0, + 0, 0, 0, 0, 3977, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 719, 0, 0, 0, 0, 0, - 0, 719, 0, 0, 0, 1858, 1858, 0, 0, 0, - 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1497, 2935, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1051, 1969, 1051, 1051, 1051, 1051, 1051, - 0, 720, 0, 0, 0, 0, 0, 0, 3946, 0, - 0, 0, 0, 0, 0, 0, 0, 195, 0, 0, + 3991, 0, 0, 3992, 0, 3993, 0, 722, 0, 0, + 0, 0, 0, 0, 0, 721, 0, 0, 899, 722, + 0, 0, 0, 0, 0, 1503, 0, 0, 0, 0, + 0, 0, 0, 0, 2106, 0, 0, 0, 0, 0, + 0, 0, 0, 1503, 0, 0, 0, 0, 0, 0, + 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 720, 195, 0, 1917, 1262, - 1262, 0, 719, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2021, 0, 1051, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1032, 0, + 0, 722, 0, 0, 0, 1504, 720, 0, 722, 722, + 1504, 195, 195, 195, 195, 195, 0, 0, 0, 0, + 0, 4071, 0, 195, 0, 0, 0, 0, 0, 195, + 0, 195, 0, 0, 195, 195, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 664, 0, 0, 0, 0, 0, 0, 1969, 664, - 719, 664, 0, 664, 2374, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 720, - 2078, 0, 0, 0, 0, 0, 0, 0, 0, 1498, - 0, 720, 0, 0, 0, 0, 0, 0, 0, 0, + 4087, 0, 4088, 0, 4089, 0, 0, 0, 1080, 0, + 1087, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 720, 2288, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 0, 0, 1504, 721, 0, 0, + 0, 722, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 720, 0, 0, + 195, 0, 4138, 0, 4139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 195, 720, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 195, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 719, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 719, 0, 0, - 0, 0, 0, 0, 720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 720, 0, 719, 0, - 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 720, 719, 720, 0, 0, - 0, 1262, 0, 0, 0, 0, 0, 0, 0, 0, - 719, 0, 0, 0, 1497, 0, 0, 719, 719, 1497, - 0, 0, 664, 0, 0, 0, 0, 0, 0, 664, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 664, - 0, 0, 664, 0, 2540, 0, 0, 0, 0, 0, - 2325, 0, 0, 0, 664, 0, 0, 0, 0, 0, - 0, 664, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2337, 0, - 3214, 0, 0, 0, 0, 0, 0, 664, 0, 0, - 0, 0, 1738, 0, 0, 1262, 0, 0, 0, 0, - 0, 0, 719, 0, 0, 1497, 0, 0, 0, 0, - 719, 0, 0, 95, 0, 1075, 957, 0, 0, 0, - 945, 958, 959, 960, 961, 946, 0, 0, 947, 948, - 0, 949, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 954, 962, 963, 0, 0, + 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3296, 0, 0, 1499, 0, 1969, 0, 0, - 0, 0, 1082, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3265, 3266, 0, 0, 0, 1075, - 0, 0, 0, 0, 0, 1082, 964, 965, 966, 967, - 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, - 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, - 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, - 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 0, 0, - 0, 0, 1075, 0, 0, 0, 0, 2078, 0, 0, - 0, 2078, 2078, 0, 0, 0, 719, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, + 0, 2465, 2466, 2467, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, + 0, 721, 0, 0, 0, 0, 0, 721, 1871, 0, + 0, 1871, 0, 1871, 0, 0, 0, 0, 0, 2497, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 195, 0, 0, 0, 721, 0, 0, 0, 0, 721, + 0, 0, 0, 721, 721, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3268, 3269, 0, 0, - 0, 0, 0, 2552, 0, 3493, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 664, - 0, 0, 0, 0, 0, 0, 0, 1917, 0, 0, + 0, 0, 0, 0, 0, 666, 0, 666, 0, 195, + 0, 0, 195, 195, 195, 0, 0, 0, 0, 0, + 0, 0, 722, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 719, - 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 664, 1262, 0, 0, 0, 664, 0, - 910, 0, 0, 0, 0, 0, 914, 0, 0, 0, - 911, 912, 0, 0, 0, 913, 915, 0, 719, 719, - 719, 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 666, 0, 0, + 0, 722, 722, 722, 722, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, + 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 664, 0, 0, 0, 0, 0, 0, 2904, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1503, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1499, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 664, - 664, 664, 664, 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 664, 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1497, 0, 0, 0, 0, 719, 0, 719, 0, 0, - 0, 0, 0, 0, 0, 1051, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2797, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2812, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 719, 0, 0, 0, 0, + 0, 0, 0, 722, 0, 722, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 719, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 719, 0, 0, 0, 0, 0, + 1268, 0, 1268, 1268, 0, 0, 1504, 0, 0, 0, + 0, 722, 0, 722, 0, 0, 0, 0, 0, 0, + 0, 0, 1432, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 0, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2894, 0, 0, 0, 0, 0, 0, + 722, 0, 0, 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 719, 0, 0, - 2337, 719, 719, 0, 0, 0, 0, 2919, 0, 0, - 0, 0, 0, 0, 0, 1051, 2924, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1036, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 719, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 721, + 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, + 666, 0, 0, 0, 722, 0, 0, 0, 0, 0, + 0, 722, 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2879, 0, 0, + 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, + 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 664, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1505, 0, 0, + 1505, 0, 1505, 666, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1499, 0, 0, 0, 0, 1499, 664, - 664, 664, 664, 664, 0, 0, 0, 0, 0, 0, - 0, 3164, 0, 0, 0, 0, 0, 1917, 0, 664, - 0, 0, 664, 3172, 1969, 0, 0, 0, 2078, 0, + 0, 0, 0, 1930, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 721, 0, 666, 0, + 0, 0, 0, 721, 0, 0, 0, 1871, 1871, 0, + 0, 0, 721, 0, 1983, 666, 0, 0, 0, 0, + 0, 0, 0, 0, 1701, 1702, 0, 0, 1503, 2952, + 0, 666, 0, 0, 0, 0, 0, 0, 666, 0, + 0, 0, 0, 0, 0, 0, 0, 2009, 2010, 666, + 666, 666, 666, 666, 666, 666, 0, 0, 0, 722, + 0, 0, 0, 0, 1751, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 0, 0, 0, 1769, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 722, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3688, 3690, 3689, 3753, 3754, 3755, 3756, 3757, - 3758, 3759, 789, 0, 0, 0, 0, 0, 664, 0, - 0, 0, 0, 0, 0, 0, 2078, 0, 719, 0, - 0, 0, 0, 0, 1499, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 664, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 0, - 0, 0, 719, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 664, 0, - 0, 664, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, + 0, 0, 0, 1080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1879, 1879, 0, 1879, 0, 1879, 1879, 0, + 1888, 1879, 1879, 1879, 1879, 1879, 0, 0, 0, 722, + 0, 0, 0, 721, 0, 0, 0, 1080, 0, 722, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1504, + 0, 722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1956, 0, 0, 722, 2303, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 719, 0, 0, 0, - 0, 3078, 0, 0, 0, 0, 1497, 0, 719, 0, - 0, 0, 0, 1262, 0, 0, 0, 0, 0, 0, + 0, 0, 195, 722, 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 719, 719, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1866, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 719, 0, 0, 0, 0, 0, - 0, 0, 3121, 0, 0, 0, 0, 0, 0, 719, - 0, 0, 0, 0, 0, 0, 1262, 0, 0, 0, - 0, 0, 0, 3148, 1866, 0, 0, 0, 0, 0, - 664, 0, 0, 0, 0, 0, 0, 0, 3694, 0, + 0, 0, 1268, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 721, 0, + 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, + 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1505, 722, 0, 0, 0, 0, + 195, 0, 0, 0, 0, 0, 1056, 1056, 0, 0, + 0, 721, 1505, 0, 722, 0, 722, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 719, 0, 3702, 3703, 0, 0, 3778, 3777, 3776, - 0, 0, 3774, 3775, 3773, 0, 0, 0, 0, 0, - 0, 0, 0, 719, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 664, 0, 0, 0, - 0, 0, 719, 0, 719, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1075, 0, - 0, 0, 0, 0, 0, 664, 2337, 3779, 910, 0, - 765, 766, 3780, 3781, 914, 3782, 768, 769, 911, 912, - 0, 763, 767, 913, 915, 664, 0, 0, 664, 664, - 664, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 721, 0, 0, 0, 1503, 0, 0, 721, + 721, 1503, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3685, - 3686, 3687, 3691, 3692, 3693, 3704, 3751, 3752, 3760, 3762, - 866, 3761, 3763, 3764, 3765, 3768, 3769, 3770, 3771, 3766, - 3767, 3772, 3668, 3672, 3669, 3670, 3671, 3683, 3673, 3674, - 3675, 3676, 3677, 3678, 3679, 3680, 3681, 3682, 3684, 3783, - 3784, 3785, 3786, 3787, 3788, 3697, 3701, 3700, 3698, 3699, - 3695, 3696, 3723, 3722, 3724, 3725, 3726, 3727, 3728, 3729, - 3731, 3730, 3732, 3733, 3734, 3735, 3736, 3737, 3705, 3706, - 3709, 3710, 3708, 3707, 3711, 3720, 3721, 3712, 3713, 3714, - 3715, 3716, 3717, 3719, 3718, 3738, 3739, 3740, 3741, 3742, - 3744, 3743, 3747, 3748, 3746, 3745, 3750, 3749, 0, 0, - 0, 0, 3409, 0, 0, 0, 0, 0, 0, 0, - 916, 0, 917, 0, 0, 921, 0, 0, 0, 923, - 922, 0, 924, 886, 885, 0, 0, 918, 919, 0, - 920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3233, 0, 0, 0, 0, 0, 0, 1268, + 1268, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2035, 0, 721, 0, 0, 1503, 0, 0, + 0, 0, 721, 0, 0, 1056, 1983, 1056, 1056, 1056, + 1056, 1056, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1917, 0, 0, 0, 0, 3789, 3790, 3791, 3792, 3793, - 3794, 3795, 3796, 0, 0, 0, 0, 0, 0, 1499, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2092, 1930, 0, 0, 3315, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1056, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1036, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 666, + 0, 0, 0, 0, 0, 0, 1983, 666, 0, 666, + 0, 666, 2391, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2337, 2337, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1917, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3557, 3558, 3559, 3560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2340, 0, 0, 0, 0, 0, 0, 3515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1751, + 666, 0, 1268, 721, 721, 0, 0, 666, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 666, 666, 0, + 0, 666, 1080, 2559, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 666, 0, 0, 0, 0, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 721, 721, 721, 721, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 666, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1087, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1080, 0, 0, 0, + 0, 0, 1087, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1505, 0, 1983, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1080, + 0, 0, 0, 0, 2092, 0, 0, 0, 2092, 2092, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3656, 0, 3658, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1503, 0, 0, + 0, 0, 721, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2571, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2337, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1917, 0, 0, 0, 3823, 0, 0, + 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1262, 0, 664, 0, 0, 0, 0, 0, 0, 0, + 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1268, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 666, + 0, 0, 0, 0, 0, 0, 0, 1930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, + 0, 0, 721, 0, 721, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 666, 0, 0, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3896, 0, 0, 0, 3896, 3896, 0, - 0, 0, 0, 0, 0, 1499, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2337, 0, 0, 0, - 0, 0, 3999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 666, 0, 0, 0, 0, 0, + 0, 2921, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1917, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 666, 666, 666, 666, 666, 666, 0, 0, 0, 0, + 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2815, 0, 0, 666, 666, 0, + 0, 0, 0, 0, 0, 0, 0, 2829, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1056, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1969, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1503, 0, 721, 0, 2911, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 721, 721, 0, 0, + 0, 2352, 0, 0, 0, 0, 0, 0, 2936, 0, + 0, 0, 0, 0, 0, 0, 0, 2941, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2337, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1056, 0, + 0, 0, 0, 0, 0, 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 721, 0, 721, 0, 2092, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, + 1505, 666, 666, 666, 666, 666, 0, 0, 2092, 0, + 0, 0, 0, 3183, 0, 0, 0, 0, 0, 1930, + 0, 666, 0, 0, 666, 3191, 1983, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3973, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3977, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1262, 1262, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1505, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 666, 3715, 3717, 3716, + 3780, 3781, 3782, 3783, 3784, 3785, 3786, 792, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3096, 0, 0, 0, 0, 0, 0, + 666, 0, 0, 666, 0, 1268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4019, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1879, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1268, 0, 0, + 0, 0, 0, 0, 3167, 1879, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3973, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2337, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 392, 3409, 0, - 4027, 1397, 1383, 520, 0, 1325, 1400, 1294, 1313, 1410, - 1316, 1319, 1362, 1272, 1340, 411, 1310, 1265, 1298, 1267, - 1305, 1268, 1296, 1327, 269, 1293, 1385, 1344, 1399, 362, - 266, 1274, 1299, 425, 1315, 203, 1364, 481, 251, 373, - 370, 575, 281, 272, 268, 249, 315, 381, 423, 510, - 417, 1406, 366, 1350, 0, 491, 396, 0, 0, 0, - 1329, 1389, 1338, 1376, 1324, 1363, 1282, 1349, 1401, 1311, - 1359, 1402, 321, 247, 323, 202, 408, 492, 285, 0, - 0, 0, 0, 4001, 941, 0, 0, 0, 0, 4002, - 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, - 0, 347, 356, 355, 336, 337, 339, 341, 346, 353, - 359, 1307, 1356, 1396, 1308, 1358, 264, 319, 271, 263, - 572, 1407, 1388, 1271, 1337, 1395, 1332, 0, 0, 228, - 1398, 1331, 0, 1361, 0, 1413, 1266, 1352, 0, 1269, - 1273, 1409, 1393, 1302, 274, 0, 0, 0, 0, 0, - 0, 0, 1328, 1339, 1373, 1377, 1322, 0, 0, 0, - 0, 0, 0, 0, 0, 1300, 0, 1348, 0, 0, - 0, 1278, 1270, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1326, 0, 0, 0, 0, - 1281, 0, 1301, 1374, 0, 1264, 296, 1275, 397, 256, - 0, 448, 1381, 1392, 1323, 616, 1394, 1321, 1320, 1368, - 1279, 1387, 1314, 361, 1277, 328, 197, 224, 0, 1312, - 407, 456, 468, 1386, 1297, 1306, 252, 1304, 466, 421, - 594, 232, 283, 453, 427, 464, 435, 286, 1347, 1366, - 465, 368, 577, 445, 591, 617, 618, 262, 401, 603, - 514, 611, 635, 225, 259, 415, 499, 597, 488, 393, - 573, 574, 327, 487, 294, 201, 365, 623, 223, 474, - 367, 241, 230, 579, 600, 288, 451, 630, 212, 509, - 589, 238, 478, 0, 0, 638, 246, 498, 214, 586, - 497, 389, 324, 325, 213, 0, 452, 267, 292, 0, - 0, 257, 410, 581, 582, 255, 639, 227, 610, 219, - 1276, 609, 403, 576, 587, 390, 379, 218, 585, 388, - 378, 332, 351, 352, 279, 305, 442, 371, 443, 304, - 306, 399, 398, 400, 206, 598, 0, 207, 0, 493, - 599, 640, 447, 211, 233, 234, 236, 1292, 278, 282, - 290, 293, 301, 302, 311, 363, 414, 441, 437, 446, - 1382, 571, 592, 604, 615, 621, 622, 624, 625, 626, - 627, 628, 631, 629, 402, 309, 489, 331, 369, 1371, - 1412, 420, 467, 239, 596, 490, 199, 1286, 1291, 1284, - 0, 253, 254, 1353, 567, 1287, 1285, 1342, 1343, 1288, - 1403, 1404, 1405, 1390, 641, 642, 643, 644, 645, 646, - 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, - 657, 658, 636, 500, 506, 501, 502, 503, 504, 505, - 0, 507, 1375, 1280, 0, 1289, 1290, 1384, 583, 584, - 659, 380, 480, 593, 333, 345, 348, 338, 357, 0, - 358, 334, 335, 340, 342, 343, 344, 349, 350, 354, - 360, 248, 209, 386, 394, 570, 310, 215, 216, 217, - 516, 517, 518, 519, 607, 608, 612, 204, 457, 458, - 459, 460, 291, 602, 307, 463, 462, 329, 330, 375, - 444, 532, 534, 545, 549, 551, 553, 559, 562, 533, - 535, 546, 550, 552, 554, 560, 563, 522, 524, 526, - 528, 541, 540, 537, 565, 566, 543, 548, 527, 539, - 544, 557, 564, 561, 521, 525, 529, 538, 556, 555, - 536, 547, 558, 542, 530, 523, 531, 1346, 196, 220, - 364, 1408, 449, 287, 637, 606, 601, 205, 222, 1283, - 261, 1295, 1303, 0, 1309, 1317, 1318, 1330, 1333, 1334, - 1335, 1336, 1354, 1355, 1357, 1365, 1367, 1370, 1372, 1379, - 1391, 1411, 198, 200, 208, 221, 231, 235, 242, 260, - 275, 277, 284, 297, 308, 316, 317, 320, 326, 376, - 382, 383, 384, 385, 404, 405, 406, 409, 412, 413, - 416, 418, 419, 422, 426, 430, 431, 432, 434, 436, - 438, 450, 455, 469, 470, 471, 472, 473, 476, 477, - 482, 483, 484, 485, 486, 494, 495, 508, 578, 580, - 595, 613, 619, 475, 299, 300, 439, 440, 312, 313, - 633, 634, 298, 590, 620, 588, 632, 614, 433, 374, - 1345, 1351, 377, 280, 303, 318, 1360, 605, 496, 226, - 461, 289, 250, 1378, 1380, 210, 245, 229, 258, 273, - 276, 322, 387, 395, 424, 429, 295, 270, 243, 454, - 240, 479, 511, 512, 513, 515, 391, 265, 428, 1341, - 1369, 372, 568, 569, 314, 392, 0, 0, 0, 1397, - 1383, 520, 0, 1325, 1400, 1294, 1313, 1410, 1316, 1319, - 1362, 1272, 1340, 411, 1310, 1265, 1298, 1267, 1305, 1268, - 1296, 1327, 269, 1293, 1385, 1344, 1399, 362, 266, 1274, - 1299, 425, 1315, 203, 1364, 481, 251, 373, 370, 575, - 281, 272, 268, 249, 315, 381, 423, 510, 417, 1406, - 366, 1350, 0, 491, 396, 0, 0, 0, 1329, 1389, - 1338, 1376, 1324, 1363, 1282, 1349, 1401, 1311, 1359, 1402, - 321, 247, 323, 202, 408, 492, 285, 0, 0, 0, - 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 237, 0, 0, 244, 0, 0, 0, 347, - 356, 355, 336, 337, 339, 341, 346, 353, 359, 1307, - 1356, 1396, 1308, 1358, 264, 319, 271, 263, 572, 1407, - 1388, 1271, 1337, 1395, 1332, 0, 0, 228, 1398, 1331, - 0, 1361, 0, 1413, 1266, 1352, 0, 1269, 1273, 1409, - 1393, 1302, 274, 0, 0, 0, 0, 0, 0, 0, - 1328, 1339, 1373, 1377, 1322, 0, 0, 0, 0, 0, - 0, 3173, 0, 1300, 0, 1348, 0, 0, 0, 1278, - 1270, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1326, 0, 0, 0, 0, 1281, 0, - 1301, 1374, 0, 1264, 296, 1275, 397, 256, 0, 448, - 1381, 1392, 1323, 616, 1394, 1321, 1320, 1368, 1279, 1387, - 1314, 361, 1277, 328, 197, 224, 0, 1312, 407, 456, - 468, 1386, 1297, 1306, 252, 1304, 466, 421, 594, 232, - 283, 453, 427, 464, 435, 286, 1347, 1366, 465, 368, - 577, 445, 591, 617, 618, 262, 401, 603, 514, 611, - 635, 225, 259, 415, 499, 597, 488, 393, 573, 574, - 327, 487, 294, 201, 365, 623, 223, 474, 367, 241, - 230, 579, 600, 288, 451, 630, 212, 509, 589, 238, - 478, 0, 0, 638, 246, 498, 214, 586, 497, 389, - 324, 325, 213, 0, 452, 267, 292, 0, 0, 257, - 410, 581, 582, 255, 639, 227, 610, 219, 1276, 609, - 403, 576, 587, 390, 379, 218, 585, 388, 378, 332, - 351, 352, 279, 305, 442, 371, 443, 304, 306, 399, - 398, 400, 206, 598, 0, 207, 0, 493, 599, 640, - 447, 211, 233, 234, 236, 1292, 278, 282, 290, 293, - 301, 302, 311, 363, 414, 441, 437, 446, 1382, 571, - 592, 604, 615, 621, 622, 624, 625, 626, 627, 628, - 631, 629, 402, 309, 489, 331, 369, 1371, 1412, 420, - 467, 239, 596, 490, 199, 1286, 1291, 1284, 0, 253, - 254, 1353, 567, 1287, 1285, 1342, 1343, 1288, 1403, 1404, - 1405, 1390, 641, 642, 643, 644, 645, 646, 647, 648, - 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, - 636, 500, 506, 501, 502, 503, 504, 505, 0, 507, - 1375, 1280, 0, 1289, 1290, 1384, 583, 584, 659, 380, - 480, 593, 333, 345, 348, 338, 357, 0, 358, 334, - 335, 340, 342, 343, 344, 349, 350, 354, 360, 248, - 209, 386, 394, 570, 310, 215, 216, 217, 516, 517, - 518, 519, 607, 608, 612, 204, 457, 458, 459, 460, - 291, 602, 307, 463, 462, 329, 330, 375, 444, 532, - 534, 545, 549, 551, 553, 559, 562, 533, 535, 546, - 550, 552, 554, 560, 563, 522, 524, 526, 528, 541, - 540, 537, 565, 566, 543, 548, 527, 539, 544, 557, - 564, 561, 521, 525, 529, 538, 556, 555, 536, 547, - 558, 542, 530, 523, 531, 1346, 196, 220, 364, 1408, - 449, 287, 637, 606, 601, 205, 222, 1283, 261, 1295, - 1303, 0, 1309, 1317, 1318, 1330, 1333, 1334, 1335, 1336, - 1354, 1355, 1357, 1365, 1367, 1370, 1372, 1379, 1391, 1411, - 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, - 284, 297, 308, 316, 317, 320, 326, 376, 382, 383, - 384, 385, 404, 405, 406, 409, 412, 413, 416, 418, - 419, 422, 426, 430, 431, 432, 434, 436, 438, 450, - 455, 469, 470, 471, 472, 473, 476, 477, 482, 483, - 484, 485, 486, 494, 495, 508, 578, 580, 595, 613, - 619, 475, 299, 300, 439, 440, 312, 313, 633, 634, - 298, 590, 620, 588, 632, 614, 433, 374, 1345, 1351, - 377, 280, 303, 318, 1360, 605, 496, 226, 461, 289, - 250, 1378, 1380, 210, 245, 229, 258, 273, 276, 322, - 387, 395, 424, 429, 295, 270, 243, 454, 240, 479, - 511, 512, 513, 515, 391, 265, 428, 1341, 1369, 372, - 568, 569, 314, 392, 0, 0, 0, 1397, 1383, 520, - 0, 1325, 1400, 1294, 1313, 1410, 1316, 1319, 1362, 1272, - 1340, 411, 1310, 1265, 1298, 1267, 1305, 1268, 1296, 1327, - 269, 1293, 1385, 1344, 1399, 362, 266, 1274, 1299, 425, - 1315, 203, 1364, 481, 251, 373, 370, 575, 281, 272, - 268, 249, 315, 381, 423, 510, 417, 1406, 366, 1350, - 0, 491, 396, 0, 0, 0, 1329, 1389, 1338, 1376, - 1324, 1363, 1282, 1349, 1401, 1311, 1359, 1402, 321, 247, - 323, 202, 408, 492, 285, 0, 0, 0, 0, 0, - 709, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 237, 0, 0, 244, 0, 0, 0, 347, 356, 355, - 336, 337, 339, 341, 346, 353, 359, 1307, 1356, 1396, - 1308, 1358, 264, 319, 271, 263, 572, 1407, 1388, 1271, - 1337, 1395, 1332, 0, 0, 228, 1398, 1331, 0, 1361, - 0, 1413, 1266, 1352, 0, 1269, 1273, 1409, 1393, 1302, - 274, 0, 0, 0, 0, 0, 0, 0, 1328, 1339, - 1373, 1377, 1322, 0, 0, 0, 0, 0, 0, 3134, - 0, 1300, 0, 1348, 0, 0, 0, 1278, 1270, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1326, 0, 0, 0, 0, 1281, 0, 1301, 1374, - 0, 1264, 296, 1275, 397, 256, 0, 448, 1381, 1392, - 1323, 616, 1394, 1321, 1320, 1368, 1279, 1387, 1314, 361, - 1277, 328, 197, 224, 0, 1312, 407, 456, 468, 1386, - 1297, 1306, 252, 1304, 466, 421, 594, 232, 283, 453, - 427, 464, 435, 286, 1347, 1366, 465, 368, 577, 445, - 591, 617, 618, 262, 401, 603, 514, 611, 635, 225, - 259, 415, 499, 597, 488, 393, 573, 574, 327, 487, - 294, 201, 365, 623, 223, 474, 367, 241, 230, 579, - 600, 288, 451, 630, 212, 509, 589, 238, 478, 0, - 0, 638, 246, 498, 214, 586, 497, 389, 324, 325, - 213, 0, 452, 267, 292, 0, 0, 257, 410, 581, - 582, 255, 639, 227, 610, 219, 1276, 609, 403, 576, - 587, 390, 379, 218, 585, 388, 378, 332, 351, 352, - 279, 305, 442, 371, 443, 304, 306, 399, 398, 400, - 206, 598, 0, 207, 0, 493, 599, 640, 447, 211, - 233, 234, 236, 1292, 278, 282, 290, 293, 301, 302, - 311, 363, 414, 441, 437, 446, 1382, 571, 592, 604, - 615, 621, 622, 624, 625, 626, 627, 628, 631, 629, - 402, 309, 489, 331, 369, 1371, 1412, 420, 467, 239, - 596, 490, 199, 1286, 1291, 1284, 0, 253, 254, 1353, - 567, 1287, 1285, 1342, 1343, 1288, 1403, 1404, 1405, 1390, - 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, - 651, 652, 653, 654, 655, 656, 657, 658, 636, 500, - 506, 501, 502, 503, 504, 505, 0, 507, 1375, 1280, - 0, 1289, 1290, 1384, 583, 584, 659, 380, 480, 593, - 333, 345, 348, 338, 357, 0, 358, 334, 335, 340, - 342, 343, 344, 349, 350, 354, 360, 248, 209, 386, - 394, 570, 310, 215, 216, 217, 516, 517, 518, 519, - 607, 608, 612, 204, 457, 458, 459, 460, 291, 602, - 307, 463, 462, 329, 330, 375, 444, 532, 534, 545, - 549, 551, 553, 559, 562, 533, 535, 546, 550, 552, - 554, 560, 563, 522, 524, 526, 528, 541, 540, 537, - 565, 566, 543, 548, 527, 539, 544, 557, 564, 561, - 521, 525, 529, 538, 556, 555, 536, 547, 558, 542, - 530, 523, 531, 1346, 196, 220, 364, 1408, 449, 287, - 637, 606, 601, 205, 222, 1283, 261, 1295, 1303, 0, - 1309, 1317, 1318, 1330, 1333, 1334, 1335, 1336, 1354, 1355, - 1357, 1365, 1367, 1370, 1372, 1379, 1391, 1411, 198, 200, - 208, 221, 231, 235, 242, 260, 275, 277, 284, 297, - 308, 316, 317, 320, 326, 376, 382, 383, 384, 385, - 404, 405, 406, 409, 412, 413, 416, 418, 419, 422, - 426, 430, 431, 432, 434, 436, 438, 450, 455, 469, - 470, 471, 472, 473, 476, 477, 482, 483, 484, 485, - 486, 494, 495, 508, 578, 580, 595, 613, 619, 475, - 299, 300, 439, 440, 312, 313, 633, 634, 298, 590, - 620, 588, 632, 614, 433, 374, 1345, 1351, 377, 280, - 303, 318, 1360, 605, 496, 226, 461, 289, 250, 1378, - 1380, 210, 245, 229, 258, 273, 276, 322, 387, 395, - 424, 429, 295, 270, 243, 454, 240, 479, 511, 512, - 513, 515, 391, 265, 428, 1341, 1369, 372, 568, 569, - 314, 392, 0, 0, 0, 1397, 1383, 520, 0, 1325, - 1400, 1294, 1313, 1410, 1316, 1319, 1362, 1272, 1340, 411, - 1310, 1265, 1298, 1267, 1305, 1268, 1296, 1327, 269, 1293, - 1385, 1344, 1399, 362, 266, 1274, 1299, 425, 1315, 203, - 1364, 481, 251, 373, 370, 575, 281, 272, 268, 249, - 315, 381, 423, 510, 417, 1406, 366, 1350, 0, 491, - 396, 0, 0, 0, 1329, 1389, 1338, 1376, 1324, 1363, - 1282, 1349, 1401, 1311, 1359, 1402, 321, 247, 323, 202, - 408, 492, 285, 0, 0, 0, 0, 0, 941, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, - 0, 244, 0, 0, 0, 347, 356, 355, 336, 337, - 339, 341, 346, 353, 359, 1307, 1356, 1396, 1308, 1358, - 264, 319, 271, 263, 572, 1407, 1388, 1271, 1337, 1395, - 1332, 0, 0, 228, 1398, 1331, 0, 1361, 0, 1413, - 1266, 1352, 0, 1269, 1273, 1409, 1393, 1302, 274, 0, - 0, 0, 0, 0, 0, 0, 1328, 1339, 1373, 1377, - 1322, 0, 0, 0, 0, 0, 0, 2353, 0, 1300, - 0, 1348, 0, 0, 0, 1278, 1270, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1326, - 0, 0, 0, 0, 1281, 0, 1301, 1374, 0, 1264, - 296, 1275, 397, 256, 0, 448, 1381, 1392, 1323, 616, - 1394, 1321, 1320, 1368, 1279, 1387, 1314, 361, 1277, 328, - 197, 224, 0, 1312, 407, 456, 468, 1386, 1297, 1306, - 252, 1304, 466, 421, 594, 232, 283, 453, 427, 464, - 435, 286, 1347, 1366, 465, 368, 577, 445, 591, 617, - 618, 262, 401, 603, 514, 611, 635, 225, 259, 415, - 499, 597, 488, 393, 573, 574, 327, 487, 294, 201, - 365, 623, 223, 474, 367, 241, 230, 579, 600, 288, - 451, 630, 212, 509, 589, 238, 478, 0, 0, 638, - 246, 498, 214, 586, 497, 389, 324, 325, 213, 0, - 452, 267, 292, 0, 0, 257, 410, 581, 582, 255, - 639, 227, 610, 219, 1276, 609, 403, 576, 587, 390, - 379, 218, 585, 388, 378, 332, 351, 352, 279, 305, - 442, 371, 443, 304, 306, 399, 398, 400, 206, 598, - 0, 207, 0, 493, 599, 640, 447, 211, 233, 234, - 236, 1292, 278, 282, 290, 293, 301, 302, 311, 363, - 414, 441, 437, 446, 1382, 571, 592, 604, 615, 621, - 622, 624, 625, 626, 627, 628, 631, 629, 402, 309, - 489, 331, 369, 1371, 1412, 420, 467, 239, 596, 490, - 199, 1286, 1291, 1284, 0, 253, 254, 1353, 567, 1287, - 1285, 1342, 1343, 1288, 1403, 1404, 1405, 1390, 641, 642, - 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, - 653, 654, 655, 656, 657, 658, 636, 500, 506, 501, - 502, 503, 504, 505, 0, 507, 1375, 1280, 0, 1289, - 1290, 1384, 583, 584, 659, 380, 480, 593, 333, 345, - 348, 338, 357, 0, 358, 334, 335, 340, 342, 343, - 344, 349, 350, 354, 360, 248, 209, 386, 394, 570, - 310, 215, 216, 217, 516, 517, 518, 519, 607, 608, - 612, 204, 457, 458, 459, 460, 291, 602, 307, 463, - 462, 329, 330, 375, 444, 532, 534, 545, 549, 551, - 553, 559, 562, 533, 535, 546, 550, 552, 554, 560, - 563, 522, 524, 526, 528, 541, 540, 537, 565, 566, - 543, 548, 527, 539, 544, 557, 564, 561, 521, 525, - 529, 538, 556, 555, 536, 547, 558, 542, 530, 523, - 531, 1346, 196, 220, 364, 1408, 449, 287, 637, 606, - 601, 205, 222, 1283, 261, 1295, 1303, 0, 1309, 1317, - 1318, 1330, 1333, 1334, 1335, 1336, 1354, 1355, 1357, 1365, - 1367, 1370, 1372, 1379, 1391, 1411, 198, 200, 208, 221, - 231, 235, 242, 260, 275, 277, 284, 297, 308, 316, - 317, 320, 326, 376, 382, 383, 384, 385, 404, 405, - 406, 409, 412, 413, 416, 418, 419, 422, 426, 430, - 431, 432, 434, 436, 438, 450, 455, 469, 470, 471, - 472, 473, 476, 477, 482, 483, 484, 485, 486, 494, - 495, 508, 578, 580, 595, 613, 619, 475, 299, 300, - 439, 440, 312, 313, 633, 634, 298, 590, 620, 588, - 632, 614, 433, 374, 1345, 1351, 377, 280, 303, 318, - 1360, 605, 496, 226, 461, 289, 250, 1378, 1380, 210, - 245, 229, 258, 273, 276, 322, 387, 395, 424, 429, - 295, 270, 243, 454, 240, 479, 511, 512, 513, 515, - 391, 265, 428, 1341, 1369, 372, 568, 569, 314, 392, - 0, 0, 0, 1397, 1383, 520, 0, 1325, 1400, 1294, - 1313, 1410, 1316, 1319, 1362, 1272, 1340, 411, 1310, 1265, - 1298, 1267, 1305, 1268, 1296, 1327, 269, 1293, 1385, 1344, - 1399, 362, 266, 1274, 1299, 425, 1315, 203, 1364, 481, - 251, 373, 370, 575, 281, 272, 268, 249, 315, 381, - 423, 510, 417, 1406, 366, 1350, 0, 491, 396, 0, - 0, 0, 1329, 1389, 1338, 1376, 1324, 1363, 1282, 1349, - 1401, 1311, 1359, 1402, 321, 247, 323, 202, 408, 492, - 285, 0, 95, 0, 0, 0, 709, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 237, 0, 0, 244, - 0, 0, 0, 347, 356, 355, 336, 337, 339, 341, - 346, 353, 359, 1307, 1356, 1396, 1308, 1358, 264, 319, - 271, 263, 572, 1407, 1388, 1271, 1337, 1395, 1332, 0, - 0, 228, 1398, 1331, 0, 1361, 0, 1413, 1266, 1352, - 0, 1269, 1273, 1409, 1393, 1302, 274, 0, 0, 0, - 0, 0, 0, 0, 1328, 1339, 1373, 1377, 1322, 0, - 0, 0, 0, 0, 0, 0, 0, 1300, 0, 1348, - 0, 0, 0, 1278, 1270, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1326, 0, 0, - 0, 0, 1281, 0, 1301, 1374, 0, 1264, 296, 1275, - 397, 256, 0, 448, 1381, 1392, 1323, 616, 1394, 1321, - 1320, 1368, 1279, 1387, 1314, 361, 1277, 328, 197, 224, - 0, 1312, 407, 456, 468, 1386, 1297, 1306, 252, 1304, - 466, 421, 594, 232, 283, 453, 427, 464, 435, 286, - 1347, 1366, 465, 368, 577, 445, 591, 617, 618, 262, - 401, 603, 514, 611, 635, 225, 259, 415, 499, 597, - 488, 393, 573, 574, 327, 487, 294, 201, 365, 623, - 223, 474, 367, 241, 230, 579, 600, 288, 451, 630, - 212, 509, 589, 238, 478, 0, 0, 638, 246, 498, - 214, 586, 497, 389, 324, 325, 213, 0, 452, 267, - 292, 0, 0, 257, 410, 581, 582, 255, 639, 227, - 610, 219, 1276, 609, 403, 576, 587, 390, 379, 218, - 585, 388, 378, 332, 351, 352, 279, 305, 442, 371, - 443, 304, 306, 399, 398, 400, 206, 598, 0, 207, - 0, 493, 599, 640, 447, 211, 233, 234, 236, 1292, - 278, 282, 290, 293, 301, 302, 311, 363, 414, 441, - 437, 446, 1382, 571, 592, 604, 615, 621, 622, 624, - 625, 626, 627, 628, 631, 629, 402, 309, 489, 331, - 369, 1371, 1412, 420, 467, 239, 596, 490, 199, 1286, - 1291, 1284, 0, 253, 254, 1353, 567, 1287, 1285, 1342, - 1343, 1288, 1403, 1404, 1405, 1390, 641, 642, 643, 644, - 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, - 655, 656, 657, 658, 636, 500, 506, 501, 502, 503, - 504, 505, 0, 507, 1375, 1280, 0, 1289, 1290, 1384, - 583, 584, 659, 380, 480, 593, 333, 345, 348, 338, - 357, 0, 358, 334, 335, 340, 342, 343, 344, 349, - 350, 354, 360, 248, 209, 386, 394, 570, 310, 215, - 216, 217, 516, 517, 518, 519, 607, 608, 612, 204, - 457, 458, 459, 460, 291, 602, 307, 463, 462, 329, - 330, 375, 444, 532, 534, 545, 549, 551, 553, 559, - 562, 533, 535, 546, 550, 552, 554, 560, 563, 522, - 524, 526, 528, 541, 540, 537, 565, 566, 543, 548, - 527, 539, 544, 557, 564, 561, 521, 525, 529, 538, - 556, 555, 536, 547, 558, 542, 530, 523, 531, 1346, - 196, 220, 364, 1408, 449, 287, 637, 606, 601, 205, - 222, 1283, 261, 1295, 1303, 0, 1309, 1317, 1318, 1330, - 1333, 1334, 1335, 1336, 1354, 1355, 1357, 1365, 1367, 1370, - 1372, 1379, 1391, 1411, 198, 200, 208, 221, 231, 235, - 242, 260, 275, 277, 284, 297, 308, 316, 317, 320, - 326, 376, 382, 383, 384, 385, 404, 405, 406, 409, - 412, 413, 416, 418, 419, 422, 426, 430, 431, 432, - 434, 436, 438, 450, 455, 469, 470, 471, 472, 473, - 476, 477, 482, 483, 484, 485, 486, 494, 495, 508, - 578, 580, 595, 613, 619, 475, 299, 300, 439, 440, - 312, 313, 633, 634, 298, 590, 620, 588, 632, 614, - 433, 374, 1345, 1351, 377, 280, 303, 318, 1360, 605, - 496, 226, 461, 289, 250, 1378, 1380, 210, 245, 229, - 258, 273, 276, 322, 387, 395, 424, 429, 295, 270, - 243, 454, 240, 479, 511, 512, 513, 515, 391, 265, - 428, 1341, 1369, 372, 568, 569, 314, 392, 0, 0, - 0, 1397, 1383, 520, 0, 1325, 1400, 1294, 1313, 1410, - 1316, 1319, 1362, 1272, 1340, 411, 1310, 1265, 1298, 1267, - 1305, 1268, 1296, 1327, 269, 1293, 1385, 1344, 1399, 362, - 266, 1274, 1299, 425, 1315, 203, 1364, 481, 251, 373, - 370, 575, 281, 272, 268, 249, 315, 381, 423, 510, - 417, 1406, 366, 1350, 0, 491, 396, 0, 0, 0, - 1329, 1389, 1338, 1376, 1324, 1363, 1282, 1349, 1401, 1311, - 1359, 1402, 321, 247, 323, 202, 408, 492, 285, 0, - 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, - 0, 347, 356, 355, 336, 337, 339, 341, 346, 353, - 359, 1307, 1356, 1396, 1308, 1358, 264, 319, 271, 263, - 572, 1407, 1388, 1271, 1337, 1395, 1332, 0, 0, 228, - 1398, 1331, 0, 1361, 0, 1413, 1266, 1352, 0, 1269, - 1273, 1409, 1393, 1302, 274, 0, 0, 0, 0, 0, - 0, 0, 1328, 1339, 1373, 1377, 1322, 0, 0, 0, - 0, 0, 0, 0, 0, 1300, 0, 1348, 0, 0, - 0, 1278, 1270, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1326, 0, 0, 0, 0, - 1281, 0, 1301, 1374, 0, 1264, 296, 1275, 397, 256, - 0, 448, 1381, 1392, 1323, 616, 1394, 1321, 1320, 1368, - 1279, 1387, 1314, 361, 1277, 328, 197, 224, 0, 1312, - 407, 456, 468, 1386, 1297, 1306, 252, 1304, 466, 421, - 594, 232, 283, 453, 427, 464, 435, 286, 1347, 1366, - 465, 368, 577, 445, 591, 617, 618, 262, 401, 603, - 514, 611, 635, 225, 259, 415, 499, 597, 488, 393, - 573, 574, 327, 487, 294, 201, 365, 623, 223, 474, - 367, 241, 230, 579, 600, 288, 451, 630, 212, 509, - 589, 238, 478, 0, 0, 638, 246, 498, 214, 586, - 497, 389, 324, 325, 213, 0, 452, 267, 292, 0, - 0, 257, 410, 581, 582, 255, 639, 227, 610, 219, - 1276, 609, 403, 576, 587, 390, 379, 218, 585, 388, - 378, 332, 351, 352, 279, 305, 442, 371, 443, 304, - 306, 399, 398, 400, 206, 598, 0, 207, 0, 493, - 599, 640, 447, 211, 233, 234, 236, 1292, 278, 282, - 290, 293, 301, 302, 311, 363, 414, 441, 437, 446, - 1382, 571, 592, 604, 615, 621, 622, 624, 625, 626, - 627, 628, 631, 629, 402, 309, 489, 331, 369, 1371, - 1412, 420, 467, 239, 596, 490, 199, 1286, 1291, 1284, - 0, 253, 254, 1353, 567, 1287, 1285, 1342, 1343, 1288, - 1403, 1404, 1405, 1390, 641, 642, 643, 644, 645, 646, - 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, - 657, 658, 636, 500, 506, 501, 502, 503, 504, 505, - 0, 507, 1375, 1280, 0, 1289, 1290, 1384, 583, 584, - 659, 380, 480, 593, 333, 345, 348, 338, 357, 0, - 358, 334, 335, 340, 342, 343, 344, 349, 350, 354, - 360, 248, 209, 386, 394, 570, 310, 215, 216, 217, - 516, 517, 518, 519, 607, 608, 612, 204, 457, 458, - 459, 460, 291, 602, 307, 463, 462, 329, 330, 375, - 444, 532, 534, 545, 549, 551, 553, 559, 562, 533, - 535, 546, 550, 552, 554, 560, 563, 522, 524, 526, - 528, 541, 540, 537, 565, 566, 543, 548, 527, 539, - 544, 557, 564, 561, 521, 525, 529, 538, 556, 555, - 536, 547, 558, 542, 530, 523, 531, 1346, 196, 220, - 364, 1408, 449, 287, 637, 606, 601, 205, 222, 1283, - 261, 1295, 1303, 0, 1309, 1317, 1318, 1330, 1333, 1334, - 1335, 1336, 1354, 1355, 1357, 1365, 1367, 1370, 1372, 1379, - 1391, 1411, 198, 200, 208, 221, 231, 235, 242, 260, - 275, 277, 284, 297, 308, 316, 317, 320, 326, 376, - 382, 383, 384, 385, 404, 405, 406, 409, 412, 413, - 416, 418, 419, 422, 426, 430, 431, 432, 434, 436, - 438, 450, 455, 469, 470, 471, 472, 473, 476, 477, - 482, 483, 484, 485, 486, 494, 495, 508, 578, 580, - 595, 613, 619, 475, 299, 300, 439, 440, 312, 313, - 633, 634, 298, 590, 620, 588, 632, 614, 433, 374, - 1345, 1351, 377, 280, 303, 318, 1360, 605, 496, 226, - 461, 289, 250, 1378, 1380, 210, 245, 229, 258, 273, - 276, 322, 387, 395, 424, 429, 295, 270, 243, 454, - 240, 479, 511, 512, 513, 515, 391, 265, 428, 1341, - 1369, 372, 568, 569, 314, 392, 0, 0, 0, 1397, - 1383, 520, 0, 1325, 1400, 1294, 1313, 1410, 1316, 1319, - 1362, 1272, 1340, 411, 1310, 1265, 1298, 1267, 1305, 1268, - 1296, 1327, 269, 1293, 1385, 1344, 1399, 362, 266, 1274, - 1299, 425, 1315, 203, 1364, 481, 251, 373, 370, 575, - 281, 272, 268, 249, 315, 381, 423, 510, 417, 1406, - 366, 1350, 0, 491, 396, 0, 0, 0, 1329, 1389, - 1338, 1376, 1324, 1363, 1282, 1349, 1401, 1311, 1359, 1402, - 321, 247, 323, 202, 408, 492, 285, 0, 0, 0, - 0, 0, 709, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 237, 0, 0, 244, 0, 0, 0, 347, - 356, 355, 336, 337, 339, 341, 346, 353, 359, 1307, - 1356, 1396, 1308, 1358, 264, 319, 271, 263, 572, 1407, - 1388, 1271, 1337, 1395, 1332, 0, 0, 228, 1398, 1331, - 0, 1361, 0, 1413, 1266, 1352, 0, 1269, 1273, 1409, - 1393, 1302, 274, 0, 0, 0, 0, 0, 0, 0, - 1328, 1339, 1373, 1377, 1322, 0, 0, 0, 0, 0, - 0, 0, 0, 1300, 0, 1348, 0, 0, 0, 1278, - 1270, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1326, 0, 0, 0, 0, 1281, 0, - 1301, 1374, 0, 1264, 296, 1275, 397, 256, 0, 448, - 1381, 1392, 1323, 616, 1394, 1321, 1320, 1368, 1279, 1387, - 1314, 361, 1277, 328, 197, 224, 0, 1312, 407, 456, - 468, 1386, 1297, 1306, 252, 1304, 466, 421, 594, 232, - 283, 453, 427, 464, 435, 286, 1347, 1366, 465, 368, - 577, 445, 591, 617, 618, 262, 401, 603, 514, 611, - 635, 225, 259, 415, 499, 597, 488, 393, 573, 574, - 327, 487, 294, 201, 365, 623, 223, 474, 367, 241, - 230, 579, 600, 288, 451, 630, 212, 509, 589, 238, - 478, 0, 0, 638, 246, 498, 214, 586, 497, 389, - 324, 325, 213, 0, 452, 267, 292, 0, 0, 257, - 410, 581, 582, 255, 639, 227, 610, 219, 1276, 609, - 403, 576, 587, 390, 379, 218, 585, 388, 378, 332, - 351, 352, 279, 305, 442, 371, 443, 304, 306, 399, - 398, 400, 206, 598, 0, 207, 0, 493, 599, 640, - 447, 211, 233, 234, 236, 1292, 278, 282, 290, 293, - 301, 302, 311, 363, 414, 441, 437, 446, 1382, 571, - 592, 604, 615, 621, 622, 624, 625, 626, 627, 628, - 631, 629, 402, 309, 489, 331, 369, 1371, 1412, 420, - 467, 239, 596, 490, 199, 1286, 1291, 1284, 0, 253, - 254, 1353, 567, 1287, 1285, 1342, 1343, 1288, 1403, 1404, - 1405, 1390, 641, 642, 643, 644, 645, 646, 647, 648, - 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, - 636, 500, 506, 501, 502, 503, 504, 505, 0, 507, - 1375, 1280, 0, 1289, 1290, 1384, 583, 584, 659, 380, - 480, 593, 333, 345, 348, 338, 357, 0, 358, 334, - 335, 340, 342, 343, 344, 349, 350, 354, 360, 248, - 209, 386, 394, 570, 310, 215, 216, 217, 516, 517, - 518, 519, 607, 608, 612, 204, 457, 458, 459, 460, - 291, 602, 307, 463, 462, 329, 330, 375, 444, 532, - 534, 545, 549, 551, 553, 559, 562, 533, 535, 546, - 550, 552, 554, 560, 563, 522, 524, 526, 528, 541, - 540, 537, 565, 566, 543, 548, 527, 539, 544, 557, - 564, 561, 521, 525, 529, 538, 556, 555, 536, 547, - 558, 542, 530, 523, 531, 1346, 196, 220, 364, 1408, - 449, 287, 637, 606, 601, 205, 222, 1283, 261, 1295, - 1303, 0, 1309, 1317, 1318, 1330, 1333, 1334, 1335, 1336, - 1354, 1355, 1357, 1365, 1367, 1370, 1372, 1379, 1391, 1411, - 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, - 284, 297, 308, 316, 317, 320, 326, 376, 382, 383, - 384, 385, 404, 405, 406, 409, 412, 413, 416, 418, - 419, 422, 426, 430, 431, 432, 434, 436, 438, 450, - 455, 469, 470, 471, 472, 473, 476, 477, 482, 483, - 484, 485, 486, 494, 495, 508, 578, 580, 595, 613, - 619, 475, 299, 300, 439, 440, 312, 313, 633, 634, - 298, 590, 620, 588, 632, 614, 433, 374, 1345, 1351, - 377, 280, 303, 318, 1360, 605, 496, 226, 461, 289, - 250, 1378, 1380, 210, 245, 229, 258, 273, 276, 322, - 387, 395, 424, 429, 295, 270, 243, 454, 240, 479, - 511, 512, 513, 515, 391, 265, 428, 1341, 1369, 372, - 568, 569, 314, 392, 0, 0, 0, 1397, 1383, 520, - 0, 1325, 1400, 1294, 1313, 1410, 1316, 1319, 1362, 1272, - 1340, 411, 1310, 1265, 1298, 1267, 1305, 1268, 1296, 1327, - 269, 1293, 1385, 1344, 1399, 362, 266, 1274, 1299, 425, - 1315, 203, 1364, 481, 251, 373, 370, 575, 281, 272, - 268, 249, 315, 381, 423, 510, 417, 1406, 366, 1350, - 0, 491, 396, 0, 0, 0, 1329, 1389, 1338, 1376, - 1324, 1363, 1282, 1349, 1401, 1311, 1359, 1402, 321, 247, - 323, 202, 408, 492, 285, 0, 0, 0, 0, 0, - 941, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 237, 0, 0, 244, 0, 0, 0, 347, 356, 355, - 336, 337, 339, 341, 346, 353, 359, 1307, 1356, 1396, - 1308, 1358, 264, 319, 271, 263, 572, 1407, 1388, 1271, - 1337, 1395, 1332, 0, 0, 228, 1398, 1331, 0, 1361, - 0, 1413, 1266, 1352, 0, 1269, 1273, 1409, 1393, 1302, - 274, 0, 0, 0, 0, 0, 0, 0, 1328, 1339, - 1373, 1377, 1322, 0, 0, 0, 0, 0, 0, 0, - 0, 1300, 0, 1348, 0, 0, 0, 1278, 1270, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1326, 0, 0, 0, 0, 1281, 0, 1301, 1374, - 0, 1264, 296, 1275, 397, 256, 0, 448, 1381, 1392, - 1323, 616, 1394, 1321, 1320, 1368, 1279, 1387, 1314, 361, - 1277, 328, 197, 224, 0, 1312, 407, 456, 468, 1386, - 1297, 1306, 252, 1304, 466, 421, 594, 232, 283, 453, - 427, 464, 435, 286, 1347, 1366, 465, 368, 577, 445, - 591, 617, 618, 262, 401, 603, 514, 611, 635, 225, - 259, 415, 499, 597, 488, 393, 573, 574, 327, 487, - 294, 201, 365, 623, 223, 474, 367, 241, 230, 579, - 600, 288, 451, 630, 212, 509, 589, 238, 478, 0, - 0, 638, 246, 498, 214, 586, 497, 389, 324, 325, - 213, 0, 452, 267, 292, 0, 0, 257, 410, 581, - 582, 255, 639, 227, 610, 219, 1276, 609, 403, 576, - 587, 390, 379, 218, 585, 388, 378, 332, 351, 352, - 279, 305, 442, 371, 443, 304, 306, 399, 398, 400, - 206, 598, 0, 207, 0, 493, 599, 640, 447, 211, - 233, 234, 236, 1292, 278, 282, 290, 293, 301, 302, - 311, 363, 414, 441, 437, 446, 1382, 571, 592, 604, - 615, 621, 622, 624, 625, 626, 627, 628, 631, 629, - 402, 309, 489, 331, 369, 1371, 1412, 420, 467, 239, - 596, 490, 199, 1286, 1291, 1284, 0, 253, 254, 1353, - 567, 1287, 1285, 1342, 1343, 1288, 1403, 1404, 1405, 1390, - 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, - 651, 652, 653, 654, 655, 656, 657, 658, 636, 500, - 506, 501, 502, 503, 504, 505, 0, 507, 1375, 1280, - 0, 1289, 1290, 1384, 583, 584, 659, 380, 480, 593, - 333, 345, 348, 338, 357, 0, 358, 334, 335, 340, - 342, 343, 344, 349, 350, 354, 360, 248, 209, 386, - 394, 570, 310, 215, 216, 217, 516, 517, 518, 519, - 607, 608, 612, 204, 457, 458, 459, 460, 291, 602, - 307, 463, 462, 329, 330, 375, 444, 532, 534, 545, - 549, 551, 553, 559, 562, 533, 535, 546, 550, 552, - 554, 560, 563, 522, 524, 526, 528, 541, 540, 537, - 565, 566, 543, 548, 527, 539, 544, 557, 564, 561, - 521, 525, 529, 538, 556, 555, 536, 547, 558, 542, - 530, 523, 531, 1346, 196, 220, 364, 1408, 449, 287, - 637, 606, 601, 205, 222, 1283, 261, 1295, 1303, 0, - 1309, 1317, 1318, 1330, 1333, 1334, 1335, 1336, 1354, 1355, - 1357, 1365, 1367, 1370, 1372, 1379, 1391, 1411, 198, 200, - 208, 221, 231, 235, 242, 260, 275, 277, 284, 297, - 308, 316, 317, 320, 326, 376, 382, 383, 384, 385, - 404, 405, 406, 409, 412, 413, 416, 418, 419, 422, - 426, 430, 431, 432, 434, 436, 438, 450, 455, 469, - 470, 471, 472, 473, 476, 477, 482, 483, 484, 485, - 486, 494, 495, 508, 578, 580, 595, 613, 619, 475, - 299, 300, 439, 440, 312, 313, 633, 634, 298, 590, - 620, 588, 632, 614, 433, 374, 1345, 1351, 377, 280, - 303, 318, 1360, 605, 496, 226, 461, 289, 250, 1378, - 1380, 210, 245, 229, 258, 273, 276, 322, 387, 395, - 424, 429, 295, 270, 243, 454, 240, 479, 511, 512, - 513, 515, 391, 265, 428, 1341, 1369, 372, 568, 569, - 314, 392, 0, 0, 0, 0, 0, 520, 0, 761, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 411, - 0, 0, 0, 0, 749, 0, 0, 0, 269, 754, - 0, 0, 0, 362, 266, 0, 0, 425, 0, 203, - 0, 481, 251, 373, 370, 575, 281, 272, 268, 249, - 315, 381, 423, 510, 417, 760, 366, 0, 0, 491, - 396, 0, 0, 0, 0, 0, 756, 757, 0, 0, - 0, 0, 0, 0, 0, 0, 321, 247, 323, 202, - 408, 492, 285, 0, 95, 0, 0, 957, 941, 733, - 907, 945, 958, 959, 960, 961, 946, 0, 237, 947, - 948, 244, 949, 0, 906, 791, 793, 792, 856, 857, - 858, 859, 860, 861, 862, 789, 954, 962, 963, 0, - 264, 319, 271, 263, 572, 0, 0, 2176, 2177, 2178, - 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, - 0, 729, 746, 0, 759, 0, 0, 0, 274, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 743, 744, 0, 0, 0, - 0, 901, 0, 745, 0, 0, 753, 964, 965, 966, - 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, - 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, - 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, - 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 755, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 296, 0, 397, 256, 0, 448, 900, 0, 0, 616, - 0, 0, 898, 0, 0, 0, 0, 361, 0, 328, - 197, 224, 0, 0, 407, 456, 468, 0, 0, 0, - 951, 0, 466, 421, 594, 232, 283, 453, 427, 464, - 435, 286, 0, 0, 465, 368, 577, 445, 591, 617, - 618, 262, 401, 603, 514, 611, 635, 225, 259, 415, - 499, 597, 488, 393, 573, 574, 327, 487, 294, 201, - 365, 623, 223, 474, 367, 241, 230, 579, 600, 288, - 451, 630, 212, 509, 589, 238, 478, 0, 0, 638, - 246, 498, 214, 586, 497, 389, 324, 325, 213, 0, - 452, 267, 292, 0, 0, 257, 410, 952, 953, 255, - 639, 797, 610, 219, 0, 609, 403, 576, 587, 390, - 379, 218, 585, 388, 378, 332, 805, 806, 279, 305, - 882, 881, 880, 304, 306, 878, 879, 877, 206, 598, - 0, 207, 0, 493, 599, 640, 447, 211, 233, 234, - 236, 0, 278, 282, 290, 293, 301, 302, 311, 363, - 414, 441, 437, 446, 0, 571, 592, 604, 615, 621, - 622, 624, 625, 626, 627, 628, 631, 629, 402, 309, - 489, 331, 369, 0, 0, 420, 467, 239, 596, 490, - 888, 910, 899, 765, 766, 889, 890, 914, 891, 768, - 769, 911, 912, 762, 763, 767, 913, 915, 641, 642, - 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, - 653, 654, 655, 656, 657, 658, 636, 500, 506, 501, - 502, 503, 504, 505, 0, 507, 902, 752, 751, 0, - 758, 0, 787, 788, 790, 794, 795, 796, 807, 854, - 855, 863, 865, 866, 864, 867, 868, 869, 872, 873, - 874, 875, 870, 871, 876, 770, 774, 771, 772, 773, - 785, 775, 776, 777, 778, 779, 780, 781, 782, 783, - 784, 786, 925, 926, 927, 928, 929, 930, 800, 804, - 803, 801, 802, 798, 799, 826, 825, 827, 828, 829, - 830, 831, 832, 834, 833, 835, 836, 837, 838, 839, - 840, 808, 809, 812, 813, 811, 810, 814, 823, 824, - 815, 816, 817, 818, 819, 820, 822, 821, 841, 842, - 843, 844, 845, 847, 846, 850, 851, 849, 848, 853, - 852, 750, 196, 220, 364, 0, 449, 287, 637, 606, - 601, 205, 222, 916, 261, 917, 0, 0, 921, 0, - 0, 0, 923, 922, 0, 924, 886, 885, 0, 0, - 918, 919, 0, 920, 0, 0, 198, 200, 208, 221, - 231, 235, 242, 260, 275, 277, 284, 297, 308, 316, - 317, 320, 326, 376, 382, 383, 384, 385, 404, 405, - 406, 409, 412, 413, 416, 418, 419, 422, 426, 430, - 431, 432, 434, 436, 438, 450, 455, 469, 470, 471, - 472, 473, 476, 477, 482, 483, 484, 485, 486, 494, - 495, 508, 578, 580, 595, 613, 619, 475, 931, 932, - 933, 934, 935, 936, 937, 938, 298, 590, 620, 588, - 632, 614, 433, 374, 0, 0, 377, 280, 303, 318, - 0, 605, 496, 226, 461, 289, 250, 956, 0, 210, - 245, 229, 258, 273, 276, 322, 387, 395, 424, 429, - 295, 270, 243, 454, 240, 479, 511, 512, 513, 515, - 391, 265, 428, 392, 0, 372, 568, 569, 314, 520, - 0, 761, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 411, 0, 0, 0, 0, 749, 0, 0, 0, - 269, 754, 0, 0, 0, 362, 266, 0, 0, 425, - 0, 203, 0, 481, 251, 373, 370, 575, 281, 272, - 268, 249, 315, 381, 423, 510, 417, 760, 366, 0, - 0, 491, 396, 0, 0, 0, 0, 0, 756, 757, - 0, 0, 0, 0, 0, 0, 2382, 0, 321, 247, - 323, 202, 408, 492, 285, 0, 95, 0, 0, 957, - 941, 733, 907, 945, 958, 959, 960, 961, 946, 0, - 237, 947, 948, 244, 949, 0, 906, 791, 793, 792, - 856, 857, 858, 859, 860, 861, 862, 789, 954, 962, - 963, 2383, 264, 319, 271, 263, 572, 0, 0, 0, - 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, - 0, 0, 0, 729, 746, 0, 759, 0, 0, 0, - 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 743, 744, 0, - 0, 0, 0, 901, 0, 745, 0, 0, 753, 964, - 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, - 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, - 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, - 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, - 1005, 755, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 296, 0, 397, 256, 0, 448, 900, 0, - 0, 616, 0, 0, 898, 0, 0, 0, 0, 361, - 0, 328, 197, 224, 0, 0, 407, 456, 468, 0, - 0, 0, 951, 0, 466, 421, 594, 232, 283, 453, - 427, 464, 435, 286, 0, 0, 465, 368, 577, 445, - 591, 617, 618, 262, 401, 603, 514, 611, 635, 225, - 259, 415, 499, 597, 488, 393, 573, 574, 327, 487, - 294, 201, 365, 623, 223, 474, 367, 241, 230, 579, - 600, 288, 451, 630, 212, 509, 589, 238, 478, 0, - 0, 638, 246, 498, 214, 586, 497, 389, 324, 325, - 213, 0, 452, 267, 292, 0, 0, 257, 410, 952, - 953, 255, 639, 797, 610, 219, 0, 609, 403, 576, - 587, 390, 379, 218, 585, 388, 378, 332, 805, 806, - 279, 305, 882, 881, 880, 304, 306, 878, 879, 877, - 206, 598, 0, 207, 0, 493, 599, 640, 447, 211, - 233, 234, 236, 0, 278, 282, 290, 293, 301, 302, - 311, 363, 414, 441, 437, 446, 0, 571, 592, 604, - 615, 621, 622, 624, 625, 626, 627, 628, 631, 629, - 402, 309, 489, 331, 369, 0, 0, 420, 467, 239, - 596, 490, 888, 910, 899, 765, 766, 889, 890, 914, - 891, 768, 769, 911, 912, 762, 763, 767, 913, 915, - 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, - 651, 652, 653, 654, 655, 656, 657, 658, 636, 500, - 506, 501, 502, 503, 504, 505, 0, 507, 902, 752, - 751, 0, 758, 0, 787, 788, 790, 794, 795, 796, - 807, 854, 855, 863, 865, 866, 864, 867, 868, 869, - 872, 873, 874, 875, 870, 871, 876, 770, 774, 771, - 772, 773, 785, 775, 776, 777, 778, 779, 780, 781, - 782, 783, 784, 786, 925, 926, 927, 928, 929, 930, - 800, 804, 803, 801, 802, 798, 799, 826, 825, 827, - 828, 829, 830, 831, 832, 834, 833, 835, 836, 837, - 838, 839, 840, 808, 809, 812, 813, 811, 810, 814, - 823, 824, 815, 816, 817, 818, 819, 820, 822, 821, - 841, 842, 843, 844, 845, 847, 846, 850, 851, 849, - 848, 853, 852, 750, 196, 220, 364, 0, 449, 287, - 637, 606, 601, 205, 222, 916, 261, 917, 0, 0, - 921, 0, 0, 0, 923, 922, 0, 924, 886, 885, - 0, 0, 918, 919, 0, 920, 0, 0, 198, 200, - 208, 221, 231, 235, 242, 260, 275, 277, 284, 297, - 308, 316, 317, 320, 326, 376, 382, 383, 384, 385, - 404, 405, 406, 409, 412, 413, 416, 418, 419, 422, - 426, 430, 431, 432, 434, 436, 438, 450, 455, 469, - 470, 471, 472, 473, 476, 477, 482, 483, 484, 485, - 486, 494, 495, 508, 578, 580, 595, 613, 619, 475, - 931, 932, 933, 934, 935, 936, 937, 938, 298, 590, - 620, 588, 632, 614, 433, 374, 0, 0, 377, 280, - 303, 318, 0, 605, 496, 226, 461, 289, 250, 956, - 0, 210, 245, 229, 258, 273, 276, 322, 387, 395, - 424, 429, 295, 270, 243, 454, 240, 479, 511, 512, - 513, 515, 391, 265, 428, 0, 392, 372, 568, 569, - 314, 86, 520, 0, 761, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 749, - 0, 0, 0, 269, 754, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 760, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 756, 757, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 95, - 0, 0, 957, 941, 733, 907, 945, 958, 959, 960, - 961, 946, 0, 237, 947, 948, 244, 949, 0, 906, - 791, 793, 792, 856, 857, 858, 859, 860, 861, 862, - 789, 954, 962, 963, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, - 0, 0, 0, 0, 0, 0, 729, 746, 0, 759, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 743, 744, 0, 0, 0, 0, 901, 0, 745, 0, - 0, 753, 964, 965, 966, 967, 968, 969, 970, 971, - 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, - 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, - 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, - 1002, 1003, 1004, 1005, 755, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 900, 0, 0, 616, 0, 0, 898, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 951, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 952, 953, 255, 639, 797, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 805, 806, 279, 305, 882, 881, 880, 304, 306, - 878, 879, 877, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 888, 910, 899, 765, 766, - 889, 890, 914, 891, 768, 769, 911, 912, 762, 763, - 767, 913, 915, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 902, 752, 751, 0, 758, 0, 787, 788, 790, - 794, 795, 796, 807, 854, 855, 863, 865, 866, 864, - 867, 868, 869, 872, 873, 874, 875, 870, 871, 876, - 770, 774, 771, 772, 773, 785, 775, 776, 777, 778, - 779, 780, 781, 782, 783, 784, 786, 925, 926, 927, - 928, 929, 930, 800, 804, 803, 801, 802, 798, 799, - 826, 825, 827, 828, 829, 830, 831, 832, 834, 833, - 835, 836, 837, 838, 839, 840, 808, 809, 812, 813, - 811, 810, 814, 823, 824, 815, 816, 817, 818, 819, - 820, 822, 821, 841, 842, 843, 844, 845, 847, 846, - 850, 851, 849, 848, 853, 852, 750, 196, 220, 364, - 94, 449, 287, 637, 606, 601, 205, 222, 916, 261, - 917, 0, 0, 921, 0, 0, 0, 923, 922, 0, - 924, 886, 885, 0, 0, 918, 919, 0, 920, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 931, 932, 933, 934, 935, 936, 937, - 938, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 956, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 761, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 749, 0, 0, 0, 269, 754, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 760, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 756, 757, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 95, 0, 0, 957, 941, 733, 907, 945, 958, - 959, 960, 961, 946, 0, 237, 947, 948, 244, 949, - 0, 906, 791, 793, 792, 856, 857, 858, 859, 860, - 861, 862, 789, 954, 962, 963, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 729, 746, - 0, 759, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 744, 0, 0, 0, 0, 901, 0, - 745, 0, 0, 753, 964, 965, 966, 967, 968, 969, - 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, - 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, - 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, - 1000, 1001, 1002, 1003, 1004, 1005, 755, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 900, 0, 0, 616, 0, 0, 898, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 951, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 3987, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 952, 953, 255, 639, 797, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 805, 806, 279, 305, 882, 881, 880, - 304, 306, 878, 879, 877, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 888, 910, 899, - 765, 766, 889, 890, 914, 891, 768, 769, 911, 912, - 762, 763, 767, 913, 915, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 902, 752, 751, 0, 758, 0, 787, - 788, 790, 794, 795, 796, 807, 854, 855, 863, 865, - 866, 864, 867, 868, 869, 872, 873, 874, 875, 870, - 871, 876, 770, 774, 771, 772, 773, 785, 775, 776, - 777, 778, 779, 780, 781, 782, 783, 784, 786, 925, - 926, 927, 928, 929, 930, 800, 804, 803, 801, 802, - 798, 799, 826, 825, 827, 828, 829, 830, 831, 832, - 834, 833, 835, 836, 837, 838, 839, 840, 808, 809, - 812, 813, 811, 810, 814, 823, 824, 815, 816, 817, - 818, 819, 820, 822, 821, 841, 842, 843, 844, 845, - 847, 846, 850, 851, 849, 848, 853, 852, 750, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 916, 261, 917, 0, 0, 921, 0, 0, 0, 923, - 922, 0, 924, 886, 885, 0, 0, 918, 919, 0, - 920, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 931, 932, 933, 934, 935, - 936, 937, 938, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 956, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 761, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 749, 0, 0, 0, 269, 754, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 760, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 756, 757, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 95, 0, 1716, 957, 941, 733, 907, - 945, 958, 959, 960, 961, 946, 0, 237, 947, 948, - 244, 949, 0, 906, 791, 793, 792, 856, 857, 858, - 859, 860, 861, 862, 789, 954, 962, 963, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 729, 746, 0, 759, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 744, 0, 0, 0, 0, - 901, 0, 745, 0, 0, 753, 964, 965, 966, 967, - 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, - 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, - 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, - 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 755, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 900, 0, 0, 616, 0, - 0, 898, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 951, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 952, 953, 255, 639, - 797, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 805, 806, 279, 305, 882, - 881, 880, 304, 306, 878, 879, 877, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 888, - 910, 899, 765, 766, 889, 890, 914, 891, 768, 769, - 911, 912, 762, 763, 767, 913, 915, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 902, 752, 751, 0, 758, - 0, 787, 788, 790, 794, 795, 796, 807, 854, 855, - 863, 865, 866, 864, 867, 868, 869, 872, 873, 874, - 875, 870, 871, 876, 770, 774, 771, 772, 773, 785, - 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, - 786, 925, 926, 927, 928, 929, 930, 800, 804, 803, - 801, 802, 798, 799, 826, 825, 827, 828, 829, 830, - 831, 832, 834, 833, 835, 836, 837, 838, 839, 840, - 808, 809, 812, 813, 811, 810, 814, 823, 824, 815, - 816, 817, 818, 819, 820, 822, 821, 841, 842, 843, - 844, 845, 847, 846, 850, 851, 849, 848, 853, 852, - 750, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 916, 261, 917, 0, 0, 921, 0, 0, - 0, 923, 922, 0, 924, 886, 885, 0, 0, 918, - 919, 0, 920, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 931, 932, 933, - 934, 935, 936, 937, 938, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 956, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 761, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 749, 0, 0, 0, 269, - 754, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 760, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 756, 757, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 95, 0, 0, 957, 941, - 733, 907, 945, 958, 959, 960, 961, 946, 0, 237, - 947, 948, 244, 949, 0, 906, 791, 793, 792, 856, - 857, 858, 859, 860, 861, 862, 789, 954, 962, 963, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 729, 746, 0, 759, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 744, 1049, 0, - 0, 0, 901, 0, 745, 0, 0, 753, 964, 965, - 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, - 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, - 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, - 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, - 755, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 900, 0, 0, - 616, 0, 0, 898, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 951, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 952, 953, - 255, 639, 797, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 805, 806, 279, - 305, 882, 881, 880, 304, 306, 878, 879, 877, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 888, 910, 899, 765, 766, 889, 890, 914, 891, - 768, 769, 911, 912, 762, 763, 767, 913, 915, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 902, 752, 751, - 0, 758, 0, 787, 788, 790, 794, 795, 796, 807, - 854, 855, 863, 865, 866, 864, 867, 868, 869, 872, - 873, 874, 875, 870, 871, 876, 770, 774, 771, 772, - 773, 785, 775, 776, 777, 778, 779, 780, 781, 782, - 783, 784, 786, 925, 926, 927, 928, 929, 930, 800, - 804, 803, 801, 802, 798, 799, 826, 825, 827, 828, - 829, 830, 831, 832, 834, 833, 835, 836, 837, 838, - 839, 840, 808, 809, 812, 813, 811, 810, 814, 823, - 824, 815, 816, 817, 818, 819, 820, 822, 821, 841, - 842, 843, 844, 845, 847, 846, 850, 851, 849, 848, - 853, 852, 750, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 916, 261, 917, 0, 0, 921, - 0, 0, 0, 923, 922, 0, 924, 886, 885, 0, - 0, 918, 919, 0, 920, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 931, - 932, 933, 934, 935, 936, 937, 938, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 956, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 761, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 749, 0, 0, - 0, 269, 754, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 760, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 756, - 757, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 95, 0, 0, - 957, 941, 733, 907, 945, 958, 959, 960, 961, 946, - 0, 237, 947, 948, 244, 949, 0, 906, 791, 793, - 792, 856, 857, 858, 859, 860, 861, 862, 789, 954, - 962, 963, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 729, 746, 0, 759, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 744, - 0, 0, 0, 0, 901, 0, 745, 0, 0, 753, - 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, - 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, - 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, - 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, - 1004, 1005, 755, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 900, - 0, 0, 616, 0, 0, 898, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 951, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 952, 953, 255, 639, 797, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 805, - 806, 279, 305, 882, 881, 880, 304, 306, 878, 879, - 877, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 888, 910, 899, 765, 766, 889, 890, - 914, 891, 768, 769, 911, 912, 762, 763, 767, 913, - 915, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 902, - 752, 751, 0, 758, 0, 787, 788, 790, 794, 795, - 796, 807, 854, 855, 863, 865, 866, 864, 867, 868, - 869, 872, 873, 874, 875, 870, 871, 876, 770, 774, - 771, 772, 773, 785, 775, 776, 777, 778, 779, 780, - 781, 782, 783, 784, 786, 925, 926, 927, 928, 929, - 930, 800, 804, 803, 801, 802, 798, 799, 826, 825, - 827, 828, 829, 830, 831, 832, 834, 833, 835, 836, - 837, 838, 839, 840, 808, 809, 812, 813, 811, 810, - 814, 823, 824, 815, 816, 817, 818, 819, 820, 822, - 821, 841, 842, 843, 844, 845, 847, 846, 850, 851, - 849, 848, 853, 852, 750, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 916, 261, 917, 0, - 0, 921, 0, 0, 0, 923, 922, 0, 924, 886, - 885, 0, 0, 918, 919, 0, 920, 0, 0, 198, - 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 931, 932, 933, 934, 935, 936, 937, 938, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 956, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 761, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 749, - 0, 0, 0, 269, 754, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 760, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 756, 757, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 95, - 0, 0, 957, 941, 733, 907, 945, 958, 959, 960, - 961, 946, 0, 237, 947, 948, 244, 949, 0, 906, - 791, 793, 792, 856, 857, 858, 859, 860, 861, 862, - 789, 954, 962, 963, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, - 0, 0, 0, 0, 0, 0, 729, 746, 0, 759, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 743, 744, 0, 0, 0, 0, 901, 0, 745, 0, - 0, 753, 964, 965, 966, 967, 968, 969, 970, 971, - 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, - 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, - 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, - 1002, 1003, 1004, 1005, 3092, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 900, 0, 0, 616, 0, 0, 898, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 951, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 952, 953, 255, 639, 797, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 805, 806, 279, 305, 882, 881, 880, 304, 306, - 878, 879, 877, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 888, 910, 899, 765, 766, - 889, 890, 914, 891, 768, 769, 911, 912, 762, 763, - 767, 913, 915, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 902, 752, 751, 0, 758, 0, 787, 788, 790, - 794, 795, 796, 807, 854, 855, 863, 865, 866, 864, - 867, 868, 869, 872, 873, 874, 875, 870, 871, 876, - 770, 774, 771, 772, 773, 785, 775, 776, 777, 778, - 779, 780, 781, 782, 783, 784, 786, 925, 926, 927, - 928, 929, 930, 800, 804, 803, 801, 802, 798, 799, - 826, 825, 827, 828, 829, 830, 831, 832, 834, 833, - 835, 836, 837, 838, 839, 840, 808, 809, 812, 813, - 811, 810, 814, 823, 824, 815, 816, 817, 818, 819, - 820, 822, 821, 841, 842, 843, 844, 845, 847, 846, - 850, 851, 849, 848, 853, 852, 750, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 916, 261, - 917, 0, 0, 921, 0, 0, 0, 923, 922, 0, - 924, 886, 885, 0, 0, 918, 919, 0, 920, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 931, 932, 933, 934, 935, 936, 937, - 938, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 956, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 761, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 749, 0, 0, 0, 269, 754, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 760, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 756, 757, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 95, 0, 0, 957, 941, 733, 907, 945, 958, - 959, 960, 961, 946, 0, 237, 947, 948, 244, 949, - 0, 906, 791, 793, 792, 856, 857, 858, 859, 860, - 861, 862, 789, 954, 962, 963, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 729, 746, - 0, 759, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 743, 744, 0, 0, 0, 0, 901, 0, - 745, 0, 0, 753, 964, 965, 966, 967, 968, 969, - 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, - 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, - 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, - 1000, 1001, 1002, 1003, 1004, 1005, 3088, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 900, 0, 0, 616, 0, 0, 898, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 951, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 952, 953, 255, 639, 797, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 805, 806, 279, 305, 882, 881, 880, - 304, 306, 878, 879, 877, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 888, 910, 899, - 765, 766, 889, 890, 914, 891, 768, 769, 911, 912, - 762, 763, 767, 913, 915, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 902, 752, 751, 0, 758, 0, 787, - 788, 790, 794, 795, 796, 807, 854, 855, 863, 865, - 866, 864, 867, 868, 869, 872, 873, 874, 875, 870, - 871, 876, 770, 774, 771, 772, 773, 785, 775, 776, - 777, 778, 779, 780, 781, 782, 783, 784, 786, 925, - 926, 927, 928, 929, 930, 800, 804, 803, 801, 802, - 798, 799, 826, 825, 827, 828, 829, 830, 831, 832, - 834, 833, 835, 836, 837, 838, 839, 840, 808, 809, - 812, 813, 811, 810, 814, 823, 824, 815, 816, 817, - 818, 819, 820, 822, 821, 841, 842, 843, 844, 845, - 847, 846, 850, 851, 849, 848, 853, 852, 750, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 916, 261, 917, 0, 0, 921, 0, 0, 0, 923, - 922, 0, 924, 886, 885, 0, 0, 918, 919, 0, - 920, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 931, 932, 933, 934, 935, - 936, 937, 938, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 956, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 761, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 749, 0, 0, 0, 269, 754, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 760, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 756, 757, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 95, 0, 0, 957, 941, 1070, 907, - 945, 958, 959, 960, 961, 946, 0, 237, 947, 948, - 244, 949, 0, 906, 791, 793, 792, 856, 857, 858, - 859, 860, 861, 862, 789, 954, 962, 963, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 746, 0, 759, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 743, 744, 0, 0, 0, 0, - 901, 0, 745, 0, 0, 753, 964, 965, 966, 967, - 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, - 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, - 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, - 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 755, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 900, 0, 0, 616, 0, - 0, 898, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 951, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 952, 953, 255, 639, - 797, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 805, 806, 279, 305, 882, - 881, 880, 304, 306, 878, 879, 877, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 888, - 910, 899, 765, 766, 889, 890, 914, 891, 768, 769, - 911, 912, 762, 763, 767, 913, 915, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 902, 752, 751, 0, 758, - 0, 787, 788, 790, 794, 795, 796, 807, 854, 855, - 863, 865, 866, 864, 867, 868, 869, 872, 873, 874, - 875, 870, 871, 876, 770, 774, 771, 772, 773, 785, - 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, - 786, 925, 926, 927, 928, 929, 930, 800, 804, 803, - 801, 802, 798, 799, 826, 825, 827, 828, 829, 830, - 831, 832, 834, 833, 835, 836, 837, 838, 839, 840, - 808, 809, 812, 813, 811, 810, 814, 823, 824, 815, - 816, 817, 818, 819, 820, 822, 821, 841, 842, 843, - 844, 845, 847, 846, 850, 851, 849, 848, 853, 852, - 750, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 916, 261, 917, 0, 0, 921, 0, 0, - 0, 923, 922, 0, 924, 886, 885, 0, 0, 918, - 919, 0, 920, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 931, 932, 933, - 934, 935, 936, 937, 938, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 956, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 761, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 749, 0, 0, 0, 269, - 754, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 760, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 756, 757, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 95, 0, 0, 957, 941, - 1070, 907, 945, 958, 959, 960, 961, 946, 0, 237, - 947, 948, 244, 949, 0, 906, 791, 793, 792, 856, - 857, 858, 859, 860, 861, 862, 789, 954, 962, 963, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 0, 746, 0, 759, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 743, 744, 0, 0, - 0, 0, 901, 0, 745, 0, 0, 753, 964, 965, - 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, - 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, - 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, - 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, - 2070, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 900, 0, 0, - 616, 0, 0, 898, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 951, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 952, 953, - 255, 639, 797, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 805, 806, 279, - 305, 882, 881, 880, 304, 306, 878, 879, 877, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 888, 910, 899, 765, 766, 889, 890, 914, 891, - 768, 769, 911, 912, 762, 763, 767, 913, 915, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 902, 752, 751, - 0, 758, 0, 787, 788, 790, 794, 795, 796, 807, - 854, 855, 863, 865, 866, 864, 867, 868, 869, 872, - 873, 874, 875, 870, 871, 876, 770, 774, 771, 772, - 773, 785, 775, 776, 777, 778, 779, 780, 781, 782, - 783, 784, 786, 925, 926, 927, 928, 929, 930, 800, - 804, 803, 801, 802, 798, 799, 826, 825, 827, 828, - 829, 830, 831, 832, 834, 833, 835, 836, 837, 838, - 839, 840, 808, 809, 812, 813, 811, 810, 814, 823, - 824, 815, 816, 817, 818, 819, 820, 822, 821, 841, - 842, 843, 844, 845, 847, 846, 850, 851, 849, 848, - 853, 852, 750, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 916, 261, 917, 0, 0, 921, - 0, 0, 0, 923, 922, 0, 924, 886, 885, 0, - 0, 918, 919, 0, 920, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 931, - 932, 933, 934, 935, 936, 937, 938, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 956, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 761, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 749, 0, 0, - 0, 269, 754, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 760, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 756, - 757, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 95, 0, 0, - 957, 941, 1070, 907, 945, 958, 959, 960, 961, 946, - 0, 237, 947, 948, 244, 949, 0, 906, 791, 793, - 792, 856, 857, 858, 859, 860, 861, 862, 789, 954, - 962, 963, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 746, 0, 759, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 743, 744, - 0, 0, 0, 0, 901, 0, 745, 0, 0, 753, - 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, - 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, - 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, - 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, - 1004, 1005, 2068, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 900, - 0, 0, 616, 0, 0, 898, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 951, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 952, 953, 255, 639, 797, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 805, - 806, 279, 305, 882, 881, 880, 304, 306, 878, 879, - 877, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 888, 910, 899, 765, 766, 889, 890, - 914, 891, 768, 769, 911, 912, 762, 763, 767, 913, - 915, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 902, - 752, 751, 0, 758, 0, 787, 788, 790, 794, 795, - 796, 807, 854, 855, 863, 865, 866, 864, 867, 868, - 869, 872, 873, 874, 875, 870, 871, 876, 770, 774, - 771, 772, 773, 785, 775, 776, 777, 778, 779, 780, - 781, 782, 783, 784, 786, 925, 926, 927, 928, 929, - 930, 800, 804, 803, 801, 802, 798, 799, 826, 825, - 827, 828, 829, 830, 831, 832, 834, 833, 835, 836, - 837, 838, 839, 840, 808, 809, 812, 813, 811, 810, - 814, 823, 824, 815, 816, 817, 818, 819, 820, 822, - 821, 841, 842, 843, 844, 845, 847, 846, 850, 851, - 849, 848, 853, 852, 750, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 916, 261, 917, 0, - 0, 921, 0, 0, 0, 923, 922, 0, 924, 886, - 885, 0, 0, 918, 919, 0, 920, 0, 0, 198, - 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 931, 932, 933, 934, 935, 936, 937, 938, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 956, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 709, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, - 1121, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 1120, 616, 0, 0, 0, 0, 0, - 1117, 1118, 361, 1078, 328, 197, 224, 1111, 1115, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 1678, 941, 0, 0, 1675, 0, - 0, 0, 0, 1673, 0, 237, 1674, 1672, 244, 1677, - 0, 906, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 0, 392, 372, 568, 569, 314, 86, 520, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 411, - 0, 0, 0, 0, 0, 0, 0, 0, 269, 0, - 0, 0, 0, 362, 266, 0, 0, 425, 0, 203, - 0, 481, 251, 373, 370, 575, 281, 272, 268, 249, - 315, 381, 423, 510, 417, 0, 366, 0, 0, 491, - 396, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 321, 247, 323, 202, - 408, 492, 285, 0, 95, 0, 0, 0, 194, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, - 0, 244, 0, 0, 0, 347, 356, 355, 336, 337, - 339, 341, 346, 353, 359, 0, 0, 0, 0, 0, - 264, 319, 271, 263, 572, 0, 0, 0, 0, 0, - 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 666, 0, 0, 0, 1080, + 0, 0, 0, 0, 0, 0, 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3721, 0, 0, 0, 0, 0, + 666, 0, 0, 0, 0, 0, 0, 0, 0, 3729, + 3730, 0, 0, 3805, 3804, 3803, 0, 0, 3801, 3802, + 3800, 0, 0, 0, 0, 0, 0, 0, 0, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 666, + 0, 0, 666, 666, 666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3806, 913, 0, 768, 769, 3807, 3808, + 917, 3809, 771, 772, 914, 915, 0, 766, 770, 916, + 918, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 296, 0, 397, 256, 0, 448, 0, 0, 0, 616, - 0, 0, 0, 0, 0, 0, 0, 361, 0, 328, - 197, 224, 0, 0, 407, 456, 468, 0, 0, 0, - 252, 0, 466, 421, 594, 232, 283, 453, 427, 464, - 435, 286, 0, 0, 465, 368, 577, 445, 591, 617, - 618, 262, 401, 603, 514, 611, 635, 225, 259, 415, - 499, 597, 488, 393, 573, 574, 327, 487, 294, 201, - 365, 623, 223, 474, 367, 241, 230, 579, 600, 288, - 451, 630, 212, 509, 589, 238, 478, 0, 0, 638, - 246, 498, 214, 586, 497, 389, 324, 325, 213, 0, - 452, 267, 292, 0, 0, 257, 410, 581, 582, 255, - 639, 227, 610, 219, 0, 609, 403, 576, 587, 390, - 379, 218, 585, 388, 378, 332, 351, 352, 279, 305, - 442, 371, 443, 304, 306, 399, 398, 400, 206, 598, - 0, 207, 0, 493, 599, 640, 447, 211, 233, 234, - 236, 0, 278, 282, 290, 293, 301, 302, 311, 363, - 414, 441, 437, 446, 0, 571, 592, 604, 615, 621, - 622, 624, 625, 626, 627, 628, 631, 629, 402, 309, - 489, 331, 369, 0, 0, 420, 467, 239, 596, 490, - 199, 0, 0, 0, 0, 253, 254, 0, 567, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 641, 642, - 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, - 653, 654, 655, 656, 657, 658, 636, 500, 506, 501, - 502, 503, 504, 505, 0, 507, 0, 0, 0, 0, - 0, 0, 583, 584, 659, 380, 480, 593, 333, 345, - 348, 338, 357, 0, 358, 334, 335, 340, 342, 343, - 344, 349, 350, 354, 360, 248, 209, 386, 394, 570, - 310, 215, 216, 217, 516, 517, 518, 519, 607, 608, - 612, 204, 457, 458, 459, 460, 291, 602, 307, 463, - 462, 329, 330, 375, 444, 532, 534, 545, 549, 551, - 553, 559, 562, 533, 535, 546, 550, 552, 554, 560, - 563, 522, 524, 526, 528, 541, 540, 537, 565, 566, - 543, 548, 527, 539, 544, 557, 564, 561, 521, 525, - 529, 538, 556, 555, 536, 547, 558, 542, 530, 523, - 531, 0, 196, 220, 364, 94, 449, 287, 637, 606, - 601, 205, 222, 0, 261, 0, 0, 0, 0, 0, - 0, 2369, 0, 0, 2368, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 200, 208, 221, - 231, 235, 242, 260, 275, 277, 284, 297, 308, 316, - 317, 320, 326, 376, 382, 383, 384, 385, 404, 405, - 406, 409, 412, 413, 416, 418, 419, 422, 426, 430, - 431, 432, 434, 436, 438, 450, 455, 469, 470, 471, - 472, 473, 476, 477, 482, 483, 484, 485, 486, 494, - 495, 508, 578, 580, 595, 613, 619, 475, 299, 300, - 439, 440, 312, 313, 633, 634, 298, 590, 620, 588, - 632, 614, 433, 374, 0, 0, 377, 280, 303, 318, - 0, 605, 496, 226, 461, 289, 250, 0, 0, 210, - 245, 229, 258, 273, 276, 322, 387, 395, 424, 429, - 295, 270, 243, 454, 240, 479, 511, 512, 513, 515, - 391, 265, 428, 1735, 0, 372, 568, 569, 314, 520, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 411, 0, 0, 0, 1737, 0, 0, 0, 0, - 269, 0, 0, 0, 0, 362, 266, 0, 0, 425, - 0, 203, 0, 481, 251, 373, 370, 575, 281, 272, - 268, 249, 315, 381, 423, 510, 417, 0, 366, 0, - 0, 491, 396, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 321, 247, - 323, 202, 408, 492, 285, 0, 0, 0, 0, 1739, - 709, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 237, 0, 0, 244, 0, 0, 0, 347, 356, 355, - 336, 337, 339, 341, 346, 353, 359, 0, 0, 0, - 0, 0, 264, 319, 271, 263, 572, 0, 0, 0, - 0, 0, 0, 0, 0, 228, 0, 0, 0, 1451, - 0, 1452, 1453, 0, 0, 0, 0, 0, 0, 0, - 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 296, 0, 397, 256, 0, 448, 0, 0, - 0, 616, 0, 0, 0, 0, 0, 0, 0, 361, - 0, 328, 197, 224, 0, 0, 407, 456, 468, 0, - 0, 0, 252, 0, 466, 421, 594, 232, 283, 453, - 427, 464, 435, 286, 0, 0, 465, 368, 577, 445, - 591, 617, 618, 262, 401, 603, 514, 611, 635, 225, - 259, 415, 499, 597, 488, 393, 573, 574, 327, 487, - 294, 201, 365, 623, 223, 474, 367, 241, 230, 579, - 600, 288, 451, 630, 212, 509, 589, 238, 478, 0, - 0, 638, 246, 498, 214, 586, 497, 389, 324, 325, - 213, 0, 452, 267, 292, 0, 0, 257, 410, 581, - 582, 255, 639, 227, 610, 219, 0, 609, 403, 576, - 587, 390, 379, 218, 585, 388, 378, 332, 351, 352, - 279, 305, 442, 371, 443, 304, 306, 399, 398, 400, - 206, 598, 0, 207, 0, 493, 599, 640, 447, 211, - 233, 234, 236, 0, 278, 282, 290, 293, 301, 302, - 311, 363, 414, 441, 437, 446, 0, 571, 592, 604, - 615, 621, 622, 624, 625, 626, 627, 628, 631, 629, - 402, 309, 489, 331, 369, 0, 0, 420, 467, 239, - 596, 490, 199, 0, 0, 0, 0, 253, 254, 0, - 567, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, - 651, 652, 653, 654, 655, 656, 657, 658, 636, 500, - 506, 501, 502, 503, 504, 505, 0, 507, 0, 0, - 0, 0, 0, 0, 583, 584, 659, 380, 480, 593, - 333, 345, 348, 338, 357, 0, 358, 334, 335, 340, - 342, 343, 344, 349, 350, 354, 360, 248, 209, 386, - 394, 570, 310, 215, 216, 217, 516, 517, 518, 519, - 607, 608, 612, 204, 457, 458, 459, 460, 291, 602, - 307, 463, 462, 329, 330, 375, 444, 532, 534, 545, - 549, 551, 553, 559, 562, 533, 535, 546, 550, 552, - 554, 560, 563, 522, 524, 526, 528, 541, 540, 537, - 565, 566, 543, 548, 527, 539, 544, 557, 564, 561, - 521, 525, 529, 538, 556, 555, 536, 547, 558, 542, - 530, 523, 531, 0, 196, 220, 364, 0, 449, 287, - 637, 606, 601, 205, 222, 0, 261, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, - 208, 221, 231, 235, 242, 260, 275, 277, 284, 297, - 308, 316, 317, 320, 326, 376, 382, 383, 384, 385, - 404, 405, 406, 409, 412, 413, 416, 418, 419, 422, - 426, 430, 431, 432, 434, 436, 438, 450, 455, 469, - 470, 471, 472, 473, 476, 477, 482, 483, 484, 485, - 486, 494, 495, 508, 578, 580, 595, 613, 619, 475, - 299, 300, 439, 440, 312, 313, 633, 634, 298, 590, - 620, 588, 632, 614, 433, 374, 0, 0, 377, 280, - 303, 318, 0, 605, 496, 226, 461, 289, 250, 0, - 0, 210, 245, 229, 258, 273, 276, 322, 387, 395, - 424, 429, 295, 270, 243, 454, 240, 479, 511, 512, - 513, 515, 391, 265, 428, 0, 392, 372, 568, 569, - 314, 86, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 95, - 0, 1716, 0, 709, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 0, 0, 0, 0, 3429, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3712, 3713, 3714, 3718, + 3719, 3720, 3731, 3778, 3779, 3787, 3789, 869, 3788, 3790, + 3791, 3792, 3795, 3796, 3797, 3798, 3793, 3794, 3799, 3695, + 3699, 3696, 3697, 3698, 3710, 3700, 3701, 3702, 3703, 3704, + 3705, 3706, 3707, 3708, 3709, 3711, 3810, 3811, 3812, 3813, + 3814, 3815, 3724, 3728, 3727, 3725, 3726, 3722, 3723, 3750, + 3749, 3751, 3752, 3753, 3754, 3755, 3756, 3758, 3757, 3759, + 3760, 3761, 3762, 3763, 3764, 3732, 3733, 3736, 3737, 3735, + 3734, 3738, 3747, 3748, 3739, 3740, 3741, 3742, 3743, 3744, + 3746, 3745, 3765, 3766, 3767, 3768, 3769, 3771, 3770, 3774, + 3775, 3773, 3772, 3777, 3776, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 919, 0, 920, + 0, 0, 924, 0, 0, 0, 926, 925, 0, 927, + 889, 888, 0, 0, 921, 922, 0, 923, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2352, 2352, + 0, 0, 0, 0, 0, 0, 0, 1930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3579, 3580, 3581, + 3582, 0, 3816, 3817, 3818, 3819, 3820, 3821, 3822, 3823, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 94, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 95, 0, 0, 0, 194, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 2369, 0, - 0, 2368, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 2319, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 1918, 194, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 2317, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 709, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, - 0, 0, 0, 0, 0, 0, 0, 0, 1072, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 1078, - 328, 197, 224, 1076, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 2319, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 0, 0, 0, - 1918, 194, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 1716, 0, 709, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3656, + 0, 3656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3683, 0, 3685, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 3897, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 2079, 709, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2080, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 2813, 709, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2814, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 709, - 0, 0, 0, 0, 2798, 0, 0, 0, 0, 237, - 0, 0, 244, 2799, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3850, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 0, 0, 0, - 0, 269, 1758, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 0, 0, 0, - 1757, 709, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 711, 712, 713, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3656, 0, 0, 0, 666, 0, 0, 3656, 0, 3656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 0, 709, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 4020, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 1918, 194, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 709, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4029, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 3897, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 95, 0, 0, - 0, 709, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, - 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 2370, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 1983, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3995, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4003, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4007, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1268, 1268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 1739, 709, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4055, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4003, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2352, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 393, 0, + 3429, 0, 4055, 1403, 1389, 522, 0, 1331, 1406, 1300, + 1319, 1416, 1322, 1325, 1368, 1278, 1346, 413, 1316, 1271, + 1304, 1273, 1311, 1274, 1302, 1333, 269, 1299, 1391, 1350, + 1405, 363, 266, 1280, 1305, 427, 1321, 203, 1370, 483, + 251, 374, 371, 577, 281, 272, 268, 249, 316, 382, + 425, 512, 419, 1412, 367, 1356, 0, 493, 398, 0, + 0, 0, 1335, 1395, 1344, 1382, 1330, 1369, 1288, 1355, + 1407, 1317, 1365, 1408, 322, 247, 324, 202, 410, 494, + 285, 0, 0, 0, 0, 4031, 944, 0, 0, 0, + 0, 4032, 0, 0, 0, 0, 237, 0, 0, 244, + 0, 0, 0, 348, 357, 356, 337, 338, 340, 342, + 347, 354, 360, 1313, 1362, 1402, 1314, 1364, 264, 320, + 271, 263, 574, 1413, 1394, 1277, 1343, 1401, 1338, 0, + 0, 228, 1404, 1337, 0, 1367, 0, 1419, 1272, 1358, + 0, 1275, 1279, 1415, 1399, 1308, 274, 0, 0, 0, + 0, 0, 0, 0, 1334, 1345, 1379, 1383, 1328, 0, + 0, 0, 0, 0, 0, 0, 0, 1306, 0, 1354, + 0, 0, 0, 1284, 1276, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1332, 0, 0, + 0, 0, 1287, 0, 1307, 1380, 0, 1270, 296, 1281, + 399, 256, 0, 450, 1387, 1398, 1329, 618, 1400, 1327, + 1326, 1374, 1285, 1393, 1320, 362, 1283, 329, 197, 224, + 0, 1318, 409, 458, 470, 1392, 1303, 1312, 252, 1310, + 468, 423, 596, 232, 283, 455, 429, 466, 437, 286, + 1353, 1372, 467, 369, 579, 447, 593, 619, 620, 262, + 403, 605, 516, 613, 637, 225, 259, 417, 501, 599, + 490, 394, 575, 576, 328, 489, 294, 201, 366, 625, + 223, 476, 368, 241, 230, 581, 602, 298, 288, 453, + 632, 212, 511, 591, 238, 480, 0, 0, 640, 246, + 500, 214, 588, 499, 390, 325, 326, 213, 0, 454, + 267, 292, 0, 0, 257, 412, 583, 584, 255, 641, + 227, 612, 219, 1282, 611, 405, 578, 589, 391, 380, + 218, 587, 389, 379, 333, 352, 353, 279, 306, 444, + 372, 445, 305, 307, 401, 400, 402, 206, 600, 0, + 207, 0, 495, 601, 642, 449, 211, 233, 234, 236, + 1298, 278, 282, 290, 293, 302, 303, 312, 364, 416, + 443, 439, 448, 1388, 573, 594, 606, 617, 623, 624, + 626, 627, 628, 629, 630, 633, 631, 404, 310, 491, + 332, 370, 1377, 1418, 422, 469, 239, 598, 492, 199, + 1292, 1297, 1290, 0, 253, 254, 1359, 569, 1293, 1291, + 1348, 1349, 1294, 1409, 1410, 1411, 1396, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 0, 194, 0, 0, + 656, 657, 658, 659, 660, 638, 502, 508, 503, 504, + 505, 506, 507, 0, 509, 1381, 1286, 0, 1295, 1296, + 395, 1390, 585, 586, 661, 381, 482, 595, 334, 346, + 349, 339, 358, 0, 359, 335, 336, 341, 343, 344, + 345, 350, 351, 355, 361, 248, 209, 387, 396, 572, + 311, 215, 216, 217, 518, 519, 520, 521, 609, 610, + 614, 204, 459, 460, 461, 462, 291, 604, 308, 465, + 464, 330, 331, 376, 446, 534, 536, 547, 551, 553, + 555, 561, 564, 535, 537, 548, 552, 554, 556, 562, + 565, 524, 526, 528, 530, 543, 542, 539, 567, 568, + 545, 550, 529, 541, 546, 559, 566, 563, 523, 527, + 531, 540, 558, 557, 538, 549, 560, 544, 532, 525, + 533, 1352, 196, 220, 365, 1414, 451, 287, 639, 608, + 603, 205, 222, 1289, 261, 1301, 1309, 0, 1315, 1323, + 1324, 1336, 1339, 1340, 1341, 1342, 1360, 1361, 1363, 1371, + 1373, 1376, 1378, 1385, 1397, 1417, 198, 200, 208, 221, + 231, 235, 242, 260, 275, 277, 284, 297, 309, 317, + 318, 321, 327, 377, 383, 384, 385, 386, 406, 407, + 408, 411, 414, 415, 418, 420, 421, 424, 428, 432, + 433, 434, 436, 438, 440, 452, 457, 471, 472, 473, + 474, 475, 478, 479, 484, 485, 486, 487, 488, 496, + 497, 510, 580, 582, 597, 615, 621, 477, 300, 301, + 441, 442, 313, 314, 635, 636, 299, 592, 622, 590, + 634, 616, 435, 375, 1351, 1357, 378, 280, 304, 319, + 1366, 607, 498, 226, 463, 289, 250, 1384, 1386, 210, + 245, 229, 258, 273, 276, 323, 388, 397, 426, 431, + 295, 270, 243, 456, 240, 481, 513, 514, 515, 517, + 392, 265, 430, 1347, 1375, 373, 570, 571, 315, 393, + 0, 0, 0, 0, 1403, 1389, 522, 0, 1331, 1406, + 1300, 1319, 1416, 1322, 1325, 1368, 1278, 1346, 413, 1316, + 1271, 1304, 1273, 1311, 1274, 1302, 1333, 269, 1299, 1391, + 1350, 1405, 363, 266, 1280, 1305, 427, 1321, 203, 1370, + 483, 251, 374, 371, 577, 281, 272, 268, 249, 316, + 382, 425, 512, 419, 1412, 367, 1356, 0, 493, 398, + 0, 0, 0, 1335, 1395, 1344, 1382, 1330, 1369, 1288, + 1355, 1407, 1317, 1365, 1408, 322, 247, 324, 202, 410, + 494, 285, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, + 244, 0, 0, 0, 348, 357, 356, 337, 338, 340, + 342, 347, 354, 360, 1313, 1362, 1402, 1314, 1364, 264, + 320, 271, 263, 574, 1413, 1394, 1277, 1343, 1401, 1338, + 0, 0, 228, 1404, 1337, 0, 1367, 0, 1419, 1272, + 1358, 0, 1275, 1279, 1415, 1399, 1308, 274, 0, 0, + 0, 0, 0, 0, 0, 1334, 1345, 1379, 1383, 1328, + 0, 0, 0, 0, 0, 0, 3192, 0, 1306, 0, + 1354, 0, 0, 0, 1284, 1276, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1332, 0, + 0, 0, 0, 1287, 0, 1307, 1380, 0, 1270, 296, + 1281, 399, 256, 0, 450, 1387, 1398, 1329, 618, 1400, + 1327, 1326, 1374, 1285, 1393, 1320, 362, 1283, 329, 197, + 224, 0, 1318, 409, 458, 470, 1392, 1303, 1312, 252, + 1310, 468, 423, 596, 232, 283, 455, 429, 466, 437, + 286, 1353, 1372, 467, 369, 579, 447, 593, 619, 620, + 262, 403, 605, 516, 613, 637, 225, 259, 417, 501, + 599, 490, 394, 575, 576, 328, 489, 294, 201, 366, + 625, 223, 476, 368, 241, 230, 581, 602, 298, 288, + 453, 632, 212, 511, 591, 238, 480, 0, 0, 640, + 246, 500, 214, 588, 499, 390, 325, 326, 213, 0, + 454, 267, 292, 0, 0, 257, 412, 583, 584, 255, + 641, 227, 612, 219, 1282, 611, 405, 578, 589, 391, + 380, 218, 587, 389, 379, 333, 352, 353, 279, 306, + 444, 372, 445, 305, 307, 401, 400, 402, 206, 600, + 0, 207, 0, 495, 601, 642, 449, 211, 233, 234, + 236, 1298, 278, 282, 290, 293, 302, 303, 312, 364, + 416, 443, 439, 448, 1388, 573, 594, 606, 617, 623, + 624, 626, 627, 628, 629, 630, 633, 631, 404, 310, + 491, 332, 370, 1377, 1418, 422, 469, 239, 598, 492, + 199, 1292, 1297, 1290, 0, 253, 254, 1359, 569, 1293, + 1291, 1348, 1349, 1294, 1409, 1410, 1411, 1396, 643, 644, + 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, + 655, 656, 657, 658, 659, 660, 638, 502, 508, 503, + 504, 505, 506, 507, 0, 509, 1381, 1286, 0, 1295, + 1296, 395, 1390, 585, 586, 661, 381, 482, 595, 334, + 346, 349, 339, 358, 0, 359, 335, 336, 341, 343, + 344, 345, 350, 351, 355, 361, 248, 209, 387, 396, + 572, 311, 215, 216, 217, 518, 519, 520, 521, 609, + 610, 614, 204, 459, 460, 461, 462, 291, 604, 308, + 465, 464, 330, 331, 376, 446, 534, 536, 547, 551, + 553, 555, 561, 564, 535, 537, 548, 552, 554, 556, + 562, 565, 524, 526, 528, 530, 543, 542, 539, 567, + 568, 545, 550, 529, 541, 546, 559, 566, 563, 523, + 527, 531, 540, 558, 557, 538, 549, 560, 544, 532, + 525, 533, 1352, 196, 220, 365, 1414, 451, 287, 639, + 608, 603, 205, 222, 1289, 261, 1301, 1309, 0, 1315, + 1323, 1324, 1336, 1339, 1340, 1341, 1342, 1360, 1361, 1363, + 1371, 1373, 1376, 1378, 1385, 1397, 1417, 198, 200, 208, + 221, 231, 235, 242, 260, 275, 277, 284, 297, 309, + 317, 318, 321, 327, 377, 383, 384, 385, 386, 406, + 407, 408, 411, 414, 415, 418, 420, 421, 424, 428, + 432, 433, 434, 436, 438, 440, 452, 457, 471, 472, + 473, 474, 475, 478, 479, 484, 485, 486, 487, 488, + 496, 497, 510, 580, 582, 597, 615, 621, 477, 300, + 301, 441, 442, 313, 314, 635, 636, 299, 592, 622, + 590, 634, 616, 435, 375, 1351, 1357, 378, 280, 304, + 319, 1366, 607, 498, 226, 463, 289, 250, 1384, 1386, + 210, 245, 229, 258, 273, 276, 323, 388, 397, 426, + 431, 295, 270, 243, 456, 240, 481, 513, 514, 515, + 517, 392, 265, 430, 1347, 1375, 373, 570, 571, 315, + 393, 0, 0, 0, 0, 1403, 1389, 522, 0, 1331, + 1406, 1300, 1319, 1416, 1322, 1325, 1368, 1278, 1346, 413, + 1316, 1271, 1304, 1273, 1311, 1274, 1302, 1333, 269, 1299, + 1391, 1350, 1405, 363, 266, 1280, 1305, 427, 1321, 203, + 1370, 483, 251, 374, 371, 577, 281, 272, 268, 249, + 316, 382, 425, 512, 419, 1412, 367, 1356, 0, 493, + 398, 0, 0, 0, 1335, 1395, 1344, 1382, 1330, 1369, + 1288, 1355, 1407, 1317, 1365, 1408, 322, 247, 324, 202, + 410, 494, 285, 0, 0, 0, 0, 0, 711, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, + 0, 244, 0, 0, 0, 348, 357, 356, 337, 338, + 340, 342, 347, 354, 360, 1313, 1362, 1402, 1314, 1364, + 264, 320, 271, 263, 574, 1413, 1394, 1277, 1343, 1401, + 1338, 0, 0, 228, 1404, 1337, 0, 1367, 0, 1419, + 1272, 1358, 0, 1275, 1279, 1415, 1399, 1308, 274, 0, + 0, 0, 0, 0, 0, 0, 1334, 1345, 1379, 1383, + 1328, 0, 0, 0, 0, 0, 0, 3153, 0, 1306, + 0, 1354, 0, 0, 0, 1284, 1276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1332, + 0, 0, 0, 0, 1287, 0, 1307, 1380, 0, 1270, + 296, 1281, 399, 256, 0, 450, 1387, 1398, 1329, 618, + 1400, 1327, 1326, 1374, 1285, 1393, 1320, 362, 1283, 329, + 197, 224, 0, 1318, 409, 458, 470, 1392, 1303, 1312, + 252, 1310, 468, 423, 596, 232, 283, 455, 429, 466, + 437, 286, 1353, 1372, 467, 369, 579, 447, 593, 619, + 620, 262, 403, 605, 516, 613, 637, 225, 259, 417, + 501, 599, 490, 394, 575, 576, 328, 489, 294, 201, + 366, 625, 223, 476, 368, 241, 230, 581, 602, 298, + 288, 453, 632, 212, 511, 591, 238, 480, 0, 0, + 640, 246, 500, 214, 588, 499, 390, 325, 326, 213, + 0, 454, 267, 292, 0, 0, 257, 412, 583, 584, + 255, 641, 227, 612, 219, 1282, 611, 405, 578, 589, + 391, 380, 218, 587, 389, 379, 333, 352, 353, 279, + 306, 444, 372, 445, 305, 307, 401, 400, 402, 206, + 600, 0, 207, 0, 495, 601, 642, 449, 211, 233, + 234, 236, 1298, 278, 282, 290, 293, 302, 303, 312, + 364, 416, 443, 439, 448, 1388, 573, 594, 606, 617, + 623, 624, 626, 627, 628, 629, 630, 633, 631, 404, + 310, 491, 332, 370, 1377, 1418, 422, 469, 239, 598, + 492, 199, 1292, 1297, 1290, 0, 253, 254, 1359, 569, + 1293, 1291, 1348, 1349, 1294, 1409, 1410, 1411, 1396, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 2031, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 2022, 709, + 654, 655, 656, 657, 658, 659, 660, 638, 502, 508, + 503, 504, 505, 506, 507, 0, 509, 1381, 1286, 0, + 1295, 1296, 395, 1390, 585, 586, 661, 381, 482, 595, + 334, 346, 349, 339, 358, 0, 359, 335, 336, 341, + 343, 344, 345, 350, 351, 355, 361, 248, 209, 387, + 396, 572, 311, 215, 216, 217, 518, 519, 520, 521, + 609, 610, 614, 204, 459, 460, 461, 462, 291, 604, + 308, 465, 464, 330, 331, 376, 446, 534, 536, 547, + 551, 553, 555, 561, 564, 535, 537, 548, 552, 554, + 556, 562, 565, 524, 526, 528, 530, 543, 542, 539, + 567, 568, 545, 550, 529, 541, 546, 559, 566, 563, + 523, 527, 531, 540, 558, 557, 538, 549, 560, 544, + 532, 525, 533, 1352, 196, 220, 365, 1414, 451, 287, + 639, 608, 603, 205, 222, 1289, 261, 1301, 1309, 0, + 1315, 1323, 1324, 1336, 1339, 1340, 1341, 1342, 1360, 1361, + 1363, 1371, 1373, 1376, 1378, 1385, 1397, 1417, 198, 200, + 208, 221, 231, 235, 242, 260, 275, 277, 284, 297, + 309, 317, 318, 321, 327, 377, 383, 384, 385, 386, + 406, 407, 408, 411, 414, 415, 418, 420, 421, 424, + 428, 432, 433, 434, 436, 438, 440, 452, 457, 471, + 472, 473, 474, 475, 478, 479, 484, 485, 486, 487, + 488, 496, 497, 510, 580, 582, 597, 615, 621, 477, + 300, 301, 441, 442, 313, 314, 635, 636, 299, 592, + 622, 590, 634, 616, 435, 375, 1351, 1357, 378, 280, + 304, 319, 1366, 607, 498, 226, 463, 289, 250, 1384, + 1386, 210, 245, 229, 258, 273, 276, 323, 388, 397, + 426, 431, 295, 270, 243, 456, 240, 481, 513, 514, + 515, 517, 392, 265, 430, 1347, 1375, 373, 570, 571, + 315, 393, 0, 0, 0, 0, 1403, 1389, 522, 0, + 1331, 1406, 1300, 1319, 1416, 1322, 1325, 1368, 1278, 1346, + 413, 1316, 1271, 1304, 1273, 1311, 1274, 1302, 1333, 269, + 1299, 1391, 1350, 1405, 363, 266, 1280, 1305, 427, 1321, + 203, 1370, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 1412, 367, 1356, 0, + 493, 398, 0, 0, 0, 1335, 1395, 1344, 1382, 1330, + 1369, 1288, 1355, 1407, 1317, 1365, 1408, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 944, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 1313, 1362, 1402, 1314, + 1364, 264, 320, 271, 263, 574, 1413, 1394, 1277, 1343, + 1401, 1338, 0, 0, 228, 1404, 1337, 0, 1367, 0, + 1419, 1272, 1358, 0, 1275, 1279, 1415, 1399, 1308, 274, + 0, 0, 0, 0, 0, 0, 0, 1334, 1345, 1379, + 1383, 1328, 0, 0, 0, 0, 0, 0, 2370, 0, + 1306, 0, 1354, 0, 0, 0, 1284, 1276, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1332, 0, 0, 0, 0, 1287, 0, 1307, 1380, 0, + 1270, 296, 1281, 399, 256, 0, 450, 1387, 1398, 1329, + 618, 1400, 1327, 1326, 1374, 1285, 1393, 1320, 362, 1283, + 329, 197, 224, 0, 1318, 409, 458, 470, 1392, 1303, + 1312, 252, 1310, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 1353, 1372, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 1282, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 1298, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 1388, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 1377, 1418, 422, 469, 239, + 598, 492, 199, 1292, 1297, 1290, 0, 253, 254, 1359, + 569, 1293, 1291, 1348, 1349, 1294, 1409, 1410, 1411, 1396, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 1381, 1286, + 0, 1295, 1296, 395, 1390, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 1352, 196, 220, 365, 1414, 451, + 287, 639, 608, 603, 205, 222, 1289, 261, 1301, 1309, + 0, 1315, 1323, 1324, 1336, 1339, 1340, 1341, 1342, 1360, + 1361, 1363, 1371, 1373, 1376, 1378, 1385, 1397, 1417, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 1351, 1357, 378, + 280, 304, 319, 1366, 607, 498, 226, 463, 289, 250, + 1384, 1386, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 1347, 1375, 373, 570, + 571, 315, 393, 0, 0, 0, 0, 1403, 1389, 522, + 0, 1331, 1406, 1300, 1319, 1416, 1322, 1325, 1368, 1278, + 1346, 413, 1316, 1271, 1304, 1273, 1311, 1274, 1302, 1333, + 269, 1299, 1391, 1350, 1405, 363, 266, 1280, 1305, 427, + 1321, 203, 1370, 483, 251, 374, 371, 577, 281, 272, + 268, 249, 316, 382, 425, 512, 419, 1412, 367, 1356, + 0, 493, 398, 0, 0, 0, 1335, 1395, 1344, 1382, + 1330, 1369, 1288, 1355, 1407, 1317, 1365, 1408, 322, 247, + 324, 202, 410, 494, 285, 0, 95, 0, 0, 0, + 711, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 237, 0, 0, 244, 0, 0, 0, 348, 357, 356, + 337, 338, 340, 342, 347, 354, 360, 1313, 1362, 1402, + 1314, 1364, 264, 320, 271, 263, 574, 1413, 1394, 1277, + 1343, 1401, 1338, 0, 0, 228, 1404, 1337, 0, 1367, + 0, 1419, 1272, 1358, 0, 1275, 1279, 1415, 1399, 1308, + 274, 0, 0, 0, 0, 0, 0, 0, 1334, 1345, + 1379, 1383, 1328, 0, 0, 0, 0, 0, 0, 0, + 0, 1306, 0, 1354, 0, 0, 0, 1284, 1276, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1332, 0, 0, 0, 0, 1287, 0, 1307, 1380, + 0, 1270, 296, 1281, 399, 256, 0, 450, 1387, 1398, + 1329, 618, 1400, 1327, 1326, 1374, 1285, 1393, 1320, 362, + 1283, 329, 197, 224, 0, 1318, 409, 458, 470, 1392, + 1303, 1312, 252, 1310, 468, 423, 596, 232, 283, 455, + 429, 466, 437, 286, 1353, 1372, 467, 369, 579, 447, + 593, 619, 620, 262, 403, 605, 516, 613, 637, 225, + 259, 417, 501, 599, 490, 394, 575, 576, 328, 489, + 294, 201, 366, 625, 223, 476, 368, 241, 230, 581, + 602, 298, 288, 453, 632, 212, 511, 591, 238, 480, + 0, 0, 640, 246, 500, 214, 588, 499, 390, 325, + 326, 213, 0, 454, 267, 292, 0, 0, 257, 412, + 583, 584, 255, 641, 227, 612, 219, 1282, 611, 405, + 578, 589, 391, 380, 218, 587, 389, 379, 333, 352, + 353, 279, 306, 444, 372, 445, 305, 307, 401, 400, + 402, 206, 600, 0, 207, 0, 495, 601, 642, 449, + 211, 233, 234, 236, 1298, 278, 282, 290, 293, 302, + 303, 312, 364, 416, 443, 439, 448, 1388, 573, 594, + 606, 617, 623, 624, 626, 627, 628, 629, 630, 633, + 631, 404, 310, 491, 332, 370, 1377, 1418, 422, 469, + 239, 598, 492, 199, 1292, 1297, 1290, 0, 253, 254, + 1359, 569, 1293, 1291, 1348, 1349, 1294, 1409, 1410, 1411, + 1396, 643, 644, 645, 646, 647, 648, 649, 650, 651, + 652, 653, 654, 655, 656, 657, 658, 659, 660, 638, + 502, 508, 503, 504, 505, 506, 507, 0, 509, 1381, + 1286, 0, 1295, 1296, 395, 1390, 585, 586, 661, 381, + 482, 595, 334, 346, 349, 339, 358, 0, 359, 335, + 336, 341, 343, 344, 345, 350, 351, 355, 361, 248, + 209, 387, 396, 572, 311, 215, 216, 217, 518, 519, + 520, 521, 609, 610, 614, 204, 459, 460, 461, 462, + 291, 604, 308, 465, 464, 330, 331, 376, 446, 534, + 536, 547, 551, 553, 555, 561, 564, 535, 537, 548, + 552, 554, 556, 562, 565, 524, 526, 528, 530, 543, + 542, 539, 567, 568, 545, 550, 529, 541, 546, 559, + 566, 563, 523, 527, 531, 540, 558, 557, 538, 549, + 560, 544, 532, 525, 533, 1352, 196, 220, 365, 1414, + 451, 287, 639, 608, 603, 205, 222, 1289, 261, 1301, + 1309, 0, 1315, 1323, 1324, 1336, 1339, 1340, 1341, 1342, + 1360, 1361, 1363, 1371, 1373, 1376, 1378, 1385, 1397, 1417, + 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, + 284, 297, 309, 317, 318, 321, 327, 377, 383, 384, + 385, 386, 406, 407, 408, 411, 414, 415, 418, 420, + 421, 424, 428, 432, 433, 434, 436, 438, 440, 452, + 457, 471, 472, 473, 474, 475, 478, 479, 484, 485, + 486, 487, 488, 496, 497, 510, 580, 582, 597, 615, + 621, 477, 300, 301, 441, 442, 313, 314, 635, 636, + 299, 592, 622, 590, 634, 616, 435, 375, 1351, 1357, + 378, 280, 304, 319, 1366, 607, 498, 226, 463, 289, + 250, 1384, 1386, 210, 245, 229, 258, 273, 276, 323, + 388, 397, 426, 431, 295, 270, 243, 456, 240, 481, + 513, 514, 515, 517, 392, 265, 430, 1347, 1375, 373, + 570, 571, 315, 393, 0, 0, 0, 0, 1403, 1389, + 522, 0, 1331, 1406, 1300, 1319, 1416, 1322, 1325, 1368, + 1278, 1346, 413, 1316, 1271, 1304, 1273, 1311, 1274, 1302, + 1333, 269, 1299, 1391, 1350, 1405, 363, 266, 1280, 1305, + 427, 1321, 203, 1370, 483, 251, 374, 371, 577, 281, + 272, 268, 249, 316, 382, 425, 512, 419, 1412, 367, + 1356, 0, 493, 398, 0, 0, 0, 1335, 1395, 1344, + 1382, 1330, 1369, 1288, 1355, 1407, 1317, 1365, 1408, 322, + 247, 324, 202, 410, 494, 285, 0, 0, 0, 0, + 0, 194, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 237, 0, 0, 244, 0, 0, 0, 348, 357, + 356, 337, 338, 340, 342, 347, 354, 360, 1313, 1362, + 1402, 1314, 1364, 264, 320, 271, 263, 574, 1413, 1394, + 1277, 1343, 1401, 1338, 0, 0, 228, 1404, 1337, 0, + 1367, 0, 1419, 1272, 1358, 0, 1275, 1279, 1415, 1399, + 1308, 274, 0, 0, 0, 0, 0, 0, 0, 1334, + 1345, 1379, 1383, 1328, 0, 0, 0, 0, 0, 0, + 0, 0, 1306, 0, 1354, 0, 0, 0, 1284, 1276, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1332, 0, 0, 0, 0, 1287, 0, 1307, + 1380, 0, 1270, 296, 1281, 399, 256, 0, 450, 1387, + 1398, 1329, 618, 1400, 1327, 1326, 1374, 1285, 1393, 1320, + 362, 1283, 329, 197, 224, 0, 1318, 409, 458, 470, + 1392, 1303, 1312, 252, 1310, 468, 423, 596, 232, 283, + 455, 429, 466, 437, 286, 1353, 1372, 467, 369, 579, + 447, 593, 619, 620, 262, 403, 605, 516, 613, 637, + 225, 259, 417, 501, 599, 490, 394, 575, 576, 328, + 489, 294, 201, 366, 625, 223, 476, 368, 241, 230, + 581, 602, 298, 288, 453, 632, 212, 511, 591, 238, + 480, 0, 0, 640, 246, 500, 214, 588, 499, 390, + 325, 326, 213, 0, 454, 267, 292, 0, 0, 257, + 412, 583, 584, 255, 641, 227, 612, 219, 1282, 611, + 405, 578, 589, 391, 380, 218, 587, 389, 379, 333, + 352, 353, 279, 306, 444, 372, 445, 305, 307, 401, + 400, 402, 206, 600, 0, 207, 0, 495, 601, 642, + 449, 211, 233, 234, 236, 1298, 278, 282, 290, 293, + 302, 303, 312, 364, 416, 443, 439, 448, 1388, 573, + 594, 606, 617, 623, 624, 626, 627, 628, 629, 630, + 633, 631, 404, 310, 491, 332, 370, 1377, 1418, 422, + 469, 239, 598, 492, 199, 1292, 1297, 1290, 0, 253, + 254, 1359, 569, 1293, 1291, 1348, 1349, 1294, 1409, 1410, + 1411, 1396, 643, 644, 645, 646, 647, 648, 649, 650, + 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, + 638, 502, 508, 503, 504, 505, 506, 507, 0, 509, + 1381, 1286, 0, 1295, 1296, 395, 1390, 585, 586, 661, + 381, 482, 595, 334, 346, 349, 339, 358, 0, 359, + 335, 336, 341, 343, 344, 345, 350, 351, 355, 361, + 248, 209, 387, 396, 572, 311, 215, 216, 217, 518, + 519, 520, 521, 609, 610, 614, 204, 459, 460, 461, + 462, 291, 604, 308, 465, 464, 330, 331, 376, 446, + 534, 536, 547, 551, 553, 555, 561, 564, 535, 537, + 548, 552, 554, 556, 562, 565, 524, 526, 528, 530, + 543, 542, 539, 567, 568, 545, 550, 529, 541, 546, + 559, 566, 563, 523, 527, 531, 540, 558, 557, 538, + 549, 560, 544, 532, 525, 533, 1352, 196, 220, 365, + 1414, 451, 287, 639, 608, 603, 205, 222, 1289, 261, + 1301, 1309, 0, 1315, 1323, 1324, 1336, 1339, 1340, 1341, + 1342, 1360, 1361, 1363, 1371, 1373, 1376, 1378, 1385, 1397, + 1417, 198, 200, 208, 221, 231, 235, 242, 260, 275, + 277, 284, 297, 309, 317, 318, 321, 327, 377, 383, + 384, 385, 386, 406, 407, 408, 411, 414, 415, 418, + 420, 421, 424, 428, 432, 433, 434, 436, 438, 440, + 452, 457, 471, 472, 473, 474, 475, 478, 479, 484, + 485, 486, 487, 488, 496, 497, 510, 580, 582, 597, + 615, 621, 477, 300, 301, 441, 442, 313, 314, 635, + 636, 299, 592, 622, 590, 634, 616, 435, 375, 1351, + 1357, 378, 280, 304, 319, 1366, 607, 498, 226, 463, + 289, 250, 1384, 1386, 210, 245, 229, 258, 273, 276, + 323, 388, 397, 426, 431, 295, 270, 243, 456, 240, + 481, 513, 514, 515, 517, 392, 265, 430, 1347, 1375, + 373, 570, 571, 315, 393, 0, 0, 0, 0, 1403, + 1389, 522, 0, 1331, 1406, 1300, 1319, 1416, 1322, 1325, + 1368, 1278, 1346, 413, 1316, 1271, 1304, 1273, 1311, 1274, + 1302, 1333, 269, 1299, 1391, 1350, 1405, 363, 266, 1280, + 1305, 427, 1321, 203, 1370, 483, 251, 374, 371, 577, + 281, 272, 268, 249, 316, 382, 425, 512, 419, 1412, + 367, 1356, 0, 493, 398, 0, 0, 0, 1335, 1395, + 1344, 1382, 1330, 1369, 1288, 1355, 1407, 1317, 1365, 1408, + 322, 247, 324, 202, 410, 494, 285, 0, 0, 0, + 0, 0, 711, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 237, 0, 0, 244, 0, 0, 0, 348, + 357, 356, 337, 338, 340, 342, 347, 354, 360, 1313, + 1362, 1402, 1314, 1364, 264, 320, 271, 263, 574, 1413, + 1394, 1277, 1343, 1401, 1338, 0, 0, 228, 1404, 1337, + 0, 1367, 0, 1419, 1272, 1358, 0, 1275, 1279, 1415, + 1399, 1308, 274, 0, 0, 0, 0, 0, 0, 0, + 1334, 1345, 1379, 1383, 1328, 0, 0, 0, 0, 0, + 0, 0, 0, 1306, 0, 1354, 0, 0, 0, 1284, + 1276, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1332, 0, 0, 0, 0, 1287, 0, + 1307, 1380, 0, 1270, 296, 1281, 399, 256, 0, 450, + 1387, 1398, 1329, 618, 1400, 1327, 1326, 1374, 1285, 1393, + 1320, 362, 1283, 329, 197, 224, 0, 1318, 409, 458, + 470, 1392, 1303, 1312, 252, 1310, 468, 423, 596, 232, + 283, 455, 429, 466, 437, 286, 1353, 1372, 467, 369, + 579, 447, 593, 619, 620, 262, 403, 605, 516, 613, + 637, 225, 259, 417, 501, 599, 490, 394, 575, 576, + 328, 489, 294, 201, 366, 625, 223, 476, 368, 241, + 230, 581, 602, 298, 288, 453, 632, 212, 511, 591, + 238, 480, 0, 0, 640, 246, 500, 214, 588, 499, + 390, 325, 326, 213, 0, 454, 267, 292, 0, 0, + 257, 412, 583, 584, 255, 641, 227, 612, 219, 1282, + 611, 405, 578, 589, 391, 380, 218, 587, 389, 379, + 333, 352, 353, 279, 306, 444, 372, 445, 305, 307, + 401, 400, 402, 206, 600, 0, 207, 0, 495, 601, + 642, 449, 211, 233, 234, 236, 1298, 278, 282, 290, + 293, 302, 303, 312, 364, 416, 443, 439, 448, 1388, + 573, 594, 606, 617, 623, 624, 626, 627, 628, 629, + 630, 633, 631, 404, 310, 491, 332, 370, 1377, 1418, + 422, 469, 239, 598, 492, 199, 1292, 1297, 1290, 0, + 253, 254, 1359, 569, 1293, 1291, 1348, 1349, 1294, 1409, + 1410, 1411, 1396, 643, 644, 645, 646, 647, 648, 649, + 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, + 660, 638, 502, 508, 503, 504, 505, 506, 507, 0, + 509, 1381, 1286, 0, 1295, 1296, 395, 1390, 585, 586, + 661, 381, 482, 595, 334, 346, 349, 339, 358, 0, + 359, 335, 336, 341, 343, 344, 345, 350, 351, 355, + 361, 248, 209, 387, 396, 572, 311, 215, 216, 217, + 518, 519, 520, 521, 609, 610, 614, 204, 459, 460, + 461, 462, 291, 604, 308, 465, 464, 330, 331, 376, + 446, 534, 536, 547, 551, 553, 555, 561, 564, 535, + 537, 548, 552, 554, 556, 562, 565, 524, 526, 528, + 530, 543, 542, 539, 567, 568, 545, 550, 529, 541, + 546, 559, 566, 563, 523, 527, 531, 540, 558, 557, + 538, 549, 560, 544, 532, 525, 533, 1352, 196, 220, + 365, 1414, 451, 287, 639, 608, 603, 205, 222, 1289, + 261, 1301, 1309, 0, 1315, 1323, 1324, 1336, 1339, 1340, + 1341, 1342, 1360, 1361, 1363, 1371, 1373, 1376, 1378, 1385, + 1397, 1417, 198, 200, 208, 221, 231, 235, 242, 260, + 275, 277, 284, 297, 309, 317, 318, 321, 327, 377, + 383, 384, 385, 386, 406, 407, 408, 411, 414, 415, + 418, 420, 421, 424, 428, 432, 433, 434, 436, 438, + 440, 452, 457, 471, 472, 473, 474, 475, 478, 479, + 484, 485, 486, 487, 488, 496, 497, 510, 580, 582, + 597, 615, 621, 477, 300, 301, 441, 442, 313, 314, + 635, 636, 299, 592, 622, 590, 634, 616, 435, 375, + 1351, 1357, 378, 280, 304, 319, 1366, 607, 498, 226, + 463, 289, 250, 1384, 1386, 210, 245, 229, 258, 273, + 276, 323, 388, 397, 426, 431, 295, 270, 243, 456, + 240, 481, 513, 514, 515, 517, 392, 265, 430, 1347, + 1375, 373, 570, 571, 315, 393, 0, 0, 0, 0, + 1403, 1389, 522, 0, 1331, 1406, 1300, 1319, 1416, 1322, + 1325, 1368, 1278, 1346, 413, 1316, 1271, 1304, 1273, 1311, + 1274, 1302, 1333, 269, 1299, 1391, 1350, 1405, 363, 266, + 1280, 1305, 427, 1321, 203, 1370, 483, 251, 374, 371, + 577, 281, 272, 268, 249, 316, 382, 425, 512, 419, + 1412, 367, 1356, 0, 493, 398, 0, 0, 0, 1335, + 1395, 1344, 1382, 1330, 1369, 1288, 1355, 1407, 1317, 1365, + 1408, 322, 247, 324, 202, 410, 494, 285, 0, 0, + 0, 0, 0, 944, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, + 348, 357, 356, 337, 338, 340, 342, 347, 354, 360, + 1313, 1362, 1402, 1314, 1364, 264, 320, 271, 263, 574, + 1413, 1394, 1277, 1343, 1401, 1338, 0, 0, 228, 1404, + 1337, 0, 1367, 0, 1419, 1272, 1358, 0, 1275, 1279, + 1415, 1399, 1308, 274, 0, 0, 0, 0, 0, 0, + 0, 1334, 1345, 1379, 1383, 1328, 0, 0, 0, 0, + 0, 0, 0, 0, 1306, 0, 1354, 0, 0, 0, + 1284, 1276, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1332, 0, 0, 0, 0, 1287, + 0, 1307, 1380, 0, 1270, 296, 1281, 399, 256, 0, + 450, 1387, 1398, 1329, 618, 1400, 1327, 1326, 1374, 1285, + 1393, 1320, 362, 1283, 329, 197, 224, 0, 1318, 409, + 458, 470, 1392, 1303, 1312, 252, 1310, 468, 423, 596, + 232, 283, 455, 429, 466, 437, 286, 1353, 1372, 467, + 369, 579, 447, 593, 619, 620, 262, 403, 605, 516, + 613, 637, 225, 259, 417, 501, 599, 490, 394, 575, + 576, 328, 489, 294, 201, 366, 625, 223, 476, 368, + 241, 230, 581, 602, 298, 288, 453, 632, 212, 511, + 591, 238, 480, 0, 0, 640, 246, 500, 214, 588, + 499, 390, 325, 326, 213, 0, 454, 267, 292, 0, + 0, 257, 412, 583, 584, 255, 641, 227, 612, 219, + 1282, 611, 405, 578, 589, 391, 380, 218, 587, 389, + 379, 333, 352, 353, 279, 306, 444, 372, 445, 305, + 307, 401, 400, 402, 206, 600, 0, 207, 0, 495, + 601, 642, 449, 211, 233, 234, 236, 1298, 278, 282, + 290, 293, 302, 303, 312, 364, 416, 443, 439, 448, + 1388, 573, 594, 606, 617, 623, 624, 626, 627, 628, + 629, 630, 633, 631, 404, 310, 491, 332, 370, 1377, + 1418, 422, 469, 239, 598, 492, 199, 1292, 1297, 1290, + 0, 253, 254, 1359, 569, 1293, 1291, 1348, 1349, 1294, + 1409, 1410, 1411, 1396, 643, 644, 645, 646, 647, 648, + 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, + 659, 660, 638, 502, 508, 503, 504, 505, 506, 507, + 0, 509, 1381, 1286, 0, 1295, 1296, 395, 1390, 585, + 586, 661, 381, 482, 595, 334, 346, 349, 339, 358, + 0, 359, 335, 336, 341, 343, 344, 345, 350, 351, + 355, 361, 248, 209, 387, 396, 572, 311, 215, 216, + 217, 518, 519, 520, 521, 609, 610, 614, 204, 459, + 460, 461, 462, 291, 604, 308, 465, 464, 330, 331, + 376, 446, 534, 536, 547, 551, 553, 555, 561, 564, + 535, 537, 548, 552, 554, 556, 562, 565, 524, 526, + 528, 530, 543, 542, 539, 567, 568, 545, 550, 529, + 541, 546, 559, 566, 563, 523, 527, 531, 540, 558, + 557, 538, 549, 560, 544, 532, 525, 533, 1352, 196, + 220, 365, 1414, 451, 287, 639, 608, 603, 205, 222, + 1289, 261, 1301, 1309, 0, 1315, 1323, 1324, 1336, 1339, + 1340, 1341, 1342, 1360, 1361, 1363, 1371, 1373, 1376, 1378, + 1385, 1397, 1417, 198, 200, 208, 221, 231, 235, 242, + 260, 275, 277, 284, 297, 309, 317, 318, 321, 327, + 377, 383, 384, 385, 386, 406, 407, 408, 411, 414, + 415, 418, 420, 421, 424, 428, 432, 433, 434, 436, + 438, 440, 452, 457, 471, 472, 473, 474, 475, 478, + 479, 484, 485, 486, 487, 488, 496, 497, 510, 580, + 582, 597, 615, 621, 477, 300, 301, 441, 442, 313, + 314, 635, 636, 299, 592, 622, 590, 634, 616, 435, + 375, 1351, 1357, 378, 280, 304, 319, 1366, 607, 498, + 226, 463, 289, 250, 1384, 1386, 210, 245, 229, 258, + 273, 276, 323, 388, 397, 426, 431, 295, 270, 243, + 456, 240, 481, 513, 514, 515, 517, 392, 265, 430, + 1347, 1375, 373, 570, 571, 315, 393, 0, 0, 0, + 0, 0, 0, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 1008, 944, 735, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 2191, 2192, 2193, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 731, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 757, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 2399, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 735, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 2400, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 731, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 0, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 757, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 86, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 1008, 944, 735, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 731, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 757, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 94, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 735, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 731, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 0, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 757, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 4017, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 1725, 1008, 944, 735, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 731, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 757, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 735, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 731, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 1054, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 757, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 1008, 944, 735, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 731, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 757, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 735, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 731, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 0, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 3110, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 1008, 944, 735, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 731, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 3106, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 1075, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 0, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 757, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 764, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 751, 0, 0, 0, 269, 756, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 763, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 758, 759, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 1008, 944, 1075, 910, 948, 1009, 961, + 962, 963, 949, 0, 237, 950, 951, 244, 952, 0, + 909, 794, 796, 795, 859, 860, 861, 862, 863, 864, + 865, 792, 957, 964, 965, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 748, 0, + 762, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 745, 746, 0, 0, 0, 0, 904, 0, 747, + 0, 0, 755, 966, 967, 968, 969, 970, 971, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 2084, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 903, 0, 0, 618, 0, 0, 901, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 954, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 955, 956, 255, 641, 800, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 808, 809, 279, 306, 885, 884, 883, + 305, 307, 881, 882, 880, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 891, 913, 902, + 768, 769, 892, 893, 917, 894, 771, 772, 914, 915, + 765, 766, 770, 916, 918, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 905, 754, 753, 0, 760, 761, 0, + 790, 791, 793, 797, 798, 799, 810, 857, 858, 866, + 868, 869, 867, 870, 871, 872, 875, 876, 877, 878, + 873, 874, 879, 773, 777, 774, 775, 776, 788, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, + 928, 929, 930, 931, 932, 933, 803, 807, 806, 804, + 805, 801, 802, 829, 828, 830, 831, 832, 833, 834, + 835, 837, 836, 838, 839, 840, 841, 842, 843, 811, + 812, 815, 816, 814, 813, 817, 826, 827, 818, 819, + 820, 821, 822, 823, 825, 824, 844, 845, 846, 847, + 848, 850, 849, 853, 854, 852, 851, 856, 855, 752, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 919, 261, 920, 0, 0, 924, 0, 0, 0, + 926, 925, 0, 927, 889, 888, 0, 0, 921, 922, + 0, 923, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 934, 935, 936, 937, + 938, 939, 940, 941, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 959, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 751, 0, 0, 0, 269, + 756, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 763, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 758, 759, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 1008, 944, + 1075, 910, 948, 1009, 961, 962, 963, 949, 0, 237, + 950, 951, 244, 952, 0, 909, 794, 796, 795, 859, + 860, 861, 862, 863, 864, 865, 792, 957, 964, 965, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 748, 0, 762, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 745, 746, 0, 0, + 0, 0, 904, 0, 747, 0, 0, 755, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, + 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, + 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 2082, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 903, 0, 0, + 618, 0, 0, 901, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 954, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 955, + 956, 255, 641, 800, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 808, 809, + 279, 306, 885, 884, 883, 305, 307, 881, 882, 880, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 891, 913, 902, 768, 769, 892, 893, 917, + 894, 771, 772, 914, 915, 765, 766, 770, 916, 918, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 905, 754, + 753, 0, 760, 761, 0, 790, 791, 793, 797, 798, + 799, 810, 857, 858, 866, 868, 869, 867, 870, 871, + 872, 875, 876, 877, 878, 873, 874, 879, 773, 777, + 774, 775, 776, 788, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 928, 929, 930, 931, 932, + 933, 803, 807, 806, 804, 805, 801, 802, 829, 828, + 830, 831, 832, 833, 834, 835, 837, 836, 838, 839, + 840, 841, 842, 843, 811, 812, 815, 816, 814, 813, + 817, 826, 827, 818, 819, 820, 821, 822, 823, 825, + 824, 844, 845, 846, 847, 848, 850, 849, 853, 854, + 852, 851, 856, 855, 752, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 919, 261, 920, 0, + 0, 924, 0, 0, 0, 926, 925, 0, 927, 889, + 888, 0, 0, 921, 922, 0, 923, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 934, 935, 936, 937, 938, 939, 940, 941, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 959, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 1126, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 1125, 618, 0, 0, 0, 0, + 0, 1122, 1123, 362, 1083, 329, 197, 224, 1116, 1120, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 1686, 944, + 0, 0, 1683, 0, 0, 0, 0, 1681, 0, 237, + 1682, 1680, 244, 1685, 0, 909, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5805,143 +4400,71 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 1885, 0, 0, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 0, 0, 0, - 0, 709, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 1883, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 709, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 86, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 0, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5949,215 +4472,71 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 1881, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 0, 709, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 1879, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 0, 709, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 1877, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 709, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 94, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 2386, + 0, 0, 2385, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 1748, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 1750, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 1752, 711, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, - 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 1457, 0, + 1458, 1459, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6166,143 +4545,71 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 1873, 0, 0, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 0, 0, 0, - 0, 709, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 1871, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 709, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 86, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 1725, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6310,213 +4617,69 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 1869, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 0, 709, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 1844, 0, 0, 0, 709, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 0, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 1743, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 194, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 94, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6527,143 +4690,71 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 404, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 95, 0, 0, - 0, 941, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 2386, 0, 0, 2385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 194, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 2334, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 1931, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6671,215 +4762,72 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1430, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 2332, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 1429, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1030, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 392, 0, 372, 568, 569, 314, 520, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 411, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 0, 0, - 0, 0, 362, 266, 0, 0, 425, 0, 203, 0, - 481, 251, 373, 370, 575, 281, 272, 268, 249, 315, - 381, 423, 510, 417, 0, 366, 0, 0, 491, 396, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 321, 247, 323, 202, 408, - 492, 285, 0, 0, 0, 0, 0, 194, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, - 244, 0, 0, 0, 347, 356, 355, 336, 337, 339, - 341, 346, 353, 359, 0, 0, 0, 0, 0, 264, - 319, 271, 263, 572, 0, 0, 0, 0, 0, 0, - 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, - 0, 397, 256, 0, 448, 0, 662, 0, 616, 0, - 0, 0, 0, 0, 0, 0, 361, 0, 328, 197, - 224, 0, 0, 407, 456, 468, 0, 0, 0, 252, - 0, 466, 421, 594, 232, 283, 453, 427, 464, 435, - 286, 0, 0, 465, 368, 577, 445, 591, 617, 618, - 262, 401, 603, 514, 611, 635, 225, 259, 415, 499, - 597, 488, 393, 573, 574, 327, 487, 294, 201, 365, - 623, 223, 474, 367, 241, 230, 579, 600, 288, 451, - 630, 212, 509, 589, 238, 478, 0, 0, 638, 246, - 498, 214, 586, 497, 389, 324, 325, 213, 0, 452, - 267, 292, 0, 0, 257, 410, 581, 582, 255, 639, - 227, 610, 219, 0, 609, 403, 576, 587, 390, 379, - 218, 585, 388, 378, 332, 351, 352, 279, 305, 442, - 371, 443, 304, 306, 399, 398, 400, 206, 598, 0, - 207, 0, 493, 599, 640, 447, 211, 233, 234, 236, - 0, 278, 282, 290, 293, 301, 302, 311, 363, 414, - 441, 437, 446, 0, 571, 592, 604, 615, 621, 622, - 624, 625, 626, 627, 628, 631, 629, 402, 309, 489, - 331, 369, 0, 0, 420, 467, 239, 596, 490, 199, - 0, 0, 0, 0, 253, 254, 0, 567, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 641, 642, 643, - 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, - 654, 655, 656, 657, 658, 636, 500, 506, 501, 502, - 503, 504, 505, 0, 507, 0, 0, 0, 0, 0, - 0, 583, 584, 659, 380, 480, 593, 333, 345, 348, - 338, 357, 0, 358, 334, 335, 340, 342, 343, 344, - 349, 350, 354, 360, 248, 209, 386, 394, 570, 310, - 215, 216, 217, 516, 517, 518, 519, 607, 608, 612, - 204, 457, 458, 459, 460, 291, 602, 307, 463, 462, - 329, 330, 375, 444, 532, 534, 545, 549, 551, 553, - 559, 562, 533, 535, 546, 550, 552, 554, 560, 563, - 522, 524, 526, 528, 541, 540, 537, 565, 566, 543, - 548, 527, 539, 544, 557, 564, 561, 521, 525, 529, - 538, 556, 555, 536, 547, 558, 542, 530, 523, 531, - 0, 196, 220, 364, 0, 449, 287, 637, 606, 601, - 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, - 235, 242, 260, 275, 277, 284, 297, 308, 316, 317, - 320, 326, 376, 382, 383, 384, 385, 404, 405, 406, - 409, 412, 413, 416, 418, 419, 422, 426, 430, 431, - 432, 434, 436, 438, 450, 455, 469, 470, 471, 472, - 473, 476, 477, 482, 483, 484, 485, 486, 494, 495, - 508, 578, 580, 595, 613, 619, 475, 299, 300, 439, - 440, 312, 313, 633, 634, 298, 590, 620, 588, 632, - 614, 433, 374, 0, 0, 377, 280, 303, 318, 0, - 605, 496, 226, 461, 289, 250, 0, 0, 210, 245, - 229, 258, 273, 276, 322, 387, 395, 424, 429, 295, - 270, 243, 454, 240, 479, 511, 512, 513, 515, 391, - 265, 428, 392, 0, 372, 568, 569, 314, 520, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 411, 0, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 362, 266, 0, 0, 425, 0, - 203, 0, 481, 251, 373, 370, 575, 281, 272, 268, - 249, 315, 381, 423, 510, 417, 0, 366, 0, 0, - 491, 396, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 321, 247, 323, - 202, 408, 492, 285, 0, 0, 0, 0, 0, 709, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, - 0, 0, 244, 0, 0, 0, 347, 356, 355, 336, - 337, 339, 341, 346, 353, 359, 0, 0, 0, 0, - 0, 264, 319, 271, 263, 572, 0, 0, 0, 0, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 1077, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6887,144 +4835,71 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 296, 0, 397, 256, 0, 448, 0, 0, 0, - 616, 0, 0, 0, 0, 0, 0, 0, 361, 0, - 328, 197, 224, 0, 0, 407, 456, 468, 0, 0, - 0, 252, 0, 466, 421, 594, 232, 283, 453, 427, - 464, 435, 286, 0, 0, 465, 368, 577, 445, 591, - 617, 618, 262, 401, 603, 514, 611, 635, 225, 259, - 415, 499, 597, 488, 393, 573, 574, 327, 487, 294, - 201, 365, 623, 223, 474, 367, 241, 230, 579, 600, - 288, 451, 630, 212, 509, 589, 238, 478, 0, 0, - 638, 246, 498, 214, 586, 497, 389, 324, 325, 213, - 0, 452, 267, 292, 0, 0, 257, 410, 581, 582, - 255, 639, 227, 610, 219, 0, 609, 403, 576, 587, - 390, 379, 218, 585, 388, 378, 332, 351, 352, 279, - 305, 442, 371, 443, 304, 306, 399, 398, 400, 206, - 598, 0, 207, 0, 493, 599, 640, 447, 211, 233, - 234, 236, 0, 278, 282, 290, 293, 301, 302, 311, - 363, 414, 441, 437, 446, 0, 571, 592, 604, 615, - 621, 622, 624, 625, 626, 627, 628, 631, 629, 402, - 309, 489, 331, 369, 0, 0, 420, 467, 239, 596, - 490, 199, 0, 0, 0, 0, 253, 254, 0, 567, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 641, - 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, - 652, 653, 654, 655, 656, 657, 658, 636, 500, 506, - 501, 502, 503, 504, 505, 0, 507, 0, 0, 0, - 0, 0, 0, 583, 584, 659, 380, 480, 593, 333, - 345, 348, 338, 357, 0, 358, 334, 335, 340, 342, - 343, 344, 349, 350, 354, 360, 248, 209, 386, 394, - 570, 310, 215, 216, 217, 516, 517, 518, 519, 607, - 608, 612, 204, 457, 458, 459, 460, 291, 602, 307, - 463, 462, 329, 330, 375, 444, 532, 534, 545, 549, - 551, 553, 559, 562, 533, 535, 546, 550, 552, 554, - 560, 563, 522, 524, 526, 528, 541, 540, 537, 565, - 566, 543, 548, 527, 539, 544, 557, 564, 561, 521, - 525, 529, 538, 556, 555, 536, 547, 558, 542, 530, - 523, 531, 0, 196, 220, 364, 0, 449, 287, 637, - 606, 601, 205, 222, 0, 261, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, - 221, 231, 235, 242, 260, 275, 277, 284, 297, 308, - 316, 317, 320, 326, 376, 382, 383, 384, 385, 4028, - 405, 406, 409, 412, 413, 416, 418, 419, 422, 426, - 430, 431, 432, 434, 436, 438, 450, 455, 469, 470, - 471, 472, 473, 476, 477, 482, 483, 484, 485, 486, - 494, 495, 508, 578, 580, 595, 613, 619, 475, 299, - 300, 439, 440, 312, 313, 633, 634, 298, 590, 620, - 588, 632, 614, 433, 374, 0, 0, 377, 280, 303, - 318, 0, 605, 496, 226, 461, 289, 250, 0, 0, - 210, 245, 229, 258, 273, 276, 322, 387, 395, 424, - 429, 295, 270, 243, 454, 240, 479, 511, 512, 513, - 515, 391, 265, 428, 392, 0, 372, 568, 569, 314, - 520, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 411, 0, 0, 0, 0, 0, 0, 0, - 0, 269, 0, 0, 0, 0, 362, 266, 0, 0, - 425, 0, 203, 0, 481, 251, 373, 370, 575, 281, - 272, 268, 249, 315, 381, 423, 510, 417, 0, 366, - 0, 0, 491, 396, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, - 247, 323, 202, 408, 492, 285, 0, 0, 0, 0, - 0, 709, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 237, 0, 0, 244, 0, 0, 0, 347, 356, - 355, 336, 337, 339, 341, 346, 353, 359, 0, 0, - 0, 0, 0, 264, 319, 271, 263, 572, 0, 0, - 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 296, 0, 397, 256, 0, 448, 0, - 0, 0, 616, 0, 0, 0, 0, 0, 0, 0, - 361, 0, 328, 197, 224, 0, 0, 407, 456, 468, - 0, 0, 0, 252, 0, 466, 421, 594, 232, 283, - 453, 427, 464, 435, 286, 0, 0, 465, 368, 577, - 445, 591, 617, 618, 262, 401, 603, 514, 611, 635, - 225, 259, 415, 499, 597, 488, 393, 573, 574, 327, - 487, 294, 201, 365, 623, 223, 474, 367, 241, 230, - 579, 600, 288, 451, 630, 212, 509, 589, 238, 478, - 0, 0, 638, 246, 498, 214, 586, 497, 389, 324, - 325, 213, 0, 452, 267, 292, 0, 0, 257, 410, - 581, 582, 255, 639, 227, 610, 219, 0, 609, 403, - 576, 587, 390, 379, 218, 585, 388, 378, 332, 351, - 352, 279, 305, 442, 371, 443, 304, 306, 399, 398, - 400, 206, 598, 0, 207, 0, 493, 599, 640, 447, - 211, 233, 234, 236, 0, 278, 282, 290, 293, 301, - 302, 311, 363, 414, 441, 437, 446, 0, 571, 592, - 604, 615, 621, 622, 624, 625, 626, 627, 628, 631, - 629, 402, 309, 489, 331, 369, 0, 0, 420, 467, - 239, 596, 490, 199, 0, 0, 0, 0, 253, 254, - 0, 567, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 641, 642, 643, 644, 645, 646, 647, 648, 649, - 650, 651, 652, 653, 654, 655, 656, 657, 658, 636, - 500, 506, 501, 502, 503, 504, 505, 0, 507, 0, - 0, 0, 0, 0, 0, 583, 584, 659, 380, 480, - 593, 333, 345, 348, 338, 357, 0, 358, 334, 335, - 340, 342, 343, 344, 349, 350, 354, 360, 248, 209, - 386, 394, 570, 310, 215, 216, 217, 516, 517, 518, - 519, 607, 608, 612, 204, 457, 458, 459, 460, 291, - 602, 307, 463, 462, 329, 330, 375, 444, 532, 534, - 545, 549, 551, 553, 559, 562, 533, 535, 546, 550, - 552, 554, 560, 563, 522, 524, 526, 528, 541, 540, - 537, 565, 566, 543, 548, 527, 539, 544, 557, 564, - 561, 521, 525, 529, 538, 556, 555, 536, 547, 558, - 542, 530, 523, 531, 0, 196, 220, 364, 0, 449, - 287, 637, 606, 601, 205, 222, 0, 261, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 1083, + 329, 197, 224, 1081, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, - 297, 308, 316, 317, 320, 326, 376, 382, 383, 384, - 385, 404, 405, 406, 409, 412, 413, 416, 418, 419, - 422, 426, 430, 431, 432, 434, 436, 438, 450, 455, - 469, 470, 471, 472, 473, 476, 477, 482, 483, 484, - 485, 486, 494, 495, 508, 578, 580, 595, 613, 619, - 475, 299, 300, 439, 440, 312, 313, 633, 634, 298, - 590, 620, 588, 632, 614, 433, 374, 0, 0, 377, - 280, 303, 318, 0, 605, 496, 226, 461, 289, 250, - 0, 0, 210, 245, 229, 258, 273, 276, 322, 387, - 395, 424, 429, 295, 270, 243, 454, 240, 479, 511, - 512, 513, 515, 391, 265, 428, 392, 0, 372, 568, - 569, 314, 520, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 362, 266, - 0, 0, 425, 0, 203, 0, 481, 251, 373, 370, - 575, 281, 272, 268, 249, 315, 381, 423, 510, 417, - 0, 366, 0, 0, 491, 396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 321, 247, 323, 202, 408, 492, 285, 0, 0, - 0, 0, 0, 941, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 0, 0, 244, 0, 0, 0, - 347, 356, 355, 336, 337, 339, 341, 346, 353, 359, - 0, 0, 0, 0, 0, 264, 319, 271, 263, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 2334, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 1931, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7032,152 +4907,2401 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 296, 0, 397, 256, 0, - 448, 0, 0, 0, 616, 0, 0, 0, 0, 0, - 0, 0, 361, 0, 328, 197, 224, 0, 0, 407, - 456, 468, 0, 0, 0, 252, 0, 466, 421, 594, - 232, 283, 453, 427, 464, 435, 286, 0, 0, 465, - 368, 577, 445, 591, 617, 618, 262, 401, 603, 514, - 611, 635, 225, 259, 415, 499, 597, 488, 393, 573, - 574, 327, 487, 294, 201, 365, 623, 223, 474, 367, - 241, 230, 579, 600, 288, 451, 630, 212, 509, 589, - 238, 478, 0, 0, 638, 246, 498, 214, 586, 497, - 389, 324, 325, 213, 0, 452, 267, 292, 0, 0, - 257, 410, 581, 582, 255, 639, 227, 610, 219, 0, - 609, 403, 576, 587, 390, 379, 218, 585, 388, 378, - 332, 351, 352, 279, 305, 442, 371, 443, 304, 306, - 399, 398, 400, 206, 598, 0, 207, 0, 493, 599, - 640, 447, 211, 233, 234, 236, 0, 278, 282, 290, - 293, 301, 302, 311, 363, 414, 441, 437, 446, 0, - 571, 592, 604, 615, 621, 622, 624, 625, 626, 627, - 628, 631, 629, 402, 309, 489, 331, 369, 0, 0, - 420, 467, 239, 596, 490, 199, 0, 0, 0, 0, - 253, 254, 0, 567, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 641, 642, 643, 644, 645, 646, 647, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 636, 500, 506, 501, 502, 503, 504, 505, 0, - 507, 0, 0, 0, 0, 0, 0, 583, 584, 659, - 380, 480, 593, 333, 345, 348, 338, 357, 0, 358, - 334, 335, 340, 342, 343, 344, 349, 350, 354, 360, - 248, 209, 386, 394, 570, 310, 215, 216, 217, 516, - 517, 518, 519, 607, 608, 612, 204, 457, 458, 459, - 460, 291, 602, 307, 463, 462, 329, 330, 375, 444, - 532, 534, 545, 549, 551, 553, 559, 562, 533, 535, - 546, 550, 552, 554, 560, 563, 522, 524, 526, 528, - 541, 540, 537, 565, 566, 543, 548, 527, 539, 544, - 557, 564, 561, 521, 525, 529, 538, 556, 555, 536, - 547, 558, 542, 530, 523, 531, 0, 196, 220, 364, - 0, 449, 287, 637, 606, 601, 205, 222, 0, 261, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 198, 200, 208, 221, 231, 235, 242, 260, 275, - 277, 284, 297, 308, 316, 317, 320, 326, 376, 382, - 383, 384, 385, 404, 405, 406, 409, 412, 413, 416, - 418, 419, 422, 426, 430, 431, 432, 434, 436, 438, - 450, 455, 469, 470, 471, 472, 473, 476, 477, 482, - 483, 484, 485, 486, 494, 495, 508, 578, 580, 595, - 613, 619, 475, 299, 300, 439, 440, 312, 313, 633, - 634, 298, 590, 620, 588, 632, 614, 433, 374, 0, - 0, 377, 280, 303, 318, 0, 605, 496, 226, 461, - 289, 250, 0, 0, 210, 245, 229, 258, 273, 276, - 322, 387, 395, 424, 429, 295, 270, 243, 454, 240, - 479, 511, 512, 513, 515, 391, 265, 428, 392, 0, - 372, 568, 569, 314, 520, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 411, 0, 0, 0, - 0, 0, 0, 0, 0, 269, 0, 0, 0, 0, - 362, 266, 0, 0, 425, 0, 203, 0, 481, 251, - 373, 370, 575, 281, 272, 268, 249, 315, 381, 423, - 510, 417, 0, 366, 0, 0, 491, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 321, 247, 323, 202, 408, 492, 285, - 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 237, 0, 0, 244, 0, - 0, 0, 347, 356, 355, 336, 337, 339, 341, 346, - 353, 359, 0, 0, 0, 0, 0, 264, 319, 271, - 263, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 296, 0, 397, - 256, 0, 448, 0, 0, 0, 616, 0, 0, 0, - 0, 0, 0, 0, 361, 0, 328, 197, 224, 0, - 0, 407, 456, 468, 0, 0, 0, 252, 0, 466, - 421, 594, 232, 283, 453, 427, 464, 435, 286, 0, - 0, 465, 368, 577, 445, 591, 617, 618, 262, 401, - 603, 514, 611, 635, 225, 259, 415, 499, 597, 488, - 393, 573, 574, 327, 487, 294, 201, 365, 623, 223, - 474, 367, 241, 230, 579, 600, 288, 451, 630, 212, - 509, 589, 238, 478, 0, 0, 638, 246, 498, 214, - 586, 497, 389, 324, 325, 213, 0, 452, 267, 292, - 0, 0, 257, 410, 581, 582, 255, 639, 227, 610, - 219, 0, 609, 403, 576, 587, 390, 379, 218, 585, - 388, 378, 332, 351, 352, 279, 305, 442, 371, 443, - 304, 306, 399, 398, 400, 206, 598, 0, 207, 0, - 493, 599, 640, 447, 211, 233, 234, 236, 0, 278, - 282, 290, 293, 301, 302, 311, 363, 414, 441, 437, - 446, 0, 571, 592, 604, 615, 621, 622, 624, 625, - 626, 627, 628, 631, 629, 402, 309, 489, 331, 369, - 0, 0, 420, 467, 239, 596, 490, 199, 0, 0, - 0, 0, 253, 254, 0, 567, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 641, 642, 643, 644, 645, - 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, - 656, 657, 658, 636, 500, 506, 501, 502, 503, 504, - 505, 0, 507, 0, 0, 0, 0, 0, 0, 583, - 584, 659, 380, 480, 593, 333, 345, 348, 338, 357, - 0, 358, 334, 335, 340, 342, 343, 344, 349, 350, - 354, 360, 248, 209, 386, 394, 570, 310, 215, 216, - 217, 516, 517, 518, 519, 607, 608, 612, 204, 457, - 458, 459, 460, 291, 602, 307, 463, 462, 329, 330, - 375, 444, 532, 534, 545, 549, 551, 553, 559, 562, - 533, 535, 546, 550, 552, 554, 560, 563, 522, 524, - 526, 528, 541, 540, 537, 565, 566, 543, 548, 527, - 539, 544, 557, 564, 561, 521, 525, 529, 538, 556, - 555, 536, 547, 558, 542, 530, 523, 531, 0, 196, - 220, 364, 0, 449, 287, 637, 606, 601, 205, 222, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 198, 200, 208, 221, 231, 235, 242, - 260, 275, 277, 284, 297, 308, 316, 317, 320, 326, - 376, 382, 383, 384, 385, 404, 405, 406, 409, 412, - 413, 416, 418, 419, 422, 426, 430, 431, 432, 434, - 436, 438, 450, 455, 469, 470, 471, 472, 473, 476, - 477, 482, 483, 484, 485, 486, 494, 495, 508, 578, - 580, 595, 613, 619, 475, 299, 300, 439, 440, 312, - 313, 633, 634, 298, 590, 620, 588, 632, 614, 433, - 374, 0, 0, 377, 280, 303, 318, 0, 605, 496, - 226, 461, 289, 250, 0, 0, 210, 245, 229, 258, - 273, 276, 322, 387, 395, 424, 429, 295, 270, 243, - 454, 240, 479, 511, 512, 513, 515, 391, 265, 428, - 0, 0, 372, 568, 569, 314, -} + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 1725, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 3657, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 2093, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2094, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 2830, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2831, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 2816, + 0, 0, 0, 0, 237, 0, 0, 244, 2817, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 1771, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 1770, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 713, 714, 715, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 3996, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 1931, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 3657, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 95, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 2387, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 194, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 1752, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 194, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 2045, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 2036, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 1898, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 1896, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 1894, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 1892, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 1890, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 1886, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 1884, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 1882, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 1857, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 1756, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 95, 0, 0, 0, 944, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1436, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 1435, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 194, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1034, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 664, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 711, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 4056, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 711, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 393, 0, 373, 570, 571, 315, 0, 522, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 413, 0, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 363, 266, 0, 0, 427, 0, + 203, 0, 483, 251, 374, 371, 577, 281, 272, 268, + 249, 316, 382, 425, 512, 419, 0, 367, 0, 0, + 493, 398, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 322, 247, 324, + 202, 410, 494, 285, 0, 0, 0, 0, 0, 944, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 244, 0, 0, 0, 348, 357, 356, 337, + 338, 340, 342, 347, 354, 360, 0, 0, 0, 0, + 0, 264, 320, 271, 263, 574, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 296, 0, 399, 256, 0, 450, 0, 0, 0, + 618, 0, 0, 0, 0, 0, 0, 0, 362, 0, + 329, 197, 224, 0, 0, 409, 458, 470, 0, 0, + 0, 252, 0, 468, 423, 596, 232, 283, 455, 429, + 466, 437, 286, 0, 0, 467, 369, 579, 447, 593, + 619, 620, 262, 403, 605, 516, 613, 637, 225, 259, + 417, 501, 599, 490, 394, 575, 576, 328, 489, 294, + 201, 366, 625, 223, 476, 368, 241, 230, 581, 602, + 298, 288, 453, 632, 212, 511, 591, 238, 480, 0, + 0, 640, 246, 500, 214, 588, 499, 390, 325, 326, + 213, 0, 454, 267, 292, 0, 0, 257, 412, 583, + 584, 255, 641, 227, 612, 219, 0, 611, 405, 578, + 589, 391, 380, 218, 587, 389, 379, 333, 352, 353, + 279, 306, 444, 372, 445, 305, 307, 401, 400, 402, + 206, 600, 0, 207, 0, 495, 601, 642, 449, 211, + 233, 234, 236, 0, 278, 282, 290, 293, 302, 303, + 312, 364, 416, 443, 439, 448, 0, 573, 594, 606, + 617, 623, 624, 626, 627, 628, 629, 630, 633, 631, + 404, 310, 491, 332, 370, 0, 0, 422, 469, 239, + 598, 492, 199, 0, 0, 0, 0, 253, 254, 0, + 569, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 658, 659, 660, 638, 502, + 508, 503, 504, 505, 506, 507, 0, 509, 0, 0, + 0, 0, 0, 395, 0, 585, 586, 661, 381, 482, + 595, 334, 346, 349, 339, 358, 0, 359, 335, 336, + 341, 343, 344, 345, 350, 351, 355, 361, 248, 209, + 387, 396, 572, 311, 215, 216, 217, 518, 519, 520, + 521, 609, 610, 614, 204, 459, 460, 461, 462, 291, + 604, 308, 465, 464, 330, 331, 376, 446, 534, 536, + 547, 551, 553, 555, 561, 564, 535, 537, 548, 552, + 554, 556, 562, 565, 524, 526, 528, 530, 543, 542, + 539, 567, 568, 545, 550, 529, 541, 546, 559, 566, + 563, 523, 527, 531, 540, 558, 557, 538, 549, 560, + 544, 532, 525, 533, 0, 196, 220, 365, 0, 451, + 287, 639, 608, 603, 205, 222, 0, 261, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, + 200, 208, 221, 231, 235, 242, 260, 275, 277, 284, + 297, 309, 317, 318, 321, 327, 377, 383, 384, 385, + 386, 406, 407, 408, 411, 414, 415, 418, 420, 421, + 424, 428, 432, 433, 434, 436, 438, 440, 452, 457, + 471, 472, 473, 474, 475, 478, 479, 484, 485, 486, + 487, 488, 496, 497, 510, 580, 582, 597, 615, 621, + 477, 300, 301, 441, 442, 313, 314, 635, 636, 299, + 592, 622, 590, 634, 616, 435, 375, 0, 0, 378, + 280, 304, 319, 0, 607, 498, 226, 463, 289, 250, + 0, 0, 210, 245, 229, 258, 273, 276, 323, 388, + 397, 426, 431, 295, 270, 243, 456, 240, 481, 513, + 514, 515, 517, 392, 265, 430, 393, 0, 373, 570, + 571, 315, 0, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, + 0, 0, 0, 0, 269, 0, 0, 0, 0, 363, + 266, 0, 0, 427, 0, 203, 0, 483, 251, 374, + 371, 577, 281, 272, 268, 249, 316, 382, 425, 512, + 419, 0, 367, 0, 0, 493, 398, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 247, 324, 202, 410, 494, 285, 0, + 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 0, 0, 244, 0, 0, + 0, 348, 357, 356, 337, 338, 340, 342, 347, 354, + 360, 0, 0, 0, 0, 0, 264, 320, 271, 263, + 574, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 296, 0, 399, 256, + 0, 450, 0, 0, 0, 618, 0, 0, 0, 0, + 0, 0, 0, 362, 0, 329, 197, 224, 0, 0, + 409, 458, 470, 0, 0, 0, 252, 0, 468, 423, + 596, 232, 283, 455, 429, 466, 437, 286, 0, 0, + 467, 369, 579, 447, 593, 619, 620, 262, 403, 605, + 516, 613, 637, 225, 259, 417, 501, 599, 490, 394, + 575, 576, 328, 489, 294, 201, 366, 625, 223, 476, + 368, 241, 230, 581, 602, 298, 288, 453, 632, 212, + 511, 591, 238, 480, 0, 0, 640, 246, 500, 214, + 588, 499, 390, 325, 326, 213, 0, 454, 267, 292, + 0, 0, 257, 412, 583, 584, 255, 641, 227, 612, + 219, 0, 611, 405, 578, 589, 391, 380, 218, 587, + 389, 379, 333, 352, 353, 279, 306, 444, 372, 445, + 305, 307, 401, 400, 402, 206, 600, 0, 207, 0, + 495, 601, 642, 449, 211, 233, 234, 236, 0, 278, + 282, 290, 293, 302, 303, 312, 364, 416, 443, 439, + 448, 0, 573, 594, 606, 617, 623, 624, 626, 627, + 628, 629, 630, 633, 631, 404, 310, 491, 332, 370, + 0, 0, 422, 469, 239, 598, 492, 199, 0, 0, + 0, 0, 253, 254, 0, 569, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, + 658, 659, 660, 638, 502, 508, 503, 504, 505, 506, + 507, 0, 509, 0, 0, 0, 0, 0, 395, 0, + 585, 586, 661, 381, 482, 595, 334, 346, 349, 339, + 358, 0, 359, 335, 336, 341, 343, 344, 345, 350, + 351, 355, 361, 248, 209, 387, 396, 572, 311, 215, + 216, 217, 518, 519, 520, 521, 609, 610, 614, 204, + 459, 460, 461, 462, 291, 604, 308, 465, 464, 330, + 331, 376, 446, 534, 536, 547, 551, 553, 555, 561, + 564, 535, 537, 548, 552, 554, 556, 562, 565, 524, + 526, 528, 530, 543, 542, 539, 567, 568, 545, 550, + 529, 541, 546, 559, 566, 563, 523, 527, 531, 540, + 558, 557, 538, 549, 560, 544, 532, 525, 533, 0, + 196, 220, 365, 0, 451, 287, 639, 608, 603, 205, + 222, 0, 261, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 200, 208, 221, 231, 235, + 242, 260, 275, 277, 284, 297, 309, 317, 318, 321, + 327, 377, 383, 384, 385, 386, 406, 407, 408, 411, + 414, 415, 418, 420, 421, 424, 428, 432, 433, 434, + 436, 438, 440, 452, 457, 471, 472, 473, 474, 475, + 478, 479, 484, 485, 486, 487, 488, 496, 497, 510, + 580, 582, 597, 615, 621, 477, 300, 301, 441, 442, + 313, 314, 635, 636, 299, 592, 622, 590, 634, 616, + 435, 375, 0, 0, 378, 280, 304, 319, 0, 607, + 498, 226, 463, 289, 250, 0, 0, 210, 245, 229, + 258, 273, 276, 323, 388, 397, 426, 431, 295, 270, + 243, 456, 240, 481, 513, 514, 515, 517, 392, 265, + 430, 0, 0, 373, 570, 571, 315, +} var yyPact = [...]int{ - -1000, -1000, 1319, -1000, -532, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 1924, -1000, -533, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 2413, 2785, -1000, -1000, -1000, -1000, 2617, -1000, 975, + 2077, -1000, 2388, 4123, -1000, 55392, 479, -1000, 52492, -438, + 845, 218, 36542, -1000, 219, -1000, 200, 53942, 213, -1000, + -1000, -1000, -1000, -438, 22042, 2308, 81, 70, 55392, -1000, + -1000, -1000, -1000, -354, 2564, 2044, -1000, 387, -1000, -1000, + -1000, -1000, -1000, -1000, 51767, -1000, 1104, -1000, -1000, 2398, + 2370, 2278, 877, 2291, -1000, 2477, 2044, -1000, 22042, 2541, + 2448, 21317, 21317, 455, -1000, -1000, 282, -1000, -1000, 31467, + 55392, 39442, 290, -1000, 2388, -1000, -1000, -1000, 217, -1000, + 379, 1962, -1000, 1961, -1000, 1045, 1076, 397, 875, 858, + 396, 395, 394, 393, 391, 389, 386, 385, 404, -1000, + 909, 909, -205, -221, 1382, 446, 443, 443, 1132, 465, + 2328, 2323, -1000, -1000, 909, 909, 909, 847, 909, 909, + 909, 909, 350, 331, 909, 909, 909, 909, 909, 909, + 909, 909, 909, 909, 909, 909, 909, 909, 909, 909, + 909, 818, 2388, 300, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 2401, 2519, -1000, -1000, -1000, -1000, 2569, -1000, 1002, - 2074, -1000, 2363, 4957, -1000, 54244, 500, -1000, 51356, -436, - 860, 234, 35472, -1000, 207, -1000, 193, 52800, 199, -1000, - -1000, -1000, -1000, -436, 21030, 2277, 56, 52, 54244, -1000, - -1000, -1000, -1000, -353, 2542, 2069, -1000, 408, -1000, -1000, - -1000, -1000, -1000, -1000, 50634, -1000, 1098, -1000, -1000, 2359, - 2349, 2577, 915, 2304, -1000, 2461, 2069, -1000, 21030, 2498, - 2432, 20308, 20308, 462, -1000, -1000, 268, -1000, -1000, 30418, - 54244, 38360, 890, -1000, 2363, -1000, -1000, -1000, 219, -1000, - 378, 1978, -1000, 1977, -1000, 469, 899, 392, 871, 868, - 391, 389, 388, 379, 377, 376, 375, 369, 398, -1000, - 938, 938, -218, -219, 361, 450, 448, 448, 1108, 479, - 2320, 2316, -1000, -1000, 938, 938, 938, 396, 938, 938, - 938, 938, 321, 320, 938, 938, 938, 938, 938, 938, - 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, - 938, 902, 2363, 300, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -7223,67 +7347,68 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 55392, 209, 55392, -1000, 532, 478, -1000, -1000, + -443, 1025, 1025, 92, 1025, 1025, 1025, 1025, 199, 948, + 69, -1000, 198, 309, 191, 312, 1028, 263, -1000, -1000, + 286, 1028, 1834, -1000, 895, 297, 183, -1000, 1025, 1025, + -1000, 14768, 265, 14768, 14768, -1000, 2369, -1000, -1000, -1000, + -1000, -1000, 1300, -1000, -1000, -1000, -1000, -19, 464, -1000, + -1000, -1000, -1000, 53942, 51042, 239, -1000, -1000, 41, 1866, + 1631, 22042, 1296, 874, -1000, -1000, 1370, 853, -1000, -1000, + -1000, -1000, -1000, 496, -1000, 24217, 24217, 24217, 24217, -1000, + -1000, 1967, 50317, 1967, 1967, 24217, 1967, 24217, 1967, 1967, + 1967, 1967, 22042, 1967, 1967, 1967, 1967, -1000, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, -1000, -1000, -1000, -1000, 1967, + 526, 1967, 1967, 1967, 1967, 1967, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 1967, 1967, 1967, 1967, 1967, 1967, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, -1000, -1000, -1000, 1641, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 27117, 1537, 1524, 1518, -1000, 19142, 1967, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 54244, 210, 54244, -1000, 809, 499, -1000, -1000, -440, 1090, - 1090, 122, 1090, 1090, 1090, 1090, 184, 973, 49, -1000, - 182, 284, 197, 293, 1079, 183, -1000, -1000, 258, 1079, - 1806, -1000, 923, 281, 163, -1000, 1090, 1090, -1000, 13785, - 209, 13785, 13785, -1000, 2345, -1000, -1000, -1000, -1000, -1000, - 1333, -1000, -1000, -1000, -1000, -18, 478, -1000, -1000, -1000, - -1000, 52800, 49912, 290, -1000, -1000, 769, 1852, 1371, 21030, - 1291, 913, -1000, -1000, 1976, 876, -1000, -1000, -1000, -1000, - -1000, 800, -1000, 23196, 23196, 23196, 23196, -1000, -1000, 1983, - 49190, 1983, 1983, 23196, 1983, 23196, 1983, 1983, 1983, 21030, - 1983, 1983, 1983, 1983, -1000, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, -1000, -1000, -1000, -1000, 1983, 808, 1983, 1983, - 1983, 1983, 1983, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 1983, 1983, 1983, 1983, 1983, 1983, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 26084, 1500, 1498, 1493, -1000, 18142, 1983, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 55392, -1000, 1967, 243, 53942, 53942, 375, 1326, -1000, + -1000, 2477, 2044, -1000, 2564, 2536, 387, -1000, 3479, 1777, + 1528, 1499, 2044, 1932, 55392, -1000, 1981, -1000, -1000, -1000, + -322, -327, 2200, 1438, 1833, -1000, -1000, -1000, -1000, 2365, + 22042, -1000, -1000, 2586, -1000, 28567, 525, 2582, 49592, -1000, + 455, 455, 1956, 444, 37, -1000, -1000, -1000, -1000, 958, + 35817, -1000, -1000, -1000, -1000, -1000, 1831, 55392, -1000, -1000, + 4884, 1332, -1000, 2076, -1000, 1825, -1000, 2029, 22042, 2064, + 477, 1332, 472, 471, 470, -1000, -51, -1000, -1000, -1000, + -1000, -1000, -1000, 909, 909, 909, -1000, 392, 2538, 4123, + 5575, -1000, -1000, -1000, 48867, 2069, 1332, -1000, 2063, -1000, + 1019, 866, 870, 870, 1332, -1000, -1000, 54667, 1332, 1018, + 1016, 1332, 1332, 53942, 53942, -1000, 48142, -1000, 47417, 46692, + 1315, 53942, 45967, 45242, 44517, 43792, 43067, -1000, 2246, -1000, + 2024, -1000, -1000, -1000, 54667, 1332, 1332, 54667, 53942, 54667, + 55392, 1332, -1000, -1000, 372, -1000, -1000, 1310, 1309, 1297, + 909, 909, 1295, 1810, 1805, 1796, 909, 909, 1292, 1783, + 37992, 1772, 288, 1290, 1289, 1234, 1319, 1768, 232, 1762, + 1314, 1278, 1230, 53942, 2056, 55392, -1000, 278, 965, 1004, + 953, 2388, 2296, 1946, 463, 475, 1332, 447, 447, 53942, + -1000, 15499, 55392, 256, -1000, 1749, 22042, -1000, 1039, 1028, + 1028, -1000, -1000, -1000, -1000, -1000, -1000, 1025, 55392, 1039, + -1000, -1000, -1000, 1028, 1025, 55392, 1025, 1025, 1025, 1025, + 1028, 1028, 1028, 1025, 55392, 55392, 55392, 55392, 55392, 55392, + 55392, 55392, 55392, 14768, 895, 1025, -444, -1000, 1744, -1000, + -1000, -1000, 2173, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 54244, -1000, 1983, - 218, 52800, 52800, 397, 1334, -1000, -1000, 2461, 2069, -1000, - 2542, 2477, 408, -1000, 3785, 1628, 1721, 1532, 2069, 1956, - 54244, -1000, 1992, -1000, -1000, -1000, -1000, 2207, 1524, 1804, - -1000, -1000, -1000, -1000, 1907, 21030, -1000, -1000, 2557, -1000, - 27529, 805, 2554, 48468, -1000, 462, 462, 1975, 430, 22, - -1000, -1000, -1000, -1000, 962, 34750, -1000, -1000, -1000, -1000, - -1000, 1902, 54244, -1000, -1000, 4793, 1173, -1000, 2071, -1000, - 1842, -1000, 2014, 21030, 2080, 498, 1173, 491, 490, 489, - -1000, -64, -1000, -1000, -1000, -1000, -1000, -1000, 938, 938, - 938, -1000, 343, 2495, 4957, 6237, -1000, -1000, -1000, 47746, - 2067, 1173, -1000, 2053, -1000, 1041, 859, 870, 870, 1173, - -1000, -1000, 53522, 1173, 1040, 1031, 1173, 1173, 52800, 52800, - -1000, 47024, -1000, 46302, 45580, 1331, 52800, 44858, 44136, 43414, - 42692, 41970, -1000, 2330, -1000, 2133, -1000, -1000, -1000, 53522, - 1173, 1173, 53522, 52800, 53522, 54244, 1173, -1000, -1000, 360, - -1000, -1000, 1330, 1328, 1327, 938, 938, 1326, 1800, 1798, - 1787, 938, 938, 1295, 1782, 36916, 1765, 286, 1293, 1292, - 1289, 1290, 1746, 229, 1719, 1281, 1230, 1288, 52800, 2036, - 54244, -1000, 254, 945, 994, 958, 2363, 2276, 1967, 476, - 494, 1173, 451, 451, 52800, -1000, 14513, 54244, 217, -1000, - 1707, 21030, -1000, 1080, 1079, 1079, -1000, -1000, -1000, -1000, - -1000, -1000, 1090, 54244, 1080, -1000, -1000, -1000, 1079, 1090, - 54244, 1090, 1090, 1090, 1090, 1079, 1079, 1079, 1090, 54244, - 54244, 54244, 54244, 54244, 54244, 54244, 54244, 54244, 13785, 923, - 1090, -441, -1000, 1672, -1000, -1000, 2171, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -7298,328 +7423,329 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 14768, 14768, -1000, -1000, -1000, -1000, -1000, 1944, -1000, 202, + 23, 211, -1000, 42342, 485, 944, -1000, 485, -1000, -1000, + -1000, 1942, 41617, -1000, -445, -446, -450, -454, -1000, -1000, + -1000, -455, -457, -1000, -1000, -1000, 22042, 22042, 22042, 22042, + -272, -1000, 1128, 24217, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 22042, 242, 981, 24217, 24217, 24217, 24217, 24217, 24217, + 24217, 25667, 24942, 24217, 24217, 24217, 24217, 24217, 24217, -1000, + -1000, 33642, 3215, 3215, 853, 853, 853, 853, -1000, -166, + 1941, 54667, -1000, -1000, -1000, 524, 22042, 22042, 853, -1000, + 1332, 1294, 19142, 21317, 21317, 22042, 22042, 970, 1631, 54667, + 22042, -1000, 1499, -1000, -1000, -1000, -1000, 1136, -1000, -1000, + 1014, 2340, 2340, 2340, 2340, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 2340, 22042, 708, 708, 912, + 22042, 22042, 22042, 22042, 22042, 22042, 17692, 22042, 22042, 24217, + 22042, 22042, 22042, 1499, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 1499, 22042, 1307, 22042, 22042, 22042, 22042, + 22042, 22042, 21317, 16961, 21317, 21317, 21317, 21317, 21317, -1000, + -1000, -1000, -1000, -1000, -1000, 22042, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 1499, 22042, 22042, 22042, 22042, 22042, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 13785, 13785, -1000, -1000, -1000, -1000, - -1000, 1966, -1000, 189, 26, 196, -1000, 41248, 480, 957, - -1000, 480, -1000, -1000, -1000, 1964, 40526, -1000, -445, -446, - -447, -450, -1000, -1000, -1000, -451, -453, -1000, -1000, -1000, - 21030, 21030, 21030, 21030, -268, -1000, 1216, 23196, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 21030, 224, 997, 23196, 23196, - 23196, 23196, 23196, 23196, 23196, 24640, 23918, 23196, 23196, 23196, - 23196, 23196, 23196, -1000, -1000, 32584, 5973, 5973, 876, 876, - 876, 876, -1000, -175, 1963, 53522, -1000, -1000, -1000, 798, - 21030, 21030, 876, -1000, 1173, 2944, 18142, 20308, 20308, 21030, - 967, 1371, 53522, 21030, -1000, 1532, -1000, -1000, -1000, -1000, - 1207, -1000, -1000, 1093, 2354, 2354, 2354, 2354, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 2354, 21030, - 1270, 1270, 833, 21030, 21030, 21030, 21030, 21030, 21030, 16697, - 21030, 21030, 23196, 21030, 21030, 21030, 1532, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 1532, 21030, 1536, 21030, - 21030, 21030, 21030, 21030, 21030, 20308, 15969, 20308, 20308, 20308, - 20308, 20308, -1000, -1000, -1000, -1000, -1000, -1000, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 1532, 21030, 21030, 21030, - 21030, 21030, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 1568, 1629, 1527, 21030, -1000, 1960, -1000, -184, - 29696, 21030, 1658, 2551, 2105, 52800, -1000, -1000, -1000, -1000, - 2461, -1000, 2461, 1568, 3572, 2218, 20308, -1000, -1000, 3572, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1667, -1000, - 54244, 1956, 2407, 52800, 2222, 1655, 357, -1000, 21030, 21030, - 1955, -1000, 1799, 54244, -1000, -268, -1000, 39804, -1000, -1000, - 13057, 54244, 346, 54244, -1000, 28974, 39082, 265, -1000, 22, - 1936, -1000, 25, 17, 17419, 864, -1000, -1000, -1000, 361, - 25362, 1737, 864, 103, -1000, -1000, -1000, 2014, -1000, 2014, - 2014, 2014, 2014, 357, 357, 357, 357, -1000, -1000, -1000, - -1000, -1000, 2034, 2032, -1000, 2014, 2014, 2014, 2014, -1000, + 1789, 1736, 1391, 22042, -1000, 1934, -1000, -185, 30742, 22042, + 1709, 2581, 2100, 53942, -1000, -1000, -1000, -1000, 2477, -1000, + 2477, 1789, 3299, 2209, 21317, -1000, -1000, 3299, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1476, -1000, 55392, + 1932, 2443, 53942, -1000, -360, -1000, -361, 2196, 1703, 357, + -1000, 22042, 22042, 1931, -1000, 1276, 55392, -1000, -272, -1000, + 40892, -1000, -1000, 14037, 55392, 381, 55392, -1000, 30017, 40167, + 311, -1000, 37, 1898, -1000, 47, 13, 18417, 851, -1000, + -1000, -1000, 1382, 26392, 1853, 851, 133, -1000, -1000, -1000, + 2029, -1000, 2029, 2029, 2029, 2029, 357, 357, 357, 357, + -1000, -1000, -1000, -1000, -1000, 2055, 2051, -1000, 2029, 2029, + 2029, 2029, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2050, + 2050, 2050, 2049, 2049, 2033, 2033, 437, -1000, 22042, 424, + 39442, 2412, 1224, 1701, 278, 453, 2098, 1332, 1332, 1332, + 453, -1000, 1385, 1352, 1349, -1000, -522, 1928, -1000, -1000, + 2533, -1000, -1000, 1153, 1063, 1053, 1078, 53942, 262, 341, + -1000, 427, -1000, 39442, 1332, 1015, 870, 1332, -1000, 1332, + -1000, -1000, -1000, -1000, -1000, 1332, -1000, -1000, 1925, -1000, + 1919, 1090, 1052, 1079, 1035, 1925, -1000, -1000, -171, 1925, + -1000, 1925, -1000, 1925, -1000, 1925, -1000, 1925, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 976, 122, -329, + 53942, 262, 459, -1000, 457, 33642, -1000, -1000, -1000, 33642, + 33642, -1000, -1000, -1000, -1000, 1697, 1675, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 2031, 2031, 2031, 2029, - 2029, 2015, 2015, 435, -1000, 21030, 416, 38360, 2419, 1282, - 1219, 254, 453, 2103, 1173, 1173, 1173, 453, -1000, 1425, - 1398, 1383, -1000, -518, 1950, -1000, -1000, 2493, -1000, -1000, - 960, 1057, 1056, 1117, 52800, 236, 334, -1000, 431, -1000, - 38360, 1173, 1028, 870, 1173, -1000, 1173, -1000, -1000, -1000, - -1000, -1000, 1173, -1000, -1000, 1949, -1000, 1845, 1168, 1055, - 1131, 1054, 1949, -1000, -1000, -182, 1949, -1000, 1949, -1000, - 1949, -1000, 1949, -1000, 1949, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 981, 304, -307, 52800, 236, 470, - -1000, 468, 32584, -1000, -1000, -1000, 32584, 32584, -1000, -1000, - -1000, -1000, 1637, 1625, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -506, 55392, -1000, 277, + 935, 364, 354, 338, 55392, 333, 2470, 2467, 2455, 2451, + 2450, 2446, 294, 323, 55392, 55392, 447, 2139, 55392, 2421, + 55392, -1000, -1000, -1000, -1000, -1000, 1673, 1643, -1000, 1631, + 55392, -1000, -1000, 1025, 1025, -1000, -1000, 55392, 1025, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1025, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -502, 54244, -1000, 248, 956, 324, 335, - 353, 54244, 352, 2439, 2434, 2427, 2418, 2414, 305, 316, - 54244, 54244, 451, 2157, 54244, 2377, 54244, -1000, -1000, -1000, - -1000, -1000, 1622, 1620, -1000, 1371, 54244, -1000, -1000, 1090, - 1090, -1000, -1000, 54244, 1090, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 1090, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 54244, - -1000, -1000, -1000, -1000, -18, 187, -1000, -1000, 52800, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -104, -1000, - 801, 15, 409, -1000, -1000, -1000, -1000, -1000, 2457, -1000, - 1371, 1009, 1006, -1000, 1983, -1000, -1000, 1069, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 224, 23196, 23196, 23196, - 1598, 828, 1429, 1392, 1229, 1239, 1239, 929, 23196, 929, - 23196, 863, 863, 863, 863, 863, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 1616, -1000, 1983, 53522, 1817, 15969, - 1952, 2163, 1532, 908, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 55392, -1000, -1000, -1000, -1000, -19, 193, + -1000, -1000, 53942, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -105, -1000, 281, 32, 412, -1000, -1000, -1000, + -1000, -1000, 2472, -1000, 1631, 995, 982, -1000, 1967, -1000, + -1000, 1257, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 242, 24217, 24217, 24217, 1554, 834, 1496, 1322, 1351, 1167, + 1167, 929, 24217, 929, 24217, 859, 859, 859, 859, 859, + -1000, -1000, -1000, -1000, -1000, -1000, 1641, -1000, 1628, -1000, + 1967, 54667, 1795, 16961, 2037, 1872, 1499, 869, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 4134, 1813, -1000, 1813, 1503, 971, - -1000, 21030, 1532, 4125, -1000, -1000, 1532, 1532, 21030, -1000, - -1000, 21030, 21030, 21030, 21030, 1219, 1219, 1219, 1219, 1219, - 1219, 1219, 1219, 1219, 1219, 21030, 1219, 1948, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4204, 1793, + -1000, 1793, 1441, 4193, 973, -1000, 22042, 1499, 4188, -1000, + -1000, 1499, 1499, 22042, -1000, -1000, 22042, 22042, 22042, 22042, + 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, + 22042, 1701, 1920, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 1943, 2541, 1314, 1219, - 1219, 1219, 1219, 1219, 21030, 2187, -1000, -1000, -1000, 1534, - 4120, 1369, 4115, 1219, 1219, -1000, 1219, 4111, 4092, 1532, - 1852, 2876, 2871, 1219, 1219, 1219, 1219, 1219, 2863, 2802, - 1219, 1219, 2756, 1219, 3876, 1219, 2721, 2680, 2675, 2633, - 2619, 2614, 2603, 2597, 2555, 2540, 2533, 2528, 2510, 2506, - 2491, 2469, 2435, 2428, 1219, 1219, 1219, 3844, 1219, 3819, - 1219, 3812, 1219, 1219, 3807, 2412, 2403, 1532, 1942, -1000, - 3803, 1219, 3525, 3514, 3476, 2399, 3448, 3439, 3434, 1219, - 1219, 1219, 2329, 3426, 3420, 3416, 3406, 3402, 3377, 3369, - 3360, 3346, 1219, 1527, 1527, 1527, 1527, 1527, 3335, -271, - 1219, 1532, -1000, -1000, -1000, -1000, -1000, 3331, 2324, 3320, - 3316, 3289, 3275, 1532, 1938, 1983, 795, -1000, -1000, 1813, - 1532, 1532, 1813, 1813, 3206, 3190, 3017, 2996, 2959, 2939, - 1219, 1219, -1000, 1219, 2908, 2902, 2312, 2303, 1532, -1000, - 1527, 54244, -1000, -431, -1000, -7, 936, 1983, -1000, 36916, - 1532, -1000, 5074, -1000, 1233, -1000, -1000, -1000, -1000, -1000, - 34028, 1951, 3572, -1000, -1000, 1983, 1776, -1000, -1000, 357, - 90, 33306, 857, 857, 128, 1371, 1371, 21030, -1000, -1000, - -1000, -1000, -1000, -1000, 792, 2512, 400, 1983, -1000, 1979, - 3285, -1000, -1000, -1000, 2402, 26807, -1000, -1000, 1983, 1983, - 54244, 1937, 1931, -1000, 790, -1000, 1255, 1936, 22, 8, - -1000, -1000, -1000, -1000, 1371, -1000, 1348, 356, 341, -1000, - 438, -1000, -1000, -1000, -1000, 2290, 92, -1000, -1000, -1000, - 365, 357, -1000, -1000, -1000, -1000, -1000, -1000, 1611, 1611, - -1000, -1000, -1000, -1000, -1000, 1280, -1000, -1000, -1000, -1000, - 1278, -1000, -1000, 1271, -1000, -1000, 2641, 2129, 416, -1000, - -1000, 938, 1609, -1000, -1000, 2305, 938, 938, 52800, -1000, - -1000, 1722, 2419, 248, 54244, 986, 2156, -1000, 2103, 2103, - 2103, 54244, -1000, -1000, -1000, -1000, -1000, -1000, -504, 165, - 618, -1000, -1000, -1000, 2008, 52800, 1758, -1000, 232, -1000, - 1718, -1000, 52800, -1000, 1742, 2028, 1173, 1173, -1000, -1000, - -1000, 52800, 1983, -1000, -1000, -1000, -1000, 493, 2358, 297, - -1000, -1000, -290, -1000, -1000, 236, 232, 53522, 1173, 864, - -1000, -1000, -1000, -1000, -1000, -505, 1734, 481, 239, 329, - 54244, 54244, 54244, 54244, 54244, 54244, 512, -1000, -1000, 35, - -1000, -1000, 215, -1000, -1000, -1000, -1000, 215, -1000, -1000, - -1000, -1000, 307, 466, -1000, 54244, 54244, 914, -1000, -1000, - -1000, -1000, -1000, 1079, -1000, -1000, 1079, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2343, - 54244, 10, -471, -1000, -468, 21030, -1000, -1000, -1000, -1000, - 1312, 496, 1429, 23196, 23196, 2944, 2944, 23196, -1000, -1000, - -1000, 350, 350, 32584, -1000, 23196, 21030, 20308, -1000, -1000, - 21030, 21030, 961, -1000, 21030, 1167, -1000, 21030, -1000, -1000, - 1527, 1219, 1219, 1219, 1219, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 1896, -1000, 21030, 21030, 21030, - 1532, 312, -1000, -1000, -1000, -1000, -1000, 2538, -1000, 21030, - -1000, 32584, 21030, 21030, 21030, -1000, -1000, -1000, 21030, 21030, - -1000, -1000, 21030, -1000, 21030, -1000, -1000, -1000, -1000, -1000, - -1000, 21030, -1000, 21030, -1000, -1000, -1000, 21030, -1000, 21030, - -1000, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, -1000, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, -1000, 21030, -1000, 21030, - -1000, 21030, -1000, 21030, 21030, -1000, 21030, 21030, 21030, -1000, - 21030, 21030, 21030, 21030, -1000, -1000, -1000, -1000, 21030, 21030, - 21030, 21030, 21030, 21030, 21030, 21030, 21030, 21030, -1000, -1000, - -1000, -1000, -1000, -1000, 21030, -1000, 38360, 23, -271, 1536, - 23, 1536, 22474, 813, 811, 21752, -1000, 20308, 15241, -1000, - -1000, -1000, -1000, -1000, 21030, 21030, 21030, 21030, 21030, 21030, - -1000, -1000, -1000, 21030, 21030, -1000, 21030, -1000, 21030, -1000, - -1000, -1000, -1000, -1000, 936, -1000, 870, 870, 870, 52800, - -1000, -1000, -1000, -1000, 1932, -1000, 2438, -1000, 2238, 2235, - 2536, 2512, -1000, 28974, 3572, -1000, -1000, 52800, -423, -1000, - 2270, 2357, 857, 857, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 12329, 2461, 21030, 2153, 53522, 247, -1000, 28252, 52800, - 53522, 28974, 28974, 28974, 28974, 28974, -1000, 2196, 2195, -1000, - 2225, 2215, 2318, 54244, -1000, 1568, 1732, -1000, 21030, 31140, - 1922, 28974, -1000, -1000, 28974, 54244, 11601, -1000, -1000, 2, - -13, -1000, -1000, -1000, -1000, 361, -1000, -1000, 1550, 2400, - 2282, -1000, -1000, -1000, -1000, -1000, 1728, -1000, 1717, 1926, - 1711, 1704, 304, -1000, 2047, 2326, 938, 938, -1000, 1254, - -1000, 1173, 1599, 1593, -1000, -1000, -1000, 472, -1000, 2375, - 54244, 2151, 2150, 2149, -1000, -515, 1251, 2026, 1982, 21030, - 2024, 2492, 1882, 52800, -1000, -1000, 53522, -1000, 294, -1000, - 416, 52800, -1000, -1000, -1000, 334, 54244, -1000, 8486, -1000, - -1000, -1000, 232, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 54244, 271, -1000, 2020, 1274, -1000, -1000, 2088, -1000, -1000, - -1000, -1000, -1000, 216, 190, 1566, 213, 1560, 213, -1000, - 54244, 911, 2129, 54244, -1000, -1000, -1000, 1090, 1090, -1000, - -1000, 2325, -1000, 1173, 1219, 23196, 23196, -1000, 876, -1000, - -1000, 384, -248, 2014, 2014, -1000, 2014, 2015, -1000, 2014, - 174, 2014, 172, 2014, -1000, -1000, 1532, 1532, -1000, 1527, - -1000, 2287, 1181, -1000, 1371, 21030, 2889, -1000, -1000, -1000, - -1000, -1000, -71, 2842, 2833, 1219, -1000, 2013, 2012, 21030, - 1219, 1532, 2279, 1219, 1219, 1219, 1219, 1219, 1219, 1219, - 1219, 1219, 1219, 1219, 1219, 2231, 2221, 2211, 2206, 2201, - 2192, 2183, 2172, 2124, 2110, 2104, 2064, 2037, 2017, 1946, - 1940, 1219, 1219, 1934, 1219, 1919, 1859, -1000, 1371, 1527, - 2516, 1527, 1219, 1219, 2445, 313, 1219, 1702, 1702, 1702, - 1702, 1702, 1527, 1527, 1527, 1527, 1219, 52800, -1000, -271, - -1000, -1000, -310, -311, -1000, 1532, -271, 1918, 23196, 1219, - 23196, 23196, 23196, 1219, 1532, -1000, 1846, 1825, 2081, 1815, - 1219, 2042, 1219, 1219, 1219, 1811, -1000, 2447, 2447, 2447, - 1696, 1233, 54244, -1000, -1000, -1000, -1000, 2512, 2505, 1899, - -1000, -1000, 90, 573, -1000, 2294, 2357, -1000, 2488, 2261, - 2486, -1000, -1000, -1000, -1000, -1000, 1371, -1000, 2350, 1916, - -1000, 955, 1857, -1000, -1000, 19586, 1698, 2226, 531, 1696, - 1894, 3285, 2111, 2143, 3000, -1000, -1000, -1000, -1000, 2178, - -1000, 2134, -1000, -1000, 1992, -1000, 1519, 346, 28974, 1778, - 1778, -1000, 525, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 1076, 8486, 2571, -1000, 1558, -1000, 1346, 205, 1247, -1000, - -1000, 938, 938, -1000, 1025, 1023, -1000, 54244, 2011, -1000, - 357, 1554, 357, 1245, -1000, -1000, 1210, -1000, -1000, -1000, - -1000, 1973, 2092, -1000, -1000, -1000, -1000, 54244, -1000, -1000, - 54244, 54244, 54244, 2010, 2485, -1000, 21030, 2009, 944, 2334, - 52800, 52800, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 471, 938, -485, 311, 310, 938, 938, - 938, -526, -1000, -1000, 1691, 1687, -1000, -207, -1000, 21030, - -1000, -1000, -1000, -1000, -1000, 1273, 1273, 1500, 1498, 1493, - -1000, 1992, -1000, -1000, -1000, 1712, -1000, -1000, -193, 52800, - 52800, 52800, 52800, -1000, -1000, -1000, 1107, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 876, - 1532, 412, -195, 1532, -1000, -1000, 357, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 21030, -1000, 21030, - -1000, 1371, 21030, 2461, 1461, 21030, 21030, -1000, 1198, 1172, - 1219, -1000, -1000, -1000, 21030, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - -1000, 21030, -1000, -1000, -1000, 21030, -1000, 21030, -1000, 21030, - -1000, -1000, -1000, 21030, 303, 350, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1532, 344, -1000, - -1000, -1000, -1000, 2514, -1000, 1532, 21030, 2944, -1000, 2944, - 2944, 2944, -1000, -1000, -1000, 21030, -1000, 21030, 21030, -1000, - 21030, -1000, 21030, -1000, -1000, -1000, -1000, 21030, 1983, 2237, - 1983, 1983, 31140, -1000, -1000, 2505, 2471, 2483, 2247, 2249, - 2249, 2294, -1000, 2482, 2474, -1000, 1440, 2473, 1435, 999, - -1000, 53522, 21030, 247, -1000, 419, 52800, 247, 52800, -1000, - 2478, -1000, -1000, 21030, 2004, -1000, 21030, -1000, -1000, -1000, - -1000, 5973, 2512, 1778, -1000, -1000, 887, -1000, 21030, -1000, - 9314, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1422, - 1419, -1000, -1000, 1994, 21030, -1000, -1000, -1000, 1692, 1630, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1992, -1000, - -1000, -1000, -1000, 334, -510, 2089, 52800, 1171, -1000, 1671, - 1882, 330, 247, 1416, 938, 938, 938, 1161, 1155, 36916, - 1654, -1000, 52800, 413, -1000, 334, -1000, -229, -244, 1219, - -1000, -1000, 2394, -1000, -1000, 15241, -1000, -1000, 1988, 2096, - -1000, -1000, -1000, -1000, 2210, -178, -208, -1000, -1000, 1219, - 1219, 1250, 1532, -1000, 1219, 1219, 1577, 1542, -1000, 1219, - 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, - 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1219, 1527, - 1785, -1000, 303, 1532, 2131, -1000, -1000, 5973, -1000, -1000, - 2478, 2470, 23, -1000, -1000, 230, 23, 1371, 985, 1532, - 1532, 985, 1779, 1219, 1749, 1736, 1219, 1219, 31862, -1000, - 2464, 2448, 37638, 37638, 936, 2471, -280, 21030, 21030, 2243, - 1123, -1000, -1000, -1000, -1000, 1409, 1385, -1000, 1380, -1000, - 2563, -1000, 1371, -1000, 247, -1000, 523, 1857, -1000, 2461, - 1371, 52800, 1371, 76, 2478, -1000, 1219, -1000, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, - 1983, 1983, 1983, 1983, 1983, 1983, 1983, -1000, -1000, 52800, - 1837, -1000, -1000, 2389, 1634, 164, -1000, 1535, 1882, -1000, - -1000, 206, -1000, 21030, -1000, 36916, 1375, 1344, -1000, -1000, - -1000, -1000, -526, -1000, -1000, -1000, -1000, -1000, -1000, 408, - 1876, -1000, 934, 52800, 54244, -1000, 2202, -1000, -1000, -1000, - 21030, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 1915, 2574, 1455, 1701, 1701, 1701, 1701, 1701, 22042, + 1426, -1000, -1000, -1000, 1513, 4177, 1485, 4163, 1701, 1701, + -1000, 1701, 4147, 4143, 1499, 1866, 2912, 2834, 1701, 1701, + 1701, 1701, 1701, 2823, 2788, 1701, 1701, 2777, 1701, 3910, + 1701, 2751, 2652, 2646, 2641, 2635, 2631, 2626, 2618, 2602, + 2585, 2559, 2553, 2544, 2539, 2529, 2525, 2519, 2510, 1701, + 1701, 1701, 3876, 1701, 3867, 1701, 3840, 1701, 1701, 3836, + 2503, 2497, 1499, 1904, -1000, 3832, 1701, 3546, 3527, 3485, + 2480, 3467, 3463, 3451, 1701, 1701, 1701, 2431, 3446, 3439, + 3434, 3374, 3365, 3358, 3350, 3301, 3209, 1701, 1391, 1391, + 1391, 1391, 1391, 3152, -277, 1701, 1499, -1000, -1000, -1000, + -1000, -1000, 3061, 2423, 3056, 3025, 3005, 2994, 1499, 1903, + 1967, 523, -1000, -1000, 1793, 1499, 1499, 1793, 1793, 2990, + 2975, 2969, 2961, 2956, 2946, 1701, 1701, -1000, 1701, 2926, + 2897, 2393, 2375, 1499, -1000, 1391, 55392, -1000, -435, -1000, + 4, 952, 1967, -1000, 37992, 1499, -1000, 4983, -1000, 1198, + -1000, -1000, -1000, -1000, -1000, 35092, 1752, -1000, -1000, -1000, + 1967, 1743, -1000, -1000, -1000, -1000, 357, 99, 34367, 842, + 842, 143, 1631, 1631, 22042, -1000, -1000, -1000, -1000, -1000, + -1000, 522, 2551, 434, 1967, -1000, 1908, 3407, -1000, -1000, + -1000, 2440, 27842, -1000, -1000, 1967, 1967, 55392, 1821, 1818, + -1000, 520, -1000, 1345, 1898, 37, 16, -1000, -1000, -1000, + -1000, 1631, -1000, 1342, 382, 369, -1000, 431, -1000, -1000, + -1000, -1000, 2314, 111, -1000, -1000, -1000, 308, 357, -1000, + -1000, -1000, -1000, -1000, -1000, 1621, 1621, -1000, -1000, -1000, + -1000, -1000, 1210, -1000, -1000, -1000, -1000, 1201, -1000, -1000, + 1188, -1000, -1000, 2667, 2105, 424, -1000, -1000, 909, 1613, + -1000, -1000, 2316, 909, 909, 53942, -1000, -1000, 1840, 2412, + 277, 55392, 980, 2138, -1000, 2098, 2098, 2098, 55392, -1000, + -1000, -1000, -1000, -1000, -1000, -512, 187, 573, -1000, -1000, + -1000, 4187, 53942, 1739, -1000, 257, -1000, 1832, -1000, 53942, + -1000, 1734, 2048, 1332, 1332, -1000, -1000, -1000, 53942, 1967, + -1000, -1000, -1000, -1000, 474, 2377, 335, -1000, -1000, -304, + -1000, -1000, 262, 257, 54667, 1332, 851, -1000, -1000, -1000, + -1000, -1000, -503, 1732, 468, 264, 330, 55392, 55392, 55392, + 55392, 55392, 55392, 499, -1000, -1000, 46, -1000, -1000, 229, + -1000, -1000, -1000, -1000, -1000, 229, -1000, -1000, -1000, -1000, + -1000, 310, 448, -1000, 55392, 55392, 928, -1000, -1000, -1000, + -1000, -1000, 1028, -1000, -1000, 1028, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 2354, 55392, 17, -474, + -1000, -470, 22042, -1000, -1000, -1000, -1000, 1405, 829, 1496, + 24217, 24217, 1294, 1294, 24217, -1000, -1000, -1000, 930, 930, + 33642, -1000, 24217, 22042, 21317, -1000, -1000, 22042, 22042, 22042, + 913, -1000, 22042, 1410, -1000, 22042, -1000, -1000, 1391, 1701, + 1701, 1701, 1701, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 1948, -1000, 22042, 22042, 22042, 1499, 334, + -1000, -1000, -1000, -1000, -1000, 2572, -1000, 22042, -1000, 33642, + 22042, 22042, 22042, -1000, -1000, -1000, 22042, 22042, -1000, -1000, + 22042, -1000, 22042, -1000, -1000, -1000, -1000, -1000, -1000, 22042, + -1000, 22042, -1000, -1000, -1000, 22042, -1000, 22042, -1000, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, -1000, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, -1000, 22042, -1000, 22042, -1000, 22042, + -1000, 22042, 22042, -1000, 22042, 22042, 22042, -1000, 22042, 22042, + 22042, 22042, -1000, -1000, -1000, -1000, 22042, 22042, 22042, 22042, + 22042, 22042, 22042, 22042, 22042, 22042, -1000, -1000, -1000, -1000, + -1000, -1000, 22042, -1000, 39442, 14, -277, 1307, 14, 1307, + 23492, 796, 795, 22767, -1000, 21317, 16230, -1000, -1000, -1000, + -1000, -1000, 22042, 22042, 22042, 22042, 22042, 22042, -1000, -1000, + -1000, 22042, 22042, -1000, 22042, -1000, 22042, -1000, -1000, -1000, + -1000, -1000, 952, -1000, 867, 815, 870, 53942, -1000, -1000, + -1000, -1000, 1893, -1000, 2465, -1000, 2225, 2221, 2555, 2551, + -1000, 30017, -1000, -1000, 53942, -426, -1000, 2276, 2282, 842, + 842, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 13306, 2477, + 22042, 2134, 54667, 250, -1000, 29292, 53942, 54667, 30017, 30017, + 30017, 30017, 30017, -1000, 2188, 2182, -1000, 2166, 2157, 2184, + 55392, -1000, 1789, 1727, -1000, 22042, 32192, 1837, 30017, -1000, + -1000, 30017, 55392, 12575, -1000, -1000, 12, 3, -1000, -1000, + -1000, -1000, 1382, -1000, -1000, 969, 2434, 2312, -1000, -1000, + -1000, -1000, -1000, 1723, -1000, 1717, 1890, 1715, 1690, 122, + -1000, 2042, 2347, 909, 909, -1000, 1185, -1000, 1332, 1606, + 1588, -1000, -1000, -1000, 467, -1000, 2419, 55392, 2131, 2128, + 2127, -1000, -520, 1182, 2047, 1992, 22042, 2045, 2532, 1880, + 53942, -1000, -1000, 54667, -1000, 299, -1000, 424, 53942, -1000, + -1000, -1000, 341, 55392, -1000, 5569, -1000, -1000, -1000, 257, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 55392, 276, -1000, + 2036, 1261, -1000, -1000, 2096, -1000, -1000, -1000, -1000, -1000, + 245, 230, 1586, 227, 1546, 227, -1000, 55392, 927, 2105, + 55392, -1000, -1000, -1000, 1025, 1025, -1000, -1000, 2338, -1000, + 1332, 1701, 24217, 24217, -1000, 853, -1000, -1000, 383, -254, + 2029, 2029, -1000, 2029, 2033, -1000, 2029, 189, 2029, 188, + 2029, -1000, -1000, 1499, 1499, -1000, 1391, -1000, 2344, 1929, + 2887, -1000, 1631, 22042, 2880, -1000, -1000, -1000, -1000, -1000, + -63, 2796, 2756, 1701, -1000, 2026, 2023, 22042, 1701, 1499, + 2319, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, + 1701, 1701, 1701, 2309, 2295, 2287, 2281, 2274, 2263, 2249, + 2218, 2206, 2193, 2168, 2159, 2146, 2130, 2080, 2070, 1701, + 1701, 2059, 1701, 2052, 2030, -1000, 1631, 1391, 2436, 1391, + 1701, 1701, 2397, 319, 1701, 1688, 1688, 1688, 1688, 1688, + 1391, 1391, 1391, 1391, 1701, 53942, -1000, -277, -1000, -1000, + -371, -374, -1000, 1499, -277, 1889, 24217, 1701, 24217, 24217, + 24217, 1701, 1499, -1000, 1987, 1972, 2325, 1937, 1701, 2302, + 1701, 1701, 1701, 1905, -1000, 2466, 1967, 2466, 1967, 2466, + 1620, 1198, 55392, -1000, -1000, -1000, -1000, 2551, 2528, 1888, + -1000, 99, 614, -1000, 2273, 2282, -1000, 2531, 2293, 2530, + -1000, -1000, -1000, -1000, -1000, 1631, -1000, 2394, 1860, -1000, + 932, 1867, -1000, -1000, 20592, 1672, 2213, 519, 1620, 1922, + 3407, 2088, 2126, 3490, -1000, -1000, -1000, -1000, 2175, -1000, + 2170, -1000, -1000, 1981, -1000, 1858, 381, 30017, 1851, 1851, + -1000, 516, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1080, + 5569, 2596, -1000, 1543, -1000, 1340, 208, 1178, -1000, -1000, + 909, 909, -1000, 1010, 1007, -1000, 55392, 2019, -1000, 357, + 1541, 357, 1172, -1000, -1000, 1165, -1000, -1000, -1000, -1000, + 2014, 2151, -1000, -1000, -1000, -1000, 55392, -1000, -1000, 55392, + 55392, 55392, 1998, 2514, -1000, 22042, 1994, 914, 2535, 53942, + 53942, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 808, 909, -485, 318, 314, 909, 909, 909, + -521, -1000, -1000, 1617, 1553, -1000, -215, -1000, 22042, -1000, + -1000, -1000, -1000, -1000, 1286, 1286, 1537, 1524, 1518, -1000, + 1981, -1000, -1000, -1000, 1790, -1000, -1000, -181, 53942, 53942, + 53942, 53942, -1000, -1000, -1000, 1122, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 853, 1499, + 343, -187, 1499, -1000, -1000, 357, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 22042, -1000, 22042, -1000, + 22042, 1631, 22042, 2477, 1509, 22042, 22042, -1000, 1163, 1155, + 1701, -1000, -1000, -1000, 22042, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + -1000, 22042, -1000, -1000, -1000, 22042, -1000, 22042, -1000, 22042, + -1000, -1000, -1000, 22042, 287, 930, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1499, 377, -1000, + -1000, -1000, -1000, 2534, -1000, 1499, 22042, 1294, -1000, 1294, + 1294, 1294, -1000, -1000, -1000, 22042, -1000, 22042, 22042, -1000, + 22042, -1000, 22042, -1000, -1000, -1000, -1000, 22042, 1967, 2208, + 38717, 1967, 38717, 1967, 32192, -1000, -1000, 2528, 2512, 2509, + 2243, 2256, 2256, 2273, -1000, 2507, 2494, -1000, 1500, 2489, + 1492, 1002, -1000, 54667, 22042, 250, -1000, 414, 53942, 250, + 53942, -1000, 2504, -1000, -1000, 22042, 1991, -1000, 22042, -1000, + -1000, -1000, -1000, 3215, 2551, 1851, -1000, -1000, 863, -1000, + 22042, -1000, 10388, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 1477, 1471, -1000, -1000, 1983, 22042, -1000, -1000, -1000, + 1753, 1740, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 1981, -1000, -1000, -1000, -1000, 341, -516, 2125, 53942, 1147, + -1000, 1532, 1880, 373, 250, 1465, 909, 909, 909, 1125, + 1117, 37992, 1517, -1000, 53942, 418, -1000, 341, -1000, -222, + -224, 1701, -1000, -1000, 2432, -1000, -1000, 16230, -1000, -1000, + 1980, 2097, -1000, -1000, -1000, -1000, 2163, -169, -198, -1000, + -1000, 1701, 1701, 1701, 1255, 1499, -1000, 1701, 1701, 1735, + 1627, -1000, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, + 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, + 1701, 1701, 1391, 1895, -1000, 287, 1499, 2123, -1000, -1000, + 3215, -1000, -1000, 2504, 2483, 14, -1000, -1000, 259, 14, + 1631, 946, 1499, 1499, 946, 1823, 1701, 1788, 1761, 1701, + 1701, 32917, -1000, 2482, 2481, 1498, -1000, -1000, 38717, 1498, + 38717, 952, 2512, -286, 22042, 22042, 2238, 1173, -1000, -1000, + -1000, -1000, 1457, 1444, -1000, 1420, -1000, 2593, -1000, 1631, + -1000, 250, -1000, 504, 1867, -1000, 2477, 1631, 53942, 1631, + 100, 2504, -1000, 1701, -1000, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, + 1967, 1967, 1967, 1967, -1000, -1000, 53942, 2118, -1000, -1000, + 2429, 1515, 164, -1000, 1505, 1880, -1000, -1000, 220, -1000, + 22042, -1000, 37992, 1418, 1408, -1000, -1000, -1000, -1000, -521, + -1000, -1000, -1000, -1000, -1000, -1000, 387, 1876, -1000, 908, + 53942, 55392, -1000, 2038, -1000, -1000, -1000, -1000, 22042, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 21030, -1000, - 1532, 2126, -1000, -358, -1000, -486, 21030, -271, -1000, -1000, - -271, -1000, -1000, -1000, -1000, -1000, 21030, -1000, -1000, 21030, - -1000, 21030, -1000, -1000, 1576, -1000, -1000, -1000, -1000, -1000, - 1576, 1576, -1000, -280, -1000, 1861, -1000, 52800, 1371, 1852, - -1000, 1122, -1000, -1000, -1000, -1000, -1000, 53522, 1857, 52800, - -1000, 1541, 1532, 1983, 2461, -1000, 1537, -1000, 408, -1000, - 1986, 1982, -1000, -1000, -1000, 18864, -1000, -1000, -1000, -1000, - -1000, 267, -188, 15241, 10873, 1531, -1000, -187, 1219, 1527, - -1000, -461, -1000, -1000, -1000, -1000, 291, -1000, -1000, 1852, - -1000, -1000, 1607, 1565, 1379, 36194, -1000, -1000, -1000, -1000, - -280, -1000, -1000, 2382, -1000, -1000, 1751, -1000, -1000, 31140, - 52078, -1000, -172, 338, -188, 21030, 1985, 1532, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -37, -1000, -1000, 519, - -1000, -1000, -1000, 2088, -199, -1000, -1000, -1000, 318, -475, - -298, -299, 23196, -1000, 21030, -1000, 21030, -1000, 21030, -1000, - -1000, -1000, 52800, 1983, -1000, 1489, -1000, 3989, -327, 2120, - -1000, -132, -1000, -1000, -1000, 1072, 1341, -1000, -1000, -1000, - -1000, -1000, -1000, 1431, 52800, -1000, 421, -1000, -1000, 14513, - -193, -215, 992, -1000, -1000, -1000, -1000, -1000, 2944, 1329, - 1129, 1219, -1000, 52800, -1000, 52078, -322, 864, 5973, -1000, - 2117, 2113, 2522, -1000, -1000, -1000, -1000, -1000, -1000, -529, - 1465, 250, -1000, -1000, -1000, 318, -301, -1000, 21030, -1000, - 21030, -1000, 1532, -1000, -1000, 2372, 76, -1000, 2560, -1000, - 2534, 1060, 1060, -1000, 1106, -529, -1000, -1000, -1000, -1000, - 1219, 1219, -1000, -329, -1000, -1000, -1000, -1000, -1000, 420, - 1302, -1000, -1000, -1000, -1000, -1000, 5973, -1000, -1000, -1000, - 263, 263, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 22042, -1000, 1499, 2116, + -1000, -362, -1000, -494, 22042, -277, -1000, -1000, -277, -1000, + -1000, -1000, -1000, -1000, 22042, -1000, -1000, 22042, -1000, 22042, + -1000, -1000, 1498, -1000, -1000, -1000, 37267, -1000, 1498, -1000, + 1498, -1000, -286, -1000, 1868, -1000, 53942, 1631, 1866, -1000, + 1145, -1000, -1000, -1000, -1000, -1000, 54667, 1867, 53942, -1000, + 1475, 1499, 1967, 2477, -1000, 1416, -1000, 387, -1000, 1978, + 1992, -1000, -1000, -1000, 19867, -1000, -1000, -1000, -1000, -1000, + 234, -175, 16230, 11844, 1396, -1000, -173, 1701, 1391, -1000, + -462, -1000, -1000, -1000, -1000, 292, -1000, -1000, 1866, -1000, + -1000, 1695, 1646, 1626, -1000, -1000, -1000, -1000, -1000, -1000, + -286, -1000, -1000, 2424, -1000, -1000, 1555, -1000, -1000, 32192, + 53217, -1000, -164, 327, -175, 22042, 1977, 1499, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -41, -1000, -1000, 503, + -1000, -1000, -1000, 2096, -196, -1000, -1000, -1000, 295, -477, + -281, -283, 24217, -1000, 22042, -1000, 22042, -1000, 22042, -1000, + 53942, 1967, -1000, 1389, -1000, 4148, -384, 2104, -1000, -134, + -1000, -1000, -1000, 1047, 1380, -1000, -1000, -1000, -1000, -1000, + -1000, 1899, 53942, -1000, 433, -1000, -1000, 15499, -181, -213, + 974, -1000, -1000, -1000, -1000, -1000, 1294, 1504, 1414, 1701, + -1000, 53942, -1000, 53217, -379, 851, 3215, -1000, 2034, 1982, + 2580, -1000, -1000, -1000, -1000, -1000, -1000, -527, 1384, 279, + -1000, -1000, -1000, 295, -288, -1000, 22042, -1000, 22042, -1000, + 1499, -1000, -1000, 2404, 100, -1000, 2587, -1000, 2588, 1003, + 1003, -1000, 1112, -527, -1000, -1000, -1000, -1000, 1701, 1701, + -1000, -385, -1000, -1000, -1000, -1000, -1000, 432, 1194, -1000, + -1000, -1000, -1000, -1000, 3215, -1000, -1000, -1000, 289, 289, + -1000, -1000, } var yyPgo = [...]int{ - 0, 3158, 3156, 28, 6, 41, 35, 3155, 3154, 3153, - 177, 3151, 3137, 3135, 3134, 3130, 3129, 2624, 2610, 2600, - 3127, 3126, 3125, 3122, 3120, 3108, 3107, 3104, 3103, 39, - 106, 68, 99, 211, 213, 3100, 176, 166, 198, 3097, - 3096, 3095, 116, 192, 83, 82, 195, 3092, 3091, 74, - 3090, 3088, 3087, 185, 184, 183, 1040, 3086, 182, 112, - 48, 3083, 3080, 3076, 3075, 3072, 3065, 3063, 3061, 3060, - 3054, 3053, 3052, 3046, 3043, 3041, 3040, 3039, 3036, 296, - 3035, 3033, 21, 3030, 76, 3028, 3026, 3025, 3024, 3023, - 11, 3022, 3017, 26, 44, 3012, 3009, 47, 3008, 3007, - 3004, 2998, 2997, 69, 2994, 22, 2983, 40, 2979, 2978, - 121, 2974, 2971, 2966, 43, 2962, 2961, 2957, 29, 167, - 2956, 2955, 139, 2954, 2953, 2950, 165, 206, 2949, 2239, - 205, 108, 111, 2948, 2947, 103, 188, 2946, 123, 2927, - 2924, 2915, 150, 2914, 3191, 2913, 2909, 64, 70, 199, - 2907, 2895, 163, 66, 8, 16, 17, 2894, 2892, 63, - 73, 2889, 101, 2885, 2884, 104, 84, 2883, 90, 98, - 2882, 2881, 5, 7, 2879, 1, 4, 2, 80, 2878, - 2877, 115, 2876, 2873, 2871, 95, 2868, 2865, 4363, 2862, - 85, 128, 102, 62, 2860, 171, 131, 2858, 2857, 2856, - 2854, 2850, 49, 2849, 2848, 2847, 138, 251, 162, 2831, - 144, 337, 52, 143, 2830, 189, 77, 197, 190, 2828, - 2825, 135, 133, 2824, 2821, 55, 164, 191, 2812, 94, - 127, 117, 168, 91, 130, 2808, 2805, 56, 60, 2803, - 2802, 2801, 2800, 174, 2799, 2797, 59, 2795, 54, 2794, - 186, 2792, 136, 79, 2791, 170, 169, 2790, 61, 2789, - 2788, 65, 96, 100, 38, 2787, 158, 161, 125, 172, - 2786, 2782, 53, 2780, 2779, 2778, 196, 292, 2774, 2772, - 294, 178, 141, 147, 89, 2771, 299, 2770, 2767, 13, - 4391, 6814, 2766, 37, 160, 2765, 2758, 6537, 20, 45, - 24, 2755, 204, 2753, 2752, 2750, 2749, 217, 202, 110, - 159, 57, 2744, 2741, 2736, 36, 2735, 2734, 2733, 2732, - 2731, 2723, 72, 34, 33, 32, 212, 58, 19, 97, - 153, 152, 67, 2709, 2706, 2705, 124, 87, 2704, 157, - 155, 120, 129, 2701, 180, 142, 119, 2700, 93, 31, - 2696, 2693, 2688, 2681, 92, 2678, 2677, 2674, 2669, 151, - 146, 118, 78, 2666, 81, 114, 149, 145, 51, 2665, - 46, 2664, 2663, 30, 193, 23, 2662, 15, 105, 109, - 2661, 5648, 181, 2660, 9, 298, 148, 2657, 2655, 10, - 12, 18, 2654, 2639, 2638, 2636, 132, 2635, 2632, 2630, - 2625, 27, 50, 25, 14, 113, 75, 2620, 2615, 140, - 2614, 2593, 2592, 0, 1005, 126, 2591, 207, + 0, 3336, 3335, 26, 12, 36, 35, 3334, 3333, 3332, + 176, 3331, 3323, 3322, 3319, 3316, 3297, 2690, 2686, 2681, + 3294, 3292, 3285, 3284, 3283, 3227, 3224, 3222, 3218, 45, + 97, 37, 110, 220, 214, 3217, 181, 159, 201, 3215, + 3211, 3198, 114, 189, 78, 82, 193, 3196, 3195, 66, + 3192, 3190, 3189, 188, 180, 179, 1020, 3187, 186, 113, + 55, 3186, 3184, 3182, 3171, 3170, 3167, 3165, 3162, 3161, + 3160, 3157, 3155, 3154, 3152, 3144, 3143, 3142, 3141, 301, + 3139, 3136, 14, 3132, 73, 3130, 3126, 3123, 3122, 3121, + 9, 3120, 3117, 32, 42, 3115, 3109, 44, 3108, 3106, + 3103, 3101, 3098, 79, 3096, 23, 3095, 38, 3093, 3091, + 121, 3088, 3086, 3085, 39, 3084, 3081, 3077, 11, 169, + 3071, 3068, 138, 3063, 3062, 3061, 168, 222, 3054, 2240, + 160, 100, 105, 3049, 3048, 3042, 163, 196, 3037, 117, + 3036, 3032, 3025, 145, 3022, 3825, 3012, 3004, 67, 65, + 203, 3003, 3000, 164, 74, 5, 2999, 20, 8, 2997, + 2996, 70, 64, 2995, 103, 2993, 2992, 98, 89, 2990, + 99, 94, 2988, 2985, 28, 2, 2982, 1, 6, 4, + 102, 2980, 2974, 109, 2963, 2962, 2959, 92, 2955, 2949, + 1068, 2941, 84, 128, 101, 63, 2938, 170, 171, 2935, + 2934, 2933, 2928, 2925, 50, 2922, 2919, 2915, 136, 252, + 166, 2914, 146, 341, 51, 147, 2910, 191, 77, 199, + 192, 2908, 2907, 130, 135, 2905, 2902, 54, 167, 195, + 2898, 91, 129, 116, 177, 90, 132, 2896, 2881, 56, + 61, 2879, 2874, 2870, 2868, 172, 2865, 2858, 68, 2857, + 53, 2854, 173, 2848, 137, 80, 2847, 182, 162, 2843, + 62, 2842, 2839, 93, 95, 60, 27, 2834, 155, 158, + 126, 183, 2833, 2831, 52, 2826, 2824, 2821, 194, 315, + 2820, 2819, 373, 174, 141, 149, 83, 2817, 334, 2814, + 2812, 19, 4982, 7758, 2804, 41, 157, 2803, 2802, 7552, + 22, 43, 15, 2801, 205, 2799, 2797, 2791, 2788, 197, + 204, 108, 156, 58, 2786, 2783, 2780, 72, 2777, 2773, + 2766, 2764, 2763, 2754, 34, 33, 31, 71, 215, 59, + 25, 96, 152, 151, 69, 2753, 2752, 2751, 120, 87, + 2750, 154, 153, 124, 165, 2748, 178, 140, 118, 2747, + 106, 30, 2744, 2740, 2739, 2738, 85, 2737, 2734, 2733, + 2732, 150, 142, 119, 81, 2727, 76, 115, 148, 143, + 57, 2723, 46, 2722, 2719, 29, 190, 24, 2718, 47, + 104, 112, 2717, 6263, 187, 2712, 13, 337, 161, 2711, + 2705, 10, 18, 21, 2704, 2701, 2699, 2698, 131, 2677, + 2667, 2661, 2659, 17, 49, 16, 7, 111, 75, 2652, + 2637, 139, 2636, 2627, 2617, 0, 1021, 127, 2616, 200, } -//line sql.y:8575 +//line sql.y:8625 type yySymType struct { union any empty struct{} @@ -7903,6 +8029,11 @@ func (st *yySymType) insertActionUnion() InsertAction { return v } +func (st *yySymType) intPtrUnion() *int { + v, _ := st.union.(*int) + return v +} + func (st *yySymType) integerUnion() int { v, _ := st.union.(int) return v @@ -8304,230 +8435,231 @@ func (st *yySymType) withUnion() *With { } var yyR1 = [...]int{ - 0, 411, 412, 412, 7, 7, 7, 7, 7, 7, + 0, 413, 414, 414, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 258, 381, 382, 382, 256, 256, 28, 74, 36, + 7, 260, 383, 384, 384, 258, 258, 28, 74, 36, 36, 35, 35, 38, 38, 37, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 29, 29, 29, 29, 30, 30, 30, 30, 30, 15, 16, 34, 34, 17, 17, 108, 108, 18, - 19, 19, 19, 19, 415, 415, 183, 183, 181, 181, - 182, 182, 261, 261, 20, 265, 265, 267, 267, 267, - 267, 257, 257, 257, 21, 21, 266, 266, 268, 268, - 268, 271, 271, 271, 271, 310, 310, 310, 22, 22, - 22, 22, 22, 128, 128, 384, 384, 383, 377, 377, - 376, 376, 375, 380, 380, 379, 379, 378, 40, 41, - 50, 50, 50, 50, 51, 52, 385, 385, 350, 57, + 19, 19, 19, 19, 417, 417, 185, 185, 183, 183, + 184, 184, 263, 263, 20, 267, 267, 269, 269, 269, + 269, 259, 259, 259, 21, 21, 268, 268, 270, 270, + 270, 273, 273, 273, 273, 312, 312, 312, 22, 22, + 22, 22, 22, 128, 128, 386, 386, 385, 379, 379, + 378, 378, 377, 382, 382, 381, 381, 380, 40, 41, + 50, 50, 50, 50, 51, 52, 387, 387, 352, 57, 57, 56, 56, 56, 56, 56, 56, 58, 58, 54, - 54, 53, 53, 55, 55, 352, 352, 338, 338, 351, - 351, 351, 351, 351, 351, 351, 337, 337, 139, 139, - 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, - 235, 235, 235, 235, 235, 235, 235, 400, 400, 400, - 399, 399, 236, 236, 236, 236, 236, 236, 236, 236, - 148, 148, 159, 159, 159, 159, 159, 159, 146, 146, - 147, 145, 145, 145, 153, 153, 153, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 153, 404, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, - 404, 404, 404, 158, 158, 154, 154, 154, 155, 155, - 155, 156, 156, 401, 401, 401, 401, 315, 315, 315, - 315, 318, 318, 316, 316, 316, 316, 316, 316, 316, - 316, 316, 317, 317, 317, 317, 317, 317, 317, 319, - 319, 319, 319, 319, 320, 320, 320, 320, 320, 320, - 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, - 321, 321, 321, 321, 321, 321, 321, 321, 336, 336, - 322, 322, 330, 330, 331, 331, 332, 332, 332, 333, - 333, 333, 334, 334, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 329, 329, 328, 328, 328, 339, 364, - 364, 363, 363, 361, 361, 361, 361, 361, 361, 361, - 361, 348, 348, 358, 358, 358, 358, 358, 347, 347, - 343, 343, 343, 344, 344, 345, 345, 342, 342, 346, - 346, 360, 360, 359, 359, 340, 340, 341, 341, 366, - 402, 402, 402, 402, 402, 403, 403, 367, 392, 394, - 394, 394, 393, 393, 390, 391, 389, 389, 389, 389, - 389, 84, 84, 84, 284, 284, 285, 285, 356, 356, - 355, 355, 355, 357, 357, 354, 354, 354, 354, 354, - 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, - 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, - 354, 354, 354, 354, 354, 354, 279, 279, 279, 388, - 388, 388, 388, 388, 388, 387, 387, 387, 353, 353, - 353, 353, 386, 386, 59, 59, 216, 216, 405, 405, - 406, 406, 406, 47, 47, 47, 47, 47, 47, 46, - 46, 46, 42, 42, 42, 42, 42, 42, 42, 42, + 54, 53, 53, 55, 55, 354, 354, 340, 340, 353, + 353, 353, 353, 353, 353, 353, 339, 339, 140, 140, + 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 402, 402, 402, + 401, 401, 238, 238, 238, 238, 238, 238, 238, 238, + 149, 149, 161, 161, 161, 161, 161, 161, 147, 147, + 148, 146, 146, 146, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 406, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 160, 160, 155, 155, 155, 157, 157, + 156, 156, 156, 158, 158, 403, 403, 403, 403, 317, + 317, 317, 317, 320, 320, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 319, 319, 319, 319, 319, 319, + 319, 321, 321, 321, 321, 321, 322, 322, 322, 322, + 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + 322, 322, 323, 323, 323, 323, 323, 323, 323, 323, + 338, 338, 327, 327, 332, 332, 333, 333, 334, 334, + 334, 335, 335, 335, 336, 336, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 331, 331, 330, 330, 330, + 341, 366, 366, 365, 365, 363, 363, 363, 363, 363, + 363, 363, 363, 350, 350, 360, 360, 360, 360, 360, + 349, 349, 345, 345, 345, 346, 346, 347, 347, 344, + 344, 348, 348, 362, 362, 361, 361, 342, 342, 343, + 343, 368, 404, 404, 404, 404, 404, 405, 405, 369, + 394, 396, 396, 396, 395, 395, 392, 393, 391, 391, + 391, 391, 391, 84, 84, 84, 286, 286, 287, 287, + 358, 358, 357, 357, 357, 359, 359, 356, 356, 356, + 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, + 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, + 356, 356, 356, 356, 356, 356, 356, 356, 281, 281, + 281, 390, 390, 390, 390, 390, 390, 389, 389, 389, + 355, 355, 355, 355, 388, 388, 59, 59, 218, 218, + 407, 407, 408, 408, 408, 47, 47, 47, 47, 47, + 47, 46, 46, 46, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 48, 48, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 23, 23, 23, 23, 23, + 42, 42, 42, 42, 42, 48, 48, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 110, 110, 111, 111, 111, - 111, 113, 113, 113, 369, 369, 60, 60, 3, 3, - 171, 173, 174, 174, 172, 172, 172, 172, 172, 172, - 62, 62, 61, 61, 176, 175, 177, 177, 177, 1, - 1, 2, 2, 4, 4, 374, 374, 374, 374, 374, - 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, - 374, 374, 374, 374, 374, 374, 374, 335, 335, 335, - 368, 368, 370, 112, 112, 112, 112, 112, 112, 112, - 112, 112, 112, 116, 115, 115, 114, 117, 117, 117, - 117, 117, 117, 117, 117, 372, 372, 372, 63, 63, - 373, 323, 324, 325, 5, 6, 349, 371, 124, 124, - 24, 39, 39, 25, 25, 25, 25, 26, 26, 64, - 67, 67, 65, 65, 65, 65, 65, 65, 65, 65, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 110, + 110, 111, 111, 111, 111, 113, 113, 113, 371, 371, + 60, 60, 3, 3, 173, 175, 176, 176, 174, 174, + 174, 174, 174, 174, 62, 62, 61, 61, 178, 177, + 179, 179, 179, 1, 1, 2, 2, 4, 4, 376, + 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, + 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, + 376, 337, 337, 337, 370, 370, 372, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 116, 115, 115, + 114, 117, 117, 117, 117, 117, 117, 117, 117, 374, + 374, 374, 63, 63, 375, 324, 325, 326, 5, 6, + 351, 373, 124, 124, 24, 39, 39, 25, 25, 25, + 25, 26, 26, 64, 67, 67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 278, 278, 287, 287, 277, 277, - 302, 302, 302, 280, 280, 280, 281, 281, 398, 398, - 398, 274, 274, 66, 66, 66, 303, 303, 303, 303, - 69, 69, 407, 407, 408, 408, 409, 409, 409, 70, - 71, 71, 305, 305, 306, 306, 72, 73, 85, 85, - 85, 85, 85, 85, 85, 86, 86, 86, 86, 109, - 109, 109, 10, 10, 10, 10, 81, 81, 81, 9, - 9, 11, 68, 68, 75, 395, 395, 396, 397, 397, - 397, 397, 76, 78, 27, 27, 27, 27, 27, 27, - 134, 134, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 129, 129, 129, 123, 123, 416, - 79, 80, 80, 127, 127, 127, 120, 120, 120, 126, - 126, 126, 12, 12, 13, 260, 260, 14, 14, 131, - 131, 133, 133, 133, 133, 133, 135, 135, 135, 135, - 135, 135, 135, 130, 130, 132, 132, 132, 132, 295, - 295, 295, 294, 294, 165, 165, 167, 166, 166, 168, - 168, 169, 169, 169, 169, 214, 214, 191, 191, 253, - 253, 254, 254, 252, 252, 259, 259, 255, 255, 255, - 255, 262, 262, 170, 170, 170, 170, 178, 178, 179, - 179, 180, 180, 304, 304, 300, 300, 300, 299, 299, - 184, 184, 184, 186, 185, 185, 185, 185, 187, 187, - 189, 189, 188, 188, 190, 195, 195, 194, 194, 192, - 192, 192, 192, 193, 193, 193, 193, 196, 196, 144, - 144, 144, 144, 144, 144, 144, 144, 157, 157, 157, - 157, 160, 160, 160, 160, 160, 160, 160, 160, 160, - 160, 160, 243, 243, 149, 149, 149, 149, 149, 149, - 149, 149, 149, 149, 149, 149, 149, 149, 149, 152, - 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, - 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, - 152, 152, 152, 152, 219, 219, 218, 218, 87, 87, - 87, 88, 88, 89, 89, 89, 89, 89, 90, 90, - 90, 90, 90, 90, 90, 92, 92, 91, 91, 209, - 209, 292, 292, 93, 94, 94, 97, 97, 96, 95, - 95, 101, 101, 98, 98, 100, 100, 99, 102, 102, - 103, 104, 104, 275, 275, 197, 197, 205, 205, 205, - 205, 198, 198, 198, 198, 198, 198, 198, 206, 206, - 206, 213, 207, 207, 203, 203, 201, 201, 201, 201, - 201, 201, 201, 201, 201, 201, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 162, 162, 162, - 162, 224, 224, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, - 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, - 164, 312, 312, 118, 118, 118, 118, 118, 118, 118, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 280, + 280, 289, 289, 279, 279, 304, 304, 304, 282, 282, + 282, 283, 283, 400, 400, 400, 276, 276, 66, 66, + 66, 305, 305, 305, 305, 69, 69, 409, 409, 410, + 410, 411, 411, 411, 70, 71, 71, 307, 307, 308, + 308, 72, 73, 85, 85, 85, 85, 85, 86, 86, + 86, 86, 109, 109, 109, 10, 10, 10, 10, 81, + 81, 81, 9, 9, 11, 68, 68, 75, 397, 397, + 398, 399, 399, 399, 399, 76, 78, 27, 27, 27, + 27, 27, 27, 135, 135, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 129, 129, 129, + 123, 123, 418, 79, 80, 80, 127, 127, 127, 120, + 120, 120, 126, 126, 126, 12, 12, 13, 262, 262, + 14, 14, 131, 131, 134, 134, 133, 133, 136, 136, + 136, 136, 136, 136, 136, 130, 130, 132, 132, 132, + 132, 297, 297, 297, 296, 296, 167, 167, 169, 168, + 168, 170, 170, 171, 171, 171, 171, 216, 216, 193, + 193, 255, 255, 256, 256, 254, 254, 261, 261, 257, + 257, 257, 257, 264, 264, 172, 172, 172, 172, 180, + 180, 181, 181, 182, 182, 306, 306, 302, 302, 302, + 301, 301, 186, 186, 186, 188, 187, 187, 187, 187, + 189, 189, 191, 191, 190, 190, 192, 197, 197, 196, + 196, 194, 194, 194, 194, 194, 194, 195, 195, 195, + 195, 198, 198, 145, 145, 145, 145, 145, 145, 145, + 145, 159, 159, 159, 159, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 245, 245, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 221, 221, + 220, 220, 87, 87, 87, 88, 88, 89, 89, 89, + 89, 89, 90, 90, 90, 90, 90, 90, 90, 92, + 92, 91, 91, 211, 211, 294, 294, 93, 94, 94, + 97, 97, 96, 95, 95, 101, 101, 98, 98, 100, + 100, 99, 102, 102, 103, 104, 104, 277, 277, 199, + 199, 207, 207, 207, 207, 200, 200, 200, 200, 200, + 200, 200, 208, 208, 208, 215, 209, 209, 205, 205, + 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, + 203, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, + 204, 204, 164, 164, 164, 164, 226, 226, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 152, 152, 165, 165, 165, 165, 166, + 166, 166, 166, 166, 166, 166, 314, 314, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, + 118, 118, 118, 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 417, 417, 326, 326, 326, 204, 204, 204, 204, - 204, 125, 125, 125, 125, 125, 309, 309, 309, 313, - 313, 313, 311, 311, 311, 311, 311, 311, 311, 311, - 311, 311, 311, 311, 311, 311, 311, 314, 314, 222, - 222, 121, 121, 220, 220, 221, 223, 223, 215, 215, - 215, 215, 217, 217, 200, 200, 200, 225, 225, 226, - 226, 105, 106, 106, 107, 107, 227, 227, 229, 228, - 228, 230, 231, 231, 231, 232, 232, 233, 233, 233, - 49, 49, 49, 49, 49, 44, 44, 44, 44, 45, - 45, 45, 45, 136, 136, 136, 136, 138, 138, 137, - 137, 82, 82, 83, 83, 83, 142, 142, 143, 143, - 143, 140, 140, 141, 141, 250, 250, 234, 234, 234, - 241, 241, 241, 237, 237, 239, 239, 239, 240, 240, - 240, 238, 247, 247, 249, 249, 248, 248, 244, 244, - 245, 245, 246, 246, 246, 242, 242, 199, 199, 199, - 199, 199, 251, 251, 251, 251, 263, 263, 210, 210, - 212, 212, 211, 211, 161, 264, 264, 272, 269, 269, - 270, 270, 296, 296, 296, 273, 273, 286, 286, 282, - 282, 283, 283, 276, 276, 288, 288, 288, 77, 208, - 208, 365, 365, 362, 291, 291, 293, 293, 297, 297, - 301, 301, 298, 298, 8, 410, 410, 410, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, - 289, 289, 289, 289, 289, 289, 289, 289, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, - 290, 290, 413, 414, 307, 308, 308, 308, + 119, 119, 119, 119, 119, 119, 419, 419, 328, 328, + 328, 206, 206, 206, 206, 206, 125, 125, 125, 125, + 125, 311, 311, 311, 315, 315, 315, 313, 313, 313, + 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, + 313, 313, 316, 316, 224, 224, 121, 121, 222, 222, + 223, 225, 225, 217, 217, 217, 217, 219, 219, 202, + 202, 202, 227, 227, 228, 228, 105, 106, 106, 107, + 107, 229, 229, 231, 230, 230, 232, 233, 233, 233, + 234, 234, 235, 235, 235, 49, 49, 49, 49, 49, + 44, 44, 44, 44, 45, 45, 45, 45, 137, 137, + 137, 137, 139, 139, 138, 138, 82, 82, 83, 83, + 83, 143, 143, 144, 144, 144, 141, 141, 142, 142, + 252, 252, 252, 252, 252, 252, 252, 236, 236, 236, + 243, 243, 243, 239, 239, 241, 241, 241, 242, 242, + 242, 240, 249, 249, 251, 251, 250, 250, 246, 246, + 247, 247, 248, 248, 248, 244, 244, 201, 201, 201, + 201, 201, 253, 253, 253, 253, 265, 265, 212, 212, + 214, 214, 213, 213, 163, 266, 266, 274, 271, 271, + 272, 272, 298, 298, 298, 275, 275, 288, 288, 284, + 284, 285, 285, 278, 278, 290, 290, 290, 77, 210, + 210, 367, 367, 364, 293, 293, 295, 295, 299, 299, + 303, 303, 300, 300, 8, 412, 412, 412, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 291, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 292, 292, 292, 292, 292, 292, + 292, 292, 292, 292, 415, 416, 309, 310, 310, 310, } var yyR2 = [...]int{ @@ -8560,130 +8692,131 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 1, 1, 2, 1, 2, 1, 3, 1, 1, - 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, - 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, - 2, 2, 2, 1, 1, 1, 1, 1, 5, 5, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, - 0, 3, 0, 5, 1, 3, 0, 3, 5, 0, - 1, 1, 0, 1, 0, 3, 3, 2, 2, 2, - 1, 2, 2, 0, 1, 0, 2, 2, 5, 0, - 1, 1, 2, 1, 3, 2, 1, 1, 3, 3, - 3, 0, 1, 4, 3, 3, 4, 2, 0, 2, - 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, - 1, 1, 3, 3, 4, 3, 1, 3, 1, 7, - 6, 7, 7, 8, 8, 0, 1, 5, 2, 1, - 1, 1, 0, 1, 3, 3, 1, 1, 2, 2, - 2, 0, 1, 1, 1, 2, 0, 1, 0, 1, - 1, 3, 2, 1, 2, 3, 3, 3, 4, 4, - 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 2, 1, 1, 2, 1, 2, 1, 3, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 1, 2, 2, 2, 2, 3, 3, 3, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 0, 3, 0, 5, 1, 3, 0, 3, + 5, 0, 1, 1, 0, 1, 0, 3, 3, 2, + 2, 2, 1, 2, 2, 0, 1, 0, 2, 2, + 5, 0, 1, 1, 2, 1, 3, 2, 1, 1, + 3, 3, 3, 0, 1, 4, 3, 3, 4, 2, + 0, 2, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 0, 1, 1, 3, 3, 4, 3, 1, 3, + 1, 7, 6, 7, 7, 8, 8, 0, 1, 5, + 2, 1, 1, 1, 0, 1, 3, 3, 1, 1, + 2, 2, 2, 0, 1, 1, 1, 2, 0, 1, + 0, 1, 1, 3, 2, 1, 2, 3, 3, 3, + 4, 4, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 4, 5, 0, 2, 2, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, - 1, 1, 0, 1, 0, 1, 0, 2, 0, 2, - 0, 2, 2, 0, 1, 5, 1, 3, 7, 1, - 3, 3, 1, 2, 2, 2, 5, 5, 5, 6, - 8, 5, 5, 4, 4, 4, 6, 5, 5, 5, - 2, 2, 2, 2, 3, 3, 3, 4, 3, 3, - 1, 3, 5, 1, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 3, 4, 4, 2, 11, - 3, 6, 8, 6, 6, 6, 13, 8, 6, 6, - 10, 7, 5, 5, 5, 7, 5, 5, 5, 5, - 5, 7, 7, 5, 5, 0, 6, 5, 6, 4, - 5, 0, 8, 9, 0, 3, 0, 1, 0, 3, - 8, 4, 1, 3, 3, 6, 7, 7, 8, 4, - 0, 1, 0, 1, 3, 3, 1, 1, 2, 1, - 1, 0, 2, 0, 2, 5, 3, 7, 4, 4, - 4, 4, 3, 3, 3, 7, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 2, 0, 2, 2, - 1, 3, 2, 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 3, 1, 3, 3, 0, 2, 2, - 2, 2, 2, 2, 2, 4, 4, 3, 0, 1, - 4, 3, 4, 4, 3, 3, 3, 2, 1, 3, - 3, 3, 5, 7, 7, 6, 5, 3, 2, 4, - 5, 5, 3, 3, 7, 3, 3, 3, 3, 4, - 7, 5, 2, 4, 4, 4, 4, 4, 5, 5, - 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, - 4, 4, 4, 4, 4, 2, 3, 3, 3, 3, - 5, 2, 3, 3, 2, 3, 4, 4, 4, 3, - 4, 4, 5, 3, 0, 1, 0, 1, 1, 1, - 0, 2, 2, 0, 2, 2, 0, 2, 0, 1, - 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, - 1, 3, 0, 1, 1, 3, 3, 2, 2, 1, - 1, 5, 0, 1, 0, 1, 2, 3, 0, 3, - 3, 3, 3, 3, 1, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, 1, 4, - 4, 4, 2, 2, 3, 1, 3, 2, 1, 2, - 1, 2, 2, 4, 3, 3, 6, 4, 7, 6, - 1, 3, 2, 2, 2, 2, 1, 1, 1, 3, - 2, 1, 1, 1, 0, 1, 1, 0, 3, 0, - 2, 0, 2, 1, 2, 2, 0, 1, 1, 0, - 1, 1, 5, 5, 4, 0, 2, 4, 4, 0, - 1, 0, 1, 2, 3, 4, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 2, 3, 5, 0, - 1, 2, 1, 1, 0, 1, 2, 1, 3, 1, - 1, 1, 4, 3, 1, 1, 2, 3, 7, 0, - 3, 0, 1, 1, 3, 1, 3, 1, 1, 3, - 3, 1, 3, 4, 4, 4, 3, 2, 4, 0, - 1, 0, 2, 0, 1, 0, 1, 2, 1, 1, - 1, 2, 2, 1, 2, 3, 2, 3, 2, 2, - 2, 1, 1, 3, 3, 0, 1, 1, 2, 6, - 5, 6, 6, 0, 2, 3, 3, 0, 2, 3, - 3, 3, 2, 3, 1, 3, 6, 3, 4, 3, - 1, 3, 4, 5, 6, 3, 4, 5, 6, 3, - 4, 1, 1, 1, 3, 3, 3, 3, 3, 3, - 5, 5, 3, 3, 3, 3, 3, 3, 1, 1, - 1, 1, 1, 3, 1, 1, 1, 2, 2, 2, - 2, 1, 1, 2, 7, 7, 6, 6, 2, 2, - 5, 6, 3, 3, 1, 3, 1, 3, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, - 2, 2, 4, 2, 4, 0, 1, 2, 5, 0, - 3, 0, 1, 4, 4, 2, 0, 1, 1, 2, - 2, 1, 1, 2, 2, 0, 1, 1, 1, 1, - 5, 1, 3, 0, 3, 1, 1, 1, 2, 1, + 3, 3, 3, 3, 3, 3, 4, 5, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 3, 4, 6, 4, 4, 8, 6, - 8, 6, 5, 4, 10, 2, 2, 1, 2, 2, - 2, 2, 2, 4, 5, 5, 5, 5, 5, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, - 4, 8, 8, 6, 5, 4, 4, 4, 4, 4, - 7, 4, 4, 6, 6, 6, 8, 6, 6, 4, - 4, 3, 4, 6, 6, 4, 4, 6, 4, 6, - 4, 4, 4, 4, 4, 4, 6, 4, 6, 4, - 4, 4, 6, 4, 6, 4, 4, 6, 4, 6, + 3, 1, 1, 1, 0, 1, 0, 1, 0, 2, + 0, 2, 0, 2, 2, 0, 1, 5, 1, 3, + 7, 1, 3, 3, 1, 2, 2, 2, 5, 5, + 5, 6, 8, 5, 5, 4, 4, 4, 6, 5, + 5, 5, 2, 2, 2, 2, 3, 3, 3, 4, + 3, 3, 1, 3, 5, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2, 2, 3, 4, 4, + 2, 11, 3, 6, 8, 6, 6, 6, 13, 8, + 6, 6, 10, 7, 5, 5, 5, 7, 5, 5, + 5, 5, 5, 7, 7, 5, 5, 5, 5, 0, + 6, 5, 6, 4, 5, 0, 8, 9, 0, 3, + 0, 1, 0, 3, 8, 4, 1, 3, 3, 6, + 7, 7, 8, 4, 0, 1, 0, 1, 3, 3, + 1, 1, 2, 1, 1, 0, 2, 0, 2, 5, + 3, 7, 4, 4, 4, 4, 3, 3, 3, 7, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 0, 2, 2, 1, 3, 2, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 1, 3, + 3, 0, 2, 2, 2, 2, 2, 2, 2, 4, + 4, 3, 0, 1, 4, 3, 4, 4, 3, 3, + 3, 2, 1, 3, 3, 3, 5, 7, 7, 6, + 5, 3, 2, 4, 5, 5, 3, 3, 7, 3, + 3, 3, 3, 4, 7, 5, 2, 4, 4, 4, + 4, 4, 5, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 2, 2, 4, 4, 4, 4, 4, 2, + 3, 3, 3, 3, 3, 5, 2, 3, 3, 2, + 3, 4, 4, 4, 3, 4, 4, 5, 3, 0, + 1, 0, 1, 1, 1, 0, 2, 2, 0, 2, + 2, 0, 2, 0, 1, 1, 1, 1, 2, 1, + 3, 1, 1, 1, 1, 1, 3, 0, 1, 1, + 3, 3, 2, 2, 1, 1, 5, 0, 1, 0, + 1, 2, 3, 0, 3, 3, 3, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 4, 4, 4, 2, 2, 3, 1, 3, + 2, 1, 2, 1, 2, 2, 4, 3, 3, 6, + 4, 7, 6, 1, 3, 2, 2, 2, 2, 1, + 1, 1, 3, 2, 1, 1, 1, 0, 1, 1, + 0, 3, 0, 2, 0, 2, 1, 2, 2, 0, + 1, 1, 0, 1, 1, 5, 5, 4, 0, 2, + 4, 4, 0, 1, 0, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 2, 3, + 5, 0, 1, 2, 1, 1, 0, 1, 2, 1, + 3, 1, 1, 1, 4, 3, 1, 1, 2, 3, + 7, 0, 3, 0, 1, 1, 3, 1, 3, 1, + 1, 3, 3, 1, 3, 4, 4, 4, 3, 2, + 4, 0, 1, 0, 2, 0, 1, 0, 1, 2, + 1, 1, 1, 2, 2, 1, 2, 3, 2, 3, + 2, 2, 2, 1, 1, 3, 3, 0, 1, 1, + 2, 6, 5, 6, 6, 5, 5, 0, 2, 3, + 3, 0, 2, 3, 3, 3, 2, 3, 1, 3, + 6, 3, 4, 3, 1, 3, 4, 5, 6, 3, + 4, 5, 6, 3, 4, 1, 1, 1, 3, 3, + 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 3, 1, 1, + 1, 2, 2, 2, 2, 1, 1, 2, 7, 7, + 6, 6, 2, 2, 5, 6, 3, 3, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 4, 2, 4, 0, + 1, 2, 5, 0, 3, 0, 1, 4, 4, 2, + 0, 1, 1, 2, 2, 1, 1, 2, 2, 0, + 1, 1, 1, 1, 5, 1, 3, 0, 3, 1, + 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 4, 6, + 4, 4, 8, 8, 6, 8, 6, 5, 4, 10, + 2, 2, 1, 2, 2, 2, 2, 2, 4, 5, + 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 8, 4, 8, 8, 6, 5, + 4, 4, 4, 4, 4, 7, 4, 4, 6, 6, + 6, 8, 6, 6, 4, 4, 3, 4, 6, 6, + 4, 4, 6, 4, 6, 4, 4, 4, 4, 4, + 4, 6, 4, 6, 4, 4, 4, 6, 4, 6, + 4, 4, 6, 4, 6, 4, 6, 8, 4, 6, + 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, 6, 8, - 4, 6, 8, 4, 6, 8, 4, 6, 8, 4, - 6, 8, 4, 6, 8, 4, 6, 8, 4, 4, - 4, 6, 4, 6, 4, 8, 6, 4, 4, 6, - 4, 6, 8, 4, 6, 8, 4, 4, 6, 8, - 6, 4, 6, 6, 8, 10, 7, 8, 8, 9, - 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, - 6, 4, 6, 5, 9, 6, 9, 8, 6, 8, - 8, 8, 6, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 2, 6, 8, 10, 12, 14, 6, 8, - 8, 10, 12, 14, 6, 8, 10, 12, 6, 8, - 4, 4, 3, 4, 6, 6, 4, 6, 4, 6, - 8, 0, 2, 1, 1, 1, 1, 1, 1, 1, + 4, 6, 8, 4, 4, 4, 6, 4, 6, 4, + 8, 6, 4, 4, 6, 4, 6, 8, 4, 6, + 8, 4, 4, 6, 8, 6, 4, 6, 6, 8, + 10, 7, 8, 8, 9, 4, 4, 4, 4, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, + 4, 4, 4, 4, 4, 6, 4, 6, 5, 9, + 6, 9, 8, 6, 8, 8, 8, 6, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 2, 6, 8, + 10, 12, 14, 6, 8, 8, 10, 12, 14, 6, + 8, 10, 12, 6, 8, 4, 4, 3, 4, 6, + 6, 4, 6, 4, 6, 8, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 2, 0, 2, 3, 4, 4, 4, 4, - 4, 0, 3, 4, 7, 3, 1, 1, 1, 0, - 5, 5, 2, 3, 1, 2, 2, 1, 2, 1, - 2, 2, 1, 2, 2, 1, 1, 0, 1, 0, - 1, 0, 2, 1, 2, 4, 0, 2, 1, 1, - 3, 5, 1, 1, 1, 2, 2, 0, 3, 0, - 2, 2, 1, 3, 0, 1, 0, 1, 3, 1, - 3, 2, 0, 1, 1, 0, 1, 2, 4, 4, - 0, 2, 2, 1, 1, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 0, 3, 1, - 1, 0, 4, 0, 1, 1, 0, 3, 1, 3, - 2, 1, 1, 0, 1, 2, 4, 9, 3, 5, + 1, 1, 1, 1, 1, 1, 0, 2, 0, 2, + 3, 4, 4, 4, 4, 4, 0, 3, 4, 7, + 3, 1, 1, 1, 0, 5, 5, 2, 3, 1, + 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, + 1, 1, 0, 1, 0, 1, 0, 2, 1, 2, + 4, 0, 2, 1, 1, 3, 5, 1, 1, 1, + 2, 2, 0, 3, 0, 2, 2, 1, 3, 0, + 1, 0, 1, 3, 1, 3, 2, 0, 1, 1, + 0, 1, 2, 4, 4, 0, 2, 2, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, + 3, 3, 0, 3, 1, 1, 0, 4, 0, 1, + 1, 0, 3, 1, 3, 2, 1, 1, 0, 1, + 2, 3, 4, 2, 3, 4, 4, 9, 3, 5, 0, 3, 3, 0, 1, 0, 2, 2, 0, 2, 2, 2, 0, 2, 1, 2, 3, 3, 0, 2, 1, 2, 3, 4, 3, 0, 1, 2, 1, 5, @@ -8754,448 +8887,450 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, } var yyChk = [...]int{ - -1000, -411, -79, -416, -7, -29, -15, -16, -17, -18, + -1000, -413, -79, -418, -7, -29, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -64, -67, -65, -66, -69, -70, -71, -72, -73, -9, -11, -68, -27, -28, -74, -75, -76, -77, -78, -12, -13, -14, - -8, -32, -31, -30, 10, 11, -108, -35, 33, -40, - -50, 227, -51, -41, 228, -52, 230, 229, 267, 231, - 379, 260, 75, 315, 316, 318, 319, 320, 321, -109, - 684, 265, 266, 233, 37, 46, 34, 35, 38, 237, - 273, 274, 236, 133, -33, -36, 9, -413, 12, 469, - 262, 261, 29, -34, 578, 87, -80, -412, 732, -250, - -234, 23, 34, 30, -233, -229, -127, -234, 21, 19, - 8, -79, -79, -79, 13, 14, -79, -350, -352, 87, - 160, 87, -79, -57, -56, -54, -53, -55, -58, 32, - -47, -48, -374, -46, -43, 232, 229, 277, 123, 124, - 267, 268, 269, 231, 251, 266, 270, 265, 286, -42, - 82, 34, 578, 581, -357, 228, 234, 235, 230, 470, - 126, 125, 76, -354, 374, 611, 702, -58, 704, 101, - 104, 703, 45, 241, 705, 706, 707, 618, 708, 250, - 709, 710, 711, 712, 718, 659, 719, 720, 721, 127, - 8, -79, -301, -297, 91, -290, 575, 253, 609, 423, - 610, 302, 82, 42, 514, 584, 371, 374, 611, 499, - 702, 380, 315, 331, 325, 504, 505, 506, 354, 346, - 576, 612, 585, 305, 254, 290, 696, 344, 136, 704, - 309, 613, 268, 381, 382, 614, 383, 101, 318, 420, - 717, 308, 615, 715, 104, 703, 323, 80, 498, 52, - 699, 45, 263, 428, 429, 342, 236, 338, 705, 291, - 616, 587, 284, 126, 123, 724, 37, 334, 51, 31, - 714, 125, 50, 706, 151, 617, 707, 618, 385, 361, - 690, 49, 386, 269, 619, 85, 274, 580, 312, 698, - 387, 519, 335, 388, 301, 713, 233, 620, 679, 671, - 672, 389, 390, 691, 366, 362, 367, 521, 621, 412, - 503, 391, 675, 676, 731, 53, 622, 623, 692, 124, - 624, 79, 708, 81, 329, 330, 625, 299, 252, 524, - 525, 414, 358, 481, 488, 489, 111, 112, 484, 113, - 490, 114, 491, 492, 493, 482, 115, 108, 483, 494, - 495, 359, 360, 116, 496, 110, 109, 485, 487, 117, - 497, 250, 36, 392, 577, 303, 59, 307, 278, 415, - 47, 364, 728, 46, 686, 526, 626, 689, 357, 353, - 478, 54, 627, 628, 629, 630, 500, 709, 356, 328, - 352, 723, 4, 296, 501, 710, 63, 235, 369, 368, - 370, 285, 411, 349, 631, 632, 633, 257, 83, 634, - 339, 22, 635, 636, 393, 292, 637, 57, 638, 639, - 418, 266, 640, 55, 711, 40, 641, 271, 725, 712, - 642, 643, 644, 685, 645, 273, 646, 395, 647, 673, - 674, 394, 363, 365, 527, 280, 396, 379, 238, 579, - 648, 313, 333, 270, 716, 649, 258, 515, 516, 517, - 518, 697, 523, 522, 272, 277, 265, 419, 259, 650, - 651, 652, 653, 654, 306, 670, 655, 656, 319, 718, - 479, 44, 657, 658, 659, 660, 661, 300, 295, 413, - 422, 62, 84, 376, 662, 663, 695, 327, 324, 293, - 460, 462, 463, 464, 465, 466, 461, 468, 664, 316, - 56, 719, 720, 721, 287, 722, 507, 508, 509, 510, - 10, 561, 544, 572, 545, 562, 546, 555, 547, 563, - 571, 573, 528, 536, 529, 537, 567, 550, 564, 556, - 549, 548, 570, 553, 557, 530, 538, 568, 554, 531, - 539, 532, 540, 533, 541, 566, 565, 558, 569, 534, - 542, 560, 535, 543, 559, 551, 552, 431, 729, 730, - 502, 398, 127, 297, 298, 48, 350, 279, 665, 310, - 666, 340, 341, 475, 476, 355, 326, 351, 682, 317, - 680, 281, 399, 480, 267, 667, 421, 294, 372, 377, - 311, 583, 520, 286, 400, 694, 582, 511, 512, 348, - 345, 288, 513, 668, 684, 401, 242, 282, 283, 669, - 681, 402, 403, 304, 404, 405, 406, 407, 408, 410, - 314, 409, 683, 677, 678, 289, 459, 581, 322, 343, - 378, 441, 442, 443, 444, 445, 446, 447, 448, 449, - 450, 451, 452, 453, 454, 455, 456, 457, 458, 477, - 240, -79, 240, -188, -297, -129, 686, 688, 179, -269, - 382, -287, 384, 397, 392, 402, 390, -278, 393, 395, - 280, -398, 412, 240, 399, 227, 385, 394, 403, 404, - 304, 410, 405, 314, 409, 289, 406, 407, 408, -381, - 179, 707, 722, 136, 347, 389, 387, 413, 686, 91, - -303, 91, 92, 93, -290, 317, -305, 322, -291, -381, - -290, 320, -79, -79, -307, -307, -129, -207, -144, 144, - -157, -258, -160, 92, -149, -152, -201, -202, -203, -204, - -158, -217, -256, 168, 169, 176, 145, -213, -161, 27, - 574, 471, 470, 179, 32, 222, 69, 70, 473, 147, - 58, 12, 436, 437, -159, 426, 427, 438, 432, 433, - 498, 500, 501, 502, 499, 504, 505, 506, 507, 508, - 509, 510, 511, 512, 513, 503, 514, 475, 476, 118, - 477, 108, 110, 109, 478, 479, 480, 344, 526, 527, - 521, 524, 525, 523, 522, 359, 360, 481, 544, 545, - 549, 548, 546, 547, 550, 553, 554, 555, 556, 557, - 558, 560, 559, 551, 552, 529, 528, 530, 531, 532, - 533, 534, 535, 537, 536, 538, 539, 540, 541, 542, - 543, 561, 562, 563, 564, 565, 567, 566, 571, 570, - 568, 569, 573, 572, 482, 483, 111, 112, 113, 114, - 115, 116, 117, 484, 487, 485, 486, 488, 489, 490, - 495, 496, 491, 492, 493, 494, 497, 370, 368, 369, - 365, 364, 363, -89, -101, 600, 599, -102, 423, 428, - 429, 431, -150, -151, -163, -164, -291, -297, 245, 425, - 239, 174, 469, -153, -147, -215, 107, 93, -31, -211, - 424, 434, 435, 439, 430, 440, 586, 588, 603, 604, - 606, 591, 596, 595, 598, 515, 516, 517, 518, 519, - 520, 671, 672, 673, 674, 675, 676, 677, 678, -381, - -290, 91, -155, -154, -197, 94, 99, 102, 103, 105, - -404, 263, 340, 341, 119, -413, 700, 90, 95, 96, - 97, 98, 120, 121, 180, 181, 182, 183, 184, 185, - 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 45, 398, 398, -188, - -79, -79, -79, -79, -410, 703, 579, -227, -127, -229, - -33, -31, -413, 9, -79, -31, -32, -30, -36, -38, - 605, -37, -297, 100, -234, -250, 13, 163, 43, 51, - -232, -233, -34, -31, -144, 20, 24, 25, -132, 170, - -144, -297, -132, -276, 244, -79, -79, -265, -310, 317, - -267, 413, 686, 412, -257, -270, 91, -256, -269, 411, - 92, -351, 160, -337, -341, -291, 255, -367, 251, -188, - -360, -359, -291, -413, -128, -286, 241, 249, 248, 137, - -385, 140, 297, 425, 239, -53, -54, -55, -269, 178, - 706, -110, 272, 276, 88, 88, -341, -340, -339, -386, - 276, 255, -366, -358, 247, 256, -347, 248, 249, -342, - 241, 138, -386, -342, 246, 256, 251, 255, 276, 276, - 127, 276, 127, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 271, -348, 152, -348, 582, 582, -354, -386, - 251, 241, -386, -386, 247, -288, -342, 243, 26, 243, - 36, 36, -348, -348, -348, -269, 178, -348, -348, -348, - -348, 284, 284, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - 240, -385, -136, 409, 304, 82, -56, 286, -39, -188, - -286, 241, 242, -385, 273, -188, 223, 240, 689, -280, - 160, 16, -280, -277, 398, 396, 383, 388, -280, -280, - -280, -280, 287, 381, -343, 241, 36, 252, 398, 287, - 381, 287, 288, 287, 288, 391, 401, 287, -302, 15, - 163, 425, 386, 390, 280, 240, 281, 242, 400, 288, - -302, 90, -281, 160, 287, 398, 283, -280, -280, -308, - -413, -293, -291, -289, 232, 24, 143, 26, 28, 146, - 179, 130, 20, 147, 38, 234, 347, 251, 178, 247, - 470, 227, 73, 586, 426, 433, 424, 432, 436, 472, - 473, 425, 384, 32, 14, 588, 29, 261, 25, 39, - 172, 229, 150, 589, 264, 27, 262, 118, 121, 591, - 23, 76, 256, 15, 249, 41, 17, 592, 593, 18, - 245, 244, 163, 241, 71, 12, 222, 30, 159, 67, - 594, 138, 133, 595, 596, 597, 598, 131, 69, 160, - 21, 726, 434, 435, 34, 687, 574, 275, 174, 74, - 60, 688, 144, 430, 599, 600, 119, 601, 122, 77, - 693, 140, 19, 72, 43, 602, 276, 603, 246, 727, - 604, 416, 605, 161, 230, 469, 70, 162, 700, 606, - 701, 239, 397, 9, 474, 33, 260, 248, 129, 68, - 440, 607, 240, 149, 243, 132, 120, 8, 137, 35, - 13, 75, 78, 437, 438, 439, 58, 128, 578, 148, - 16, 608, 417, 142, -381, 689, -308, -308, 33, 92, - -407, -408, -409, 578, 416, 243, -291, -188, -85, 679, - 231, -86, 685, 24, 238, -134, 398, -122, 179, 707, - 690, 691, 692, 689, 395, 697, 695, 693, 287, 694, - 88, 140, 142, 143, 4, -144, 159, -198, 152, 153, - 154, 155, 156, 157, 158, 164, 163, 144, 146, 160, - -243, 141, 165, 166, 167, 168, 169, 170, 171, 173, - 172, 174, 175, 161, 162, 178, 225, 226, -152, -152, - -152, -152, -213, -219, -218, -413, -215, -381, -290, -297, - -413, -413, -152, -275, -413, -149, -413, -413, -413, -413, - -222, -144, -413, -413, -417, -413, -417, -417, -417, -326, - -413, -326, -326, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, 223, -413, -413, -413, - -413, -413, -326, -326, -326, -326, -326, -326, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, 103, 99, 102, 94, -217, 105, 90, 90, - 90, 90, -31, -32, -207, -413, -307, -395, -396, -191, - -188, -413, 304, -291, -291, 273, 96, -232, -34, -31, - -227, -233, -229, -31, -79, -120, -133, 64, 65, -135, - 25, 39, 68, 66, 24, -414, 89, -414, -250, -414, - 88, -38, -253, 87, 62, 44, 90, 90, 88, 22, - -228, -230, -144, 15, -295, 4, -294, 26, -291, 90, - 223, 15, -189, 30, -188, -276, -276, 88, 91, 317, - -266, -268, 414, 416, 152, -296, -291, 90, 32, 89, - 88, -188, -315, -318, -320, -319, -321, -316, -317, 344, - 345, 179, 348, 350, 351, 352, 353, 354, 355, 356, - 357, 358, 361, 33, 263, 340, 341, 342, 343, 362, - 363, 364, 365, 367, 368, 369, 370, 325, 346, 576, - 326, 327, 328, 329, 330, 331, 333, 334, 337, 335, - 336, 338, 339, -382, -381, 87, 89, 88, -322, 87, - -144, -136, 240, -381, 241, 241, 241, -79, 469, -348, - -348, -348, 271, 20, -46, -43, -374, 19, -42, -43, - 232, 123, 124, 229, 87, -337, 87, -346, -382, -381, - 87, 138, 246, 137, -345, -342, -345, -346, -381, -215, - -381, 138, 138, -381, -381, -262, -291, -262, -262, 24, - -262, 24, -262, 24, 96, -291, -262, 24, -262, 24, - -262, 24, -262, 24, -262, 24, 32, 79, 80, 81, - 32, 83, 84, 85, -215, -381, -381, -215, -337, -215, - -188, -381, -269, 96, 96, 96, -348, -348, 96, 90, - 90, 90, -348, -348, 96, 90, -299, -297, 90, 90, - -387, 257, 301, 303, 96, 96, 96, 96, 32, 90, - -388, 32, 714, 713, 715, 716, 717, 90, 96, 32, - 96, 32, 96, -291, 87, -188, -142, 291, 227, 229, - 232, 77, 90, 307, 308, 305, 310, 311, 152, 45, - 88, 243, 240, -381, -282, 245, -282, -291, -298, -297, - -289, -188, 243, 380, 90, -144, -344, 15, 163, -302, - -302, -280, -188, -344, -302, -280, -188, -280, -280, -280, - -280, -302, -302, -302, -280, -297, -297, -188, -188, -188, - -188, -188, -188, -188, -308, -281, -280, 689, 90, -274, - 15, 77, -308, -308, 88, 323, 417, 418, -306, 320, - -81, -291, 90, -10, -29, -18, -17, -19, 152, -10, - 88, 578, -181, -188, 689, 689, 689, 689, 689, 689, - -144, -144, -144, -144, 601, -205, 119, 144, 120, 121, - -160, -144, -206, -211, -213, 106, 163, 146, 160, -243, - -149, -152, -149, -149, -149, -149, -149, -149, 222, -149, - 222, -149, -149, -149, -149, -149, -149, -309, -291, 90, - 179, -156, -155, 105, -404, -156, 575, 88, -218, 223, - -144, -144, -381, -118, 442, 443, 444, 445, 447, 448, - 449, 452, 453, 457, 458, 441, 459, 446, 451, 454, - 455, 456, 450, 343, -144, -130, -132, -130, -144, -220, - -221, 148, -215, -144, -414, -414, 96, 170, -126, 25, - 39, -126, -126, -126, -126, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -126, -144, -119, 441, 459, - 446, 451, 454, 455, 456, 450, 343, 460, 461, 462, - 463, 464, 465, 466, 467, 468, -119, -118, -144, -144, - -144, -144, -144, -144, -87, -144, 130, 131, 132, -207, - -144, -149, -144, -144, -144, -414, -144, -144, -144, -208, - -207, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -380, -379, -378, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -207, -207, -207, -207, -207, -144, -414, - -144, -162, -147, 96, -258, 105, 92, -144, -144, -144, - -144, -144, -144, -131, -130, -293, -298, -289, -290, -130, - -131, -131, -130, -130, -144, -144, -144, -144, -144, -144, - -144, -144, -414, -144, -144, -144, -144, -144, -250, -414, - -207, 88, -397, 416, 417, 687, -300, 276, -299, 26, - -208, 90, 15, -260, 78, -291, -232, -232, 64, 65, - 60, -130, -135, -414, -37, 26, -252, -291, 63, 90, - -327, -269, 371, 372, 179, -144, -144, 88, -231, 28, - 29, -188, -294, 170, -298, -188, -261, 276, -188, -166, - -168, -169, -170, -191, -214, -413, -171, -31, 597, 594, - 15, -181, -182, -190, -297, -267, -310, -266, 88, 415, - 417, 418, 77, 122, -144, -328, 178, -356, -355, -354, - -337, -339, -340, -341, 89, -328, -333, 377, 376, -322, - -322, -322, -322, -322, -327, -327, -327, -327, 87, 87, - -322, -322, -322, -322, -330, 87, -330, -330, -331, -330, - 87, -331, -332, 87, -332, -367, -144, -364, -363, -361, - -362, 250, 101, 669, 625, 578, 618, 659, 78, -359, - -231, 96, -414, -142, -283, 245, -365, -362, -381, -381, - -381, -283, 91, 90, 91, 90, 91, 90, -111, -60, - -1, 726, 727, 728, 88, 20, -338, -337, -59, 301, - -370, -371, 276, -366, -360, -346, 138, -345, -346, -346, - -381, 88, 30, 127, 127, 127, 127, 578, 229, 33, - -284, 617, 144, 669, 625, -337, -59, 243, 243, -309, - -309, -309, 90, 90, -279, 722, -181, -138, 293, 152, - 282, 282, 240, 295, 240, 295, -188, 306, 309, 307, - 308, 305, 310, 311, 24, 24, 24, 24, 24, 294, - 296, 298, 284, -188, -188, -282, 77, -183, -188, 27, - -297, 90, 90, -188, -280, -280, -188, -280, -280, -188, - -409, 324, -291, 358, 680, 681, 683, 682, -122, 416, - 88, 578, 23, -123, 23, -413, 119, 120, 121, -206, - -149, -152, -149, 143, 264, -149, -149, -413, -215, -414, - -293, 26, 88, 78, -414, 168, 88, 88, -414, -414, - 88, 15, -223, -221, 150, -144, -414, 88, -414, -414, - -207, -144, -144, -144, -144, -414, -414, -414, -414, -414, - -414, -414, -414, -414, -414, -207, -414, 88, 88, 15, - -313, 26, -414, -414, -414, -414, -414, -222, -414, 15, - -414, 78, 88, 163, 88, -414, -414, -414, 88, 88, - -414, -414, 88, -414, 88, -414, -414, -414, -414, -414, - -414, 88, -414, 88, -414, -414, -414, 88, -414, 88, - -414, -414, 88, -414, 88, -414, 88, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, -414, -414, - 88, -414, 88, -414, 88, -414, -414, 88, -414, 88, - -414, 88, -414, 88, 88, -414, 88, 88, 88, -414, - 88, 88, 88, 88, -414, -414, -414, -414, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, -414, -414, - -414, -414, -414, -414, 88, -94, 602, -414, -414, 88, - -414, 88, 88, 88, 88, 88, -414, -413, 223, -414, - -414, -414, -414, -414, 88, 88, 88, 88, 88, 88, - -414, -414, -414, 88, 88, -414, 88, -414, 88, -414, - -396, 686, 417, -195, -194, -192, 75, 244, 76, -413, - -299, -414, -156, -258, -259, -258, -200, -291, 96, 105, - -234, -165, -167, 15, -135, -213, 89, 88, -327, -238, - -244, -277, -291, 90, 179, -329, 179, -329, 371, 372, - -230, 223, -196, 16, -199, 33, 58, -29, -413, -413, - 33, 88, -184, -186, -185, -187, 67, 71, 73, 68, - 69, 70, 74, -304, 26, -31, -166, -31, -413, -188, - -181, -415, 15, 78, -415, 88, 223, -268, -271, 419, - 416, 422, -381, 90, -110, 88, -354, -341, -235, -139, - 41, -334, 378, -327, 585, -327, -336, 90, -336, 96, - 96, 96, 89, -49, -44, -45, 34, 82, -361, -348, - 90, 40, -348, -348, -291, 89, -231, -138, -188, 144, - 77, -365, -365, -365, -297, -2, 725, 731, 138, 87, - 383, 19, -252, 88, 89, -216, 302, 89, -112, -291, - 89, 87, -346, -346, -291, -413, 240, 32, 32, 669, - 625, 617, -59, -216, -215, -381, -328, 724, 723, 89, - 242, 300, -143, 436, -140, 90, 91, -188, -188, -188, - -188, -188, -188, 232, 229, 406, -405, 312, -405, 285, - 243, -181, -188, 88, -84, 259, 254, -302, -302, 34, - -188, 416, 698, 696, -144, 143, 264, -160, -152, -118, - -118, -149, -311, 179, 344, 263, 342, 338, 358, 349, - 376, 340, 377, 335, 334, 333, -311, -309, -149, -207, - -132, -144, -144, 151, -144, 149, -144, -414, -414, -414, - -414, -414, -227, -144, -144, -144, -414, 179, 344, 15, - -144, -309, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -378, -144, -207, - -144, -207, -144, -144, -144, -144, -144, -379, -379, -379, - -379, -379, -207, -207, -207, -207, -144, -413, -291, -97, - -96, -95, 652, 244, -94, -162, -97, -162, 222, -144, - 222, 222, 222, -144, -131, -293, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -192, -342, -342, -342, - -262, 88, -273, 23, 15, 58, 58, -165, -196, -166, - -135, -291, -241, 679, -247, 47, -245, -246, 48, -242, - 49, 57, -329, -329, 170, -232, -144, -263, 77, -264, - -272, -215, -210, -212, -211, -413, -251, -414, -291, -262, - -264, -168, -169, -169, -168, -169, 67, 67, 67, 72, - 67, 72, 67, -185, -297, -414, -144, -300, 78, -166, - -166, -190, -297, 170, 416, 420, 421, -354, -403, 119, - 144, 32, 77, 374, 101, -401, 178, 614, 664, 669, - 625, 618, 659, -402, 246, 137, 138, 258, 26, 42, - 89, 88, 89, 88, 89, 89, 88, -285, -284, -45, - -44, -348, -348, 96, -381, 90, 90, 242, 27, -188, - 77, 77, 77, -113, 729, 96, 87, -3, 82, -144, - 87, 20, -337, -215, -372, -323, -373, -324, -325, -5, - -6, -349, -116, 58, 101, -63, 45, 241, 709, 710, - 127, -413, 722, -364, -252, -368, -370, -188, -148, -413, - -159, -146, -145, -147, -153, 168, 169, 263, 340, 341, - -216, -188, -137, 291, 299, 87, -141, 92, -384, 78, - 282, 374, 282, 374, 90, -406, 313, 90, -406, -188, - -84, -49, -188, -280, -280, 34, -381, -414, -160, -152, - -125, 163, 578, -314, 584, -322, -322, -322, -332, -322, - 330, -322, 330, -322, -414, -414, -414, 88, -414, 23, - -414, -144, 88, -121, 474, 88, 88, -414, 87, 87, - -144, -414, -414, -414, 88, -414, -414, -414, -414, -414, - -414, -414, -414, -414, -414, -414, -414, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, 88, -414, - 88, -414, 88, -414, 88, -414, 88, -414, 88, -414, - -414, 88, -414, -414, -414, 88, -414, 88, -414, 88, - -414, -414, -414, 88, -312, 670, -414, -414, -414, -414, - -414, -414, -414, -414, -414, -414, -414, -93, -292, -291, - -94, 634, 634, -414, -94, -224, 88, -149, -414, -149, - -149, -149, -414, -414, -414, 88, -414, 88, 88, -414, - 88, -414, 88, -414, -414, -414, -414, 88, -193, 23, - -193, -193, -414, -258, -188, -196, -225, 17, -238, 52, - 350, -249, -248, 56, 48, -246, 20, 50, 20, 31, - -263, 88, 152, 88, -414, -414, 88, 58, 223, -414, - -196, -179, -178, 77, 78, -180, 77, -178, 67, 67, - -253, 88, -261, -166, -196, -196, 223, 119, -413, -148, - 13, 90, 90, -381, -400, 713, 714, 32, 96, -348, - -348, 138, 138, -188, 87, -327, 90, -327, 96, 96, - 32, 83, 84, 85, 32, 79, 80, 81, -188, -188, - -188, -188, -369, 87, 20, -144, 87, 152, 89, -252, - -252, 278, 163, -348, 707, 284, 284, -348, -348, -348, - -115, -114, 729, 89, -414, 88, -335, 578, 581, -144, - -154, -154, -253, 89, -377, 578, -383, -291, -291, -291, - -291, 96, 98, -414, 576, 74, 579, -414, -327, -144, - -144, -144, -232, 90, -144, -144, 96, 96, -414, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, -207, - -144, -414, -176, -175, -177, 690, 119, 32, -311, -414, - -209, 276, -100, -99, -98, 15, -414, -144, -118, -118, - -118, -118, -144, -144, -144, -144, -144, -144, -413, 67, - 19, 17, -413, -413, -300, -225, -226, 18, 20, -239, - 54, -237, 53, -237, -248, 20, 20, 90, 20, 90, - 138, -272, -144, -212, 58, -29, -291, -210, -291, -227, - -144, 87, -144, -156, -196, -196, -144, -202, 498, 500, - 501, 502, 499, 504, 505, 506, 507, 508, 509, 510, - 511, 512, 513, 503, 514, 475, 476, 477, 108, 110, - 109, 478, 479, 480, 344, 526, 527, 521, 524, 525, - 523, 522, 359, 360, 481, 544, 545, 549, 548, 546, - 547, 550, 553, 554, 555, 556, 557, 558, 560, 559, - 551, 552, 529, 528, 530, 531, 532, 533, 534, 535, - 537, 536, 538, 539, 540, 541, 542, 543, 561, 562, - 563, 564, 565, 567, 566, 571, 570, 568, 569, 573, - 572, 482, 483, 111, 112, 113, 114, 115, 116, 117, - 484, 487, 485, 488, 489, 490, 495, 496, 491, 492, - 493, 494, 497, 370, 368, 369, 365, 364, 363, 423, - 428, 429, 431, 515, 516, 517, 518, 519, 520, 671, - 672, 673, 674, 675, 676, 677, 678, 90, 90, 87, - -144, 89, 89, -253, -368, -60, 89, -254, -252, 96, - 89, 279, -211, -413, 90, -348, -348, -348, 96, 96, - -299, -414, 88, -291, -402, -370, 582, 582, -414, 26, - -376, -375, -293, 87, 78, 63, 577, 580, -414, -414, - 88, -414, -414, -414, 89, 89, -414, -414, -414, -414, - -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, - -414, -414, -414, -414, -414, -414, -414, -414, 88, -414, - -175, -177, -414, 77, -156, -227, 20, -97, 301, 303, - -97, -414, -414, -414, -414, -414, 88, -414, -414, 88, - -414, 88, -414, -414, -255, -414, -291, 246, 20, 20, - -255, -255, -195, -226, -107, -106, -105, 608, -144, -207, - -240, 55, 77, 122, 90, 90, 90, 13, -210, 223, - -232, -252, -173, 383, -227, -414, -252, 89, 26, 89, - 731, 138, 89, -211, -124, -413, 275, -299, 90, 90, - -114, -117, -29, 88, 152, -252, -188, 63, -144, -207, - -414, 77, 589, 690, -92, -91, -88, 701, 727, -207, - -94, -94, -144, -144, -144, 88, -414, -414, -414, -107, - 88, -104, -103, -291, 77, 122, -264, -291, 89, -414, - -413, -232, 89, -236, -29, 87, -3, 275, -323, -373, - -324, -325, -5, -6, -349, -82, 578, -375, -353, -297, - -293, 90, 96, 89, 578, -414, -414, -90, 146, 699, - 667, -154, 222, -414, 88, -414, 88, -414, 88, -291, - 246, -105, 88, 26, -300, -174, -172, -291, 631, -393, - -392, 574, -403, -399, 119, 144, 101, -401, 669, 625, - 128, 129, -82, -144, 87, -414, -83, 290, 686, 223, - -384, 579, -90, 700, 645, 620, 645, 620, -149, -144, - -144, -144, -103, -413, -414, 88, 23, -315, -62, 642, - -390, -391, 77, -394, 389, 641, 662, 119, 90, 89, - -252, 251, -298, -377, 580, 143, -118, -414, 88, -414, - 88, -414, -93, -172, 638, -328, -156, -391, 77, -390, - 77, 14, 13, -4, 730, 89, 292, -90, 645, 620, - -144, -144, -414, -61, 27, -173, -389, 259, 254, 257, - 33, -389, 96, -4, -414, -414, 642, 253, 32, 119, - -156, -176, -175, -175, + -8, -32, -31, -30, 11, 12, -108, -35, 34, -40, + -50, 228, -51, -41, 229, -52, 231, 230, 268, 232, + 381, 261, 76, 317, 318, 320, 321, 322, 323, -109, + 687, 266, 267, 234, 38, 47, 35, 36, 39, 238, + 274, 275, 237, 134, -33, -36, 10, -415, 13, 471, + 263, 262, 30, -34, 581, 88, -80, -414, 735, -252, + -236, 24, 35, 31, -235, -231, -127, -236, 22, 20, + 9, -79, -79, -79, 14, 15, -79, -352, -354, 88, + 161, 88, -79, -57, -56, -54, -53, -55, -58, 33, + -47, -48, -376, -46, -43, 233, 230, 278, 124, 125, + 268, 269, 270, 232, 252, 267, 271, 266, 287, -42, + 83, 35, 581, 584, -359, 229, 235, 236, 231, 472, + 127, 126, 77, -356, 376, 614, 705, -58, 707, 102, + 105, 706, 46, 242, 708, 709, 710, 621, 711, 251, + 712, 713, 714, 715, 721, 662, 722, 723, 724, 128, + 9, -79, -303, -299, 92, -292, 578, 254, 612, 425, + 613, 303, 83, 43, 517, 587, 373, 376, 614, 502, + 705, 382, 317, 333, 327, 507, 508, 509, 356, 348, + 579, 615, 588, 306, 255, 291, 699, 346, 137, 707, + 310, 616, 269, 383, 384, 617, 385, 102, 320, 422, + 720, 309, 618, 718, 105, 706, 325, 81, 501, 53, + 702, 46, 264, 430, 431, 344, 237, 340, 708, 292, + 619, 590, 285, 127, 124, 727, 38, 336, 52, 32, + 717, 126, 51, 709, 152, 620, 710, 621, 387, 363, + 693, 50, 388, 270, 622, 86, 275, 583, 314, 701, + 389, 522, 337, 390, 302, 716, 234, 623, 313, 682, + 674, 675, 391, 392, 694, 368, 364, 369, 524, 624, + 414, 506, 393, 678, 679, 734, 54, 625, 626, 695, + 125, 627, 80, 711, 82, 331, 332, 628, 300, 253, + 527, 528, 416, 360, 484, 491, 492, 112, 113, 487, + 114, 493, 115, 494, 495, 496, 485, 116, 109, 486, + 497, 498, 361, 362, 117, 499, 111, 110, 488, 490, + 118, 500, 251, 37, 394, 580, 304, 60, 308, 279, + 417, 48, 366, 731, 47, 689, 529, 629, 692, 359, + 355, 481, 55, 630, 631, 632, 633, 503, 712, 358, + 330, 354, 726, 4, 297, 476, 504, 713, 64, 236, + 371, 370, 372, 286, 413, 351, 634, 635, 636, 258, + 84, 637, 341, 23, 638, 639, 395, 293, 640, 58, + 641, 642, 420, 267, 643, 56, 714, 41, 644, 272, + 728, 715, 645, 646, 647, 688, 648, 274, 649, 397, + 650, 676, 677, 396, 365, 367, 530, 281, 398, 381, + 239, 582, 651, 315, 335, 271, 719, 652, 259, 518, + 519, 520, 521, 700, 526, 525, 273, 278, 266, 421, + 260, 653, 654, 655, 656, 657, 307, 673, 658, 659, + 321, 721, 482, 45, 660, 661, 662, 663, 664, 301, + 296, 415, 424, 63, 85, 378, 665, 666, 698, 329, + 326, 294, 462, 464, 465, 466, 467, 468, 463, 470, + 667, 318, 57, 722, 723, 724, 288, 725, 510, 511, + 512, 513, 11, 564, 547, 575, 548, 565, 549, 558, + 550, 566, 574, 576, 531, 539, 532, 540, 570, 553, + 567, 559, 552, 551, 573, 556, 560, 533, 541, 571, + 557, 534, 542, 535, 543, 536, 544, 569, 568, 561, + 572, 537, 545, 563, 538, 546, 562, 554, 555, 433, + 732, 733, 505, 400, 128, 298, 299, 49, 352, 280, + 668, 311, 669, 342, 343, 478, 479, 357, 328, 353, + 685, 319, 683, 282, 401, 483, 268, 670, 423, 295, + 374, 379, 312, 586, 523, 287, 402, 697, 585, 514, + 515, 350, 347, 289, 516, 671, 687, 403, 243, 283, + 284, 672, 684, 404, 405, 305, 406, 407, 408, 409, + 410, 412, 316, 411, 686, 680, 681, 290, 461, 584, + 324, 345, 380, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 480, 241, -79, 241, -190, -299, -129, 689, 691, + 180, -271, 384, -289, 386, 399, 394, 404, 392, -280, + 395, 397, 281, -400, 414, 241, 401, 228, 387, 396, + 405, 406, 305, 412, 407, 316, 411, 290, 408, 409, + 410, -383, 180, 710, 725, 137, 349, 391, 389, 415, + 689, 92, -305, 92, 93, 94, -292, 319, -307, 324, + -293, -383, -292, 322, -79, -79, -309, -309, -129, -209, + -145, 145, -159, -260, -162, 93, -150, -153, -203, -204, + -205, -206, -160, -219, -258, 169, 170, 177, 146, -215, + -163, 28, 577, 473, 472, 180, 33, 223, 70, 71, + 475, 476, 148, 59, 13, 438, 439, -161, 428, 429, + 440, 434, 435, 501, 503, 504, 505, 502, 507, 508, + 509, 510, 511, 512, 513, 514, 515, 516, 506, 517, + 478, 479, 119, 480, 109, 111, 110, 481, 482, 483, + 346, 529, 530, 524, 527, 528, 526, 525, 361, 362, + 484, 547, 548, 552, 551, 549, 550, 553, 556, 557, + 558, 559, 560, 561, 563, 562, 554, 555, 532, 531, + 533, 534, 535, 536, 537, 538, 540, 539, 541, 542, + 543, 544, 545, 546, 564, 565, 566, 567, 568, 570, + 569, 574, 573, 571, 572, 576, 575, 485, 486, 112, + 113, 114, 115, 116, 117, 118, 487, 490, 488, 489, + 491, 492, 493, 498, 499, 494, 495, 496, 497, 500, + 372, 370, 371, 367, 366, 365, -89, -101, 603, 602, + -102, 425, 430, 431, 433, -151, -152, -165, -166, -293, + -299, 246, 427, 240, 175, 471, -154, -148, -217, 108, + 94, -31, -213, 426, 436, 437, 441, 432, 442, 589, + 591, 606, 607, 609, 594, 599, 598, 601, 518, 519, + 520, 521, 522, 523, 674, 675, 676, 677, 678, 679, + 680, 681, -383, -292, 92, -157, -155, -199, 95, 100, + 103, 104, 106, -406, 264, 342, 343, 120, -415, 703, + -156, 97, 98, 99, 121, 122, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 91, 96, + 46, 400, 400, -190, -79, -79, -79, -79, -412, 706, + 582, -229, -127, -231, -33, -31, -415, 10, -79, -31, + -32, -30, -36, -38, 608, -37, -299, 101, -236, -252, + 14, 63, 164, 44, 52, -234, -235, -34, -31, -145, + 21, 25, 26, -132, 171, -145, -299, -132, -278, 245, + -79, -79, -267, -312, 319, -269, 415, 689, 414, -259, + -272, 92, -258, -271, 413, 93, -353, 161, -339, -343, + -293, 256, -369, 252, -190, -362, -361, -293, -415, -128, + -288, 242, 250, 249, 138, -387, 141, 298, 427, 240, + -53, -54, -55, -271, 179, 709, -110, 273, 277, 89, + 89, -343, -342, -341, -388, 277, 256, -368, -360, 248, + 257, -349, 249, 250, -344, 242, 139, -388, -344, 247, + 257, 252, 256, 277, 277, 128, 277, 128, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 272, -350, 153, + -350, 585, 585, -356, -388, 252, 242, -388, -388, 248, + -290, -344, 244, 27, 244, 37, 37, -350, -350, -350, + -271, 179, -350, -350, -350, -350, 285, 285, -350, -350, + -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, + -350, -350, -350, -350, -350, 241, -387, -137, 411, 305, + 83, -56, 287, -39, -190, -288, 242, 243, -387, 274, + -190, 224, 241, 692, -282, 161, 17, -282, -279, 400, + 398, 385, 390, -282, -282, -282, -282, 288, 383, -345, + 242, 37, 253, 400, 288, 383, 288, 289, 288, 289, + 393, 403, 288, -304, 16, 164, 427, 388, 392, 281, + 241, 282, 243, 402, 289, -304, 91, -283, 161, 288, + 400, 394, 284, -282, -282, -310, -415, -295, -293, -291, + 233, 25, 144, 27, 29, 147, 180, 131, 21, 148, + 39, 235, 349, 252, 179, 248, 472, 228, 74, 589, + 428, 435, 426, 434, 438, 474, 475, 427, 386, 33, + 15, 591, 30, 262, 26, 40, 173, 230, 151, 592, + 265, 28, 263, 119, 122, 594, 24, 77, 257, 16, + 250, 42, 18, 595, 596, 19, 246, 245, 164, 242, + 72, 13, 223, 31, 160, 68, 597, 139, 134, 598, + 599, 600, 601, 132, 70, 161, 22, 729, 436, 437, + 35, 690, 577, 276, 175, 75, 61, 691, 145, 432, + 602, 603, 120, 604, 123, 78, 696, 141, 20, 73, + 44, 605, 277, 606, 247, 730, 607, 418, 608, 162, + 231, 471, 71, 163, 703, 609, 704, 240, 399, 10, + 477, 34, 261, 249, 130, 69, 442, 610, 241, 150, + 244, 133, 121, 9, 138, 36, 14, 76, 79, 439, + 440, 441, 59, 129, 581, 149, 17, 611, 419, 143, + -383, 692, -310, -310, 34, 93, -409, -410, -411, 581, + 418, 244, -293, -190, -85, 682, 232, -86, 688, 25, + 239, -135, 400, -122, 180, 710, 693, 694, 695, 692, + 397, 700, 698, 696, 288, 697, 89, 141, 143, 144, + 4, -145, 160, -200, 153, 154, 155, 156, 157, 158, + 159, 165, 164, 145, 147, 161, -245, 142, 166, 167, + 168, 169, 170, 171, 172, 174, 173, 175, 176, 162, + 163, 179, 226, 227, -153, -153, -153, -153, -215, -221, + -220, -415, -217, -383, -292, -299, -415, -415, -153, -277, + -415, -150, -415, -415, -415, -415, -415, -224, -145, -415, + -415, -419, -415, -419, -419, -419, -328, -415, -328, -328, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, 224, -415, -415, -415, -415, -415, -328, + -328, -328, -328, -328, -328, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, 91, + 104, 100, 103, 95, -219, 106, 91, 91, 91, 91, + -31, -32, -209, -415, -309, -397, -398, -193, -190, -415, + 305, -293, -293, 274, 97, -234, -34, -31, -229, -235, + -231, -31, -79, -120, -134, 65, 66, -133, -136, 26, + 40, 69, 67, 25, -416, 90, -416, -252, -416, 89, + -38, -255, 88, 636, 666, 636, 666, 63, 45, 91, + 91, 89, 23, -230, -232, -145, 16, -297, 4, -296, + 27, -293, 91, 224, 16, -191, 31, -190, -278, -278, + 89, 92, 319, -268, -270, 416, 418, 153, -298, -293, + 91, 33, 90, 89, -190, -317, -320, -322, -321, -323, + -318, -319, 346, 347, 180, 350, 352, 353, 354, 355, + 356, 357, 358, 359, 360, 363, 34, 264, 342, 343, + 344, 345, 364, 365, 366, 367, 369, 370, 371, 372, + 327, 348, 579, 328, 329, 330, 331, 332, 333, 335, + 336, 339, 337, 338, 340, 341, -384, -383, 88, 90, + 89, -327, 88, -145, -137, 241, -383, 242, 242, 242, + -79, 471, -350, -350, -350, 272, 21, -46, -43, -376, + 20, -42, -43, 233, 124, 125, 230, 88, -339, 88, + -348, -384, -383, 88, 139, 247, 138, -347, -344, -347, + -348, -383, -217, -383, 139, 139, -383, -383, -264, -293, + -264, -264, 25, -264, 25, -264, 25, 97, -293, -264, + 25, -264, 25, -264, 25, -264, 25, -264, 25, 33, + 80, 81, 82, 33, 84, 85, 86, -217, -383, -383, + -217, -339, -217, -190, -383, -271, 97, 97, 97, -350, + -350, 97, 91, 91, 91, -350, -350, 97, 91, -301, + -299, 91, 91, -389, 258, 302, 304, 97, 97, 97, + 97, 33, 91, -390, 33, 717, 716, 718, 719, 720, + 91, 97, 33, 97, 33, 97, -293, 88, -190, -143, + 292, 228, 230, 233, 78, 91, 308, 309, 306, 311, + 312, 313, 153, 46, 89, 244, 241, -383, -284, 246, + -284, -293, -300, -299, -291, -190, 244, 382, 91, -145, + -346, 16, 164, -304, -304, -282, -190, -346, -304, -282, + -190, -282, -282, -282, -282, -304, -304, -304, -282, -299, + -299, -190, -190, -190, -190, -190, -190, -190, -310, -283, + -282, 692, 91, -276, 16, 78, -310, -310, 89, 325, + 419, 420, -308, 322, -81, -293, 91, -10, -29, -18, + -17, -19, 153, -10, 89, 581, -183, -190, 692, 692, + 692, 692, 692, 692, -145, -145, -145, -145, 604, -207, + 120, 145, 121, 122, -162, -145, -208, -213, -215, 107, + 164, 147, 161, -245, -150, -153, -150, -150, -150, -150, + -150, -150, 223, -150, 223, -150, -150, -150, -150, -150, + -150, -311, -293, 91, 180, -158, -157, 106, -406, -158, + 578, 89, -220, 224, -145, -145, -383, -118, 444, 445, + 446, 447, 449, 450, 451, 454, 455, 459, 460, 443, + 461, 448, 453, 456, 457, 458, 452, 345, -145, -130, + -132, -130, -145, -145, -222, -223, 149, -217, -145, -416, + -416, 97, 171, -126, 26, 40, -126, -126, -126, -126, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -126, -145, -119, 443, 461, 448, 453, 456, 457, 458, + 452, 345, 462, 463, 464, 465, 466, 467, 468, 469, + 470, -119, -118, -145, -145, -145, -145, -145, -145, -87, + -145, 131, 132, 133, -209, -145, -150, -145, -145, -145, + -416, -145, -145, -145, -210, -209, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -382, -381, -380, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -209, -209, + -209, -209, -209, -145, -416, -145, -164, -148, 97, -260, + 106, 93, -145, -145, -145, -145, -145, -145, -131, -130, + -295, -300, -291, -292, -130, -131, -131, -130, -130, -145, + -145, -145, -145, -145, -145, -145, -145, -416, -145, -145, + -145, -145, -145, -252, -416, -209, 89, -399, 418, 419, + 690, -302, 277, -301, 27, -210, 91, 16, -262, 79, + -293, -234, -234, 65, 66, 61, -130, -136, -416, -37, + 27, -254, -293, 629, 629, 64, 91, -329, -271, 373, + 374, 180, -145, -145, 89, -233, 29, 30, -190, -296, + 171, -300, -190, -263, 277, -190, -168, -170, -171, -172, + -193, -216, -415, -173, -31, 600, 597, 16, -183, -184, + -192, -299, -269, -312, -268, 89, 417, 419, 420, 78, + 123, -145, -330, 179, -358, -357, -356, -339, -341, -342, + -343, 90, -330, -335, 379, 378, -327, -327, -327, -327, + -327, -329, -329, -329, -329, 88, 88, -327, -327, -327, + -327, -332, 88, -332, -332, -333, -332, 88, -333, -334, + 88, -334, -369, -145, -366, -365, -363, -364, 251, 102, + 672, 628, 581, 621, 662, 79, -361, -233, 97, -416, + -143, -285, 246, -367, -364, -383, -383, -383, -285, 92, + 91, 92, 91, 92, 91, -111, -60, -1, 729, 730, + 731, 89, 21, -340, -339, -59, 302, -372, -373, 277, + -368, -362, -348, 139, -347, -348, -348, -383, 89, 31, + 128, 128, 128, 128, 581, 230, 34, -286, 620, 145, + 672, 628, -339, -59, 244, 244, -311, -311, -311, 91, + 91, -281, 725, -183, -139, 294, 153, 283, 283, 241, + 296, 241, 296, -190, 307, 310, 308, 309, 306, 311, + 312, 313, 25, 25, 25, 25, 25, 25, 295, 297, + 299, 285, -190, -190, -284, 78, -185, -190, 28, -299, + 91, 91, -190, -282, -282, -190, -282, -282, -190, -411, + 326, -293, 360, 683, 685, -122, 418, 89, 581, 24, + -123, 24, -415, 120, 121, 122, -208, -150, -153, -150, + 144, 265, -150, -150, -415, -217, -416, -295, 27, 89, + 79, -416, 169, 89, 89, -416, -416, 89, 16, 89, + -225, -223, 151, -145, -416, 89, -416, -416, -209, -145, + -145, -145, -145, -416, -416, -416, -416, -416, -416, -416, + -416, -416, -416, -209, -416, 89, 89, 16, -315, 27, + -416, -416, -416, -416, -416, -224, -416, 16, -416, 79, + 89, 164, 89, -416, -416, -416, 89, 89, -416, -416, + 89, -416, 89, -416, -416, -416, -416, -416, -416, 89, + -416, 89, -416, -416, -416, 89, -416, 89, -416, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + 89, -416, 89, -416, 89, -416, -416, -416, 89, -416, + 89, -416, 89, -416, -416, 89, -416, 89, -416, 89, + -416, 89, 89, -416, 89, 89, 89, -416, 89, 89, + 89, 89, -416, -416, -416, -416, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, -416, -416, -416, -416, + -416, -416, 89, -94, 605, -416, -416, 89, -416, 89, + 89, 89, 89, 89, -416, -415, 224, -416, -416, -416, + -416, -416, 89, 89, 89, 89, 89, 89, -416, -416, + -416, 89, 89, -416, 89, -416, 89, -416, -398, 689, + 419, -197, -196, -194, 76, 245, 77, -415, -301, -416, + -158, -260, -261, -260, -202, -293, 97, 106, -236, -167, + -169, 16, -215, 90, 89, -329, -240, -246, -279, -293, + 91, 180, -331, 180, -331, 373, 374, -232, 224, -198, + 17, -201, 34, 59, -29, -415, -415, 34, 89, -186, + -188, -187, -189, 68, 72, 74, 69, 70, 71, 75, + -306, 27, -31, -168, -31, -415, -190, -183, -417, 16, + 79, -417, 89, 224, -270, -273, 421, 418, 424, -383, + 91, -110, 89, -356, -343, -237, -140, 42, -336, 380, + -329, 588, -329, -338, 91, -338, 97, 97, 97, 90, + -49, -44, -45, 35, 83, -363, -350, 91, 41, -350, + -350, -293, 90, -233, -139, -190, 145, 78, -367, -367, + -367, -299, -2, 728, 734, 139, 88, 385, 20, -254, + 89, 90, -218, 303, 90, -112, -293, 90, 88, -348, + -348, -293, -415, 241, 33, 33, 672, 628, 620, -59, + -218, -217, -383, -330, 727, 726, 90, 243, 301, -144, + 438, -141, 91, 92, -190, -190, -190, -190, -190, -190, + 233, 230, 408, -407, 314, -407, 286, 244, -183, -190, + 89, -84, 260, 255, -304, -304, 35, -190, 418, 701, + 699, -145, 144, 265, -162, -153, -118, -118, -150, -313, + 180, 346, 264, 344, 340, 360, 351, 378, 342, 379, + 337, 336, 335, -313, -311, -150, -209, -132, -145, -145, + -145, 152, -145, 150, -145, -416, -416, -416, -416, -416, + -229, -145, -145, -145, -416, 180, 346, 16, -145, -311, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -380, -145, -209, -145, -209, + -145, -145, -145, -145, -145, -381, -381, -381, -381, -381, + -209, -209, -209, -209, -145, -415, -293, -97, -96, -95, + 655, 245, -94, -164, -97, -164, 223, -145, 223, 223, + 223, -145, -131, -295, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -194, -344, 283, -344, 283, -344, + -264, 89, -275, 24, 16, 59, 59, -167, -198, -168, + -293, -243, 682, -249, 48, -247, -248, 49, -244, 50, + 58, -331, -331, 171, -234, -145, -265, 78, -266, -274, + -217, -212, -214, -213, -415, -253, -416, -293, -264, -266, + -170, -171, -171, -170, -171, 68, 68, 68, 73, 68, + 73, 68, -187, -299, -416, -145, -302, 79, -168, -168, + -192, -299, 171, 418, 422, 423, -356, -405, 120, 145, + 33, 78, 376, 102, -403, 179, 617, 667, 672, 628, + 621, 662, -404, 247, 138, 139, 259, 27, 43, 90, + 89, 90, 89, 90, 90, 89, -287, -286, -45, -44, + -350, -350, 97, -383, 91, 91, 243, 28, -190, 78, + 78, 78, -113, 732, 97, 88, -3, 83, -145, 88, + 21, -339, -217, -374, -324, -375, -325, -326, -5, -6, + -351, -116, 59, 102, -63, 46, 242, 712, 713, 128, + -415, 725, -366, -254, -370, -372, -190, -149, -415, -161, + -147, -146, -148, -154, 169, 170, 264, 342, 343, -218, + -190, -138, 292, 300, 88, -142, 93, -386, 79, 283, + 376, 283, 376, 91, -408, 315, 91, -408, -190, -84, + -49, -190, -282, -282, 35, -383, -416, -162, -153, -125, + 164, 581, -316, 587, -327, -327, -327, -334, -327, 332, + -327, 332, -327, -416, -416, -416, 89, -416, 24, -416, + 89, -145, 89, -121, 477, 89, 89, -416, 88, 88, + -145, -416, -416, -416, 89, -416, -416, -416, -416, -416, + -416, -416, -416, -416, -416, -416, -416, -416, 89, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + 89, -416, 89, -416, 89, -416, 89, -416, 89, -416, + -416, 89, -416, -416, -416, 89, -416, 89, -416, 89, + -416, -416, -416, 89, -314, 673, -416, -416, -416, -416, + -416, -416, -416, -416, -416, -416, -416, -93, -294, -293, + -94, 637, 637, -416, -94, -226, 89, -150, -416, -150, + -150, -150, -416, -416, -416, 89, -416, 89, 89, -416, + 89, -416, 89, -416, -416, -416, -416, 89, -195, 24, + -415, -195, -415, -195, -416, -260, -190, -198, -227, 18, + -240, 53, 352, -251, -250, 57, 49, -248, 21, 51, + 21, 32, -265, 89, 153, 89, -416, -416, 89, 59, + 224, -416, -198, -181, -180, 78, 79, -182, 78, -180, + 68, 68, -255, 89, -263, -168, -198, -198, 224, 120, + -415, -149, 14, 91, 91, -383, -402, 716, 717, 33, + 97, -350, -350, 139, 139, -190, 88, -329, 91, -329, + 97, 97, 33, 84, 85, 86, 33, 80, 81, 82, + -190, -190, -190, -190, -371, 88, 21, -145, 88, 153, + 90, -254, -254, 279, 164, -350, 710, 285, 285, -350, + -350, -350, -115, -114, 732, 90, -416, 89, -337, 581, + 584, -145, -155, -155, -255, 90, -379, 581, -385, -293, + -293, -293, -293, 97, 99, -416, 579, 75, 582, -416, + -329, -145, -145, -145, -145, -234, 91, -145, -145, 97, + 97, -416, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -209, -145, -416, -178, -177, -179, 693, 120, + 33, -313, -416, -211, 277, -100, -99, -98, 16, -416, + -145, -118, -118, -118, -118, -145, -145, -145, -145, -145, + -145, -415, 68, 20, 18, -257, -293, 247, -415, -257, + -415, -302, -227, -228, 19, 21, -241, 55, -239, 54, + -239, -250, 21, 21, 91, 21, 91, 139, -274, -145, + -214, 59, -29, -293, -212, -293, -229, -145, 88, -145, + -158, -198, -198, -145, -204, 501, 503, 504, 505, 502, + 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, + 506, 517, 478, 479, 480, 109, 111, 110, 481, 482, + 483, 346, 529, 530, 524, 527, 528, 526, 525, 361, + 362, 484, 547, 548, 552, 551, 549, 550, 553, 556, + 557, 558, 559, 560, 561, 563, 562, 554, 555, 532, + 531, 533, 534, 535, 536, 537, 538, 540, 539, 541, + 542, 543, 544, 545, 546, 564, 565, 566, 567, 568, + 570, 569, 574, 573, 571, 572, 576, 575, 485, 486, + 112, 113, 114, 115, 116, 117, 118, 487, 490, 488, + 491, 492, 493, 498, 499, 494, 495, 496, 497, 500, + 372, 370, 371, 367, 366, 365, 425, 430, 431, 433, + 518, 519, 520, 521, 522, 523, 674, 675, 676, 677, + 678, 679, 680, 681, 91, 91, 88, -145, 90, 90, + -255, -370, -60, 90, -256, -254, 97, 90, 280, -213, + -415, 91, -350, -350, -350, 97, 97, -301, -416, 89, + -293, -404, -372, 585, 585, -416, 27, -378, -377, -295, + 88, 79, 64, 580, 583, -416, -416, -416, 89, -416, + -416, -416, 90, 90, -416, -416, -416, -416, -416, -416, + -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, + -416, -416, -416, -416, -416, -416, 89, -416, -177, -179, + -416, 78, -158, -229, 21, -97, 302, 304, -97, -416, + -416, -416, -416, -416, 89, -416, -416, 89, -416, 89, + -416, -416, -257, -416, 21, 21, 89, -416, -257, -416, + -257, -197, -228, -107, -106, -105, 611, -145, -209, -242, + 56, 78, 123, 91, 91, 91, 14, -212, 224, -234, + -254, -175, 385, -229, -416, -254, 90, 27, 90, 734, + 139, 90, -213, -124, -415, 276, -301, 91, 91, -114, + -117, -29, 89, 153, -254, -190, 64, -145, -209, -416, + 78, 592, 693, -92, -91, -88, 704, 730, -209, -94, + -94, -145, -145, -145, -416, -293, 247, -416, -416, -107, + 89, -104, -103, -293, 78, 123, -266, -293, 90, -416, + -415, -234, 90, -238, -29, 88, -3, 276, -324, -375, + -325, -326, -5, -6, -351, -82, 581, -377, -355, -299, + -295, 91, 97, 90, 581, -416, -416, -90, 147, 702, + 670, -155, 223, -416, 89, -416, 89, -416, 89, -105, + 89, 27, -302, -176, -174, -293, 634, -395, -394, 577, + -405, -401, 120, 145, 102, -403, 672, 628, 129, 130, + -82, -145, 88, -416, -83, 291, 689, 224, -386, 582, + -90, 703, 648, 623, 648, 623, -150, -145, -145, -145, + -103, -415, -416, 89, 24, -317, -62, 645, -392, -393, + 78, -396, 391, 644, 665, 120, 91, 90, -254, 252, + -300, -379, 583, 144, -118, -416, 89, -416, 89, -416, + -93, -174, 641, -330, -158, -393, 78, -392, 78, 15, + 14, -4, 733, 90, 293, -90, 648, 623, -145, -145, + -416, -61, 28, -175, -391, 260, 255, 258, 34, -391, + 97, -4, -416, -416, 645, 254, 33, 120, -158, -178, + -177, -177, } var yyDef = [...]int{ - 879, -2, -2, 881, 2, 4, 5, 6, 7, 8, + 882, -2, -2, 884, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 72, 74, 75, 879, 879, 879, 0, 879, 0, - 0, 879, -2, -2, 879, 1610, 0, 879, 0, 874, - 0, -2, 794, 800, 0, 809, -2, 0, 0, 879, - 879, 2234, 2234, 874, 0, 0, 0, 0, 0, 879, - 879, 879, 879, 1615, 1476, 52, 879, 0, 87, 88, - 829, 830, 831, 67, 0, 2232, 880, 1, 3, 73, - 77, 0, 0, 0, 60, 1485, 0, 80, 0, 0, - 883, 0, 0, 1593, 879, 879, 0, 128, 129, 0, + 39, 72, 74, 75, 882, 882, 882, 0, 882, 0, + 0, 882, -2, -2, 882, 1620, 0, 882, 0, 877, + 0, -2, 799, 805, 0, 814, -2, 0, 0, 882, + 882, 2246, 2246, 877, 0, 0, 0, 0, 0, 882, + 882, 882, 882, 1625, 1481, 52, 882, 0, 87, 88, + 832, 833, 834, 67, 0, 2244, 883, 1, 3, 73, + 77, 0, 0, 0, 60, 1490, 0, 80, 0, 0, + 886, 0, 0, 1603, 882, 882, 0, 128, 129, 0, 0, 0, -2, 132, -2, 161, 162, 163, 0, 168, - 605, 526, 578, 524, 563, -2, 512, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 529, - 401, 401, 0, 0, -2, 512, 512, 512, 1595, 0, - 0, 0, 560, 463, 401, 401, 401, 0, 401, 401, - 401, 401, 0, 0, 401, 401, 401, 401, 401, 401, - 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - 401, 1503, 167, 1611, 1608, 1609, 1768, 1769, 1770, 1771, - 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, + 609, 528, 580, 526, 565, -2, 514, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 531, + 403, 403, 0, 0, -2, 514, 514, 514, 1605, 0, + 0, 0, 562, 465, 403, 403, 403, 0, 403, 403, + 403, 403, 0, 0, 403, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + 403, 1508, 167, 1621, 1618, 1619, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, @@ -9241,370 +9376,374 @@ var yyDef = [...]int{ 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229, 2230, 2231, - 0, 1587, 0, 718, 982, 0, 875, 876, 0, 783, - 783, 0, 783, 783, 783, 783, 0, 0, 0, 732, - 0, 0, 0, 0, 780, 0, 748, 749, 0, 780, - 0, 755, 786, 0, 0, 761, 783, 783, 764, 2235, - 0, 2235, 2235, 1578, 0, 777, 775, 789, 790, 42, - 793, 796, 797, 798, 799, 802, 0, 813, 816, 1604, - 1605, 0, 818, 825, 842, 843, 0, 47, 1132, 0, - 1004, 0, 1010, -2, 1021, 1038, 1039, 1040, 1041, 1042, - 1044, 1045, 1046, 0, 0, 0, 0, 1051, 1052, 0, - 0, 0, 0, 0, 1113, 0, 0, 0, 0, 1449, - 0, 0, 1411, 1411, 1147, 1411, 1411, 1413, 1413, 1413, - 1820, 1958, 1966, 2142, 1781, 1787, 1788, 1789, 2088, 2089, - 2090, 2091, 2179, 2180, 2184, 1882, 1776, 2155, 2156, 0, - 2231, 1919, 1927, 1928, 1952, 2052, 2165, 1799, 1947, 2016, - 1879, 1901, 1902, 2034, 2035, 1923, 1924, 1905, 2094, 2096, - 2112, 2113, 2098, 2100, 2109, 2115, 2120, 2099, 2111, 2116, - 2129, 2133, 2136, 2137, 2138, 2106, 2104, 2117, 2121, 2123, - 2125, 2131, 2134, 2107, 2105, 2118, 2122, 2124, 2126, 2132, - 2135, 2093, 2097, 2101, 2110, 2128, 2108, 2127, 2102, 2114, - 2119, 2130, 2103, 2095, 1917, 1920, 1908, 1909, 1911, 1913, - 1918, 1925, 1931, 1910, 1930, 1929, 0, 1906, 1907, 1912, - 1922, 1926, 1914, 1915, 1916, 1921, 1932, 1972, 1971, 1970, - 2015, 1943, 2014, 0, 0, 0, 0, 0, 1771, 1825, - 1826, 2139, 1333, 1334, 1335, 1336, 0, 0, 0, 0, - 0, 0, 0, 293, 294, 1462, 1463, 46, 1131, 1574, - 1413, 1413, 1413, 1413, 1413, 1413, 1073, 1074, 1075, 1076, - 1077, 1101, 1102, 1108, 1109, 2029, 2030, 2031, 2032, 1863, - 2174, 1871, 1872, 2011, 2012, 1884, 1885, 2205, 2206, -2, - -2, -2, 234, 235, 236, 237, 238, 239, 240, 241, - 0, 1824, 2153, 2154, 230, 0, 0, 298, 299, 295, - 296, 297, 1115, 1116, 251, 252, 253, 254, 255, 256, - 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, - 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, - 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, - 287, 288, 289, 290, 291, 292, 2234, 0, 852, 0, - 0, 0, 0, 0, 0, 1616, 1617, 1485, 0, 1477, - 1476, 65, 0, 879, -2, 0, 0, 0, 0, 49, - 0, 54, 939, 882, 79, 78, 1525, 0, 0, 0, - 61, 1486, 69, 71, 1487, 0, 884, 885, 0, 915, - 919, 0, 0, 0, 1594, 1593, 1593, 104, 0, 0, - 105, 125, 126, 127, 0, 0, 111, 112, 1580, 1581, - 45, 0, 0, 179, 180, 0, 43, 428, 0, 175, - 0, 421, 360, 0, 1503, 0, 0, 0, 0, 0, - 879, 0, 1588, 156, 157, 164, 165, 166, 401, 401, - 401, 575, 0, 0, 167, 167, 533, 534, 535, 0, - 0, -2, 426, 0, 513, 0, 0, 415, 415, 419, - 417, 418, 0, 0, 0, 0, 0, 0, 0, 0, - 552, 0, 553, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 666, 0, 402, 0, 573, 574, 464, 0, - 0, 0, 0, 0, 0, 0, 0, 1596, 1597, 0, - 550, 551, 0, 0, 0, 401, 401, 0, 0, 0, - 0, 401, 401, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 155, 1516, 0, 0, 0, -2, 0, 710, 0, - 0, 0, 1589, 1589, 0, 717, 0, 0, 0, 722, - 0, 0, 723, 0, 780, 780, 778, 779, 725, 726, - 727, 728, 783, 0, 0, 410, 411, 412, 780, 783, - 0, 783, 783, 783, 783, 780, 780, 780, 783, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2235, 786, - 783, 0, 756, 0, 757, 758, 759, 762, 763, 765, - 2236, 2237, 1606, 1607, 1618, 1619, 1620, 1621, 1622, 1623, - 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633, - 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, - 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, - 1654, 1655, 1656, 1657, 1658, 1659, 1660, 1661, 1662, 1663, - 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, - 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, - 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, - 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, - 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, - 1714, 1715, 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, - 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, - 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, - 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, - 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, - 1764, 1765, 1766, 1767, 2235, 2235, 769, 773, 1579, 795, - 801, 803, 804, 0, 0, 814, 817, 836, 51, 1870, - 824, 51, 826, 827, 828, 854, 855, 860, 0, 0, - 0, 0, 866, 867, 868, 0, 0, 871, 872, 873, - 0, 0, 0, 0, 0, 1002, 0, 0, 1121, 1122, - 1123, 1124, 1125, 1126, 1127, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1022, 1023, 0, 0, 0, 1047, 1048, - 1049, 1050, 1053, 0, 1064, 0, 1066, 1458, -2, 0, - 0, 0, 1058, 1059, 0, 0, 0, 0, 0, 0, - 0, 1450, 0, 0, 1145, 0, 1146, 1148, 1149, 1150, - 0, 1151, 1152, 889, 889, 889, 889, 889, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 889, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1599, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 899, 0, 0, 899, 899, - 0, 0, 222, 223, 224, 225, 226, 227, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 242, 243, 244, 245, 246, 247, 300, 248, - 249, 250, 1131, 0, 0, 0, 48, 844, 845, 0, - 965, 1599, 0, 0, 895, 0, 1614, 59, 68, 70, - 1485, 63, 1485, 0, 901, 0, 0, -2, -2, 902, - 908, 909, 910, 911, 912, 56, 2233, 57, 0, 76, - 0, 50, 0, 0, 0, 0, 374, 1528, 0, 0, - 1478, 1479, 1482, 0, 916, 1964, 920, 0, 922, 923, - 0, 0, 102, 0, 981, 0, 0, 0, 113, 0, - 115, 116, 0, 0, 0, 385, 1582, 1583, 1584, -2, - 408, 0, 385, 369, 308, 309, 310, 360, 312, 360, - 360, 360, 360, 374, 374, 374, 374, 343, 344, 345, - 346, 347, 0, 0, 329, 360, 360, 360, 360, 350, - 351, 352, 353, 354, 355, 356, 357, 313, 314, 315, - 316, 317, 318, 319, 320, 321, 362, 362, 362, 362, - 362, 366, 366, 0, 44, 0, 389, 0, 1482, 0, - 0, 1516, 1591, 1601, 0, 0, 0, 1591, 134, 0, - 0, 0, 576, 616, 527, 564, 577, 0, 530, 531, - -2, 0, 0, 512, 0, 514, 0, 409, 0, -2, - 0, 419, 0, 415, 419, 416, 419, 407, 420, 554, - 555, 556, 0, 558, 559, 646, 951, 0, 0, 0, - 0, 0, 652, 653, 654, 0, 656, 657, 658, 659, - 660, 661, 662, 663, 664, 665, 565, 566, 567, 568, - 569, 570, 571, 572, 0, 0, 0, 0, 514, 0, - 561, 0, 0, 465, 466, 467, 0, 0, 470, 471, - 472, 473, 0, 0, 476, 477, 478, 968, 969, 479, - 480, 505, 506, 507, 481, 482, 483, 484, 485, 486, - 487, 499, 500, 501, 502, 503, 504, 488, 489, 490, - 491, 492, 493, 496, 0, 149, 1507, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1589, 0, 0, 0, 0, 898, 983, 1612, - 1613, 719, 0, 0, 784, 785, 0, 413, 414, 783, - 783, 729, 770, 0, 783, 733, 771, 734, 736, 735, - 737, 750, 751, 783, 740, 781, 782, 741, 742, 743, - 744, 745, 746, 747, 766, 752, 753, 754, 787, 0, - 791, 792, 767, 768, 0, 0, 807, 808, 0, 815, - 839, 837, 838, 840, 832, 833, 834, 835, 0, 841, - 0, 0, 857, 98, 862, 863, 864, 865, 877, 870, - 1133, 999, 1000, 1001, 0, 1003, 1007, 0, 1117, 1119, - 1009, 1005, 1011, 1128, 1129, 1130, 0, 0, 0, 0, - 0, 1015, 1019, 1024, 1025, 1026, 1027, 1028, 0, 1029, - 0, 1032, 1033, 1034, 1035, 1036, 1037, 1043, 1426, 1427, - 1428, 1062, 301, 302, 0, 1063, 0, 0, 0, 0, - 0, 0, 0, 0, 1373, 1374, 1375, 1376, 1377, 1378, - 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, - 1389, 1390, 1391, 1392, 1132, 0, 913, 0, 0, 1456, - 1453, 0, 0, 0, 1412, 1414, 0, 0, 0, 890, - 891, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1393, 1394, - 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, - 1405, 1406, 1407, 1408, 1409, 1410, 0, 0, 1429, 0, - 0, 0, 0, 0, 1449, 0, 1068, 1069, 1070, 0, - 0, 0, 0, 0, 0, 1191, 0, 0, 0, 0, - 1600, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 144, 145, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1337, 1338, 1339, 1340, 41, 0, 0, 0, - 0, 0, 0, 0, 900, 1460, 0, -2, -2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1362, 0, 0, 0, 0, 0, 0, 1572, - 0, 0, 847, 848, 850, 0, 985, 0, 966, 0, - 0, 853, 0, 894, 0, 897, 62, 64, 906, 907, - 0, 924, 903, 58, 53, 0, 0, 943, 1526, 374, - 1548, 0, 383, 383, 380, 1488, 1489, 0, 1481, 1483, - 1484, 81, 921, 917, 0, 997, 0, 0, 980, 0, - 927, 929, 930, 931, 963, 0, 934, 935, 0, 0, - 0, 0, 0, 100, 982, 106, 0, 114, 0, 0, - 119, 120, 107, 108, 109, 110, 0, 605, -2, 460, - 181, 183, 184, 185, 176, -2, 372, 370, 371, 311, - 374, 374, 337, 338, 339, 340, 341, 342, 0, 0, - 330, 331, 332, 333, 322, 0, 323, 324, 325, 364, - 0, 326, 327, 0, 328, 427, 0, 1490, 390, 391, - 393, 401, 0, 396, 397, 0, 401, 401, 0, 422, - 423, 0, 1482, 1507, 0, 0, 0, 1602, 1601, 1601, - 1601, 0, 169, 170, 171, 172, 173, 174, 641, 0, - 0, 617, 639, 640, 167, 0, 0, 177, 516, 515, - 0, 673, 0, 425, 0, 0, 419, 419, 404, 405, - 557, 0, 0, 648, 649, 650, 651, 0, 0, 0, - 543, 454, 0, 544, 545, 514, 516, 0, 0, 385, - 468, 469, 474, 475, 494, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 592, 593, 594, - 597, 599, 518, 603, 596, 598, 600, 518, 604, 1504, - 1505, 1506, 0, 0, 711, 0, 0, 451, 96, 1590, - 716, 720, 721, 780, 739, 772, 780, 731, 738, 760, - 805, 806, 811, 819, 820, 821, 822, 823, 861, 0, - 0, 0, 0, 869, 0, 0, 1008, 1118, 1120, 1012, - 0, 1016, 1020, 0, 0, 0, 0, 0, 1067, 1065, - 1460, 0, 0, 0, 1114, 0, 0, 0, 1136, 1137, - 0, 0, 0, 1454, 0, 0, 1143, 0, 1415, 1153, - 0, 0, 0, 0, 0, 1159, 1160, 1161, 1162, 1163, - 1164, 1165, 1166, 1167, 1168, 1476, 1170, 0, 0, 0, - 0, 0, 1175, 1176, 1177, 1178, 1179, 0, 1181, 0, - 1182, 0, 0, 0, 0, 1189, 1190, 1192, 0, 0, - 1195, 1196, 0, 1198, 0, 1200, 1201, 1202, 1203, 1204, - 1205, 0, 1207, 0, 1209, 1210, 1211, 0, 1213, 0, - 1215, 1216, 0, 1218, 0, 1220, 0, 1223, 0, 1226, - 0, 1229, 0, 1232, 0, 1235, 0, 1238, 0, 1241, - 0, 1244, 0, 1247, 0, 1250, 0, 1253, 0, 1256, - 0, 1259, 0, 1262, 0, 1265, 0, 1268, 1269, 1270, - 0, 1272, 0, 1274, 0, 1277, 1278, 0, 1280, 0, - 1283, 0, 1286, 0, 0, 1287, 0, 0, 0, 1291, - 0, 0, 0, 0, 1300, 1301, 1302, 1303, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1314, 1315, - 1316, 1317, 1318, 1319, 0, 1321, 0, 1096, 0, 0, - 1096, 0, 0, 0, 0, 0, 1134, 899, 0, 1416, - 1417, 1418, 1419, 1420, 0, 0, 0, 0, 0, 0, - 1360, 1361, 1363, 0, 0, 1366, 0, 1368, 0, 1573, - 846, 849, 851, 937, 986, 987, 0, 0, 0, 0, - 967, 1598, 892, 893, 896, 945, 0, 1464, 0, 0, - 924, 997, 925, 0, 904, 55, 940, 0, 1530, 1529, - 1542, 1555, 383, 383, 377, 378, 384, 379, 381, 382, - 1480, 0, 1485, 0, 1566, 0, 0, 1558, 0, 0, - 0, 0, 0, 0, 0, 0, 970, 0, 0, 973, - 0, 0, 0, 0, 964, 935, 0, 936, 0, -2, - 0, 0, 94, 95, 0, 0, 0, 117, 118, 0, - 0, 124, 386, 387, 158, 167, 462, 182, 435, 0, - 0, 307, 373, 334, 335, 336, 0, 358, 0, 0, - 0, 0, 456, 130, 1494, 1493, 401, 401, 392, 0, - 395, 0, 0, 0, 1603, 361, 424, 0, 148, 0, - 0, 0, 0, 0, 154, 611, 0, 0, 618, 0, - 0, 0, 525, 0, 536, 537, 0, 645, -2, 707, - 389, 0, 403, 406, 952, 0, 0, 538, 0, 541, - 542, 455, 516, 547, 548, 562, 549, 497, 498, 495, - 0, 0, 1517, 1518, 1523, 1521, 1522, 135, 583, 585, - 589, 584, 588, 0, 0, 0, 520, 0, 520, 581, - 0, 451, 1490, 0, 715, 452, 453, 783, 783, 856, - 99, 0, 859, 0, 0, 0, 0, 1013, 1017, 1030, - 1031, 1421, 1447, 360, 360, 1434, 360, 366, 1437, 360, - 1439, 360, 1442, 360, 1445, 1446, 0, 0, 1060, 0, - 914, 0, 0, 1142, 1457, 0, 0, 1154, 1155, 1156, - 1157, 1158, 1451, 0, 0, 0, 1174, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 146, 147, 0, - 0, 0, 0, 0, 0, 1371, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1091, 1095, 0, - 1097, 1098, 0, 0, 1323, 0, 0, 1341, 0, 0, - 0, 0, 0, 0, 0, 1461, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 988, 993, 993, 993, - 0, 0, 0, 1585, 1586, 1465, 1466, 997, 1467, 926, - 905, 944, 1548, 0, 1541, 0, -2, 1550, 0, 0, - 0, 1556, 375, 376, 918, 82, 998, 85, 0, 1566, - 1575, 0, 1557, 1568, 1570, 0, 0, 0, 1562, 0, - 997, 928, 959, 961, 0, 956, 971, 972, 974, 0, - 976, 0, 978, 979, 939, 933, 0, 102, 0, 997, - 997, 101, 0, 984, 121, 122, 123, 461, 186, 191, - 0, 0, 0, 196, 0, 198, 0, 0, 0, 203, - 204, 401, 401, 436, 0, 304, 306, 0, 0, 189, - 374, 0, 374, 0, 365, 367, 0, 437, 457, 1491, - 1492, 0, 0, 394, 398, 399, 400, 0, 1592, 150, - 0, 0, 0, 614, 0, 642, 0, 0, 0, 0, - 0, 0, 178, 517, 674, 675, 676, 677, 678, 679, - 680, 681, 682, 0, 401, 0, 0, 0, 401, 401, - 401, 0, 699, 388, 0, 0, 670, 667, 539, 0, - 220, 221, 228, 229, 231, 0, 0, 0, 0, 0, - 546, 939, 1508, 1509, 1510, 0, 1520, 1524, 138, 0, - 0, 0, 0, 591, 595, 601, 0, 519, 602, 712, - 713, 714, 97, 724, 730, 858, 878, 1006, 1014, 1018, - 0, 0, 0, 0, 1448, 1432, 374, 1435, 1436, 1438, - 1440, 1441, 1443, 1444, 1056, 1057, 1061, 0, 1139, 0, - 1141, 1455, 0, 1485, 0, 0, 0, 1173, 0, 0, - 0, 1184, 1183, 1185, 0, 1187, 1188, 1193, 1194, 1197, - 1199, 1206, 1208, 1212, 1214, 1217, 1219, 1221, 0, 1224, - 0, 1227, 0, 1230, 0, 1233, 0, 1236, 0, 1239, - 0, 1242, 0, 1245, 0, 1248, 0, 1251, 0, 1254, - 0, 1257, 0, 1260, 0, 1263, 0, 1266, 0, 1271, - 1273, 0, 1276, 1279, 1281, 0, 1284, 0, 1288, 0, - 1290, 1292, 1293, 0, 0, 0, 1304, 1305, 1306, 1307, - 1308, 1309, 1310, 1311, 1312, 1313, 1320, 0, 1089, 1092, - 1322, 1099, 1100, 1105, 1325, 0, 0, 0, 1328, 0, - 0, 0, 1332, 1135, 1343, 0, 1348, 0, 0, 1354, - 0, 1358, 0, 1364, 1365, 1367, 1369, 0, 0, 0, - 0, 0, 965, 946, 66, 1467, 1469, 0, 1535, 1533, - 1533, 1543, 1544, 0, 0, 1551, 0, 0, 0, 0, - 86, 0, 0, 0, 1571, 0, 0, 0, 0, 103, - 1476, 953, 960, 0, 0, 954, 0, 955, 975, 977, - 932, 0, 997, 997, 92, 93, 0, 192, 0, 194, - 0, 197, 199, 200, 201, 207, 208, 209, 202, 0, - 0, 303, 305, 0, 0, 348, 359, 349, 0, 0, - 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 939, 151, - 152, 153, 606, 0, 616, 0, 941, 0, 609, 0, - 528, 0, 0, 0, 401, 401, 401, 0, 0, 0, - 0, 684, 0, 0, 647, 0, 655, 0, 0, 0, - 232, 233, 0, 1519, 582, 0, 136, 137, 0, 0, - 587, 521, 522, 1054, 0, 0, 0, 1055, 1433, 0, - 0, 0, 0, 1452, 0, 0, 0, 0, 1180, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1296, 0, 0, 0, 636, 637, 0, 1372, 1094, - 1476, 0, 1096, 1106, 1107, 0, 1096, 1342, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, - 0, 0, 0, 0, 985, 1469, 1474, 0, 0, 1538, - 0, 1531, 1534, 1532, 1545, 0, 0, 1552, 0, 1554, - 0, 1576, 1577, 1569, 0, 1561, 1564, 1560, 1563, 1485, - 957, 0, 962, 0, 1476, 91, 0, 195, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 205, 206, 0, - 0, 363, 368, 0, 0, 0, 607, 0, 942, 619, - 610, 0, 697, 0, 701, 0, 0, 0, 704, 705, - 706, 683, 0, 687, 429, 671, 668, 669, 540, 0, - 139, 140, 0, 0, 0, 1422, 0, 1425, 1138, 1140, - 0, 1169, 1171, 1172, 1430, 1431, 1186, 1222, 1225, 1228, - 1231, 1234, 1237, 1240, 1243, 1246, 1249, 1252, 1255, 1258, - 1261, 1264, 1267, 1275, 1282, 1285, 1289, 1294, 0, 1297, - 0, 0, 1298, 0, 638, 1085, 0, 0, 1103, 1104, - 0, 1327, 1329, 1330, 1331, 1344, 0, 1349, 1350, 0, - 1355, 0, 1359, 1370, 0, 990, 947, 948, 995, 996, - 0, 0, 938, 1474, 84, 1475, 1472, 0, 1470, 1468, - 1527, 0, 1536, 1537, 1546, 1547, 1553, 0, 1559, 0, - 89, 0, 0, 0, 1485, 193, 0, 212, 0, 615, - 0, 618, 608, 695, 696, 0, 708, 700, 702, 703, - 685, -2, 1511, 0, 0, 0, 590, 1423, 0, 0, - 1299, 0, 634, 635, 1093, 1086, 0, 1071, 1072, 1090, - 1324, 1326, 0, 0, 0, 0, 989, 991, 992, 83, - 0, 1471, 1111, 0, 1539, 1540, 1567, 1565, 958, 965, - 0, 90, 442, 435, 1511, 0, 0, 0, 688, 689, - 690, 691, 692, 693, 694, 579, 1513, 141, 142, 0, - 509, 510, 511, 135, 0, 1144, 1295, 1087, 0, 0, - 0, 0, 0, 1345, 0, 1351, 0, 1356, 0, 949, - 950, 1473, 0, 0, 620, 0, 622, 0, -2, 430, - 443, 0, 187, 213, 214, 0, 0, 217, 218, 219, - 210, 211, 131, 0, 0, 709, 0, 1514, 1515, 0, - 138, 0, 0, 1078, 1079, 1080, 1081, 1083, 0, 0, - 0, 0, 1112, 1091, 621, 0, 0, 385, 0, 631, - 431, 432, 0, 438, 439, 440, 441, 215, 216, 643, - 0, 0, 508, 586, 1424, 0, 0, 1346, 0, 1352, - 0, 1357, 0, 623, 624, 632, 0, 433, 0, 434, - 0, 0, 0, 612, 0, 643, 1512, 1088, 1082, 1084, - 0, 0, 1110, 0, 633, 629, 444, 446, 447, 0, - 0, 445, 644, 613, 1347, 1353, 0, 448, 449, 450, - 625, 626, 627, 628, + 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, + 2242, 2243, 0, 1597, 0, 722, 984, 0, 878, 879, + 0, 788, 788, 0, 788, 788, 788, 788, 0, 0, + 0, 736, 0, 0, 0, 0, 785, 0, 752, 753, + 0, 785, 0, 759, 791, 0, 0, 766, 788, 788, + 769, 2247, 0, 2247, 2247, 1588, 0, 782, 780, 794, + 795, 42, 798, 801, 802, 803, 804, 807, 0, 818, + 821, 1614, 1615, 0, 823, 828, 845, 846, 0, 47, + 1136, 0, 1008, 0, 1014, -2, 1025, 1042, 1043, 1044, + 1045, 1046, 1048, 1049, 1050, 0, 0, 0, 0, 1055, + 1056, 0, 0, 0, 0, 0, 1117, 0, 0, 0, + 0, 1977, 1454, 0, 0, 1416, 1416, 1152, 1416, 1416, + 1418, 1418, 1418, 1830, 1969, 1978, 2154, 1791, 1797, 1798, + 1799, 2100, 2101, 2102, 2103, 2191, 2192, 2196, 1893, 1786, + 2167, 2168, 0, 2243, 1930, 1938, 1939, 1963, 2064, 2177, + 1809, 1958, 2028, 1890, 1912, 1913, 2046, 2047, 1934, 1935, + 1916, 2106, 2108, 2124, 2125, 2110, 2112, 2121, 2127, 2132, + 2111, 2123, 2128, 2141, 2145, 2148, 2149, 2150, 2118, 2116, + 2129, 2133, 2135, 2137, 2143, 2146, 2119, 2117, 2130, 2134, + 2136, 2138, 2144, 2147, 2105, 2109, 2113, 2122, 2140, 2120, + 2139, 2114, 2126, 2131, 2142, 2115, 2107, 1928, 1931, 1919, + 1920, 1922, 1924, 1929, 1936, 1942, 1921, 1941, 1940, 0, + 1917, 1918, 1923, 1933, 1937, 1925, 1926, 1927, 1932, 1943, + 1984, 1983, 1982, 2027, 1954, 2026, 0, 0, 0, 0, + 0, 1781, 1835, 1836, 2151, 1338, 1339, 1340, 1341, 0, + 0, 0, 0, 0, 0, 0, 293, 294, 1467, 1468, + 46, 1135, 1584, 1418, 1418, 1418, 1418, 1418, 1418, 1077, + 1078, 1079, 1080, 1081, 1105, 1106, 1112, 1113, 2041, 2042, + 2043, 2044, 1873, 2186, 1882, 1883, 2023, 2024, 1895, 1896, + 2217, 2218, -2, -2, -2, 234, 235, 236, 237, 238, + 239, 240, 241, 0, 1834, 2165, 2166, 230, 0, 0, + 298, 295, 296, 297, 1119, 1120, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 300, 301, + 2246, 0, 855, 0, 0, 0, 0, 0, 0, 1626, + 1627, 1490, 0, 1482, 1481, 65, 0, 882, -2, 0, + 0, 0, 0, 49, 0, 54, 941, 885, 79, 78, + 1530, 1533, 0, 0, 0, 61, 1491, 69, 71, 1492, + 0, 887, 888, 0, 917, 921, 0, 0, 0, 1604, + 1603, 1603, 104, 0, 0, 105, 125, 126, 127, 0, + 0, 111, 112, 1590, 1591, 45, 0, 0, 179, 180, + 0, 43, 430, 0, 175, 0, 423, 362, 0, 1508, + 0, 0, 0, 0, 0, 882, 0, 1598, 156, 157, + 164, 165, 166, 403, 403, 403, 577, 0, 0, 167, + 167, 535, 536, 537, 0, 0, -2, 428, 0, 515, + 0, 0, 417, 417, 421, 419, 420, 0, 0, 0, + 0, 0, 0, 0, 0, 554, 0, 555, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 670, 0, 404, + 0, 575, 576, 466, 0, 0, 0, 0, 0, 0, + 0, 0, 1606, 1607, 0, 552, 553, 0, 0, 0, + 403, 403, 0, 0, 0, 0, 403, 403, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 155, 1521, 0, 0, + 0, -2, 0, 714, 0, 0, 0, 1599, 1599, 0, + 721, 0, 0, 0, 726, 0, 0, 727, 0, 785, + 785, 783, 784, 729, 730, 731, 732, 788, 0, 0, + 412, 413, 414, 785, 788, 0, 788, 788, 788, 788, + 785, 785, 785, 788, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2247, 791, 788, 0, 760, 0, 761, + 762, 763, 764, 767, 768, 770, 2248, 2249, 1616, 1617, + 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, + 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, + 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, + 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, + 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, + 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, + 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, + 1698, 1699, 1700, 1701, 1702, 1703, 1704, 1705, 1706, 1707, + 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, 1716, 1717, + 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, + 1728, 1729, 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, + 1738, 1739, 1740, 1741, 1742, 1743, 1744, 1745, 1746, 1747, + 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, + 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, + 1768, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, 1777, + 2247, 2247, 774, 778, 1589, 800, 806, 808, 809, 0, + 0, 819, 822, 839, 51, 1881, 827, 51, 829, 830, + 831, 857, 858, 863, 0, 0, 0, 0, 869, 870, + 871, 0, 0, 874, 875, 876, 0, 0, 0, 0, + 0, 1006, 0, 0, 1125, 1126, 1127, 1128, 1129, 1130, + 1131, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1026, + 1027, 0, 0, 0, 1051, 1052, 1053, 1054, 1057, 0, + 1068, 0, 1070, 1463, -2, 0, 0, 0, 1062, 1063, + 0, 0, 0, 0, 0, 0, 0, 0, 1455, 0, + 0, 1150, 0, 1151, 1153, 1154, 1155, 0, 1156, 1157, + 892, 892, 892, 892, 892, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 892, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1609, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 902, 0, 0, 902, 902, 0, 0, 222, + 223, 224, 225, 226, 227, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 299, + 242, 243, 244, 245, 246, 247, 302, 248, 249, 250, + 1135, 0, 0, 0, 48, 847, 848, 0, 967, 1609, + 0, 0, 898, 0, 1624, 59, 68, 70, 1490, 63, + 1490, 0, 904, 0, 0, -2, -2, 905, 906, 910, + 911, 912, 913, 914, 56, 2245, 57, 0, 76, 0, + 50, 0, 0, 1531, 0, 1534, 0, 0, 0, 376, + 1538, 0, 0, 1483, 1484, 1487, 0, 918, 1975, 922, + 0, 924, 925, 0, 0, 102, 0, 983, 0, 0, + 0, 113, 0, 115, 116, 0, 0, 0, 387, 1592, + 1593, 1594, -2, 410, 0, 387, 371, 310, 311, 312, + 362, 314, 362, 362, 362, 362, 376, 376, 376, 376, + 345, 346, 347, 348, 349, 0, 0, 331, 362, 362, + 362, 362, 352, 353, 354, 355, 356, 357, 358, 359, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 364, + 364, 364, 364, 364, 368, 368, 0, 44, 0, 391, + 0, 1487, 0, 0, 1521, 1601, 1611, 0, 0, 0, + 1601, 134, 0, 0, 0, 578, 620, 529, 566, 579, + 0, 532, 533, -2, 0, 0, 514, 0, 516, 0, + 411, 0, -2, 0, 421, 0, 417, 421, 418, 421, + 409, 422, 556, 557, 558, 0, 560, 561, 650, 953, + 0, 0, 0, 0, 0, 656, 657, 658, 0, 660, + 661, 662, 663, 664, 665, 666, 667, 668, 669, 567, + 568, 569, 570, 571, 572, 573, 574, 0, 0, 0, + 0, 516, 0, 563, 0, 0, 467, 468, 469, 0, + 0, 472, 473, 474, 475, 0, 0, 478, 479, 480, + 970, 971, 481, 482, 507, 508, 509, 483, 484, 485, + 486, 487, 488, 489, 501, 502, 503, 504, 505, 506, + 490, 491, 492, 493, 494, 495, 498, 0, 149, 1512, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1599, 0, 0, 0, + 0, 901, 985, 1622, 1623, 723, 0, 0, 789, 790, + 0, 415, 416, 788, 788, 733, 775, 0, 788, 737, + 776, 738, 740, 739, 741, 754, 755, 788, 744, 786, + 787, 745, 746, 747, 748, 749, 750, 751, 771, 756, + 757, 758, 792, 0, 796, 797, 772, 773, 0, 0, + 812, 813, 0, 820, 842, 840, 841, 843, 835, 836, + 837, 838, 0, 844, 0, 0, 860, 98, 865, 866, + 867, 868, 880, 873, 1137, 1003, 1004, 1005, 0, 1007, + 1011, 0, 1121, 1123, 1013, 1009, 1015, 1132, 1133, 1134, + 0, 0, 0, 0, 0, 1019, 1023, 1028, 1029, 1030, + 1031, 1032, 0, 1033, 0, 1036, 1037, 1038, 1039, 1040, + 1041, 1047, 1431, 1432, 1433, 1066, 303, 304, 0, 1067, + 0, 0, 0, 0, 0, 0, 0, 0, 1378, 1379, + 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, + 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1136, 0, + 915, 0, 0, 0, 1461, 1458, 0, 0, 0, 1417, + 1419, 0, 0, 0, 893, 894, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1398, 1399, 1400, 1401, 1402, 1403, 1404, + 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, + 1415, 0, 0, 1434, 0, 0, 0, 0, 0, 1454, + 0, 1072, 1073, 1074, 0, 0, 0, 0, 0, 0, + 1196, 0, 0, 0, 0, 1610, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 145, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1342, 1343, 1344, + 1345, 41, 0, 0, 0, 0, 0, 0, 0, 903, + 1465, 0, -2, -2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1367, 0, 0, + 0, 0, 0, 0, 1582, 0, 0, 850, 851, 853, + 0, 987, 0, 968, 0, 0, 856, 0, 897, 0, + 900, 62, 64, 908, 909, 0, 926, 907, 58, 53, + 0, 0, 945, 1532, 1535, 1536, 376, 1558, 0, 385, + 385, 382, 1493, 1494, 0, 1486, 1488, 1489, 81, 923, + 919, 0, 1001, 0, 0, 982, 0, 929, 931, 932, + 933, 965, 0, 936, 937, 0, 0, 0, 0, 0, + 100, 984, 106, 0, 114, 0, 0, 119, 120, 107, + 108, 109, 110, 0, 609, -2, 462, 181, 183, 184, + 185, 176, -2, 374, 372, 373, 313, 376, 376, 339, + 340, 341, 342, 343, 344, 0, 0, 332, 333, 334, + 335, 324, 0, 325, 326, 327, 366, 0, 328, 329, + 0, 330, 429, 0, 1495, 392, 393, 395, 403, 0, + 398, 399, 0, 403, 403, 0, 424, 425, 0, 1487, + 1512, 0, 0, 0, 1612, 1611, 1611, 1611, 0, 169, + 170, 171, 172, 173, 174, 645, 0, 0, 621, 643, + 644, 167, 0, 0, 177, 518, 517, 0, 677, 0, + 427, 0, 0, 421, 421, 406, 407, 559, 0, 0, + 652, 653, 654, 655, 0, 0, 0, 545, 456, 0, + 546, 547, 516, 518, 0, 0, 387, 470, 471, 476, + 477, 496, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 594, 595, 596, 599, 601, 520, + 605, 607, 598, 600, 602, 520, 606, 608, 1509, 1510, + 1511, 0, 0, 715, 0, 0, 453, 96, 1600, 720, + 724, 725, 785, 743, 777, 785, 735, 742, 765, 810, + 811, 816, 824, 825, 826, 864, 0, 0, 0, 0, + 872, 0, 0, 1012, 1122, 1124, 1016, 0, 1020, 1024, + 0, 0, 0, 0, 0, 1071, 1069, 1465, 0, 0, + 0, 1118, 0, 0, 0, 1140, 1141, 0, 0, 0, + 0, 1459, 0, 0, 1148, 0, 1420, 1158, 0, 0, + 0, 0, 0, 1164, 1165, 1166, 1167, 1168, 1169, 1170, + 1171, 1172, 1173, 1481, 1175, 0, 0, 0, 0, 0, + 1180, 1181, 1182, 1183, 1184, 0, 1186, 0, 1187, 0, + 0, 0, 0, 1194, 1195, 1197, 0, 0, 1200, 1201, + 0, 1203, 0, 1205, 1206, 1207, 1208, 1209, 1210, 0, + 1212, 0, 1214, 1215, 1216, 0, 1218, 0, 1220, 1221, + 0, 1223, 0, 1225, 0, 1228, 0, 1231, 0, 1234, + 0, 1237, 0, 1240, 0, 1243, 0, 1246, 0, 1249, + 0, 1252, 0, 1255, 0, 1258, 0, 1261, 0, 1264, + 0, 1267, 0, 1270, 0, 1273, 1274, 1275, 0, 1277, + 0, 1279, 0, 1282, 1283, 0, 1285, 0, 1288, 0, + 1291, 0, 0, 1292, 0, 0, 0, 1296, 0, 0, + 0, 0, 1305, 1306, 1307, 1308, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1319, 1320, 1321, 1322, + 1323, 1324, 0, 1326, 0, 1100, 0, 0, 1100, 0, + 0, 0, 0, 0, 1138, 902, 0, 1421, 1422, 1423, + 1424, 1425, 0, 0, 0, 0, 0, 0, 1365, 1366, + 1368, 0, 0, 1371, 0, 1373, 0, 1583, 849, 852, + 854, 939, 988, 989, 0, 0, 0, 0, 969, 1608, + 895, 896, 899, 947, 0, 1469, 0, 0, 926, 1001, + 927, 0, 55, 942, 0, 1540, 1539, 1552, 1565, 385, + 385, 379, 380, 386, 381, 383, 384, 1485, 0, 1490, + 0, 1576, 0, 0, 1568, 0, 0, 0, 0, 0, + 0, 0, 0, 972, 0, 0, 975, 0, 0, 0, + 0, 966, 937, 0, 938, 0, -2, 0, 0, 94, + 95, 0, 0, 0, 117, 118, 0, 0, 124, 388, + 389, 158, 167, 464, 182, 437, 0, 0, 309, 375, + 336, 337, 338, 0, 360, 0, 0, 0, 0, 458, + 130, 1499, 1498, 403, 403, 394, 0, 397, 0, 0, + 0, 1613, 363, 426, 0, 148, 0, 0, 0, 0, + 0, 154, 615, 0, 0, 622, 0, 0, 0, 527, + 0, 538, 539, 0, 649, -2, 711, 391, 0, 405, + 408, 954, 0, 0, 540, 0, 543, 544, 457, 518, + 549, 550, 564, 551, 499, 500, 497, 0, 0, 1522, + 1523, 1528, 1526, 1527, 135, 585, 587, 591, 586, 590, + 0, 0, 0, 522, 0, 522, 583, 0, 453, 1495, + 0, 719, 454, 455, 788, 788, 859, 99, 0, 862, + 0, 0, 0, 0, 1017, 1021, 1034, 1035, 1426, 1452, + 362, 362, 1439, 362, 368, 1442, 362, 1444, 362, 1447, + 362, 1450, 1451, 0, 0, 1064, 0, 916, 0, 0, + 0, 1147, 1462, 0, 0, 1159, 1160, 1161, 1162, 1163, + 1456, 0, 0, 0, 1179, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 146, 147, 0, 0, 0, + 0, 0, 0, 1376, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1095, 1099, 0, 1101, 1102, + 0, 0, 1328, 0, 0, 1346, 0, 0, 0, 0, + 0, 0, 0, 1466, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 990, 997, 0, 997, 0, 997, + 0, 0, 0, 1595, 1596, 1470, 1471, 1001, 1472, 928, + 946, 1558, 0, 1551, 0, -2, 1560, 0, 0, 0, + 1566, 377, 378, 920, 82, 1002, 85, 0, 1576, 1585, + 0, 1567, 1578, 1580, 0, 0, 0, 1572, 0, 1001, + 930, 961, 963, 0, 958, 973, 974, 976, 0, 978, + 0, 980, 981, 941, 935, 0, 102, 0, 1001, 1001, + 101, 0, 986, 121, 122, 123, 463, 186, 191, 0, + 0, 0, 196, 0, 198, 0, 0, 0, 203, 204, + 403, 403, 438, 0, 306, 308, 0, 0, 189, 376, + 0, 376, 0, 367, 369, 0, 439, 459, 1496, 1497, + 0, 0, 396, 400, 401, 402, 0, 1602, 150, 0, + 0, 0, 618, 0, 646, 0, 0, 0, 0, 0, + 0, 178, 519, 678, 679, 680, 681, 682, 683, 684, + 685, 686, 0, 403, 0, 0, 0, 403, 403, 403, + 0, 703, 390, 0, 0, 674, 671, 541, 0, 220, + 221, 228, 229, 231, 0, 0, 0, 0, 0, 548, + 941, 1513, 1514, 1515, 0, 1525, 1529, 138, 0, 0, + 0, 0, 593, 597, 603, 0, 521, 604, 716, 717, + 718, 97, 728, 734, 861, 881, 1010, 1018, 1022, 0, + 0, 0, 0, 1453, 1437, 376, 1440, 1441, 1443, 1445, + 1446, 1448, 1449, 1060, 1061, 1065, 0, 1144, 0, 1146, + 0, 1460, 0, 1490, 0, 0, 0, 1178, 0, 0, + 0, 1189, 1188, 1190, 0, 1192, 1193, 1198, 1199, 1202, + 1204, 1211, 1213, 1217, 1219, 1222, 1224, 1226, 0, 1229, + 0, 1232, 0, 1235, 0, 1238, 0, 1241, 0, 1244, + 0, 1247, 0, 1250, 0, 1253, 0, 1256, 0, 1259, + 0, 1262, 0, 1265, 0, 1268, 0, 1271, 0, 1276, + 1278, 0, 1281, 1284, 1286, 0, 1289, 0, 1293, 0, + 1295, 1297, 1298, 0, 0, 0, 1309, 1310, 1311, 1312, + 1313, 1314, 1315, 1316, 1317, 1318, 1325, 0, 1093, 1096, + 1327, 1103, 1104, 1109, 1330, 0, 0, 0, 1333, 0, + 0, 0, 1337, 1139, 1348, 0, 1353, 0, 0, 1359, + 0, 1363, 0, 1369, 1370, 1372, 1374, 0, 0, 0, + 0, 0, 0, 0, 967, 948, 66, 1472, 1474, 0, + 1545, 1543, 1543, 1553, 1554, 0, 0, 1561, 0, 0, + 0, 0, 86, 0, 0, 0, 1581, 0, 0, 0, + 0, 103, 1481, 955, 962, 0, 0, 956, 0, 957, + 977, 979, 934, 0, 1001, 1001, 92, 93, 0, 192, + 0, 194, 0, 197, 199, 200, 201, 207, 208, 209, + 202, 0, 0, 305, 307, 0, 0, 350, 361, 351, + 0, 0, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, + 941, 151, 152, 153, 610, 0, 620, 0, 943, 0, + 613, 0, 530, 0, 0, 0, 403, 403, 403, 0, + 0, 0, 0, 688, 0, 0, 651, 0, 659, 0, + 0, 0, 232, 233, 0, 1524, 584, 0, 136, 137, + 0, 0, 589, 523, 524, 1058, 0, 0, 0, 1059, + 1438, 0, 0, 0, 0, 0, 1457, 0, 0, 0, + 0, 1185, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1301, 0, 0, 0, 640, 641, + 0, 1377, 1098, 1481, 0, 1100, 1110, 1111, 0, 1100, + 1347, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 998, 0, 0, 0, 949, 950, 0, 0, + 0, 987, 1474, 1479, 0, 0, 1548, 0, 1541, 1544, + 1542, 1555, 0, 0, 1562, 0, 1564, 0, 1586, 1587, + 1579, 0, 1571, 1574, 1570, 1573, 1490, 959, 0, 964, + 0, 1481, 91, 0, 195, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 206, 0, 0, 365, 370, + 0, 0, 0, 611, 0, 944, 623, 614, 0, 701, + 0, 705, 0, 0, 0, 708, 709, 710, 687, 0, + 691, 431, 675, 672, 673, 542, 0, 139, 140, 0, + 0, 0, 1427, 0, 1430, 1142, 1145, 1143, 0, 1174, + 1176, 1177, 1435, 1436, 1191, 1227, 1230, 1233, 1236, 1239, + 1242, 1245, 1248, 1251, 1254, 1257, 1260, 1263, 1266, 1269, + 1272, 1280, 1287, 1290, 1294, 1299, 0, 1302, 0, 0, + 1303, 0, 642, 1089, 0, 0, 1107, 1108, 0, 1332, + 1334, 1335, 1336, 1349, 0, 1354, 1355, 0, 1360, 0, + 1364, 1375, 0, 992, 999, 1000, 0, 995, 0, 996, + 0, 940, 1479, 84, 1480, 1477, 0, 1475, 1473, 1537, + 0, 1546, 1547, 1556, 1557, 1563, 0, 1569, 0, 89, + 0, 0, 0, 1490, 193, 0, 212, 0, 619, 0, + 622, 612, 699, 700, 0, 712, 704, 706, 707, 689, + -2, 1516, 0, 0, 0, 592, 1428, 0, 0, 1304, + 0, 638, 639, 1097, 1090, 0, 1075, 1076, 1094, 1329, + 1331, 0, 0, 0, 991, 951, 952, 993, 994, 83, + 0, 1476, 1115, 0, 1549, 1550, 1577, 1575, 960, 967, + 0, 90, 444, 437, 1516, 0, 0, 0, 692, 693, + 694, 695, 696, 697, 698, 581, 1518, 141, 142, 0, + 511, 512, 513, 135, 0, 1149, 1300, 1091, 0, 0, + 0, 0, 0, 1350, 0, 1356, 0, 1361, 0, 1478, + 0, 0, 624, 0, 626, 0, -2, 432, 445, 0, + 187, 213, 214, 0, 0, 217, 218, 219, 210, 211, + 131, 0, 0, 713, 0, 1519, 1520, 0, 138, 0, + 0, 1082, 1083, 1084, 1085, 1087, 0, 0, 0, 0, + 1116, 1095, 625, 0, 0, 387, 0, 635, 433, 434, + 0, 440, 441, 442, 443, 215, 216, 647, 0, 0, + 510, 588, 1429, 0, 0, 1351, 0, 1357, 0, 1362, + 0, 627, 628, 636, 0, 435, 0, 436, 0, 0, + 0, 616, 0, 647, 1517, 1092, 1086, 1088, 0, 0, + 1114, 0, 637, 633, 446, 448, 449, 0, 0, 447, + 648, 617, 1352, 1358, 0, 450, 451, 452, 629, 630, + 631, 632, } var yyTok1 = [...]int{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 145, 3, 3, 3, 173, 165, 3, - 87, 89, 170, 168, 88, 169, 223, 171, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 732, - 153, 152, 154, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 146, 3, 3, 3, 174, 166, 3, + 88, 90, 171, 169, 89, 170, 224, 172, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 735, + 154, 153, 155, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 175, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 176, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 141, 3, 176, + 3, 3, 3, 3, 142, 3, 177, } var yyTok2 = [...]int{ @@ -9616,19 +9755,19 @@ var yyTok2 = [...]int{ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 90, 91, 92, 93, 94, + 82, 83, 84, 85, 86, 87, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, - 147, 148, 149, 150, 151, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 166, 167, 172, 174, 177, + 135, 136, 137, 138, 139, 140, 141, 143, 144, 145, + 147, 148, 149, 150, 151, 152, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 167, 168, 173, 175, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, - 218, 219, 220, 221, 222, 224, 225, 226, 227, 228, + 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, @@ -9728,7 +9867,8 @@ var yyTok3 = [...]int{ 58040, 715, 58041, 716, 58042, 717, 58043, 718, 58044, 719, 58045, 720, 58046, 721, 58047, 722, 58048, 723, 58049, 724, 58050, 725, 58051, 726, 58052, 727, 58053, 728, 58054, 729, - 58055, 730, 58056, 731, 0, + 58055, 730, 58056, 731, 58057, 732, 58058, 733, 58059, 734, + 0, } var yyErrorMessages = [...]struct { @@ -10078,7 +10218,7 @@ yydefault: case 1: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:599 +//line sql.y:612 { stmt := yyDollar[2].statementUnion() // If the statement is empty and we have comments @@ -10092,58 +10232,58 @@ yydefault: } case 2: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:612 +//line sql.y:625 { } case 3: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:613 +//line sql.y:626 { } case 4: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:617 +//line sql.y:630 { yyLOCAL = yyDollar[1].selStmtUnion() } yyVAL.union = yyLOCAL case 40: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:656 +//line sql.y:669 { setParseTree(yylex, nil) } case 41: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Variable -//line sql.y:662 +//line sql.y:675 { yyLOCAL = NewVariableExpression(yyDollar[1].str, SingleAt) } yyVAL.union = yyLOCAL case 42: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:668 +//line sql.y:681 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } case 43: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:673 +//line sql.y:686 { yyVAL.identifierCI = NewIdentifierCI("") } case 44: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:677 +//line sql.y:690 { yyVAL.identifierCI = yyDollar[1].identifierCI } case 45: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Variable -//line sql.y:683 +//line sql.y:696 { yyLOCAL = NewVariableExpression(string(yyDollar[1].str), SingleAt) } @@ -10151,7 +10291,7 @@ yydefault: case 46: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Variable -//line sql.y:687 +//line sql.y:700 { yyLOCAL = NewVariableExpression(string(yyDollar[1].str), DoubleAt) } @@ -10159,7 +10299,7 @@ yydefault: case 47: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:693 +//line sql.y:706 { yyLOCAL = &OtherAdmin{} } @@ -10167,7 +10307,7 @@ yydefault: case 48: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:699 +//line sql.y:712 { yyLOCAL = &Load{} } @@ -10175,7 +10315,7 @@ yydefault: case 49: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *With -//line sql.y:705 +//line sql.y:718 { yyLOCAL = &With{CTEs: yyDollar[2].ctesUnion(), Recursive: false} } @@ -10183,7 +10323,7 @@ yydefault: case 50: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *With -//line sql.y:709 +//line sql.y:722 { yyLOCAL = &With{CTEs: yyDollar[3].ctesUnion(), Recursive: true} } @@ -10191,7 +10331,7 @@ yydefault: case 51: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *With -//line sql.y:714 +//line sql.y:727 { yyLOCAL = nil } @@ -10199,14 +10339,14 @@ yydefault: case 52: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *With -//line sql.y:718 +//line sql.y:731 { yyLOCAL = yyDollar[1].withUnion() } yyVAL.union = yyLOCAL case 53: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:724 +//line sql.y:737 { yySLICE := (*[]*CommonTableExpr)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].cteUnion()) @@ -10214,7 +10354,7 @@ yydefault: case 54: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*CommonTableExpr -//line sql.y:728 +//line sql.y:741 { yyLOCAL = []*CommonTableExpr{yyDollar[1].cteUnion()} } @@ -10222,7 +10362,7 @@ yydefault: case 55: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *CommonTableExpr -//line sql.y:734 +//line sql.y:747 { yyLOCAL = &CommonTableExpr{ID: yyDollar[1].identifierCS, Columns: yyDollar[2].columnsUnion(), Subquery: yyDollar[4].subqueryUnion()} } @@ -10230,7 +10370,7 @@ yydefault: case 56: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:740 +//line sql.y:753 { yyLOCAL = yyDollar[2].selStmtUnion() } @@ -10238,7 +10378,7 @@ yydefault: case 57: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:744 +//line sql.y:757 { yyLOCAL = yyDollar[2].selStmtUnion() } @@ -10246,7 +10386,7 @@ yydefault: case 58: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:748 +//line sql.y:761 { setLockInSelect(yyDollar[2].selStmtUnion(), yyDollar[3].lockUnion()) yyLOCAL = yyDollar[2].selStmtUnion() @@ -10255,7 +10395,7 @@ yydefault: case 59: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:771 +//line sql.y:784 { yyDollar[1].selStmtUnion().SetOrderBy(yyDollar[2].orderByUnion()) yyDollar[1].selStmtUnion().SetLimit(yyDollar[3].limitUnion()) @@ -10265,7 +10405,7 @@ yydefault: case 60: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:777 +//line sql.y:790 { yyDollar[1].selStmtUnion().SetLimit(yyDollar[2].limitUnion()) yyLOCAL = yyDollar[1].selStmtUnion() @@ -10274,7 +10414,7 @@ yydefault: case 61: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:782 +//line sql.y:795 { yyDollar[1].selStmtUnion().SetOrderBy(yyDollar[2].orderByUnion()) yyDollar[1].selStmtUnion().SetLimit(yyDollar[3].limitUnion()) @@ -10284,7 +10424,7 @@ yydefault: case 62: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:788 +//line sql.y:801 { yyDollar[2].selStmtUnion().SetWith(yyDollar[1].withUnion()) yyDollar[2].selStmtUnion().SetOrderBy(yyDollar[3].orderByUnion()) @@ -10295,7 +10435,7 @@ yydefault: case 63: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:795 +//line sql.y:808 { yyDollar[2].selStmtUnion().SetWith(yyDollar[1].withUnion()) yyDollar[2].selStmtUnion().SetLimit(yyDollar[3].limitUnion()) @@ -10305,7 +10445,7 @@ yydefault: case 64: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:801 +//line sql.y:814 { yyDollar[2].selStmtUnion().SetWith(yyDollar[1].withUnion()) yyDollar[2].selStmtUnion().SetOrderBy(yyDollar[3].orderByUnion()) @@ -10315,14 +10455,14 @@ yydefault: yyVAL.union = yyLOCAL case 65: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:808 +//line sql.y:821 { yyDollar[2].selStmtUnion().SetWith(yyDollar[1].withUnion()) } case 66: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:812 +//line sql.y:825 { yyLOCAL = NewSelect(Comments(yyDollar[2].strs), SelectExprs{&Nextval{Expr: yyDollar[5].exprUnion()}}, []string{yyDollar[3].str} /*options*/, nil, TableExprs{&AliasedTableExpr{Expr: yyDollar[7].tableName}}, nil /*where*/, nil /*groupBy*/, nil /*having*/, nil) } @@ -10330,7 +10470,7 @@ yydefault: case 67: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:818 +//line sql.y:831 { yyLOCAL = yyDollar[1].selStmtUnion() } @@ -10338,7 +10478,7 @@ yydefault: case 68: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:822 +//line sql.y:835 { yyLOCAL = &Union{Left: yyDollar[1].selStmtUnion(), Distinct: yyDollar[2].booleanUnion(), Right: yyDollar[3].selStmtUnion()} } @@ -10346,7 +10486,7 @@ yydefault: case 69: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:826 +//line sql.y:839 { yyLOCAL = &Union{Left: yyDollar[1].selStmtUnion(), Distinct: yyDollar[2].booleanUnion(), Right: yyDollar[3].selStmtUnion()} } @@ -10354,7 +10494,7 @@ yydefault: case 70: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:830 +//line sql.y:843 { yyLOCAL = &Union{Left: yyDollar[1].selStmtUnion(), Distinct: yyDollar[2].booleanUnion(), Right: yyDollar[3].selStmtUnion()} } @@ -10362,7 +10502,7 @@ yydefault: case 71: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:834 +//line sql.y:847 { yyLOCAL = &Union{Left: yyDollar[1].selStmtUnion(), Distinct: yyDollar[2].booleanUnion(), Right: yyDollar[3].selStmtUnion()} } @@ -10370,7 +10510,7 @@ yydefault: case 72: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:840 +//line sql.y:853 { yyLOCAL = yyDollar[1].selStmtUnion() } @@ -10378,7 +10518,7 @@ yydefault: case 73: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:844 +//line sql.y:857 { setLockInSelect(yyDollar[1].selStmtUnion(), yyDollar[2].lockUnion()) yyLOCAL = yyDollar[1].selStmtUnion() @@ -10387,7 +10527,7 @@ yydefault: case 74: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:849 +//line sql.y:862 { yyLOCAL = yyDollar[1].selStmtUnion() } @@ -10395,7 +10535,7 @@ yydefault: case 75: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:853 +//line sql.y:866 { yyLOCAL = yyDollar[1].selStmtUnion() } @@ -10403,7 +10543,7 @@ yydefault: case 76: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:859 +//line sql.y:872 { yyLOCAL = yyDollar[2].selStmtUnion() } @@ -10411,7 +10551,7 @@ yydefault: case 77: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:863 +//line sql.y:876 { yyDollar[1].selStmtUnion().SetInto(yyDollar[2].selectIntoUnion()) yyLOCAL = yyDollar[1].selStmtUnion() @@ -10420,7 +10560,7 @@ yydefault: case 78: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:868 +//line sql.y:881 { yyDollar[1].selStmtUnion().SetInto(yyDollar[2].selectIntoUnion()) yyDollar[1].selStmtUnion().SetLock(yyDollar[3].lockUnion()) @@ -10430,7 +10570,7 @@ yydefault: case 79: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:874 +//line sql.y:887 { yyDollar[1].selStmtUnion().SetInto(yyDollar[3].selectIntoUnion()) yyDollar[1].selStmtUnion().SetLock(yyDollar[2].lockUnion()) @@ -10440,7 +10580,7 @@ yydefault: case 80: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:880 +//line sql.y:893 { yyDollar[1].selStmtUnion().SetInto(yyDollar[2].selectIntoUnion()) yyLOCAL = yyDollar[1].selStmtUnion() @@ -10449,7 +10589,7 @@ yydefault: case 81: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:887 +//line sql.y:900 { yyLOCAL = &Stream{Comments: Comments(yyDollar[2].strs).Parsed(), SelectExpr: yyDollar[3].selectExprUnion(), Table: yyDollar[5].tableName} } @@ -10457,7 +10597,7 @@ yydefault: case 82: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:893 +//line sql.y:906 { yyLOCAL = &VStream{Comments: Comments(yyDollar[2].strs).Parsed(), SelectExpr: yyDollar[3].selectExprUnion(), Table: yyDollar[5].tableName, Where: NewWhere(WhereClause, yyDollar[6].exprUnion()), Limit: yyDollar[7].limitUnion()} } @@ -10465,7 +10605,7 @@ yydefault: case 83: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:901 +//line sql.y:914 { yyLOCAL = NewSelect(Comments(yyDollar[2].strs), yyDollar[4].selectExprsUnion() /*SelectExprs*/, yyDollar[3].strs /*options*/, yyDollar[5].selectIntoUnion() /*into*/, yyDollar[6].tableExprsUnion() /*from*/, NewWhere(WhereClause, yyDollar[7].exprUnion()), GroupBy(yyDollar[8].exprsUnion()), NewWhere(HavingClause, yyDollar[9].exprUnion()), yyDollar[10].namedWindowsUnion()) } @@ -10473,7 +10613,7 @@ yydefault: case 84: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL SelectStatement -//line sql.y:905 +//line sql.y:918 { yyLOCAL = NewSelect(Comments(yyDollar[2].strs), yyDollar[4].selectExprsUnion() /*SelectExprs*/, yyDollar[3].strs /*options*/, nil, yyDollar[5].tableExprsUnion() /*from*/, NewWhere(WhereClause, yyDollar[6].exprUnion()), GroupBy(yyDollar[7].exprsUnion()), NewWhere(HavingClause, yyDollar[8].exprUnion()), yyDollar[9].namedWindowsUnion()) } @@ -10481,7 +10621,7 @@ yydefault: case 85: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:911 +//line sql.y:924 { // insert_data returns a *Insert pre-filled with Columns & Values ins := yyDollar[6].insUnion() @@ -10497,7 +10637,7 @@ yydefault: case 86: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Statement -//line sql.y:923 +//line sql.y:936 { cols := make(Columns, 0, len(yyDollar[7].updateExprsUnion())) vals := make(ValTuple, 0, len(yyDollar[8].updateExprsUnion())) @@ -10511,7 +10651,7 @@ yydefault: case 87: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL InsertAction -//line sql.y:935 +//line sql.y:948 { yyLOCAL = InsertAct } @@ -10519,7 +10659,7 @@ yydefault: case 88: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL InsertAction -//line sql.y:939 +//line sql.y:952 { yyLOCAL = ReplaceAct } @@ -10527,7 +10667,7 @@ yydefault: case 89: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Statement -//line sql.y:945 +//line sql.y:958 { yyLOCAL = &Update{With: yyDollar[1].withUnion(), Comments: Comments(yyDollar[3].strs).Parsed(), Ignore: yyDollar[4].ignoreUnion(), TableExprs: yyDollar[5].tableExprsUnion(), Exprs: yyDollar[7].updateExprsUnion(), Where: NewWhere(WhereClause, yyDollar[8].exprUnion()), OrderBy: yyDollar[9].orderByUnion(), Limit: yyDollar[10].limitUnion()} } @@ -10535,7 +10675,7 @@ yydefault: case 90: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL Statement -//line sql.y:951 +//line sql.y:964 { yyLOCAL = &Delete{With: yyDollar[1].withUnion(), Comments: Comments(yyDollar[3].strs).Parsed(), Ignore: yyDollar[4].ignoreUnion(), TableExprs: TableExprs{&AliasedTableExpr{Expr: yyDollar[6].tableName, As: yyDollar[7].identifierCS}}, Partitions: yyDollar[8].partitionsUnion(), Where: NewWhere(WhereClause, yyDollar[9].exprUnion()), OrderBy: yyDollar[10].orderByUnion(), Limit: yyDollar[11].limitUnion()} } @@ -10543,7 +10683,7 @@ yydefault: case 91: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Statement -//line sql.y:955 +//line sql.y:968 { yyLOCAL = &Delete{With: yyDollar[1].withUnion(), Comments: Comments(yyDollar[3].strs).Parsed(), Ignore: yyDollar[4].ignoreUnion(), Targets: yyDollar[6].tableNamesUnion(), TableExprs: yyDollar[8].tableExprsUnion(), Where: NewWhere(WhereClause, yyDollar[9].exprUnion())} } @@ -10551,7 +10691,7 @@ yydefault: case 92: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Statement -//line sql.y:959 +//line sql.y:972 { yyLOCAL = &Delete{With: yyDollar[1].withUnion(), Comments: Comments(yyDollar[3].strs).Parsed(), Ignore: yyDollar[4].ignoreUnion(), Targets: yyDollar[5].tableNamesUnion(), TableExprs: yyDollar[7].tableExprsUnion(), Where: NewWhere(WhereClause, yyDollar[8].exprUnion())} } @@ -10559,32 +10699,32 @@ yydefault: case 93: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Statement -//line sql.y:963 +//line sql.y:976 { yyLOCAL = &Delete{With: yyDollar[1].withUnion(), Comments: Comments(yyDollar[3].strs).Parsed(), Ignore: yyDollar[4].ignoreUnion(), Targets: yyDollar[5].tableNamesUnion(), TableExprs: yyDollar[7].tableExprsUnion(), Where: NewWhere(WhereClause, yyDollar[8].exprUnion())} } yyVAL.union = yyLOCAL case 94: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:968 +//line sql.y:981 { } case 95: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:969 +//line sql.y:982 { } case 96: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableNames -//line sql.y:973 +//line sql.y:986 { yyLOCAL = TableNames{yyDollar[1].tableName} } yyVAL.union = yyLOCAL case 97: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:977 +//line sql.y:990 { yySLICE := (*TableNames)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableName) @@ -10592,14 +10732,14 @@ yydefault: case 98: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableNames -//line sql.y:983 +//line sql.y:996 { yyLOCAL = TableNames{yyDollar[1].tableName} } yyVAL.union = yyLOCAL case 99: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:987 +//line sql.y:1000 { yySLICE := (*TableNames)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableName) @@ -10607,14 +10747,14 @@ yydefault: case 100: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableNames -//line sql.y:993 +//line sql.y:1006 { yyLOCAL = TableNames{yyDollar[1].tableName} } yyVAL.union = yyLOCAL case 101: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:997 +//line sql.y:1010 { yySLICE := (*TableNames)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableName) @@ -10622,7 +10762,7 @@ yydefault: case 102: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Partitions -//line sql.y:1002 +//line sql.y:1015 { yyLOCAL = nil } @@ -10630,7 +10770,7 @@ yydefault: case 103: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Partitions -//line sql.y:1006 +//line sql.y:1019 { yyLOCAL = yyDollar[3].partitionsUnion() } @@ -10638,7 +10778,7 @@ yydefault: case 104: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:1012 +//line sql.y:1025 { yyLOCAL = NewSetStatement(Comments(yyDollar[2].strs).Parsed(), yyDollar[3].setExprsUnion()) } @@ -10646,14 +10786,14 @@ yydefault: case 105: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SetExprs -//line sql.y:1018 +//line sql.y:1031 { yyLOCAL = SetExprs{yyDollar[1].setExprUnion()} } yyVAL.union = yyLOCAL case 106: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1022 +//line sql.y:1035 { yySLICE := (*SetExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].setExprUnion()) @@ -10661,7 +10801,7 @@ yydefault: case 107: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1028 +//line sql.y:1041 { yyLOCAL = &SetExpr{Var: yyDollar[1].variableUnion(), Expr: NewStrLiteral("on")} } @@ -10669,7 +10809,7 @@ yydefault: case 108: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1032 +//line sql.y:1045 { yyLOCAL = &SetExpr{Var: yyDollar[1].variableUnion(), Expr: NewStrLiteral("off")} } @@ -10677,7 +10817,7 @@ yydefault: case 109: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1036 +//line sql.y:1049 { yyLOCAL = &SetExpr{Var: yyDollar[1].variableUnion(), Expr: yyDollar[3].exprUnion()} } @@ -10685,7 +10825,7 @@ yydefault: case 110: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1040 +//line sql.y:1053 { yyLOCAL = &SetExpr{Var: NewSetVariable(string(yyDollar[1].str), SessionScope), Expr: yyDollar[2].exprUnion()} } @@ -10693,7 +10833,7 @@ yydefault: case 111: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Variable -//line sql.y:1046 +//line sql.y:1059 { yyLOCAL = NewSetVariable(string(yyDollar[1].str), SessionScope) } @@ -10701,7 +10841,7 @@ yydefault: case 112: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Variable -//line sql.y:1050 +//line sql.y:1063 { yyLOCAL = yyDollar[1].variableUnion() } @@ -10709,7 +10849,7 @@ yydefault: case 113: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Variable -//line sql.y:1054 +//line sql.y:1067 { yyLOCAL = NewSetVariable(string(yyDollar[2].str), yyDollar[1].scopeUnion()) } @@ -10717,7 +10857,7 @@ yydefault: case 114: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:1060 +//line sql.y:1073 { yyLOCAL = NewSetStatement(Comments(yyDollar[2].strs).Parsed(), UpdateSetExprsScope(yyDollar[5].setExprsUnion(), yyDollar[3].scopeUnion())) } @@ -10725,7 +10865,7 @@ yydefault: case 115: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:1064 +//line sql.y:1077 { yyLOCAL = NewSetStatement(Comments(yyDollar[2].strs).Parsed(), yyDollar[4].setExprsUnion()) } @@ -10733,14 +10873,14 @@ yydefault: case 116: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SetExprs -//line sql.y:1070 +//line sql.y:1083 { yyLOCAL = SetExprs{yyDollar[1].setExprUnion()} } yyVAL.union = yyLOCAL case 117: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1074 +//line sql.y:1087 { yySLICE := (*SetExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].setExprUnion()) @@ -10748,7 +10888,7 @@ yydefault: case 118: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1080 +//line sql.y:1093 { yyLOCAL = &SetExpr{Var: NewSetVariable(TransactionIsolationStr, NextTxScope), Expr: NewStrLiteral(yyDollar[3].str)} } @@ -10756,7 +10896,7 @@ yydefault: case 119: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1084 +//line sql.y:1097 { yyLOCAL = &SetExpr{Var: NewSetVariable(TransactionReadOnlyStr, NextTxScope), Expr: NewStrLiteral("off")} } @@ -10764,39 +10904,39 @@ yydefault: case 120: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SetExpr -//line sql.y:1088 +//line sql.y:1101 { yyLOCAL = &SetExpr{Var: NewSetVariable(TransactionReadOnlyStr, NextTxScope), Expr: NewStrLiteral("on")} } yyVAL.union = yyLOCAL case 121: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1094 +//line sql.y:1107 { yyVAL.str = RepeatableReadStr } case 122: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1098 +//line sql.y:1111 { yyVAL.str = ReadCommittedStr } case 123: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1102 +//line sql.y:1115 { yyVAL.str = ReadUncommittedStr } case 124: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1106 +//line sql.y:1119 { yyVAL.str = SerializableStr } case 125: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Scope -//line sql.y:1112 +//line sql.y:1125 { yyLOCAL = SessionScope } @@ -10804,7 +10944,7 @@ yydefault: case 126: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Scope -//line sql.y:1116 +//line sql.y:1129 { yyLOCAL = SessionScope } @@ -10812,7 +10952,7 @@ yydefault: case 127: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Scope -//line sql.y:1120 +//line sql.y:1133 { yyLOCAL = GlobalScope } @@ -10820,7 +10960,7 @@ yydefault: case 128: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:1126 +//line sql.y:1139 { yyDollar[1].createTableUnion().TableSpec = yyDollar[2].tableSpecUnion() yyDollar[1].createTableUnion().FullyParsed = true @@ -10830,7 +10970,7 @@ yydefault: case 129: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:1132 +//line sql.y:1145 { // Create table [name] like [name] yyDollar[1].createTableUnion().OptLike = yyDollar[2].optLikeUnion() @@ -10841,7 +10981,7 @@ yydefault: case 130: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:1139 +//line sql.y:1152 { indexDef := yyDollar[1].alterTableUnion().AlterOptions[0].(*AddIndexDefinition).IndexDefinition indexDef.Columns = yyDollar[3].indexColumnsUnion() @@ -10854,7 +10994,7 @@ yydefault: case 131: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Statement -//line sql.y:1148 +//line sql.y:1161 { yyLOCAL = &CreateView{ViewName: yyDollar[8].tableName, Comments: Comments(yyDollar[2].strs).Parsed(), IsReplace: yyDollar[3].booleanUnion(), Algorithm: yyDollar[4].str, Definer: yyDollar[5].definerUnion(), Security: yyDollar[6].str, Columns: yyDollar[9].columnsUnion(), Select: yyDollar[11].selStmtUnion(), CheckOption: yyDollar[12].str} } @@ -10862,7 +11002,7 @@ yydefault: case 132: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:1152 +//line sql.y:1165 { yyDollar[1].createDatabaseUnion().FullyParsed = true yyDollar[1].createDatabaseUnion().CreateOptions = yyDollar[2].databaseOptionsUnion() @@ -10872,7 +11012,7 @@ yydefault: case 133: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:1159 +//line sql.y:1172 { yyLOCAL = false } @@ -10880,33 +11020,33 @@ yydefault: case 134: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:1163 +//line sql.y:1176 { yyLOCAL = true } yyVAL.union = yyLOCAL case 135: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1168 +//line sql.y:1181 { yyVAL.identifierCI = NewIdentifierCI("") } case 136: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1172 +//line sql.y:1185 { yyVAL.identifierCI = yyDollar[2].identifierCI } case 137: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1178 +//line sql.y:1191 { yyVAL.identifierCI = yyDollar[1].identifierCI } case 138: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []VindexParam -//line sql.y:1183 +//line sql.y:1196 { var v []VindexParam yyLOCAL = v @@ -10915,7 +11055,7 @@ yydefault: case 139: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []VindexParam -//line sql.y:1188 +//line sql.y:1201 { yyLOCAL = yyDollar[2].vindexParamsUnion() } @@ -10923,7 +11063,7 @@ yydefault: case 140: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []VindexParam -//line sql.y:1194 +//line sql.y:1207 { yyLOCAL = make([]VindexParam, 0, 4) yyLOCAL = append(yyLOCAL, yyDollar[1].vindexParam) @@ -10931,21 +11071,21 @@ yydefault: yyVAL.union = yyLOCAL case 141: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1199 +//line sql.y:1212 { yySLICE := (*[]VindexParam)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].vindexParam) } case 142: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1205 +//line sql.y:1218 { yyVAL.vindexParam = VindexParam{Key: yyDollar[1].identifierCI, Val: yyDollar[3].str} } case 143: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*JSONObjectParam -//line sql.y:1210 +//line sql.y:1223 { yyLOCAL = nil } @@ -10953,7 +11093,7 @@ yydefault: case 144: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*JSONObjectParam -//line sql.y:1214 +//line sql.y:1227 { yyLOCAL = yyDollar[1].jsonObjectParamsUnion() } @@ -10961,28 +11101,28 @@ yydefault: case 145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*JSONObjectParam -//line sql.y:1220 +//line sql.y:1233 { yyLOCAL = []*JSONObjectParam{yyDollar[1].jsonObjectParam} } yyVAL.union = yyLOCAL case 146: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1224 +//line sql.y:1237 { yySLICE := (*[]*JSONObjectParam)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].jsonObjectParam) } case 147: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1230 +//line sql.y:1243 { yyVAL.jsonObjectParam = &JSONObjectParam{Key: yyDollar[1].exprUnion(), Value: yyDollar[3].exprUnion()} } case 148: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *CreateTable -//line sql.y:1236 +//line sql.y:1249 { yyLOCAL = &CreateTable{Comments: Comments(yyDollar[2].strs).Parsed(), Table: yyDollar[6].tableName, IfNotExists: yyDollar[5].booleanUnion(), Temp: yyDollar[3].booleanUnion()} setDDL(yylex, yyLOCAL) @@ -10991,7 +11131,7 @@ yydefault: case 149: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *AlterTable -//line sql.y:1243 +//line sql.y:1256 { yyLOCAL = &AlterTable{Comments: Comments(yyDollar[2].strs).Parsed(), Table: yyDollar[4].tableName} setDDL(yylex, yyLOCAL) @@ -11000,7 +11140,7 @@ yydefault: case 150: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *AlterTable -//line sql.y:1250 +//line sql.y:1263 { yyLOCAL = &AlterTable{Table: yyDollar[7].tableName, AlterOptions: []AlterOption{&AddIndexDefinition{IndexDefinition: &IndexDefinition{Info: &IndexInfo{Name: yyDollar[4].identifierCI}, Options: yyDollar[5].indexOptionsUnion()}}}} setDDL(yylex, yyLOCAL) @@ -11009,7 +11149,7 @@ yydefault: case 151: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *AlterTable -//line sql.y:1255 +//line sql.y:1268 { yyLOCAL = &AlterTable{Table: yyDollar[8].tableName, AlterOptions: []AlterOption{&AddIndexDefinition{IndexDefinition: &IndexDefinition{Info: &IndexInfo{Name: yyDollar[5].identifierCI, Type: IndexTypeFullText}, Options: yyDollar[6].indexOptionsUnion()}}}} setDDL(yylex, yyLOCAL) @@ -11018,7 +11158,7 @@ yydefault: case 152: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *AlterTable -//line sql.y:1260 +//line sql.y:1273 { yyLOCAL = &AlterTable{Table: yyDollar[8].tableName, AlterOptions: []AlterOption{&AddIndexDefinition{IndexDefinition: &IndexDefinition{Info: &IndexInfo{Name: yyDollar[5].identifierCI, Type: IndexTypeSpatial}, Options: yyDollar[6].indexOptionsUnion()}}}} setDDL(yylex, yyLOCAL) @@ -11027,7 +11167,7 @@ yydefault: case 153: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *AlterTable -//line sql.y:1265 +//line sql.y:1278 { yyLOCAL = &AlterTable{Table: yyDollar[8].tableName, AlterOptions: []AlterOption{&AddIndexDefinition{IndexDefinition: &IndexDefinition{Info: &IndexInfo{Name: yyDollar[5].identifierCI, Type: IndexTypeUnique}, Options: yyDollar[6].indexOptionsUnion()}}}} setDDL(yylex, yyLOCAL) @@ -11036,7 +11176,7 @@ yydefault: case 154: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *CreateDatabase -//line sql.y:1272 +//line sql.y:1285 { yyLOCAL = &CreateDatabase{Comments: Comments(yyDollar[4].strs).Parsed(), DBName: yyDollar[6].identifierCS, IfNotExists: yyDollar[5].booleanUnion()} setDDL(yylex, yyLOCAL) @@ -11045,7 +11185,7 @@ yydefault: case 155: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *AlterDatabase -//line sql.y:1279 +//line sql.y:1292 { yyLOCAL = &AlterDatabase{} setDDL(yylex, yyLOCAL) @@ -11054,7 +11194,7 @@ yydefault: case 158: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *TableSpec -//line sql.y:1290 +//line sql.y:1303 { yyLOCAL = yyDollar[2].tableSpecUnion() yyLOCAL.Options = yyDollar[4].tableOptionsUnion() @@ -11064,7 +11204,7 @@ yydefault: case 159: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []DatabaseOption -//line sql.y:1297 +//line sql.y:1310 { yyLOCAL = nil } @@ -11072,7 +11212,7 @@ yydefault: case 160: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []DatabaseOption -//line sql.y:1301 +//line sql.y:1314 { yyLOCAL = yyDollar[1].databaseOptionsUnion() } @@ -11080,7 +11220,7 @@ yydefault: case 161: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []DatabaseOption -//line sql.y:1307 +//line sql.y:1320 { yyLOCAL = []DatabaseOption{yyDollar[1].databaseOption} } @@ -11088,7 +11228,7 @@ yydefault: case 162: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []DatabaseOption -//line sql.y:1311 +//line sql.y:1324 { yyLOCAL = []DatabaseOption{yyDollar[1].databaseOption} } @@ -11096,28 +11236,28 @@ yydefault: case 163: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []DatabaseOption -//line sql.y:1315 +//line sql.y:1328 { yyLOCAL = []DatabaseOption{yyDollar[1].databaseOption} } yyVAL.union = yyLOCAL case 164: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1319 +//line sql.y:1332 { yySLICE := (*[]DatabaseOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].databaseOption) } case 165: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1323 +//line sql.y:1336 { yySLICE := (*[]DatabaseOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].databaseOption) } case 166: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1327 +//line sql.y:1340 { yySLICE := (*[]DatabaseOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].databaseOption) @@ -11125,7 +11265,7 @@ yydefault: case 167: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:1333 +//line sql.y:1346 { yyLOCAL = false } @@ -11133,51 +11273,51 @@ yydefault: case 168: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:1337 +//line sql.y:1350 { yyLOCAL = true } yyVAL.union = yyLOCAL case 169: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1343 +//line sql.y:1356 { yyVAL.databaseOption = DatabaseOption{Type: CharacterSetType, Value: string(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 170: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1347 +//line sql.y:1360 { yyVAL.databaseOption = DatabaseOption{Type: CharacterSetType, Value: encodeSQLString(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 171: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1353 +//line sql.y:1366 { yyVAL.databaseOption = DatabaseOption{Type: CollateType, Value: string(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 172: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1357 +//line sql.y:1370 { yyVAL.databaseOption = DatabaseOption{Type: CollateType, Value: encodeSQLString(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 173: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1363 +//line sql.y:1376 { yyVAL.databaseOption = DatabaseOption{Type: EncryptionType, Value: string(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 174: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1367 +//line sql.y:1380 { yyVAL.databaseOption = DatabaseOption{Type: EncryptionType, Value: encodeSQLString(yyDollar[4].str), IsDefault: yyDollar[1].booleanUnion()} } case 175: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *OptLike -//line sql.y:1373 +//line sql.y:1386 { yyLOCAL = &OptLike{LikeTable: yyDollar[2].tableName} } @@ -11185,7 +11325,7 @@ yydefault: case 176: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *OptLike -//line sql.y:1377 +//line sql.y:1390 { yyLOCAL = &OptLike{LikeTable: yyDollar[3].tableName} } @@ -11193,14 +11333,14 @@ yydefault: case 177: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*ColumnDefinition -//line sql.y:1383 +//line sql.y:1396 { yyLOCAL = []*ColumnDefinition{yyDollar[1].columnDefinitionUnion()} } yyVAL.union = yyLOCAL case 178: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1387 +//line sql.y:1400 { yySLICE := (*[]*ColumnDefinition)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].columnDefinitionUnion()) @@ -11208,7 +11348,7 @@ yydefault: case 179: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *TableSpec -//line sql.y:1393 +//line sql.y:1406 { yyLOCAL = &TableSpec{} yyLOCAL.AddColumn(yyDollar[1].columnDefinitionUnion()) @@ -11217,7 +11357,7 @@ yydefault: case 180: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *TableSpec -//line sql.y:1398 +//line sql.y:1411 { yyLOCAL = &TableSpec{} yyLOCAL.AddConstraint(yyDollar[1].constraintDefinitionUnion()) @@ -11225,39 +11365,39 @@ yydefault: yyVAL.union = yyLOCAL case 181: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1403 +//line sql.y:1416 { yyVAL.tableSpecUnion().AddColumn(yyDollar[3].columnDefinitionUnion()) } case 182: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1407 +//line sql.y:1420 { yyVAL.tableSpecUnion().AddColumn(yyDollar[3].columnDefinitionUnion()) yyVAL.tableSpecUnion().AddConstraint(yyDollar[4].constraintDefinitionUnion()) } case 183: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1412 +//line sql.y:1425 { yyVAL.tableSpecUnion().AddIndex(yyDollar[3].indexDefinitionUnion()) } case 184: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1416 +//line sql.y:1429 { yyVAL.tableSpecUnion().AddConstraint(yyDollar[3].constraintDefinitionUnion()) } case 185: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1420 +//line sql.y:1433 { yyVAL.tableSpecUnion().AddConstraint(yyDollar[3].constraintDefinitionUnion()) } case 186: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ColumnDefinition -//line sql.y:1431 +//line sql.y:1444 { yyDollar[2].columnType.Options = yyDollar[4].columnTypeOptionsUnion() if yyDollar[2].columnType.Options.Collate == "" { @@ -11270,7 +11410,7 @@ yydefault: case 187: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *ColumnDefinition -//line sql.y:1440 +//line sql.y:1453 { yyDollar[2].columnType.Options = yyDollar[9].columnTypeOptionsUnion() yyDollar[2].columnType.Options.As = yyDollar[7].exprUnion() @@ -11281,20 +11421,20 @@ yydefault: yyVAL.union = yyLOCAL case 188: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1449 +//line sql.y:1462 { yyVAL.str = "" } case 189: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1453 +//line sql.y:1466 { yyVAL.str = "" } case 190: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1462 +//line sql.y:1475 { yyLOCAL = &ColumnTypeOptions{Null: nil, Default: nil, OnUpdate: nil, Autoincrement: false, KeyOpt: ColKeyNone, Comment: nil, As: nil, Invisible: nil, Format: UnspecifiedFormat, EngineAttribute: nil, SecondaryEngineAttribute: nil} } @@ -11302,27 +11442,25 @@ yydefault: case 191: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1466 +//line sql.y:1479 { - val := true - yyDollar[1].columnTypeOptionsUnion().Null = &val + yyDollar[1].columnTypeOptionsUnion().Null = ptr.Of(true) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 192: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1472 +//line sql.y:1484 { - val := false - yyDollar[1].columnTypeOptionsUnion().Null = &val + yyDollar[1].columnTypeOptionsUnion().Null = ptr.Of(false) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 193: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1478 +//line sql.y:1489 { yyDollar[1].columnTypeOptionsUnion().Default = yyDollar[4].exprUnion() yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11331,7 +11469,7 @@ yydefault: case 194: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1483 +//line sql.y:1494 { yyDollar[1].columnTypeOptionsUnion().Default = yyDollar[3].exprUnion() yyDollar[1].columnTypeOptionsUnion().DefaultLiteral = true @@ -11341,7 +11479,7 @@ yydefault: case 195: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1489 +//line sql.y:1500 { yyDollar[1].columnTypeOptionsUnion().OnUpdate = yyDollar[4].exprUnion() yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11350,7 +11488,7 @@ yydefault: case 196: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1494 +//line sql.y:1505 { yyDollar[1].columnTypeOptionsUnion().Autoincrement = true yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11359,7 +11497,7 @@ yydefault: case 197: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1499 +//line sql.y:1510 { yyDollar[1].columnTypeOptionsUnion().Comment = NewStrLiteral(yyDollar[3].str) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11368,7 +11506,7 @@ yydefault: case 198: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1504 +//line sql.y:1515 { yyDollar[1].columnTypeOptionsUnion().KeyOpt = yyDollar[2].colKeyOptUnion() yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11376,14 +11514,14 @@ yydefault: yyVAL.union = yyLOCAL case 199: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1509 +//line sql.y:1520 { yyDollar[1].columnTypeOptionsUnion().Collate = encodeSQLString(yyDollar[3].str) } case 200: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1513 +//line sql.y:1524 { yyDollar[1].columnTypeOptionsUnion().Collate = string(yyDollar[3].identifierCI.String()) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11391,14 +11529,14 @@ yydefault: yyVAL.union = yyLOCAL case 201: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1518 +//line sql.y:1529 { yyDollar[1].columnTypeOptionsUnion().Format = yyDollar[3].columnFormatUnion() } case 202: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1522 +//line sql.y:1533 { yyDollar[1].columnTypeOptionsUnion().SRID = NewIntLiteral(yyDollar[3].str) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11407,39 +11545,37 @@ yydefault: case 203: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1527 +//line sql.y:1538 { - val := false - yyDollar[1].columnTypeOptionsUnion().Invisible = &val + yyDollar[1].columnTypeOptionsUnion().Invisible = ptr.Of(false) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 204: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1533 +//line sql.y:1543 { - val := true - yyDollar[1].columnTypeOptionsUnion().Invisible = &val + yyDollar[1].columnTypeOptionsUnion().Invisible = ptr.Of(true) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 205: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1539 +//line sql.y:1548 { yyDollar[1].columnTypeOptionsUnion().EngineAttribute = NewStrLiteral(yyDollar[4].str) } case 206: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1543 +//line sql.y:1552 { yyDollar[1].columnTypeOptionsUnion().SecondaryEngineAttribute = NewStrLiteral(yyDollar[4].str) } case 207: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnFormat -//line sql.y:1549 +//line sql.y:1558 { yyLOCAL = FixedFormat } @@ -11447,7 +11583,7 @@ yydefault: case 208: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnFormat -//line sql.y:1553 +//line sql.y:1562 { yyLOCAL = DynamicFormat } @@ -11455,7 +11591,7 @@ yydefault: case 209: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnFormat -//line sql.y:1557 +//line sql.y:1566 { yyLOCAL = DefaultFormat } @@ -11463,7 +11599,7 @@ yydefault: case 210: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnStorage -//line sql.y:1563 +//line sql.y:1572 { yyLOCAL = VirtualStorage } @@ -11471,7 +11607,7 @@ yydefault: case 211: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnStorage -//line sql.y:1567 +//line sql.y:1576 { yyLOCAL = StoredStorage } @@ -11479,7 +11615,7 @@ yydefault: case 212: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1572 +//line sql.y:1581 { yyLOCAL = &ColumnTypeOptions{} } @@ -11487,7 +11623,7 @@ yydefault: case 213: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1576 +//line sql.y:1585 { yyDollar[1].columnTypeOptionsUnion().Storage = yyDollar[2].columnStorageUnion() yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11496,27 +11632,25 @@ yydefault: case 214: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1581 +//line sql.y:1590 { - val := true - yyDollar[1].columnTypeOptionsUnion().Null = &val + yyDollar[1].columnTypeOptionsUnion().Null = ptr.Of(true) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 215: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1587 +//line sql.y:1595 { - val := false - yyDollar[1].columnTypeOptionsUnion().Null = &val + yyDollar[1].columnTypeOptionsUnion().Null = ptr.Of(false) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 216: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1593 +//line sql.y:1600 { yyDollar[1].columnTypeOptionsUnion().Comment = NewStrLiteral(yyDollar[3].str) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11525,7 +11659,7 @@ yydefault: case 217: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1598 +//line sql.y:1605 { yyDollar[1].columnTypeOptionsUnion().KeyOpt = yyDollar[2].colKeyOptUnion() yyLOCAL = yyDollar[1].columnTypeOptionsUnion() @@ -11534,27 +11668,25 @@ yydefault: case 218: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1603 +//line sql.y:1610 { - val := false - yyDollar[1].columnTypeOptionsUnion().Invisible = &val + yyDollar[1].columnTypeOptionsUnion().Invisible = ptr.Of(false) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 219: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColumnTypeOptions -//line sql.y:1609 +//line sql.y:1615 { - val := true - yyDollar[1].columnTypeOptionsUnion().Invisible = &val + yyDollar[1].columnTypeOptionsUnion().Invisible = ptr.Of(true) yyLOCAL = yyDollar[1].columnTypeOptionsUnion() } yyVAL.union = yyLOCAL case 220: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1617 +//line sql.y:1622 { yyLOCAL = yyDollar[1].exprUnion() } @@ -11562,7 +11694,7 @@ yydefault: case 222: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1624 +//line sql.y:1629 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("current_timestamp"), Fsp: yyDollar[2].integerUnion()} } @@ -11570,7 +11702,7 @@ yydefault: case 223: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1628 +//line sql.y:1633 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("localtime"), Fsp: yyDollar[2].integerUnion()} } @@ -11578,7 +11710,7 @@ yydefault: case 224: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1632 +//line sql.y:1637 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("localtimestamp"), Fsp: yyDollar[2].integerUnion()} } @@ -11586,7 +11718,7 @@ yydefault: case 225: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1636 +//line sql.y:1641 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("utc_timestamp"), Fsp: yyDollar[2].integerUnion()} } @@ -11594,7 +11726,7 @@ yydefault: case 226: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1640 +//line sql.y:1645 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("now"), Fsp: yyDollar[2].integerUnion()} } @@ -11602,7 +11734,7 @@ yydefault: case 227: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1644 +//line sql.y:1649 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("sysdate"), Fsp: yyDollar[2].integerUnion()} } @@ -11610,7 +11742,7 @@ yydefault: case 230: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1654 +//line sql.y:1659 { yyLOCAL = &NullVal{} } @@ -11618,7 +11750,7 @@ yydefault: case 232: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1661 +//line sql.y:1666 { yyLOCAL = yyDollar[2].exprUnion() } @@ -11626,7 +11758,7 @@ yydefault: case 233: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1665 +//line sql.y:1670 { yyLOCAL = &UnaryExpr{Operator: UMinusOp, Expr: yyDollar[2].exprUnion()} } @@ -11634,7 +11766,7 @@ yydefault: case 234: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1671 +//line sql.y:1676 { yyLOCAL = yyDollar[1].exprUnion() } @@ -11642,7 +11774,7 @@ yydefault: case 235: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1675 +//line sql.y:1680 { yyLOCAL = yyDollar[1].exprUnion() } @@ -11650,7 +11782,7 @@ yydefault: case 236: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1679 +//line sql.y:1684 { yyLOCAL = yyDollar[1].boolValUnion() } @@ -11658,7 +11790,7 @@ yydefault: case 237: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1683 +//line sql.y:1688 { yyLOCAL = NewHexLiteral(yyDollar[1].str) } @@ -11666,7 +11798,7 @@ yydefault: case 238: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1687 +//line sql.y:1692 { yyLOCAL = NewHexNumLiteral(yyDollar[1].str) } @@ -11674,7 +11806,7 @@ yydefault: case 239: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1691 +//line sql.y:1696 { yyLOCAL = NewBitLiteral(yyDollar[1].str) } @@ -11682,7 +11814,7 @@ yydefault: case 240: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1695 +//line sql.y:1700 { yyLOCAL = NewBitLiteral("0b" + yyDollar[1].str) } @@ -11690,7 +11822,7 @@ yydefault: case 241: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1699 +//line sql.y:1704 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } @@ -11698,7 +11830,7 @@ yydefault: case 242: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1703 +//line sql.y:1708 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: NewBitLiteral("0b" + yyDollar[2].str)} } @@ -11706,7 +11838,7 @@ yydefault: case 243: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1707 +//line sql.y:1712 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: NewHexNumLiteral(yyDollar[2].str)} } @@ -11714,7 +11846,7 @@ yydefault: case 244: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1711 +//line sql.y:1716 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: NewBitLiteral(yyDollar[2].str)} } @@ -11722,7 +11854,7 @@ yydefault: case 245: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1715 +//line sql.y:1720 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: NewHexLiteral(yyDollar[2].str)} } @@ -11730,7 +11862,7 @@ yydefault: case 246: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1719 +//line sql.y:1724 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: yyDollar[2].exprUnion()} } @@ -11738,7 +11870,7 @@ yydefault: case 247: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1723 +//line sql.y:1728 { arg := parseBindVariable(yylex, yyDollar[2].str[1:]) yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: arg} @@ -11747,7 +11879,7 @@ yydefault: case 248: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1728 +//line sql.y:1733 { yyLOCAL = NewDateLiteral(yyDollar[2].str) } @@ -11755,7 +11887,7 @@ yydefault: case 249: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1732 +//line sql.y:1737 { yyLOCAL = NewTimeLiteral(yyDollar[2].str) } @@ -11763,267 +11895,267 @@ yydefault: case 250: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1736 +//line sql.y:1741 { yyLOCAL = NewTimestampLiteral(yyDollar[2].str) } yyVAL.union = yyLOCAL case 251: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1742 +//line sql.y:1747 { yyVAL.str = Armscii8Str } case 252: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1746 +//line sql.y:1751 { yyVAL.str = ASCIIStr } case 253: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1750 +//line sql.y:1755 { yyVAL.str = Big5Str } case 254: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1754 +//line sql.y:1759 { yyVAL.str = UBinaryStr } case 255: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1758 +//line sql.y:1763 { yyVAL.str = Cp1250Str } case 256: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1762 +//line sql.y:1767 { yyVAL.str = Cp1251Str } case 257: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1766 +//line sql.y:1771 { yyVAL.str = Cp1256Str } case 258: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1770 +//line sql.y:1775 { yyVAL.str = Cp1257Str } case 259: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1774 +//line sql.y:1779 { yyVAL.str = Cp850Str } case 260: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1778 +//line sql.y:1783 { yyVAL.str = Cp852Str } case 261: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1782 +//line sql.y:1787 { yyVAL.str = Cp866Str } case 262: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1786 +//line sql.y:1791 { yyVAL.str = Cp932Str } case 263: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1790 +//line sql.y:1795 { yyVAL.str = Dec8Str } case 264: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1794 +//line sql.y:1799 { yyVAL.str = EucjpmsStr } case 265: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1798 +//line sql.y:1803 { yyVAL.str = EuckrStr } case 266: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1802 +//line sql.y:1807 { yyVAL.str = Gb18030Str } case 267: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1806 +//line sql.y:1811 { yyVAL.str = Gb2312Str } case 268: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1810 +//line sql.y:1815 { yyVAL.str = GbkStr } case 269: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1814 +//line sql.y:1819 { yyVAL.str = Geostd8Str } case 270: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1818 +//line sql.y:1823 { yyVAL.str = GreekStr } case 271: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1822 +//line sql.y:1827 { yyVAL.str = HebrewStr } case 272: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1826 +//line sql.y:1831 { yyVAL.str = Hp8Str } case 273: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1830 +//line sql.y:1835 { yyVAL.str = Keybcs2Str } case 274: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1834 +//line sql.y:1839 { yyVAL.str = Koi8rStr } case 275: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1838 +//line sql.y:1843 { yyVAL.str = Koi8uStr } case 276: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1842 +//line sql.y:1847 { yyVAL.str = Latin1Str } case 277: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1846 +//line sql.y:1851 { yyVAL.str = Latin2Str } case 278: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1850 +//line sql.y:1855 { yyVAL.str = Latin5Str } case 279: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1854 +//line sql.y:1859 { yyVAL.str = Latin7Str } case 280: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1858 +//line sql.y:1863 { yyVAL.str = MacceStr } case 281: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1862 +//line sql.y:1867 { yyVAL.str = MacromanStr } case 282: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1866 +//line sql.y:1871 { yyVAL.str = SjisStr } case 283: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1870 +//line sql.y:1875 { yyVAL.str = Swe7Str } case 284: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1874 +//line sql.y:1879 { yyVAL.str = Tis620Str } case 285: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1878 +//line sql.y:1883 { yyVAL.str = Ucs2Str } case 286: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1882 +//line sql.y:1887 { yyVAL.str = UjisStr } case 287: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1886 +//line sql.y:1891 { yyVAL.str = Utf16Str } case 288: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1890 +//line sql.y:1895 { yyVAL.str = Utf16leStr } case 289: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1894 +//line sql.y:1899 { yyVAL.str = Utf32Str } case 290: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1898 +//line sql.y:1903 { yyVAL.str = Utf8mb3Str } case 291: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1902 +//line sql.y:1907 { yyVAL.str = Utf8mb4Str } case 292: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1906 +//line sql.y:1911 { yyVAL.str = Utf8mb3Str } case 295: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1916 +//line sql.y:1921 { yyLOCAL = NewIntLiteral(yyDollar[1].str) } @@ -12031,7 +12163,7 @@ yydefault: case 296: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1920 +//line sql.y:1925 { yyLOCAL = NewFloatLiteral(yyDollar[1].str) } @@ -12039,7 +12171,7 @@ yydefault: case 297: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1924 +//line sql.y:1929 { yyLOCAL = NewDecimalLiteral(yyDollar[1].str) } @@ -12047,169 +12179,169 @@ yydefault: case 298: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1930 +//line sql.y:1935 { - yyLOCAL = NewStrLiteral(yyDollar[1].str) + yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL case 299: + yyDollar = yyS[yypt-2 : yypt+1] + var yyLOCAL Expr +//line sql.y:1939 + { + yyLOCAL = AppendString(yyDollar[1].exprUnion(), yyDollar[2].str) + } + yyVAL.union = yyLOCAL + case 300: + yyDollar = yyS[yypt-1 : yypt+1] + var yyLOCAL Expr +//line sql.y:1945 + { + yyLOCAL = NewStrLiteral(yyDollar[1].str) + } + yyVAL.union = yyLOCAL + case 301: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1934 +//line sql.y:1949 { yyLOCAL = &UnaryExpr{Operator: NStringOp, Expr: NewStrLiteral(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 300: + case 302: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:1938 +//line sql.y:1953 { yyLOCAL = &IntroducerExpr{CharacterSet: yyDollar[1].str, Expr: NewStrLiteral(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 301: + case 303: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1944 +//line sql.y:1959 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 302: + case 304: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:1948 +//line sql.y:1963 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } yyVAL.union = yyLOCAL - case 303: + case 305: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ColumnKeyOption -//line sql.y:1954 +//line sql.y:1969 { yyLOCAL = ColKeyPrimary } yyVAL.union = yyLOCAL - case 304: + case 306: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnKeyOption -//line sql.y:1958 +//line sql.y:1973 { yyLOCAL = ColKeyUnique } yyVAL.union = yyLOCAL - case 305: + case 307: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ColumnKeyOption -//line sql.y:1962 +//line sql.y:1977 { yyLOCAL = ColKeyUniqueKey } yyVAL.union = yyLOCAL - case 306: + case 308: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColumnKeyOption -//line sql.y:1966 +//line sql.y:1981 { yyLOCAL = ColKey } yyVAL.union = yyLOCAL - case 307: + case 309: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1972 +//line sql.y:1987 { yyVAL.columnType = yyDollar[1].columnType yyVAL.columnType.Unsigned = yyDollar[2].booleanUnion() yyVAL.columnType.Zerofill = yyDollar[3].booleanUnion() } - case 311: + case 313: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1983 - { - yyVAL.columnType = yyDollar[1].columnType - yyVAL.columnType.Length = yyDollar[2].literalUnion() - } - case 312: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1988 +//line sql.y:1998 { yyVAL.columnType = yyDollar[1].columnType - } - case 313: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1994 - { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType.Length = yyDollar[2].intPtrUnion() } case 314: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1998 +//line sql.y:2003 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType = yyDollar[1].columnType } case 315: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2002 +//line sql.y:2009 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 316: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2006 +//line sql.y:2013 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 317: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2010 +//line sql.y:2017 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 318: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2014 +//line sql.y:2021 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 319: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2018 +//line sql.y:2025 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 320: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2022 +//line sql.y:2029 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 321: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2026 +//line sql.y:2033 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 322: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2032 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2037 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} - yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 323: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2038 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2041 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} - yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 324: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2044 +//line sql.y:2047 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -12217,7 +12349,7 @@ yydefault: } case 325: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2050 +//line sql.y:2053 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -12225,7 +12357,7 @@ yydefault: } case 326: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2056 +//line sql.y:2059 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -12233,7 +12365,7 @@ yydefault: } case 327: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2062 +//line sql.y:2065 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -12241,1759 +12373,1773 @@ yydefault: } case 328: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2068 +//line sql.y:2071 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 329: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2076 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2077 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 330: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2080 +//line sql.y:2083 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 331: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2084 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2091 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 332: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2088 +//line sql.y:2095 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 333: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2092 +//line sql.y:2099 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 334: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2098 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2103 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion(), Charset: yyDollar[3].columnCharset} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 335: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2102 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2107 { - // CHAR BYTE is an alias for binary. See also: - // https://dev.mysql.com/doc/refman/8.0/en/string-type-syntax.html - yyVAL.columnType = &ColumnType{Type: "binary", Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 336: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2108 +//line sql.y:2113 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion(), Charset: yyDollar[3].columnCharset} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion(), Charset: yyDollar[3].columnCharset} } case 337: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2112 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2117 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + // CHAR BYTE is an alias for binary. See also: + // https://dev.mysql.com/doc/refman/8.0/en/string-type-syntax.html + yyVAL.columnType = &ColumnType{Type: "binary", Length: yyDollar[2].intPtrUnion()} } case 338: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2116 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2123 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion(), Charset: yyDollar[3].columnCharset} } case 339: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2120 +//line sql.y:2127 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 340: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2124 +//line sql.y:2131 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } case 341: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2128 +//line sql.y:2135 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} } case 342: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2132 +//line sql.y:2139 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} } case 343: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2136 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2143 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} } case 344: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2140 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2147 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), Charset: yyDollar[2].columnCharset} } case 345: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2144 +//line sql.y:2151 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 346: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2148 +//line sql.y:2155 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 347: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2152 +//line sql.y:2159 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 348: - yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2156 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2163 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].columnCharset} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 349: - yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2161 - { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].columnCharset} - } - case 350: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2167 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } - case 351: - yyDollar = yyS[yypt-1 : yypt+1] + case 350: + yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2171 { - yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].columnCharset} + } + case 351: + yyDollar = yyS[yypt-5 : yypt+1] +//line sql.y:2176 + { + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].columnCharset} } case 352: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2175 +//line sql.y:2182 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 353: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2179 +//line sql.y:2186 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 354: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2183 +//line sql.y:2190 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 355: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2187 +//line sql.y:2194 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 356: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2191 +//line sql.y:2198 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 357: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2195 +//line sql.y:2202 { yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} } case 358: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2201 +//line sql.y:2206 + { + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + } + case 359: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2210 + { + yyVAL.columnType = &ColumnType{Type: string(yyDollar[1].str)} + } + case 360: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2216 { yyVAL.strs = make([]string, 0, 4) yyVAL.strs = append(yyVAL.strs, encodeSQLString(yyDollar[1].str)) } - case 359: + case 361: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2206 +//line sql.y:2221 { yyVAL.strs = append(yyDollar[1].strs, encodeSQLString(yyDollar[3].str)) } - case 360: + case 362: yyDollar = yyS[yypt-0 : yypt+1] - var yyLOCAL *Literal -//line sql.y:2211 + var yyLOCAL *int +//line sql.y:2226 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 361: + case 363: yyDollar = yyS[yypt-3 : yypt+1] - var yyLOCAL *Literal -//line sql.y:2215 + var yyLOCAL *int +//line sql.y:2230 { - yyLOCAL = NewIntLiteral(yyDollar[2].str) + yyLOCAL = ptr.Of(convertStringToInt(yyDollar[2].str)) } yyVAL.union = yyLOCAL - case 362: + case 364: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2220 +//line sql.y:2235 { yyVAL.LengthScaleOption = LengthScaleOption{} } - case 363: + case 365: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2224 +//line sql.y:2239 { yyVAL.LengthScaleOption = LengthScaleOption{ - Length: NewIntLiteral(yyDollar[2].str), - Scale: NewIntLiteral(yyDollar[4].str), + Length: ptr.Of(convertStringToInt(yyDollar[2].str)), + Scale: ptr.Of(convertStringToInt(yyDollar[4].str)), } } - case 364: + case 366: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2233 +//line sql.y:2248 { yyVAL.LengthScaleOption = yyDollar[1].LengthScaleOption } - case 365: + case 367: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2237 +//line sql.y:2252 { yyVAL.LengthScaleOption = LengthScaleOption{ - Length: NewIntLiteral(yyDollar[2].str), + Length: ptr.Of(convertStringToInt(yyDollar[2].str)), } } - case 366: + case 368: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2244 +//line sql.y:2259 { yyVAL.LengthScaleOption = LengthScaleOption{} } - case 367: + case 369: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2248 +//line sql.y:2263 { yyVAL.LengthScaleOption = LengthScaleOption{ - Length: NewIntLiteral(yyDollar[2].str), + Length: ptr.Of(convertStringToInt(yyDollar[2].str)), } } - case 368: + case 370: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2254 +//line sql.y:2269 { yyVAL.LengthScaleOption = LengthScaleOption{ - Length: NewIntLiteral(yyDollar[2].str), - Scale: NewIntLiteral(yyDollar[4].str), + Length: ptr.Of(convertStringToInt(yyDollar[2].str)), + Scale: ptr.Of(convertStringToInt(yyDollar[4].str)), } } - case 369: + case 371: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:2262 +//line sql.y:2277 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 370: + case 372: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2266 +//line sql.y:2281 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 371: + case 373: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2270 +//line sql.y:2285 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 372: + case 374: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:2275 +//line sql.y:2290 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 373: + case 375: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2279 +//line sql.y:2294 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 374: + case 376: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2284 +//line sql.y:2299 { yyVAL.columnCharset = ColumnCharset{} } - case 375: + case 377: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2288 +//line sql.y:2303 { yyVAL.columnCharset = ColumnCharset{Name: string(yyDollar[2].identifierCI.String()), Binary: yyDollar[3].booleanUnion()} } - case 376: + case 378: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2292 +//line sql.y:2307 { yyVAL.columnCharset = ColumnCharset{Name: encodeSQLString(yyDollar[2].str), Binary: yyDollar[3].booleanUnion()} } - case 377: + case 379: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2296 +//line sql.y:2311 { yyVAL.columnCharset = ColumnCharset{Name: string(yyDollar[2].str)} } - case 378: + case 380: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2300 +//line sql.y:2315 { // ASCII: Shorthand for CHARACTER SET latin1. yyVAL.columnCharset = ColumnCharset{Name: "latin1", Binary: yyDollar[2].booleanUnion()} } - case 379: + case 381: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2305 +//line sql.y:2320 { // UNICODE: Shorthand for CHARACTER SET ucs2. yyVAL.columnCharset = ColumnCharset{Name: "ucs2", Binary: yyDollar[2].booleanUnion()} } - case 380: + case 382: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2310 +//line sql.y:2325 { // BINARY: Shorthand for default CHARACTER SET but with binary collation yyVAL.columnCharset = ColumnCharset{Name: "", Binary: true} } - case 381: + case 383: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2315 +//line sql.y:2330 { // BINARY ASCII: Shorthand for CHARACTER SET latin1 with binary collation yyVAL.columnCharset = ColumnCharset{Name: "latin1", Binary: true} } - case 382: + case 384: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2320 +//line sql.y:2335 { // BINARY UNICODE: Shorthand for CHARACTER SET ucs2 with binary collation yyVAL.columnCharset = ColumnCharset{Name: "ucs2", Binary: true} } - case 383: + case 385: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:2326 +//line sql.y:2341 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 384: + case 386: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2330 +//line sql.y:2345 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 385: + case 387: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2335 +//line sql.y:2350 { yyVAL.str = "" } - case 386: + case 388: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2339 +//line sql.y:2354 { yyVAL.str = string(yyDollar[2].identifierCI.String()) } - case 387: + case 389: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2343 +//line sql.y:2358 { yyVAL.str = encodeSQLString(yyDollar[2].str) } - case 388: + case 390: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *IndexDefinition -//line sql.y:2349 +//line sql.y:2364 { yyLOCAL = &IndexDefinition{Info: yyDollar[1].indexInfoUnion(), Columns: yyDollar[3].indexColumnsUnion(), Options: yyDollar[5].indexOptionsUnion()} } yyVAL.union = yyLOCAL - case 389: + case 391: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:2354 +//line sql.y:2369 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 390: + case 392: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:2358 +//line sql.y:2373 { yyLOCAL = yyDollar[1].indexOptionsUnion() } yyVAL.union = yyLOCAL - case 391: + case 393: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:2364 +//line sql.y:2379 { yyLOCAL = []*IndexOption{yyDollar[1].indexOptionUnion()} } yyVAL.union = yyLOCAL - case 392: + case 394: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2368 +//line sql.y:2383 { yySLICE := (*[]*IndexOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].indexOptionUnion()) } - case 393: + case 395: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2374 +//line sql.y:2389 { yyLOCAL = yyDollar[1].indexOptionUnion() } yyVAL.union = yyLOCAL - case 394: + case 396: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2378 +//line sql.y:2393 { // should not be string yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 395: + case 397: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2383 +//line sql.y:2398 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 396: + case 398: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2387 +//line sql.y:2402 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 397: + case 399: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2391 +//line sql.y:2406 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 398: + case 400: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2395 +//line sql.y:2410 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str) + " " + string(yyDollar[2].str), String: yyDollar[3].identifierCI.String()} } yyVAL.union = yyLOCAL - case 399: + case 401: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2399 +//line sql.y:2414 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 400: + case 402: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:2403 +//line sql.y:2418 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 401: + case 403: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2409 +//line sql.y:2424 { yyVAL.str = "" } - case 402: + case 404: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2413 +//line sql.y:2428 { yyVAL.str = string(yyDollar[1].str) } - case 403: + case 405: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *IndexInfo -//line sql.y:2419 +//line sql.y:2434 { yyLOCAL = &IndexInfo{Type: IndexTypePrimary, ConstraintName: NewIdentifierCI(yyDollar[1].str), Name: NewIdentifierCI("PRIMARY")} } yyVAL.union = yyLOCAL - case 404: + case 406: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexInfo -//line sql.y:2423 +//line sql.y:2438 { yyLOCAL = &IndexInfo{Type: IndexTypeSpatial, Name: NewIdentifierCI(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 405: + case 407: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexInfo -//line sql.y:2427 +//line sql.y:2442 { yyLOCAL = &IndexInfo{Type: IndexTypeFullText, Name: NewIdentifierCI(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 406: + case 408: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *IndexInfo -//line sql.y:2431 +//line sql.y:2446 { yyLOCAL = &IndexInfo{Type: IndexTypeUnique, ConstraintName: NewIdentifierCI(yyDollar[1].str), Name: NewIdentifierCI(yyDollar[4].str)} } yyVAL.union = yyLOCAL - case 407: + case 409: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *IndexInfo -//line sql.y:2435 +//line sql.y:2450 { yyLOCAL = &IndexInfo{Type: IndexTypeDefault, Name: NewIdentifierCI(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 408: + case 410: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2440 +//line sql.y:2455 { yyVAL.str = "" } - case 409: + case 411: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2444 +//line sql.y:2459 { yyVAL.str = yyDollar[2].str } - case 410: + case 412: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2450 +//line sql.y:2465 { yyVAL.str = string(yyDollar[1].str) } - case 411: + case 413: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2454 +//line sql.y:2469 { yyVAL.str = string(yyDollar[1].str) } - case 412: + case 414: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2458 +//line sql.y:2473 { yyVAL.str = string(yyDollar[1].str) } - case 413: + case 415: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2464 +//line sql.y:2479 { yyVAL.str = string(yyDollar[1].str) } - case 414: + case 416: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2468 +//line sql.y:2483 { yyVAL.str = string(yyDollar[1].str) } - case 415: + case 417: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2473 +//line sql.y:2488 { yyVAL.str = "" } - case 416: + case 418: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2477 +//line sql.y:2492 { yyVAL.str = yyDollar[1].str } - case 417: + case 419: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2483 +//line sql.y:2498 { yyVAL.str = string(yyDollar[1].str) } - case 418: + case 420: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2487 +//line sql.y:2502 { yyVAL.str = string(yyDollar[1].str) } - case 419: + case 421: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2492 +//line sql.y:2507 { yyVAL.str = "" } - case 420: + case 422: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2496 +//line sql.y:2511 { yyVAL.str = string(yyDollar[1].identifierCI.String()) } - case 421: + case 423: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*IndexColumn -//line sql.y:2502 +//line sql.y:2517 { yyLOCAL = []*IndexColumn{yyDollar[1].indexColumnUnion()} } yyVAL.union = yyLOCAL - case 422: + case 424: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2506 +//line sql.y:2521 { yySLICE := (*[]*IndexColumn)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].indexColumnUnion()) } - case 423: + case 425: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *IndexColumn -//line sql.y:2512 +//line sql.y:2527 { - yyLOCAL = &IndexColumn{Column: yyDollar[1].identifierCI, Length: yyDollar[2].literalUnion(), Direction: yyDollar[3].orderDirectionUnion()} + yyLOCAL = &IndexColumn{Column: yyDollar[1].identifierCI, Length: yyDollar[2].intPtrUnion(), Direction: yyDollar[3].orderDirectionUnion()} } yyVAL.union = yyLOCAL - case 424: + case 426: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *IndexColumn -//line sql.y:2516 +//line sql.y:2531 { yyLOCAL = &IndexColumn{Expression: yyDollar[2].exprUnion(), Direction: yyDollar[4].orderDirectionUnion()} } yyVAL.union = yyLOCAL - case 425: + case 427: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ConstraintDefinition -//line sql.y:2522 +//line sql.y:2537 { yyLOCAL = &ConstraintDefinition{Name: yyDollar[2].identifierCI, Details: yyDollar[3].constraintInfoUnion()} } yyVAL.union = yyLOCAL - case 426: + case 428: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConstraintDefinition -//line sql.y:2526 +//line sql.y:2541 { yyLOCAL = &ConstraintDefinition{Details: yyDollar[1].constraintInfoUnion()} } yyVAL.union = yyLOCAL - case 427: + case 429: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ConstraintDefinition -//line sql.y:2532 +//line sql.y:2547 { yyLOCAL = &ConstraintDefinition{Name: yyDollar[2].identifierCI, Details: yyDollar[3].constraintInfoUnion()} } yyVAL.union = yyLOCAL - case 428: + case 430: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConstraintDefinition -//line sql.y:2536 +//line sql.y:2551 { yyLOCAL = &ConstraintDefinition{Details: yyDollar[1].constraintInfoUnion()} } yyVAL.union = yyLOCAL - case 429: + case 431: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL ConstraintInfo -//line sql.y:2542 +//line sql.y:2557 { yyLOCAL = &ForeignKeyDefinition{IndexName: NewIdentifierCI(yyDollar[3].str), Source: yyDollar[5].columnsUnion(), ReferenceDefinition: yyDollar[7].referenceDefinitionUnion()} } yyVAL.union = yyLOCAL - case 430: + case 432: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2548 +//line sql.y:2563 { yyLOCAL = &ReferenceDefinition{ReferencedTable: yyDollar[2].tableName, ReferencedColumns: yyDollar[4].columnsUnion(), Match: yyDollar[6].matchActionUnion()} } yyVAL.union = yyLOCAL - case 431: + case 433: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2552 +//line sql.y:2567 { yyLOCAL = &ReferenceDefinition{ReferencedTable: yyDollar[2].tableName, ReferencedColumns: yyDollar[4].columnsUnion(), Match: yyDollar[6].matchActionUnion(), OnDelete: yyDollar[7].referenceActionUnion()} } yyVAL.union = yyLOCAL - case 432: + case 434: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2556 +//line sql.y:2571 { yyLOCAL = &ReferenceDefinition{ReferencedTable: yyDollar[2].tableName, ReferencedColumns: yyDollar[4].columnsUnion(), Match: yyDollar[6].matchActionUnion(), OnUpdate: yyDollar[7].referenceActionUnion()} } yyVAL.union = yyLOCAL - case 433: + case 435: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2560 +//line sql.y:2575 { yyLOCAL = &ReferenceDefinition{ReferencedTable: yyDollar[2].tableName, ReferencedColumns: yyDollar[4].columnsUnion(), Match: yyDollar[6].matchActionUnion(), OnDelete: yyDollar[7].referenceActionUnion(), OnUpdate: yyDollar[8].referenceActionUnion()} } yyVAL.union = yyLOCAL - case 434: + case 436: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2564 +//line sql.y:2579 { yyLOCAL = &ReferenceDefinition{ReferencedTable: yyDollar[2].tableName, ReferencedColumns: yyDollar[4].columnsUnion(), Match: yyDollar[6].matchActionUnion(), OnUpdate: yyDollar[7].referenceActionUnion(), OnDelete: yyDollar[8].referenceActionUnion()} } yyVAL.union = yyLOCAL - case 435: + case 437: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2569 +//line sql.y:2584 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 436: + case 438: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ReferenceDefinition -//line sql.y:2573 +//line sql.y:2588 { yyLOCAL = yyDollar[1].referenceDefinitionUnion() } yyVAL.union = yyLOCAL - case 437: + case 439: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL ConstraintInfo -//line sql.y:2579 +//line sql.y:2594 { yyLOCAL = &CheckConstraintDefinition{Expr: yyDollar[3].exprUnion(), Enforced: yyDollar[5].booleanUnion()} } yyVAL.union = yyLOCAL - case 438: + case 440: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2585 +//line sql.y:2600 { yyLOCAL = yyDollar[2].matchActionUnion() } yyVAL.union = yyLOCAL - case 439: + case 441: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2591 +//line sql.y:2606 { yyLOCAL = Full } yyVAL.union = yyLOCAL - case 440: + case 442: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2595 +//line sql.y:2610 { yyLOCAL = Partial } yyVAL.union = yyLOCAL - case 441: + case 443: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2599 +//line sql.y:2614 { yyLOCAL = Simple } yyVAL.union = yyLOCAL - case 442: + case 444: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2604 +//line sql.y:2619 { yyLOCAL = DefaultMatch } yyVAL.union = yyLOCAL - case 443: + case 445: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL MatchAction -//line sql.y:2608 +//line sql.y:2623 { yyLOCAL = yyDollar[1].matchActionUnion() } yyVAL.union = yyLOCAL - case 444: + case 446: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2614 +//line sql.y:2629 { yyLOCAL = yyDollar[3].referenceActionUnion() } yyVAL.union = yyLOCAL - case 445: + case 447: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2620 +//line sql.y:2635 { yyLOCAL = yyDollar[3].referenceActionUnion() } yyVAL.union = yyLOCAL - case 446: + case 448: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2626 +//line sql.y:2641 { yyLOCAL = Restrict } yyVAL.union = yyLOCAL - case 447: + case 449: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2630 +//line sql.y:2645 { yyLOCAL = Cascade } yyVAL.union = yyLOCAL - case 448: + case 450: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2634 +//line sql.y:2649 { yyLOCAL = NoAction } yyVAL.union = yyLOCAL - case 449: + case 451: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2638 +//line sql.y:2653 { yyLOCAL = SetDefault } yyVAL.union = yyLOCAL - case 450: + case 452: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ReferenceAction -//line sql.y:2642 +//line sql.y:2657 { yyLOCAL = SetNull } yyVAL.union = yyLOCAL - case 451: + case 453: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2647 +//line sql.y:2662 { yyVAL.str = "" } - case 452: + case 454: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2651 +//line sql.y:2666 { yyVAL.str = string(yyDollar[1].str) } - case 453: + case 455: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2655 +//line sql.y:2670 { yyVAL.str = string(yyDollar[1].str) } - case 454: + case 456: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2661 +//line sql.y:2676 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 455: + case 457: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:2665 +//line sql.y:2680 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 456: + case 458: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:2670 +//line sql.y:2685 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 457: + case 459: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2674 +//line sql.y:2689 { yyLOCAL = yyDollar[1].booleanUnion() } yyVAL.union = yyLOCAL - case 458: + case 460: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL TableOptions -//line sql.y:2679 +//line sql.y:2694 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 459: + case 461: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableOptions -//line sql.y:2683 +//line sql.y:2698 { yyLOCAL = yyDollar[1].tableOptionsUnion() } yyVAL.union = yyLOCAL - case 460: + case 462: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableOptions -//line sql.y:2689 +//line sql.y:2704 { yyLOCAL = TableOptions{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 461: + case 463: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2693 +//line sql.y:2708 { yySLICE := (*TableOptions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableOptionUnion()) } - case 462: + case 464: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2697 +//line sql.y:2712 { yySLICE := (*TableOptions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].tableOptionUnion()) } - case 463: + case 465: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableOptions -//line sql.y:2703 +//line sql.y:2718 { yyLOCAL = TableOptions{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 464: + case 466: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2707 +//line sql.y:2722 { yySLICE := (*TableOptions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].tableOptionUnion()) } - case 465: + case 467: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2713 +//line sql.y:2728 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 466: + case 468: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2717 +//line sql.y:2732 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 467: + case 469: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2721 +//line sql.y:2736 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 468: + case 470: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2725 +//line sql.y:2740 { yyLOCAL = &TableOption{Name: (string(yyDollar[2].str)), String: yyDollar[4].str, CaseSensitive: true} } yyVAL.union = yyLOCAL - case 469: + case 471: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2729 +//line sql.y:2744 { yyLOCAL = &TableOption{Name: string(yyDollar[2].str), String: yyDollar[4].str, CaseSensitive: true} } yyVAL.union = yyLOCAL - case 470: + case 472: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2733 +//line sql.y:2748 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 471: + case 473: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2737 +//line sql.y:2752 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 472: + case 474: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2741 +//line sql.y:2756 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 473: + case 475: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2745 +//line sql.y:2760 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 474: + case 476: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2749 +//line sql.y:2764 { yyLOCAL = &TableOption{Name: (string(yyDollar[1].str) + " " + string(yyDollar[2].str)), Value: NewStrLiteral(yyDollar[4].str)} } yyVAL.union = yyLOCAL - case 475: + case 477: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2753 +//line sql.y:2768 { yyLOCAL = &TableOption{Name: (string(yyDollar[1].str) + " " + string(yyDollar[2].str)), Value: NewStrLiteral(yyDollar[4].str)} } yyVAL.union = yyLOCAL - case 476: + case 478: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2757 +//line sql.y:2772 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 477: + case 479: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2761 +//line sql.y:2776 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 478: + case 480: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2765 +//line sql.y:2780 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: yyDollar[3].identifierCS.String(), CaseSensitive: true} } yyVAL.union = yyLOCAL - case 479: + case 481: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2769 +//line sql.y:2784 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 480: + case 482: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2773 +//line sql.y:2788 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 481: + case 483: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2777 +//line sql.y:2792 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 482: + case 484: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2781 +//line sql.y:2796 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 483: + case 485: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2785 +//line sql.y:2800 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 484: + case 486: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2789 +//line sql.y:2804 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 485: + case 487: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2793 +//line sql.y:2808 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 486: + case 488: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2797 +//line sql.y:2812 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 487: + case 489: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2801 +//line sql.y:2816 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 488: + case 490: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2805 +//line sql.y:2820 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewStrLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 489: + case 491: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2809 +//line sql.y:2824 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 490: + case 492: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2813 +//line sql.y:2828 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 491: + case 493: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2817 +//line sql.y:2832 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 492: + case 494: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2821 +//line sql.y:2836 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 493: + case 495: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2825 +//line sql.y:2840 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Value: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 494: + case 496: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2829 +//line sql.y:2844 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), String: (yyDollar[3].identifierCI.String() + yyDollar[4].str), CaseSensitive: true} } yyVAL.union = yyLOCAL - case 495: + case 497: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *TableOption -//line sql.y:2833 +//line sql.y:2848 { yyLOCAL = &TableOption{Name: string(yyDollar[1].str), Tables: yyDollar[4].tableNamesUnion()} } yyVAL.union = yyLOCAL - case 496: + case 498: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2838 +//line sql.y:2853 { yyVAL.str = "" } - case 497: + case 499: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2842 +//line sql.y:2857 { yyVAL.str = " " + string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 498: + case 500: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2846 +//line sql.y:2861 { yyVAL.str = " " + string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 508: + case 510: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2865 +//line sql.y:2880 { yyVAL.str = String(TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS}) } - case 509: + case 511: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2869 +//line sql.y:2884 { yyVAL.str = yyDollar[1].identifierCI.String() } - case 510: + case 512: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2873 +//line sql.y:2888 { yyVAL.str = encodeSQLString(yyDollar[1].str) } - case 511: + case 513: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2877 +//line sql.y:2892 { yyVAL.str = string(yyDollar[1].str) } - case 512: + case 514: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2882 +//line sql.y:2897 { yyVAL.str = "" } - case 514: + case 516: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:2888 +//line sql.y:2903 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 515: + case 517: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:2892 +//line sql.y:2907 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 516: + case 518: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ColName -//line sql.y:2897 +//line sql.y:2912 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 517: + case 519: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ColName -//line sql.y:2901 +//line sql.y:2916 { yyLOCAL = yyDollar[2].colNameUnion() } yyVAL.union = yyLOCAL - case 518: + case 520: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2906 +//line sql.y:2921 { yyVAL.str = "" } - case 519: + case 521: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2910 +//line sql.y:2925 { yyVAL.str = string(yyDollar[2].str) } - case 520: + case 522: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *Literal -//line sql.y:2915 +//line sql.y:2930 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 521: + case 523: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Literal -//line sql.y:2919 +//line sql.y:2934 { yyLOCAL = NewIntLiteral(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 522: + case 524: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Literal -//line sql.y:2923 +//line sql.y:2938 { yyLOCAL = NewDecimalLiteral(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 523: + case 525: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:2928 +//line sql.y:2943 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 524: + case 526: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:2932 +//line sql.y:2947 { yyLOCAL = yyDollar[1].alterOptionsUnion() } yyVAL.union = yyLOCAL - case 525: + case 527: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2936 +//line sql.y:2951 { yySLICE := (*[]AlterOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, &OrderByOption{Cols: yyDollar[5].columnsUnion()}) } - case 526: + case 528: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:2940 +//line sql.y:2955 { yyLOCAL = yyDollar[1].alterOptionsUnion() } yyVAL.union = yyLOCAL - case 527: + case 529: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2944 +//line sql.y:2959 { yySLICE := (*[]AlterOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].alterOptionsUnion()...) } - case 528: + case 530: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:2948 +//line sql.y:2963 { yyLOCAL = append(append(yyDollar[1].alterOptionsUnion(), yyDollar[3].alterOptionsUnion()...), &OrderByOption{Cols: yyDollar[7].columnsUnion()}) } yyVAL.union = yyLOCAL - case 529: + case 531: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:2954 +//line sql.y:2969 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 530: + case 532: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2958 +//line sql.y:2973 { yySLICE := (*[]AlterOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].alterOptionUnion()) } - case 531: + case 533: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2962 +//line sql.y:2977 { yySLICE := (*[]AlterOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].alterOptionUnion()) } - case 532: + case 534: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2968 +//line sql.y:2983 { yyLOCAL = yyDollar[1].tableOptionsUnion() } yyVAL.union = yyLOCAL - case 533: + case 535: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2972 +//line sql.y:2987 { yyLOCAL = &AddConstraintDefinition{ConstraintDefinition: yyDollar[2].constraintDefinitionUnion()} } yyVAL.union = yyLOCAL - case 534: + case 536: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2976 +//line sql.y:2991 { yyLOCAL = &AddConstraintDefinition{ConstraintDefinition: yyDollar[2].constraintDefinitionUnion()} } yyVAL.union = yyLOCAL - case 535: + case 537: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2980 +//line sql.y:2995 { yyLOCAL = &AddIndexDefinition{IndexDefinition: yyDollar[2].indexDefinitionUnion()} } yyVAL.union = yyLOCAL - case 536: + case 538: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2984 +//line sql.y:2999 { yyLOCAL = &AddColumns{Columns: yyDollar[4].columnDefinitionsUnion()} } yyVAL.union = yyLOCAL - case 537: + case 539: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2988 +//line sql.y:3003 { yyLOCAL = &AddColumns{Columns: []*ColumnDefinition{yyDollar[3].columnDefinitionUnion()}, First: yyDollar[4].booleanUnion(), After: yyDollar[5].colNameUnion()} } yyVAL.union = yyLOCAL - case 538: + case 540: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2992 +//line sql.y:3007 { yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), DropDefault: true} } yyVAL.union = yyLOCAL - case 539: + case 541: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL AlterOption -//line sql.y:2996 +//line sql.y:3011 { yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), DropDefault: false, DefaultVal: yyDollar[6].exprUnion(), DefaultLiteral: true} } yyVAL.union = yyLOCAL - case 540: + case 542: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3000 +//line sql.y:3015 { yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), DropDefault: false, DefaultVal: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 541: + case 543: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3004 +//line sql.y:3019 { - val := false - yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), Invisible: &val} + yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), Invisible: ptr.Of(false)} } yyVAL.union = yyLOCAL - case 542: + case 544: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3009 +//line sql.y:3023 { - val := true - yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), Invisible: &val} + yyLOCAL = &AlterColumn{Column: yyDollar[3].colNameUnion(), Invisible: ptr.Of(true)} } yyVAL.union = yyLOCAL - case 543: + case 545: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3014 +//line sql.y:3027 { yyLOCAL = &AlterCheck{Name: yyDollar[3].identifierCI, Enforced: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 544: + case 546: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3018 +//line sql.y:3031 { yyLOCAL = &AlterIndex{Name: yyDollar[3].identifierCI, Invisible: false} } yyVAL.union = yyLOCAL - case 545: + case 547: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3022 +//line sql.y:3035 { yyLOCAL = &AlterIndex{Name: yyDollar[3].identifierCI, Invisible: true} } yyVAL.union = yyLOCAL - case 546: + case 548: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3026 +//line sql.y:3039 { yyLOCAL = &ChangeColumn{OldColumn: yyDollar[3].colNameUnion(), NewColDefinition: yyDollar[4].columnDefinitionUnion(), First: yyDollar[5].booleanUnion(), After: yyDollar[6].colNameUnion()} } yyVAL.union = yyLOCAL - case 547: + case 549: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3030 +//line sql.y:3043 { yyLOCAL = &ModifyColumn{NewColDefinition: yyDollar[3].columnDefinitionUnion(), First: yyDollar[4].booleanUnion(), After: yyDollar[5].colNameUnion()} } yyVAL.union = yyLOCAL - case 548: + case 550: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3034 +//line sql.y:3047 { yyLOCAL = &RenameColumn{OldName: yyDollar[3].colNameUnion(), NewName: yyDollar[5].colNameUnion()} } yyVAL.union = yyLOCAL - case 549: + case 551: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3038 +//line sql.y:3051 { yyLOCAL = &AlterCharset{CharacterSet: yyDollar[4].str, Collate: yyDollar[5].str} } yyVAL.union = yyLOCAL - case 550: + case 552: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3042 +//line sql.y:3055 { yyLOCAL = &KeyState{Enable: false} } yyVAL.union = yyLOCAL - case 551: + case 553: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3046 +//line sql.y:3059 { yyLOCAL = &KeyState{Enable: true} } yyVAL.union = yyLOCAL - case 552: + case 554: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3050 +//line sql.y:3063 { yyLOCAL = &TablespaceOperation{Import: false} } yyVAL.union = yyLOCAL - case 553: + case 555: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3054 +//line sql.y:3067 { yyLOCAL = &TablespaceOperation{Import: true} } yyVAL.union = yyLOCAL - case 554: + case 556: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3058 +//line sql.y:3071 { yyLOCAL = &DropColumn{Name: yyDollar[3].colNameUnion()} } yyVAL.union = yyLOCAL - case 555: + case 557: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3062 +//line sql.y:3075 { yyLOCAL = &DropKey{Type: NormalKeyType, Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 556: + case 558: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3066 +//line sql.y:3079 { yyLOCAL = &DropKey{Type: PrimaryKeyType} } yyVAL.union = yyLOCAL - case 557: + case 559: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3070 +//line sql.y:3083 { yyLOCAL = &DropKey{Type: ForeignKeyType, Name: yyDollar[4].identifierCI} } yyVAL.union = yyLOCAL - case 558: + case 560: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3074 +//line sql.y:3087 { yyLOCAL = &DropKey{Type: CheckKeyType, Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 559: + case 561: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3078 +//line sql.y:3091 { yyLOCAL = &DropKey{Type: CheckKeyType, Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 560: + case 562: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3082 +//line sql.y:3095 { yyLOCAL = &Force{} } yyVAL.union = yyLOCAL - case 561: + case 563: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3086 +//line sql.y:3099 { yyLOCAL = &RenameTableName{Table: yyDollar[3].tableName} } yyVAL.union = yyLOCAL - case 562: + case 564: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3090 +//line sql.y:3103 { yyLOCAL = &RenameIndex{OldName: yyDollar[3].identifierCI, NewName: yyDollar[5].identifierCI} } yyVAL.union = yyLOCAL - case 563: + case 565: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:3096 +//line sql.y:3109 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 564: + case 566: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3100 +//line sql.y:3113 { yySLICE := (*[]AlterOption)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].alterOptionUnion()) } - case 565: + case 567: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3106 +//line sql.y:3119 { yyLOCAL = AlgorithmValue(string(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 566: + case 568: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3110 +//line sql.y:3123 { yyLOCAL = AlgorithmValue(string(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 567: + case 569: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3114 +//line sql.y:3127 { yyLOCAL = AlgorithmValue(string(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 568: + case 570: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3118 +//line sql.y:3131 { yyLOCAL = AlgorithmValue(string(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 569: + case 571: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3122 +//line sql.y:3135 { yyLOCAL = &LockOption{Type: DefaultType} } yyVAL.union = yyLOCAL - case 570: + case 572: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3126 +//line sql.y:3139 { yyLOCAL = &LockOption{Type: NoneType} } yyVAL.union = yyLOCAL - case 571: + case 573: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3130 +//line sql.y:3143 { yyLOCAL = &LockOption{Type: SharedType} } yyVAL.union = yyLOCAL - case 572: + case 574: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3134 +//line sql.y:3147 { yyLOCAL = &LockOption{Type: ExclusiveType} } yyVAL.union = yyLOCAL - case 573: + case 575: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3138 +//line sql.y:3151 { yyLOCAL = &Validation{With: true} } yyVAL.union = yyLOCAL - case 574: + case 576: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL AlterOption -//line sql.y:3142 +//line sql.y:3155 { yyLOCAL = &Validation{With: false} } yyVAL.union = yyLOCAL - case 575: + case 577: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3148 +//line sql.y:3161 { yyDollar[1].alterTableUnion().FullyParsed = true yyDollar[1].alterTableUnion().AlterOptions = yyDollar[2].alterOptionsUnion() @@ -14001,10 +14147,10 @@ yydefault: yyLOCAL = yyDollar[1].alterTableUnion() } yyVAL.union = yyLOCAL - case 576: + case 578: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:3155 +//line sql.y:3168 { yyDollar[1].alterTableUnion().FullyParsed = true yyDollar[1].alterTableUnion().AlterOptions = yyDollar[2].alterOptionsUnion() @@ -14012,10 +14158,10 @@ yydefault: yyLOCAL = yyDollar[1].alterTableUnion() } yyVAL.union = yyLOCAL - case 577: + case 579: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:3162 +//line sql.y:3175 { yyDollar[1].alterTableUnion().FullyParsed = true yyDollar[1].alterTableUnion().AlterOptions = yyDollar[2].alterOptionsUnion() @@ -14023,28 +14169,28 @@ yydefault: yyLOCAL = yyDollar[1].alterTableUnion() } yyVAL.union = yyLOCAL - case 578: + case 580: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:3169 +//line sql.y:3182 { yyDollar[1].alterTableUnion().FullyParsed = true yyDollar[1].alterTableUnion().PartitionSpec = yyDollar[2].partSpecUnion() yyLOCAL = yyDollar[1].alterTableUnion() } yyVAL.union = yyLOCAL - case 579: + case 581: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL Statement -//line sql.y:3175 +//line sql.y:3188 { yyLOCAL = &AlterView{ViewName: yyDollar[7].tableName, Comments: Comments(yyDollar[2].strs).Parsed(), Algorithm: yyDollar[3].str, Definer: yyDollar[4].definerUnion(), Security: yyDollar[5].str, Columns: yyDollar[8].columnsUnion(), Select: yyDollar[10].selStmtUnion(), CheckOption: yyDollar[11].str} } yyVAL.union = yyLOCAL - case 580: + case 582: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3185 +//line sql.y:3198 { yyDollar[1].alterDatabaseUnion().FullyParsed = true yyDollar[1].alterDatabaseUnion().DBName = yyDollar[2].identifierCS @@ -14052,10 +14198,10 @@ yydefault: yyLOCAL = yyDollar[1].alterDatabaseUnion() } yyVAL.union = yyLOCAL - case 581: + case 583: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3192 +//line sql.y:3205 { yyDollar[1].alterDatabaseUnion().FullyParsed = true yyDollar[1].alterDatabaseUnion().DBName = yyDollar[2].identifierCS @@ -14063,10 +14209,10 @@ yydefault: yyLOCAL = yyDollar[1].alterDatabaseUnion() } yyVAL.union = yyLOCAL - case 582: + case 584: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Statement -//line sql.y:3199 +//line sql.y:3212 { yyLOCAL = &AlterVschema{ Action: CreateVindexDDLAction, @@ -14079,10 +14225,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 583: + case 585: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3211 +//line sql.y:3224 { yyLOCAL = &AlterVschema{ Action: DropVindexDDLAction, @@ -14093,26 +14239,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 584: + case 586: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3221 +//line sql.y:3234 { yyLOCAL = &AlterVschema{Action: AddVschemaTableDDLAction, Table: yyDollar[6].tableName} } yyVAL.union = yyLOCAL - case 585: + case 587: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3225 +//line sql.y:3238 { yyLOCAL = &AlterVschema{Action: DropVschemaTableDDLAction, Table: yyDollar[6].tableName} } yyVAL.union = yyLOCAL - case 586: + case 588: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL Statement -//line sql.y:3229 +//line sql.y:3242 { yyLOCAL = &AlterVschema{ Action: AddColVindexDDLAction, @@ -14126,10 +14272,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 587: + case 589: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Statement -//line sql.y:3242 +//line sql.y:3255 { yyLOCAL = &AlterVschema{ Action: DropColVindexDDLAction, @@ -14140,26 +14286,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 588: + case 590: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3252 +//line sql.y:3265 { yyLOCAL = &AlterVschema{Action: AddSequenceDDLAction, Table: yyDollar[6].tableName} } yyVAL.union = yyLOCAL - case 589: + case 591: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3256 +//line sql.y:3269 { yyLOCAL = &AlterVschema{Action: DropSequenceDDLAction, Table: yyDollar[6].tableName} } yyVAL.union = yyLOCAL - case 590: + case 592: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Statement -//line sql.y:3260 +//line sql.y:3273 { yyLOCAL = &AlterVschema{ Action: AddAutoIncDDLAction, @@ -14171,10 +14317,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 591: + case 593: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3271 +//line sql.y:3284 { yyLOCAL = &AlterVschema{ Action: DropAutoIncDDLAction, @@ -14182,10 +14328,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 592: + case 594: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3278 +//line sql.y:3291 { yyLOCAL = &AlterMigration{ Type: RetryMigrationType, @@ -14193,10 +14339,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 593: + case 595: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3285 +//line sql.y:3298 { yyLOCAL = &AlterMigration{ Type: CleanupMigrationType, @@ -14204,10 +14350,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 594: + case 596: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3292 +//line sql.y:3305 { yyLOCAL = &AlterMigration{ Type: LaunchMigrationType, @@ -14215,10 +14361,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 595: + case 597: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3299 +//line sql.y:3312 { yyLOCAL = &AlterMigration{ Type: LaunchMigrationType, @@ -14227,20 +14373,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 596: + case 598: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3307 +//line sql.y:3320 { yyLOCAL = &AlterMigration{ Type: LaunchAllMigrationType, } } yyVAL.union = yyLOCAL - case 597: + case 599: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3313 +//line sql.y:3326 { yyLOCAL = &AlterMigration{ Type: CompleteMigrationType, @@ -14248,20 +14394,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 598: + case 600: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3320 +//line sql.y:3333 { yyLOCAL = &AlterMigration{ Type: CompleteAllMigrationType, } } yyVAL.union = yyLOCAL - case 599: + case 601: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3326 +//line sql.y:3339 { yyLOCAL = &AlterMigration{ Type: CancelMigrationType, @@ -14269,20 +14415,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 600: + case 602: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3333 +//line sql.y:3346 { yyLOCAL = &AlterMigration{ Type: CancelAllMigrationType, } } yyVAL.union = yyLOCAL - case 601: + case 603: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3339 +//line sql.y:3352 { yyLOCAL = &AlterMigration{ Type: ThrottleMigrationType, @@ -14292,10 +14438,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 602: + case 604: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3348 +//line sql.y:3361 { yyLOCAL = &AlterMigration{ Type: ThrottleAllMigrationType, @@ -14304,10 +14450,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 603: + case 605: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3356 +//line sql.y:3369 { yyLOCAL = &AlterMigration{ Type: UnthrottleMigrationType, @@ -14315,28 +14461,49 @@ yydefault: } } yyVAL.union = yyLOCAL - case 604: + case 606: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3363 +//line sql.y:3376 { yyLOCAL = &AlterMigration{ Type: UnthrottleAllMigrationType, } } yyVAL.union = yyLOCAL - case 605: + case 607: + yyDollar = yyS[yypt-5 : yypt+1] + var yyLOCAL Statement +//line sql.y:3382 + { + yyLOCAL = &AlterMigration{ + Type: ForceCutOverMigrationType, + UUID: string(yyDollar[4].str), + } + } + yyVAL.union = yyLOCAL + case 608: + yyDollar = yyS[yypt-5 : yypt+1] + var yyLOCAL Statement +//line sql.y:3389 + { + yyLOCAL = &AlterMigration{ + Type: ForceCutOverAllMigrationType, + } + } + yyVAL.union = yyLOCAL + case 609: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3370 +//line sql.y:3396 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 606: + case 610: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3374 +//line sql.y:3400 { yyDollar[3].partitionOptionUnion().Partitions = yyDollar[4].integerUnion() yyDollar[3].partitionOptionUnion().SubPartition = yyDollar[5].subPartitionUnion() @@ -14344,10 +14511,10 @@ yydefault: yyLOCAL = yyDollar[3].partitionOptionUnion() } yyVAL.union = yyLOCAL - case 607: + case 611: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3383 +//line sql.y:3409 { yyLOCAL = &PartitionOption{ IsLinear: yyDollar[1].booleanUnion(), @@ -14356,10 +14523,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 608: + case 612: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3391 +//line sql.y:3417 { yyLOCAL = &PartitionOption{ IsLinear: yyDollar[1].booleanUnion(), @@ -14369,10 +14536,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 609: + case 613: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3400 +//line sql.y:3426 { yyLOCAL = &PartitionOption{ Type: yyDollar[1].partitionByTypeUnion(), @@ -14380,10 +14547,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 610: + case 614: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionOption -//line sql.y:3407 +//line sql.y:3433 { yyLOCAL = &PartitionOption{ Type: yyDollar[1].partitionByTypeUnion(), @@ -14391,18 +14558,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 611: + case 615: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3415 +//line sql.y:3441 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 612: + case 616: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3419 +//line sql.y:3445 { yyLOCAL = &SubPartition{ IsLinear: yyDollar[3].booleanUnion(), @@ -14412,10 +14579,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 613: + case 617: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *SubPartition -//line sql.y:3428 +//line sql.y:3454 { yyLOCAL = &SubPartition{ IsLinear: yyDollar[3].booleanUnion(), @@ -14426,682 +14593,678 @@ yydefault: } } yyVAL.union = yyLOCAL - case 614: + case 618: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3439 +//line sql.y:3465 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 615: + case 619: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3443 +//line sql.y:3469 { yyLOCAL = yyDollar[2].partDefsUnion() } yyVAL.union = yyLOCAL - case 616: + case 620: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3448 +//line sql.y:3474 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 617: + case 621: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3452 +//line sql.y:3478 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 618: + case 622: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3457 +//line sql.y:3483 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 619: + case 623: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3461 +//line sql.y:3487 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 620: + case 624: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL TableExpr -//line sql.y:3467 +//line sql.y:3493 { yyLOCAL = &JSONTableExpr{Expr: yyDollar[3].exprUnion(), Filter: yyDollar[5].exprUnion(), Columns: yyDollar[6].jtColumnListUnion(), Alias: yyDollar[8].identifierCS} } yyVAL.union = yyLOCAL - case 621: + case 625: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL []*JtColumnDefinition -//line sql.y:3473 +//line sql.y:3499 { yyLOCAL = yyDollar[3].jtColumnListUnion() } yyVAL.union = yyLOCAL - case 622: + case 626: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*JtColumnDefinition -//line sql.y:3479 +//line sql.y:3505 { yyLOCAL = []*JtColumnDefinition{yyDollar[1].jtColumnDefinitionUnion()} } yyVAL.union = yyLOCAL - case 623: + case 627: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3483 +//line sql.y:3509 { yySLICE := (*[]*JtColumnDefinition)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].jtColumnDefinitionUnion()) } - case 624: + case 628: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3489 +//line sql.y:3515 { yyLOCAL = &JtColumnDefinition{JtOrdinal: &JtOrdinalColDef{Name: yyDollar[1].identifierCI}} } yyVAL.union = yyLOCAL - case 625: + case 629: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3493 +//line sql.y:3519 { yyDollar[2].columnType.Options = &ColumnTypeOptions{Collate: yyDollar[3].str} jtPath := &JtPathColDef{Name: yyDollar[1].identifierCI, Type: yyDollar[2].columnType, JtColExists: yyDollar[4].booleanUnion(), Path: yyDollar[6].exprUnion()} yyLOCAL = &JtColumnDefinition{JtPath: jtPath} } yyVAL.union = yyLOCAL - case 626: + case 630: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3499 +//line sql.y:3525 { yyDollar[2].columnType.Options = &ColumnTypeOptions{Collate: yyDollar[3].str} jtPath := &JtPathColDef{Name: yyDollar[1].identifierCI, Type: yyDollar[2].columnType, JtColExists: yyDollar[4].booleanUnion(), Path: yyDollar[6].exprUnion(), EmptyOnResponse: yyDollar[7].jtOnResponseUnion()} yyLOCAL = &JtColumnDefinition{JtPath: jtPath} } yyVAL.union = yyLOCAL - case 627: + case 631: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3505 +//line sql.y:3531 { yyDollar[2].columnType.Options = &ColumnTypeOptions{Collate: yyDollar[3].str} jtPath := &JtPathColDef{Name: yyDollar[1].identifierCI, Type: yyDollar[2].columnType, JtColExists: yyDollar[4].booleanUnion(), Path: yyDollar[6].exprUnion(), ErrorOnResponse: yyDollar[7].jtOnResponseUnion()} yyLOCAL = &JtColumnDefinition{JtPath: jtPath} } yyVAL.union = yyLOCAL - case 628: + case 632: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3511 +//line sql.y:3537 { yyDollar[2].columnType.Options = &ColumnTypeOptions{Collate: yyDollar[3].str} jtPath := &JtPathColDef{Name: yyDollar[1].identifierCI, Type: yyDollar[2].columnType, JtColExists: yyDollar[4].booleanUnion(), Path: yyDollar[6].exprUnion(), EmptyOnResponse: yyDollar[7].jtOnResponseUnion(), ErrorOnResponse: yyDollar[8].jtOnResponseUnion()} yyLOCAL = &JtColumnDefinition{JtPath: jtPath} } yyVAL.union = yyLOCAL - case 629: + case 633: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *JtColumnDefinition -//line sql.y:3517 +//line sql.y:3543 { jtNestedPath := &JtNestedPathColDef{Path: yyDollar[3].exprUnion(), Columns: yyDollar[4].jtColumnListUnion()} yyLOCAL = &JtColumnDefinition{JtNestedPath: jtNestedPath} } yyVAL.union = yyLOCAL - case 630: + case 634: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3523 +//line sql.y:3549 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 631: + case 635: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3527 +//line sql.y:3553 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 632: + case 636: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3531 +//line sql.y:3557 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 633: + case 637: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3535 +//line sql.y:3561 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 634: + case 638: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3541 +//line sql.y:3567 { yyLOCAL = yyDollar[1].jtOnResponseUnion() } yyVAL.union = yyLOCAL - case 635: + case 639: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3547 +//line sql.y:3573 { yyLOCAL = yyDollar[1].jtOnResponseUnion() } yyVAL.union = yyLOCAL - case 636: + case 640: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3553 +//line sql.y:3579 { yyLOCAL = &JtOnResponse{ResponseType: ErrorJSONType} } yyVAL.union = yyLOCAL - case 637: + case 641: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3557 +//line sql.y:3583 { yyLOCAL = &JtOnResponse{ResponseType: NullJSONType} } yyVAL.union = yyLOCAL - case 638: + case 642: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *JtOnResponse -//line sql.y:3561 +//line sql.y:3587 { yyLOCAL = &JtOnResponse{ResponseType: DefaultJSONType, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 639: + case 643: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL PartitionByType -//line sql.y:3567 +//line sql.y:3593 { yyLOCAL = RangeType } yyVAL.union = yyLOCAL - case 640: + case 644: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL PartitionByType -//line sql.y:3571 +//line sql.y:3597 { yyLOCAL = ListType } yyVAL.union = yyLOCAL - case 641: + case 645: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3576 +//line sql.y:3602 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 642: + case 646: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:3580 +//line sql.y:3606 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 643: + case 647: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:3585 +//line sql.y:3611 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 644: + case 648: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:3589 +//line sql.y:3615 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 645: + case 649: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3595 +//line sql.y:3621 { yyLOCAL = &PartitionSpec{Action: AddAction, Definitions: []*PartitionDefinition{yyDollar[4].partDefUnion()}} } yyVAL.union = yyLOCAL - case 646: + case 650: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3599 +//line sql.y:3625 { yyLOCAL = &PartitionSpec{Action: DropAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 647: + case 651: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3603 +//line sql.y:3629 { yyLOCAL = &PartitionSpec{Action: ReorganizeAction, Names: yyDollar[3].partitionsUnion(), Definitions: yyDollar[6].partDefsUnion()} } yyVAL.union = yyLOCAL - case 648: + case 652: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3607 +//line sql.y:3633 { yyLOCAL = &PartitionSpec{Action: DiscardAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 649: + case 653: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3611 +//line sql.y:3637 { yyLOCAL = &PartitionSpec{Action: DiscardAction, IsAll: true} } yyVAL.union = yyLOCAL - case 650: + case 654: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3615 +//line sql.y:3641 { yyLOCAL = &PartitionSpec{Action: ImportAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 651: + case 655: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3619 +//line sql.y:3645 { yyLOCAL = &PartitionSpec{Action: ImportAction, IsAll: true} } yyVAL.union = yyLOCAL - case 652: + case 656: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3623 +//line sql.y:3649 { yyLOCAL = &PartitionSpec{Action: TruncateAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 653: + case 657: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3627 +//line sql.y:3653 { yyLOCAL = &PartitionSpec{Action: TruncateAction, IsAll: true} } yyVAL.union = yyLOCAL - case 654: + case 658: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3631 +//line sql.y:3657 { yyLOCAL = &PartitionSpec{Action: CoalesceAction, Number: NewIntLiteral(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 655: + case 659: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3635 +//line sql.y:3661 { yyLOCAL = &PartitionSpec{Action: ExchangeAction, Names: Partitions{yyDollar[3].identifierCI}, TableName: yyDollar[6].tableName, WithoutValidation: yyDollar[7].booleanUnion()} } yyVAL.union = yyLOCAL - case 656: + case 660: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3639 +//line sql.y:3665 { yyLOCAL = &PartitionSpec{Action: AnalyzeAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 657: + case 661: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3643 +//line sql.y:3669 { yyLOCAL = &PartitionSpec{Action: AnalyzeAction, IsAll: true} } yyVAL.union = yyLOCAL - case 658: + case 662: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3647 +//line sql.y:3673 { yyLOCAL = &PartitionSpec{Action: CheckAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 659: + case 663: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3651 +//line sql.y:3677 { yyLOCAL = &PartitionSpec{Action: CheckAction, IsAll: true} } yyVAL.union = yyLOCAL - case 660: + case 664: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3655 +//line sql.y:3681 { yyLOCAL = &PartitionSpec{Action: OptimizeAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 661: + case 665: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3659 +//line sql.y:3685 { yyLOCAL = &PartitionSpec{Action: OptimizeAction, IsAll: true} } yyVAL.union = yyLOCAL - case 662: + case 666: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3663 +//line sql.y:3689 { yyLOCAL = &PartitionSpec{Action: RebuildAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 663: + case 667: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3667 +//line sql.y:3693 { yyLOCAL = &PartitionSpec{Action: RebuildAction, IsAll: true} } yyVAL.union = yyLOCAL - case 664: + case 668: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3671 +//line sql.y:3697 { yyLOCAL = &PartitionSpec{Action: RepairAction, Names: yyDollar[3].partitionsUnion()} } yyVAL.union = yyLOCAL - case 665: + case 669: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3675 +//line sql.y:3701 { yyLOCAL = &PartitionSpec{Action: RepairAction, IsAll: true} } yyVAL.union = yyLOCAL - case 666: + case 670: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionSpec -//line sql.y:3679 +//line sql.y:3705 { yyLOCAL = &PartitionSpec{Action: UpgradeAction} } yyVAL.union = yyLOCAL - case 667: + case 671: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3684 +//line sql.y:3710 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 668: + case 672: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:3688 +//line sql.y:3714 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 669: + case 673: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:3692 +//line sql.y:3718 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 670: + case 674: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*PartitionDefinition -//line sql.y:3698 +//line sql.y:3724 { yyLOCAL = []*PartitionDefinition{yyDollar[1].partDefUnion()} } yyVAL.union = yyLOCAL - case 671: + case 675: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3702 +//line sql.y:3728 { yySLICE := (*[]*PartitionDefinition)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].partDefUnion()) } - case 672: + case 676: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3708 +//line sql.y:3734 { yyVAL.partDefUnion().Options = yyDollar[2].partitionDefinitionOptionsUnion() } - case 673: + case 677: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3713 +//line sql.y:3739 { yyLOCAL = &PartitionDefinitionOptions{} } yyVAL.union = yyLOCAL - case 674: + case 678: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3717 +//line sql.y:3743 { yyDollar[1].partitionDefinitionOptionsUnion().ValueRange = yyDollar[2].partitionValueRangeUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 675: + case 679: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3722 +//line sql.y:3748 { yyDollar[1].partitionDefinitionOptionsUnion().Comment = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 676: + case 680: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3727 +//line sql.y:3753 { yyDollar[1].partitionDefinitionOptionsUnion().Engine = yyDollar[2].partitionEngineUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 677: + case 681: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3732 +//line sql.y:3758 { yyDollar[1].partitionDefinitionOptionsUnion().DataDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 678: + case 682: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3737 +//line sql.y:3763 { yyDollar[1].partitionDefinitionOptionsUnion().IndexDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 679: + case 683: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3742 +//line sql.y:3768 { - val := yyDollar[2].integerUnion() - yyDollar[1].partitionDefinitionOptionsUnion().MaxRows = &val + yyDollar[1].partitionDefinitionOptionsUnion().MaxRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 680: + case 684: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3748 +//line sql.y:3773 { - val := yyDollar[2].integerUnion() - yyDollar[1].partitionDefinitionOptionsUnion().MinRows = &val + yyDollar[1].partitionDefinitionOptionsUnion().MinRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 681: + case 685: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3754 +//line sql.y:3778 { yyDollar[1].partitionDefinitionOptionsUnion().TableSpace = yyDollar[2].str yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 682: + case 686: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinitionOptions -//line sql.y:3759 +//line sql.y:3783 { yyDollar[1].partitionDefinitionOptionsUnion().SubPartitionDefinitions = yyDollar[2].subPartitionDefinitionsUnion() yyLOCAL = yyDollar[1].partitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 683: + case 687: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SubPartitionDefinitions -//line sql.y:3765 +//line sql.y:3789 { yyLOCAL = yyDollar[2].subPartitionDefinitionsUnion() } yyVAL.union = yyLOCAL - case 684: + case 688: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SubPartitionDefinitions -//line sql.y:3771 +//line sql.y:3795 { yyLOCAL = SubPartitionDefinitions{yyDollar[1].subPartitionDefinitionUnion()} } yyVAL.union = yyLOCAL - case 685: + case 689: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3775 +//line sql.y:3799 { yySLICE := (*SubPartitionDefinitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].subPartitionDefinitionUnion()) } - case 686: + case 690: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SubPartitionDefinition -//line sql.y:3781 +//line sql.y:3805 { yyLOCAL = &SubPartitionDefinition{Name: yyDollar[2].identifierCI, Options: yyDollar[3].subPartitionDefinitionOptionsUnion()} } yyVAL.union = yyLOCAL - case 687: + case 691: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3786 +//line sql.y:3810 { yyLOCAL = &SubPartitionDefinitionOptions{} } yyVAL.union = yyLOCAL - case 688: + case 692: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3790 +//line sql.y:3814 { yyDollar[1].subPartitionDefinitionOptionsUnion().Comment = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 689: + case 693: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3795 +//line sql.y:3819 { yyDollar[1].subPartitionDefinitionOptionsUnion().Engine = yyDollar[2].partitionEngineUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 690: + case 694: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3800 +//line sql.y:3824 { yyDollar[1].subPartitionDefinitionOptionsUnion().DataDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 691: + case 695: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3805 +//line sql.y:3829 { yyDollar[1].subPartitionDefinitionOptionsUnion().IndexDirectory = yyDollar[2].literalUnion() yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 692: + case 696: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3810 +//line sql.y:3834 { - val := yyDollar[2].integerUnion() - yyDollar[1].subPartitionDefinitionOptionsUnion().MaxRows = &val + yyDollar[1].subPartitionDefinitionOptionsUnion().MaxRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 693: + case 697: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3816 +//line sql.y:3839 { - val := yyDollar[2].integerUnion() - yyDollar[1].subPartitionDefinitionOptionsUnion().MinRows = &val + yyDollar[1].subPartitionDefinitionOptionsUnion().MinRows = ptr.Of(yyDollar[2].integerUnion()) yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 694: + case 698: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *SubPartitionDefinitionOptions -//line sql.y:3822 +//line sql.y:3844 { yyDollar[1].subPartitionDefinitionOptionsUnion().TableSpace = yyDollar[2].str yyLOCAL = yyDollar[1].subPartitionDefinitionOptionsUnion() } yyVAL.union = yyLOCAL - case 695: + case 699: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3829 +//line sql.y:3851 { yyLOCAL = &PartitionValueRange{ Type: LessThanType, @@ -15109,10 +15272,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 696: + case 700: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3836 +//line sql.y:3858 { yyLOCAL = &PartitionValueRange{ Type: LessThanType, @@ -15120,10 +15283,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 697: + case 701: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *PartitionValueRange -//line sql.y:3843 +//line sql.y:3865 { yyLOCAL = &PartitionValueRange{ Type: InType, @@ -15131,131 +15294,131 @@ yydefault: } } yyVAL.union = yyLOCAL - case 698: + case 702: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:3851 +//line sql.y:3873 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 699: + case 703: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:3855 +//line sql.y:3877 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 700: + case 704: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *PartitionEngine -//line sql.y:3861 +//line sql.y:3883 { yyLOCAL = &PartitionEngine{Storage: yyDollar[1].booleanUnion(), Name: yyDollar[4].identifierCS.String()} } yyVAL.union = yyLOCAL - case 701: + case 705: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Literal -//line sql.y:3867 +//line sql.y:3889 { yyLOCAL = NewStrLiteral(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 702: + case 706: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Literal -//line sql.y:3873 +//line sql.y:3895 { yyLOCAL = NewStrLiteral(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 703: + case 707: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Literal -//line sql.y:3879 +//line sql.y:3901 { yyLOCAL = NewStrLiteral(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 704: + case 708: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3885 +//line sql.y:3907 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 705: + case 709: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:3891 +//line sql.y:3913 { yyLOCAL = convertStringToInt(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 706: + case 710: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3897 +//line sql.y:3919 { yyVAL.str = yyDollar[3].identifierCS.String() } - case 707: + case 711: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *PartitionDefinition -//line sql.y:3903 +//line sql.y:3925 { yyLOCAL = &PartitionDefinition{Name: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 708: + case 712: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3909 +//line sql.y:3931 { yyVAL.str = "" } - case 709: + case 713: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3913 +//line sql.y:3935 { yyVAL.str = "" } - case 710: + case 714: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3919 +//line sql.y:3941 { yyLOCAL = &RenameTable{TablePairs: yyDollar[3].renameTablePairsUnion()} } yyVAL.union = yyLOCAL - case 711: + case 715: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*RenameTablePair -//line sql.y:3925 +//line sql.y:3947 { yyLOCAL = []*RenameTablePair{{FromTable: yyDollar[1].tableName, ToTable: yyDollar[3].tableName}} } yyVAL.union = yyLOCAL - case 712: + case 716: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3929 +//line sql.y:3951 { yySLICE := (*[]*RenameTablePair)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, &RenameTablePair{FromTable: yyDollar[3].tableName, ToTable: yyDollar[5].tableName}) } - case 713: + case 717: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3935 +//line sql.y:3957 { yyLOCAL = &DropTable{FromTables: yyDollar[6].tableNamesUnion(), IfExists: yyDollar[5].booleanUnion(), Comments: Comments(yyDollar[2].strs).Parsed(), Temp: yyDollar[3].booleanUnion()} } yyVAL.union = yyLOCAL - case 714: + case 718: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3939 +//line sql.y:3961 { // Change this to an alter statement if yyDollar[4].identifierCI.Lowered() == "primary" { @@ -15265,1335 +15428,1327 @@ yydefault: } } yyVAL.union = yyLOCAL - case 715: + case 719: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:3948 +//line sql.y:3970 { yyLOCAL = &DropView{FromTables: yyDollar[5].tableNamesUnion(), Comments: Comments(yyDollar[2].strs).Parsed(), IfExists: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 716: + case 720: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3952 +//line sql.y:3974 { yyLOCAL = &DropDatabase{Comments: Comments(yyDollar[2].strs).Parsed(), DBName: yyDollar[5].identifierCS, IfExists: yyDollar[4].booleanUnion()} } yyVAL.union = yyLOCAL - case 717: + case 721: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3958 +//line sql.y:3980 { yyLOCAL = &TruncateTable{Table: yyDollar[3].tableName} } yyVAL.union = yyLOCAL - case 718: + case 722: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:3962 +//line sql.y:3984 { yyLOCAL = &TruncateTable{Table: yyDollar[2].tableName} } yyVAL.union = yyLOCAL - case 719: + case 723: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:3968 +//line sql.y:3990 { yyLOCAL = &Analyze{IsLocal: yyDollar[2].booleanUnion(), Table: yyDollar[4].tableName} } yyVAL.union = yyLOCAL - case 720: + case 724: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3974 +//line sql.y:3996 { yyLOCAL = &PurgeBinaryLogs{To: string(yyDollar[5].str)} } yyVAL.union = yyLOCAL - case 721: + case 725: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:3978 +//line sql.y:4000 { yyLOCAL = &PurgeBinaryLogs{Before: string(yyDollar[5].str)} } yyVAL.union = yyLOCAL - case 722: + case 726: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3984 +//line sql.y:4006 { yyLOCAL = &Show{&ShowBasic{Command: Charset, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 723: + case 727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3988 +//line sql.y:4010 { yyLOCAL = &Show{&ShowBasic{Command: Collation, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 724: + case 728: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:3992 +//line sql.y:4014 { yyLOCAL = &Show{&ShowBasic{Full: yyDollar[2].booleanUnion(), Command: Column, Tbl: yyDollar[5].tableName, DbName: yyDollar[6].identifierCS, Filter: yyDollar[7].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 725: + case 729: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:3996 +//line sql.y:4018 { yyLOCAL = &Show{&ShowBasic{Command: Database, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 726: + case 730: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4000 +//line sql.y:4022 { yyLOCAL = &Show{&ShowBasic{Command: Database, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 727: + case 731: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4004 +//line sql.y:4026 { yyLOCAL = &Show{&ShowBasic{Command: Keyspace, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 728: + case 732: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4008 +//line sql.y:4030 { yyLOCAL = &Show{&ShowBasic{Command: Keyspace, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 729: + case 733: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4012 +//line sql.y:4034 { yyLOCAL = &Show{&ShowBasic{Command: Function, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 730: + case 734: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:4016 +//line sql.y:4038 { yyLOCAL = &Show{&ShowBasic{Command: Index, Tbl: yyDollar[5].tableName, DbName: yyDollar[6].identifierCS, Filter: yyDollar[7].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 731: + case 735: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4020 +//line sql.y:4042 { yyLOCAL = &Show{&ShowBasic{Command: OpenTable, DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 732: + case 736: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4024 +//line sql.y:4046 { yyLOCAL = &Show{&ShowBasic{Command: Privilege}} } yyVAL.union = yyLOCAL - case 733: + case 737: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4028 +//line sql.y:4050 { yyLOCAL = &Show{&ShowBasic{Command: Procedure, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 734: + case 738: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4032 +//line sql.y:4054 { yyLOCAL = &Show{&ShowBasic{Command: StatusSession, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 735: + case 739: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4036 +//line sql.y:4058 { yyLOCAL = &Show{&ShowBasic{Command: StatusGlobal, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 736: + case 740: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4040 +//line sql.y:4062 { yyLOCAL = &Show{&ShowBasic{Command: VariableSession, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 737: + case 741: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4044 +//line sql.y:4066 { yyLOCAL = &Show{&ShowBasic{Command: VariableGlobal, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 738: + case 742: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4048 +//line sql.y:4070 { yyLOCAL = &Show{&ShowBasic{Command: TableStatus, DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 739: + case 743: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4052 +//line sql.y:4074 { yyLOCAL = &Show{&ShowBasic{Command: Table, Full: yyDollar[2].booleanUnion(), DbName: yyDollar[4].identifierCS, Filter: yyDollar[5].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 740: + case 744: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4056 +//line sql.y:4078 { yyLOCAL = &Show{&ShowBasic{Command: Trigger, DbName: yyDollar[3].identifierCS, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 741: + case 745: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4060 +//line sql.y:4082 { yyLOCAL = &Show{&ShowCreate{Command: CreateDb, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 742: + case 746: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4064 +//line sql.y:4086 { yyLOCAL = &Show{&ShowCreate{Command: CreateE, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 743: + case 747: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4068 +//line sql.y:4090 { yyLOCAL = &Show{&ShowCreate{Command: CreateF, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 744: + case 748: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4072 +//line sql.y:4094 { yyLOCAL = &Show{&ShowCreate{Command: CreateProc, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 745: + case 749: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4076 +//line sql.y:4098 { yyLOCAL = &Show{&ShowCreate{Command: CreateTbl, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 746: + case 750: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4080 +//line sql.y:4102 { yyLOCAL = &Show{&ShowCreate{Command: CreateTr, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 747: + case 751: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4084 +//line sql.y:4106 { yyLOCAL = &Show{&ShowCreate{Command: CreateV, Op: yyDollar[4].tableName}} } yyVAL.union = yyLOCAL - case 748: + case 752: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4088 +//line sql.y:4110 { yyLOCAL = &Show{&ShowBasic{Command: Engines}} } yyVAL.union = yyLOCAL - case 749: + case 753: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4092 +//line sql.y:4114 { yyLOCAL = &Show{&ShowBasic{Command: Plugins}} } yyVAL.union = yyLOCAL - case 750: + case 754: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4096 +//line sql.y:4118 { yyLOCAL = &Show{&ShowBasic{Command: GtidExecGlobal, DbName: yyDollar[4].identifierCS}} } yyVAL.union = yyLOCAL - case 751: + case 755: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4100 +//line sql.y:4122 { yyLOCAL = &Show{&ShowBasic{Command: VGtidExecGlobal, DbName: yyDollar[4].identifierCS}} } yyVAL.union = yyLOCAL - case 752: + case 756: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4104 +//line sql.y:4126 { yyLOCAL = &Show{&ShowBasic{Command: VitessVariables, Filter: yyDollar[4].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 753: + case 757: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4108 +//line sql.y:4130 { yyLOCAL = &Show{&ShowBasic{Command: VitessMigrations, Filter: yyDollar[4].showFilterUnion(), DbName: yyDollar[3].identifierCS}} } yyVAL.union = yyLOCAL - case 754: + case 758: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4112 +//line sql.y:4134 { yyLOCAL = &ShowMigrationLogs{UUID: string(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 755: + case 759: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4116 +//line sql.y:4138 { yyLOCAL = &ShowThrottledApps{} } yyVAL.union = yyLOCAL - case 756: + case 760: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4120 +//line sql.y:4142 { yyLOCAL = &Show{&ShowBasic{Command: VitessReplicationStatus, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 757: + case 761: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4124 +//line sql.y:4146 { yyLOCAL = &ShowThrottlerStatus{} } yyVAL.union = yyLOCAL - case 758: + case 762: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4128 +//line sql.y:4150 { yyLOCAL = &Show{&ShowBasic{Command: VschemaTables}} } yyVAL.union = yyLOCAL - case 759: + case 763: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL Statement +//line sql.y:4154 + { + yyLOCAL = &Show{&ShowBasic{Command: VschemaKeyspaces}} + } + yyVAL.union = yyLOCAL + case 764: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4132 +//line sql.y:4158 { yyLOCAL = &Show{&ShowBasic{Command: VschemaVindexes}} } yyVAL.union = yyLOCAL - case 760: + case 765: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4136 +//line sql.y:4162 { yyLOCAL = &Show{&ShowBasic{Command: VschemaVindexes, Tbl: yyDollar[5].tableName}} } yyVAL.union = yyLOCAL - case 761: + case 766: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4140 +//line sql.y:4166 { yyLOCAL = &Show{&ShowBasic{Command: Warnings}} } yyVAL.union = yyLOCAL - case 762: + case 767: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4144 +//line sql.y:4170 { yyLOCAL = &Show{&ShowBasic{Command: VitessShards, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 763: + case 768: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4148 +//line sql.y:4174 { yyLOCAL = &Show{&ShowBasic{Command: VitessTablets, Filter: yyDollar[3].showFilterUnion()}} } yyVAL.union = yyLOCAL - case 764: + case 769: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4152 +//line sql.y:4178 { yyLOCAL = &Show{&ShowBasic{Command: VitessTarget}} } yyVAL.union = yyLOCAL - case 765: + case 770: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4159 +//line sql.y:4185 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].identifierCI.String())}} } yyVAL.union = yyLOCAL - case 766: + case 771: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4163 +//line sql.y:4189 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 767: + case 772: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4167 +//line sql.y:4193 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + yyDollar[3].identifierCI.String()}} } yyVAL.union = yyLOCAL - case 768: + case 773: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4171 +//line sql.y:4197 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 769: + case 774: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4175 +//line sql.y:4201 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str)}} } yyVAL.union = yyLOCAL - case 770: + case 775: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4179 +//line sql.y:4205 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str) + " " + String(yyDollar[4].tableName)}} } yyVAL.union = yyLOCAL - case 771: + case 776: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4183 +//line sql.y:4209 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str) + " " + string(yyDollar[3].str) + " " + String(yyDollar[4].tableName)}} } yyVAL.union = yyLOCAL - case 772: + case 777: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4187 +//line sql.y:4213 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[3].str)}} } yyVAL.union = yyLOCAL - case 773: + case 778: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4191 +//line sql.y:4217 { yyLOCAL = &Show{&ShowOther{Command: string(yyDollar[2].str)}} } yyVAL.union = yyLOCAL - case 774: + case 779: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4197 +//line sql.y:4223 { yyVAL.str = "" } - case 775: + case 780: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4201 +//line sql.y:4227 { yyVAL.str = "extended " } - case 776: + case 781: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4207 +//line sql.y:4233 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 777: + case 782: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4211 +//line sql.y:4237 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 778: + case 783: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4217 +//line sql.y:4243 { yyVAL.str = string(yyDollar[1].str) } - case 779: + case 784: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4221 +//line sql.y:4247 { yyVAL.str = string(yyDollar[1].str) } - case 780: + case 785: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4227 +//line sql.y:4253 { yyVAL.identifierCS = NewIdentifierCS("") } - case 781: + case 786: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4231 +//line sql.y:4257 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 782: + case 787: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4235 +//line sql.y:4261 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 783: + case 788: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4241 +//line sql.y:4267 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 784: + case 789: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4245 +//line sql.y:4271 { yyLOCAL = &ShowFilter{Like: string(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 785: + case 790: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4249 +//line sql.y:4275 { yyLOCAL = &ShowFilter{Filter: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 786: + case 791: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4255 +//line sql.y:4281 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 787: + case 792: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ShowFilter -//line sql.y:4259 +//line sql.y:4285 { yyLOCAL = &ShowFilter{Like: string(yyDollar[2].str)} } yyVAL.union = yyLOCAL - case 788: + case 793: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4265 +//line sql.y:4291 { yyVAL.empty = struct{}{} } - case 789: + case 794: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4269 +//line sql.y:4295 { yyVAL.empty = struct{}{} } - case 790: + case 795: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4273 +//line sql.y:4299 { yyVAL.empty = struct{}{} } - case 791: + case 796: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4279 +//line sql.y:4305 { yyVAL.str = string(yyDollar[1].str) } - case 792: + case 797: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4283 +//line sql.y:4309 { yyVAL.str = string(yyDollar[1].str) } - case 793: + case 798: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4289 +//line sql.y:4315 { yyLOCAL = &Use{DBName: yyDollar[2].identifierCS} } yyVAL.union = yyLOCAL - case 794: + case 799: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4293 +//line sql.y:4319 { yyLOCAL = &Use{DBName: IdentifierCS{v: ""}} } yyVAL.union = yyLOCAL - case 795: + case 800: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4297 +//line sql.y:4323 { yyLOCAL = &Use{DBName: NewIdentifierCS(yyDollar[2].identifierCS.String() + "@" + string(yyDollar[3].str))} } yyVAL.union = yyLOCAL - case 796: + case 801: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4304 +//line sql.y:4330 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 797: + case 802: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4308 +//line sql.y:4334 { yyVAL.identifierCS = NewIdentifierCS("@" + string(yyDollar[1].str)) } - case 798: + case 803: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4312 +//line sql.y:4338 { yyVAL.identifierCS = NewIdentifierCS("@@" + string(yyDollar[1].str)) } - case 799: + case 804: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4316 +//line sql.y:4342 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 800: + case 805: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4323 +//line sql.y:4349 { yyLOCAL = &Begin{} } yyVAL.union = yyLOCAL - case 801: + case 806: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4327 +//line sql.y:4353 { yyLOCAL = &Begin{TxAccessModes: yyDollar[3].txAccessModesUnion()} } yyVAL.union = yyLOCAL - case 802: + case 807: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4332 +//line sql.y:4358 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 803: + case 808: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4336 +//line sql.y:4362 { yyLOCAL = yyDollar[1].txAccessModesUnion() } yyVAL.union = yyLOCAL - case 804: + case 809: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []TxAccessMode -//line sql.y:4342 +//line sql.y:4368 { yyLOCAL = []TxAccessMode{yyDollar[1].txAccessModeUnion()} } yyVAL.union = yyLOCAL - case 805: + case 810: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4346 +//line sql.y:4372 { yySLICE := (*[]TxAccessMode)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].txAccessModeUnion()) } - case 806: + case 811: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4352 +//line sql.y:4378 { yyLOCAL = WithConsistentSnapshot } yyVAL.union = yyLOCAL - case 807: + case 812: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4356 +//line sql.y:4382 { yyLOCAL = ReadWrite } yyVAL.union = yyLOCAL - case 808: + case 813: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TxAccessMode -//line sql.y:4360 +//line sql.y:4386 { yyLOCAL = ReadOnly } yyVAL.union = yyLOCAL - case 809: + case 814: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4367 +//line sql.y:4393 { yyLOCAL = &Commit{} } yyVAL.union = yyLOCAL - case 810: + case 815: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4373 +//line sql.y:4399 { yyLOCAL = &Rollback{} } yyVAL.union = yyLOCAL - case 811: + case 816: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4377 +//line sql.y:4403 { yyLOCAL = &SRollback{Name: yyDollar[5].identifierCI} } yyVAL.union = yyLOCAL - case 812: + case 817: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4382 +//line sql.y:4408 { yyVAL.empty = struct{}{} } - case 813: + case 818: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4384 +//line sql.y:4410 { yyVAL.empty = struct{}{} } - case 814: + case 819: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4387 +//line sql.y:4413 { yyVAL.empty = struct{}{} } - case 815: + case 820: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4389 +//line sql.y:4415 { yyVAL.empty = struct{}{} } - case 816: + case 821: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4393 +//line sql.y:4419 { yyLOCAL = &Savepoint{Name: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 817: - yyDollar = yyS[yypt-3 : yypt+1] - var yyLOCAL Statement -//line sql.y:4399 - { - yyLOCAL = &Release{Name: yyDollar[3].identifierCI} - } - yyVAL.union = yyLOCAL - case 818: - yyDollar = yyS[yypt-0 : yypt+1] - var yyLOCAL ExplainType -//line sql.y:4404 - { - yyLOCAL = EmptyType - } - yyVAL.union = yyLOCAL - case 819: + case 822: yyDollar = yyS[yypt-3 : yypt+1] - var yyLOCAL ExplainType -//line sql.y:4408 + var yyLOCAL Statement +//line sql.y:4425 { - yyLOCAL = JSONType + yyLOCAL = &Release{Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 820: - yyDollar = yyS[yypt-3 : yypt+1] + case 823: + yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4412 +//line sql.y:4430 { - yyLOCAL = TreeType + yyLOCAL = EmptyType } yyVAL.union = yyLOCAL - case 821: + case 824: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4416 +//line sql.y:4434 { - yyLOCAL = VitessType + yyLOCAL = JSONType } yyVAL.union = yyLOCAL - case 822: + case 825: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4420 +//line sql.y:4438 { - yyLOCAL = VTExplainType + yyLOCAL = TreeType } yyVAL.union = yyLOCAL - case 823: + case 826: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4424 +//line sql.y:4442 { yyLOCAL = TraditionalType } yyVAL.union = yyLOCAL - case 824: + case 827: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ExplainType -//line sql.y:4428 +//line sql.y:4446 { yyLOCAL = AnalyzeType } yyVAL.union = yyLOCAL - case 825: + case 828: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4433 +//line sql.y:4451 { yyLOCAL = PlanVExplainType } yyVAL.union = yyLOCAL - case 826: + case 829: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4437 +//line sql.y:4455 { yyLOCAL = PlanVExplainType } yyVAL.union = yyLOCAL - case 827: + case 830: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4441 +//line sql.y:4459 { yyLOCAL = AllVExplainType } yyVAL.union = yyLOCAL - case 828: + case 831: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL VExplainType -//line sql.y:4445 +//line sql.y:4463 { yyLOCAL = QueriesVExplainType } yyVAL.union = yyLOCAL - case 829: + case 832: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4451 +//line sql.y:4469 { yyVAL.str = yyDollar[1].str } - case 830: + case 833: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4455 +//line sql.y:4473 { yyVAL.str = yyDollar[1].str } - case 831: + case 834: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4459 +//line sql.y:4477 { yyVAL.str = yyDollar[1].str } - case 832: + case 835: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4465 +//line sql.y:4483 { yyLOCAL = yyDollar[1].selStmtUnion() } yyVAL.union = yyLOCAL - case 833: + case 836: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4469 +//line sql.y:4487 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 834: + case 837: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4473 +//line sql.y:4491 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 835: + case 838: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Statement -//line sql.y:4477 +//line sql.y:4495 { yyLOCAL = yyDollar[1].statementUnion() } yyVAL.union = yyLOCAL - case 836: + case 839: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4482 +//line sql.y:4500 { yyVAL.str = "" } - case 837: + case 840: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4486 +//line sql.y:4504 { yyVAL.str = yyDollar[1].identifierCI.val } - case 838: + case 841: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4490 +//line sql.y:4508 { yyVAL.str = encodeSQLString(yyDollar[1].str) } - case 839: + case 842: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4496 +//line sql.y:4514 { yyLOCAL = &ExplainTab{Table: yyDollar[3].tableName, Wild: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 840: + case 843: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4500 +//line sql.y:4518 { yyLOCAL = &ExplainStmt{Type: yyDollar[3].explainTypeUnion(), Statement: yyDollar[4].statementUnion(), Comments: Comments(yyDollar[2].strs).Parsed()} } yyVAL.union = yyLOCAL - case 841: + case 844: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4506 +//line sql.y:4524 { yyLOCAL = &VExplainStmt{Type: yyDollar[3].vexplainTypeUnion(), Statement: yyDollar[4].statementUnion(), Comments: Comments(yyDollar[2].strs).Parsed()} } yyVAL.union = yyLOCAL - case 842: + case 845: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4512 +//line sql.y:4530 { yyLOCAL = &OtherAdmin{} } yyVAL.union = yyLOCAL - case 843: + case 846: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4516 +//line sql.y:4534 { yyLOCAL = &OtherAdmin{} } yyVAL.union = yyLOCAL - case 844: + case 847: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4522 +//line sql.y:4540 { yyLOCAL = &LockTables{Tables: yyDollar[3].tableAndLockTypesUnion()} } yyVAL.union = yyLOCAL - case 845: + case 848: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableAndLockTypes -//line sql.y:4528 +//line sql.y:4546 { yyLOCAL = TableAndLockTypes{yyDollar[1].tableAndLockTypeUnion()} } yyVAL.union = yyLOCAL - case 846: + case 849: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4532 +//line sql.y:4550 { yySLICE := (*TableAndLockTypes)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableAndLockTypeUnion()) } - case 847: + case 850: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *TableAndLockType -//line sql.y:4538 +//line sql.y:4556 { yyLOCAL = &TableAndLockType{Table: yyDollar[1].aliasedTableNameUnion(), Lock: yyDollar[2].lockTypeUnion()} } yyVAL.union = yyLOCAL - case 848: + case 851: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LockType -//line sql.y:4544 +//line sql.y:4562 { yyLOCAL = Read } yyVAL.union = yyLOCAL - case 849: + case 852: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL LockType -//line sql.y:4548 +//line sql.y:4566 { yyLOCAL = ReadLocal } yyVAL.union = yyLOCAL - case 850: + case 853: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LockType -//line sql.y:4552 +//line sql.y:4570 { yyLOCAL = Write } yyVAL.union = yyLOCAL - case 851: + case 854: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL LockType -//line sql.y:4556 +//line sql.y:4574 { yyLOCAL = LowPriorityWrite } yyVAL.union = yyLOCAL - case 852: + case 855: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Statement -//line sql.y:4562 +//line sql.y:4580 { yyLOCAL = &UnlockTables{} } yyVAL.union = yyLOCAL - case 853: + case 856: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4568 +//line sql.y:4586 { yyLOCAL = &RevertMigration{Comments: Comments(yyDollar[2].strs).Parsed(), UUID: string(yyDollar[4].str)} } yyVAL.union = yyLOCAL - case 854: + case 857: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4574 +//line sql.y:4592 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), FlushOptions: yyDollar[3].strs} } yyVAL.union = yyLOCAL - case 855: + case 858: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:4578 +//line sql.y:4596 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion()} } yyVAL.union = yyLOCAL - case 856: + case 859: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:4582 +//line sql.y:4600 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), WithLock: true} } yyVAL.union = yyLOCAL - case 857: + case 860: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4586 +//line sql.y:4604 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion()} } yyVAL.union = yyLOCAL - case 858: + case 861: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Statement -//line sql.y:4590 +//line sql.y:4608 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion(), WithLock: true} } yyVAL.union = yyLOCAL - case 859: + case 862: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Statement -//line sql.y:4594 +//line sql.y:4612 { yyLOCAL = &Flush{IsLocal: yyDollar[2].booleanUnion(), TableNames: yyDollar[4].tableNamesUnion(), ForExport: true} } yyVAL.union = yyLOCAL - case 860: + case 863: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4600 +//line sql.y:4618 { yyVAL.strs = []string{yyDollar[1].str} } - case 861: + case 864: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4604 +//line sql.y:4622 { yyVAL.strs = append(yyDollar[1].strs, yyDollar[3].str) } - case 862: + case 865: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4610 +//line sql.y:4628 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 863: + case 866: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4614 +//line sql.y:4632 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 864: + case 867: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4618 +//line sql.y:4636 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 865: + case 868: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4622 +//line sql.y:4640 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 866: + case 869: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4626 +//line sql.y:4644 { yyVAL.str = string(yyDollar[1].str) } - case 867: + case 870: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4630 +//line sql.y:4648 { yyVAL.str = string(yyDollar[1].str) } - case 868: + case 871: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4634 +//line sql.y:4652 { yyVAL.str = string(yyDollar[1].str) } - case 869: + case 872: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4638 +//line sql.y:4656 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) + yyDollar[3].str } - case 870: + case 873: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4642 +//line sql.y:4660 { yyVAL.str = string(yyDollar[1].str) + " " + string(yyDollar[2].str) } - case 871: + case 874: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4646 +//line sql.y:4664 { yyVAL.str = string(yyDollar[1].str) } - case 872: + case 875: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4650 +//line sql.y:4668 { yyVAL.str = string(yyDollar[1].str) } - case 873: + case 876: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4654 +//line sql.y:4672 { yyVAL.str = string(yyDollar[1].str) } - case 874: + case 877: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4659 +//line sql.y:4677 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 875: + case 878: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4663 +//line sql.y:4681 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 876: + case 879: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4667 +//line sql.y:4685 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 877: + case 880: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4672 +//line sql.y:4690 { yyVAL.str = "" } - case 878: + case 881: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4676 +//line sql.y:4694 { yyVAL.str = " " + string(yyDollar[1].str) + " " + string(yyDollar[2].str) + " " + yyDollar[3].identifierCI.String() } - case 879: + case 882: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4681 +//line sql.y:4699 { setAllowComments(yylex, true) } - case 880: + case 883: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4685 +//line sql.y:4703 { yyVAL.strs = yyDollar[2].strs setAllowComments(yylex, false) } - case 881: + case 884: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4691 +//line sql.y:4709 { yyVAL.strs = nil } - case 882: + case 885: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4695 +//line sql.y:4713 { yyVAL.strs = append(yyDollar[1].strs, yyDollar[2].str) } - case 883: + case 886: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4701 +//line sql.y:4719 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 884: + case 887: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:4705 +//line sql.y:4723 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 885: + case 888: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:4709 +//line sql.y:4727 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 886: + case 889: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4714 +//line sql.y:4732 { yyVAL.str = "" } - case 887: + case 890: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4718 +//line sql.y:4736 { yyVAL.str = SQLNoCacheStr } - case 888: + case 891: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4722 +//line sql.y:4740 { yyVAL.str = SQLCacheStr } - case 889: + case 892: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:4727 +//line sql.y:4745 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 890: + case 893: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4731 +//line sql.y:4749 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 891: + case 894: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:4735 +//line sql.y:4753 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 892: + case 895: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4741 +//line sql.y:4759 { yyLOCAL = &PrepareStmt{Name: yyDollar[3].identifierCI, Comments: Comments(yyDollar[2].strs).Parsed(), Statement: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 893: + case 896: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:4745 +//line sql.y:4763 { yyLOCAL = &PrepareStmt{ Name: yyDollar[3].identifierCI, @@ -16602,595 +16757,589 @@ yydefault: } } yyVAL.union = yyLOCAL - case 894: + case 897: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4755 +//line sql.y:4773 { yyLOCAL = &ExecuteStmt{Name: yyDollar[3].identifierCI, Comments: Comments(yyDollar[2].strs).Parsed(), Arguments: yyDollar[4].variablesUnion()} } yyVAL.union = yyLOCAL - case 895: + case 898: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*Variable -//line sql.y:4760 +//line sql.y:4778 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 896: + case 899: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*Variable -//line sql.y:4764 +//line sql.y:4782 { yyLOCAL = yyDollar[2].variablesUnion() } yyVAL.union = yyLOCAL - case 897: + case 900: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4770 +//line sql.y:4788 { yyLOCAL = &DeallocateStmt{Comments: Comments(yyDollar[2].strs).Parsed(), Name: yyDollar[4].identifierCI} } yyVAL.union = yyLOCAL - case 898: + case 901: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Statement -//line sql.y:4774 +//line sql.y:4792 { yyLOCAL = &DeallocateStmt{Comments: Comments(yyDollar[2].strs).Parsed(), Name: yyDollar[4].identifierCI} } yyVAL.union = yyLOCAL - case 899: + case 902: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL SelectExprs -//line sql.y:4779 +//line sql.y:4797 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 900: + case 903: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectExprs -//line sql.y:4783 +//line sql.y:4801 { yyLOCAL = yyDollar[1].selectExprsUnion() } yyVAL.union = yyLOCAL - case 901: + case 904: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4788 +//line sql.y:4806 { yyVAL.strs = nil } - case 902: + case 905: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4792 +//line sql.y:4810 { - yyVAL.strs = []string{yyDollar[1].str} - } - case 903: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4796 - { // TODO: This is a hack since I couldn't get it to work in a nicer way. I got 'conflicts: 8 shift/reduce' - yyVAL.strs = []string{yyDollar[1].str, yyDollar[2].str} + yyVAL.strs = yyDollar[1].strs } - case 904: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4800 + case 906: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:4816 { - yyVAL.strs = []string{yyDollar[1].str, yyDollar[2].str, yyDollar[3].str} + yyVAL.strs = []string{yyDollar[1].str} } - case 905: - yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:4804 + case 907: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:4820 { - yyVAL.strs = []string{yyDollar[1].str, yyDollar[2].str, yyDollar[3].str, yyDollar[4].str} + yyVAL.strs = append(yyDollar[1].strs, yyDollar[2].str) } - case 906: + case 908: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4810 +//line sql.y:4826 { yyVAL.str = SQLNoCacheStr } - case 907: + case 909: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4814 +//line sql.y:4830 { yyVAL.str = SQLCacheStr } - case 908: + case 910: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4818 +//line sql.y:4834 { yyVAL.str = DistinctStr } - case 909: + case 911: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4822 +//line sql.y:4838 { yyVAL.str = DistinctStr } - case 910: + case 912: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4826 +//line sql.y:4842 { yyVAL.str = StraightJoinHint } - case 911: + case 913: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4830 +//line sql.y:4846 { yyVAL.str = SQLCalcFoundRowsStr } - case 912: + case 914: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4834 +//line sql.y:4850 { yyVAL.str = AllStr // These are not picked up by NewSelect, and so ALL will be dropped. But this is OK, since it's redundant anyway } - case 913: + case 915: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectExprs -//line sql.y:4840 +//line sql.y:4856 { yyLOCAL = SelectExprs{yyDollar[1].selectExprUnion()} } yyVAL.union = yyLOCAL - case 914: + case 916: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4844 +//line sql.y:4860 { yySLICE := (*SelectExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].selectExprUnion()) } - case 915: + case 917: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4850 +//line sql.y:4866 { yyLOCAL = &StarExpr{} } yyVAL.union = yyLOCAL - case 916: + case 918: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4854 +//line sql.y:4870 { yyLOCAL = &AliasedExpr{Expr: yyDollar[1].exprUnion(), As: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 917: + case 919: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4858 +//line sql.y:4874 { yyLOCAL = &StarExpr{TableName: TableName{Name: yyDollar[1].identifierCS}} } yyVAL.union = yyLOCAL - case 918: + case 920: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL SelectExpr -//line sql.y:4862 +//line sql.y:4878 { yyLOCAL = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS}} } yyVAL.union = yyLOCAL - case 919: + case 921: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:4867 +//line sql.y:4883 { yyVAL.identifierCI = IdentifierCI{} } - case 920: + case 922: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4871 +//line sql.y:4887 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 921: + case 923: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:4875 +//line sql.y:4891 { yyVAL.identifierCI = yyDollar[2].identifierCI } - case 923: + case 925: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:4882 +//line sql.y:4898 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 924: + case 926: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4887 +//line sql.y:4903 { yyLOCAL = TableExprs{&AliasedTableExpr{Expr: TableName{Name: NewIdentifierCS("dual")}}} } yyVAL.union = yyLOCAL - case 925: + case 927: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4891 +//line sql.y:4907 { yyLOCAL = yyDollar[1].tableExprsUnion() } yyVAL.union = yyLOCAL - case 926: + case 928: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4897 +//line sql.y:4913 { yyLOCAL = yyDollar[2].tableExprsUnion() } yyVAL.union = yyLOCAL - case 927: + case 929: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExprs -//line sql.y:4903 +//line sql.y:4919 { yyLOCAL = TableExprs{yyDollar[1].tableExprUnion()} } yyVAL.union = yyLOCAL - case 928: + case 930: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4907 +//line sql.y:4923 { yySLICE := (*TableExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].tableExprUnion()) } - case 931: + case 933: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4917 +//line sql.y:4933 { yyLOCAL = yyDollar[1].aliasedTableNameUnion() } yyVAL.union = yyLOCAL - case 932: + case 934: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4921 +//line sql.y:4937 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].derivedTableUnion(), As: yyDollar[3].identifierCS, Columns: yyDollar[4].columnsUnion()} } yyVAL.union = yyLOCAL - case 933: + case 935: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4925 +//line sql.y:4941 { yyLOCAL = &ParenTableExpr{Exprs: yyDollar[2].tableExprsUnion()} } yyVAL.union = yyLOCAL - case 934: + case 936: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TableExpr -//line sql.y:4929 +//line sql.y:4945 { yyLOCAL = yyDollar[1].tableExprUnion() } yyVAL.union = yyLOCAL - case 935: + case 937: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *DerivedTable -//line sql.y:4935 +//line sql.y:4951 { yyLOCAL = &DerivedTable{Lateral: false, Select: yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 936: + case 938: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *DerivedTable -//line sql.y:4939 +//line sql.y:4955 { yyLOCAL = &DerivedTable{Lateral: true, Select: yyDollar[2].selStmtUnion()} } yyVAL.union = yyLOCAL - case 937: + case 939: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *AliasedTableExpr -//line sql.y:4945 +//line sql.y:4961 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].identifierCS, Hints: yyDollar[3].indexHintsUnion()} } yyVAL.union = yyLOCAL - case 938: + case 940: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *AliasedTableExpr -//line sql.y:4949 +//line sql.y:4965 { yyLOCAL = &AliasedTableExpr{Expr: yyDollar[1].tableName, Partitions: yyDollar[4].partitionsUnion(), As: yyDollar[6].identifierCS, Hints: yyDollar[7].indexHintsUnion()} } yyVAL.union = yyLOCAL - case 939: + case 941: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Columns -//line sql.y:4954 +//line sql.y:4970 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 940: + case 942: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Columns -//line sql.y:4958 +//line sql.y:4974 { yyLOCAL = yyDollar[2].columnsUnion() } yyVAL.union = yyLOCAL - case 941: + case 943: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Columns -//line sql.y:4963 +//line sql.y:4979 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 942: + case 944: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:4967 +//line sql.y:4983 { yyLOCAL = yyDollar[1].columnsUnion() } yyVAL.union = yyLOCAL - case 943: + case 945: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:4973 +//line sql.y:4989 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 944: + case 946: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4977 +//line sql.y:4993 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 945: + case 947: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*Variable -//line sql.y:4983 +//line sql.y:4999 { yyLOCAL = []*Variable{yyDollar[1].variableUnion()} } yyVAL.union = yyLOCAL - case 946: + case 948: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:4987 +//line sql.y:5003 { yySLICE := (*[]*Variable)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].variableUnion()) } - case 947: + case 949: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:4993 +//line sql.y:5009 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 948: + case 950: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:4997 +//line sql.y:5013 { yyLOCAL = Columns{NewIdentifierCI(string(yyDollar[1].str))} } yyVAL.union = yyLOCAL - case 949: + case 951: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5001 +//line sql.y:5017 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 950: + case 952: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5005 +//line sql.y:5021 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, NewIdentifierCI(string(yyDollar[3].str))) } - case 951: + case 953: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Partitions -//line sql.y:5011 +//line sql.y:5027 { yyLOCAL = Partitions{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 952: + case 954: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5015 +//line sql.y:5031 { yySLICE := (*Partitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 953: + case 955: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5028 +//line sql.y:5044 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 954: + case 956: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5032 +//line sql.y:5048 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 955: + case 957: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5036 +//line sql.y:5052 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion(), Condition: yyDollar[4].joinCondition} } yyVAL.union = yyLOCAL - case 956: + case 958: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL TableExpr -//line sql.y:5040 +//line sql.y:5056 { yyLOCAL = &JoinTableExpr{LeftExpr: yyDollar[1].tableExprUnion(), Join: yyDollar[2].joinTypeUnion(), RightExpr: yyDollar[3].tableExprUnion()} } yyVAL.union = yyLOCAL - case 957: + case 959: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5046 +//line sql.y:5062 { yyVAL.joinCondition = &JoinCondition{On: yyDollar[2].exprUnion()} } - case 958: + case 960: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:5048 +//line sql.y:5064 { yyVAL.joinCondition = &JoinCondition{Using: yyDollar[3].columnsUnion()} } - case 959: + case 961: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5052 +//line sql.y:5068 { yyVAL.joinCondition = &JoinCondition{} } - case 960: + case 962: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5054 +//line sql.y:5070 { yyVAL.joinCondition = yyDollar[1].joinCondition } - case 961: + case 963: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5058 +//line sql.y:5074 { yyVAL.joinCondition = &JoinCondition{} } - case 962: + case 964: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5060 +//line sql.y:5076 { yyVAL.joinCondition = &JoinCondition{On: yyDollar[2].exprUnion()} } - case 963: + case 965: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5063 +//line sql.y:5079 { yyVAL.empty = struct{}{} } - case 964: + case 966: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5065 +//line sql.y:5081 { yyVAL.empty = struct{}{} } - case 965: + case 967: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5068 +//line sql.y:5084 { yyVAL.identifierCS = NewIdentifierCS("") } - case 966: + case 968: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5072 +//line sql.y:5088 { yyVAL.identifierCS = yyDollar[1].identifierCS } - case 967: + case 969: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5076 +//line sql.y:5092 { yyVAL.identifierCS = yyDollar[2].identifierCS } - case 969: + case 971: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5083 +//line sql.y:5099 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 970: + case 972: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL JoinType -//line sql.y:5089 +//line sql.y:5105 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 971: + case 973: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5093 +//line sql.y:5109 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 972: + case 974: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5097 +//line sql.y:5113 { yyLOCAL = NormalJoinType } yyVAL.union = yyLOCAL - case 973: + case 975: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL JoinType -//line sql.y:5103 +//line sql.y:5119 { yyLOCAL = StraightJoinType } yyVAL.union = yyLOCAL - case 974: + case 976: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5109 +//line sql.y:5125 { yyLOCAL = LeftJoinType } yyVAL.union = yyLOCAL - case 975: + case 977: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL JoinType -//line sql.y:5113 +//line sql.y:5129 { yyLOCAL = LeftJoinType } yyVAL.union = yyLOCAL - case 976: + case 978: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5117 +//line sql.y:5133 { yyLOCAL = RightJoinType } yyVAL.union = yyLOCAL - case 977: + case 979: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL JoinType -//line sql.y:5121 +//line sql.y:5137 { yyLOCAL = RightJoinType } yyVAL.union = yyLOCAL - case 978: + case 980: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5127 +//line sql.y:5143 { yyLOCAL = NaturalJoinType } yyVAL.union = yyLOCAL - case 979: + case 981: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL JoinType -//line sql.y:5131 +//line sql.y:5147 { if yyDollar[2].joinTypeUnion() == LeftJoinType { yyLOCAL = NaturalLeftJoinType @@ -17199,617 +17348,633 @@ yydefault: } } yyVAL.union = yyLOCAL - case 980: + case 982: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5141 +//line sql.y:5157 { yyVAL.tableName = yyDollar[2].tableName } - case 981: + case 983: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5145 +//line sql.y:5161 { yyVAL.tableName = yyDollar[1].tableName } - case 982: + case 984: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5151 +//line sql.y:5167 { yyVAL.tableName = TableName{Name: yyDollar[1].identifierCS} } - case 983: + case 985: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5155 +//line sql.y:5171 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS} } - case 984: + case 986: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5161 +//line sql.y:5177 { yyVAL.tableName = TableName{Name: yyDollar[1].identifierCS} } - case 985: + case 987: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5166 +//line sql.y:5182 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 986: + case 988: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5170 +//line sql.y:5186 { yyLOCAL = yyDollar[1].indexHintsUnion() } yyVAL.union = yyLOCAL - case 987: + case 989: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IndexHints -//line sql.y:5176 +//line sql.y:5192 { yyLOCAL = IndexHints{yyDollar[1].indexHintUnion()} } yyVAL.union = yyLOCAL - case 988: + case 990: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:5180 +//line sql.y:5196 { yySLICE := (*IndexHints)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].indexHintUnion()) } - case 989: + case 991: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5186 +//line sql.y:5202 { yyLOCAL = &IndexHint{Type: UseOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 990: + case 992: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5190 +//line sql.y:5206 { yyLOCAL = &IndexHint{Type: UseOp, ForType: yyDollar[3].indexHintForTypeUnion()} } yyVAL.union = yyLOCAL - case 991: + case 993: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5194 +//line sql.y:5210 { yyLOCAL = &IndexHint{Type: IgnoreOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 992: + case 994: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *IndexHint -//line sql.y:5198 +//line sql.y:5214 { yyLOCAL = &IndexHint{Type: ForceOp, ForType: yyDollar[3].indexHintForTypeUnion(), Indexes: yyDollar[5].columnsUnion()} } yyVAL.union = yyLOCAL - case 993: + case 995: + yyDollar = yyS[yypt-5 : yypt+1] + var yyLOCAL *IndexHint +//line sql.y:5218 + { + yyLOCAL = &IndexHint{Type: UseVindexOp, Indexes: yyDollar[4].columnsUnion()} + } + yyVAL.union = yyLOCAL + case 996: + yyDollar = yyS[yypt-5 : yypt+1] + var yyLOCAL *IndexHint +//line sql.y:5222 + { + yyLOCAL = &IndexHint{Type: IgnoreVindexOp, Indexes: yyDollar[4].columnsUnion()} + } + yyVAL.union = yyLOCAL + case 997: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5203 +//line sql.y:5227 { yyLOCAL = NoForType } yyVAL.union = yyLOCAL - case 994: + case 998: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5207 +//line sql.y:5231 { yyLOCAL = JoinForType } yyVAL.union = yyLOCAL - case 995: + case 999: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5211 +//line sql.y:5235 { yyLOCAL = OrderByForType } yyVAL.union = yyLOCAL - case 996: + case 1000: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL IndexHintForType -//line sql.y:5215 +//line sql.y:5239 { yyLOCAL = GroupByForType } yyVAL.union = yyLOCAL - case 997: + case 1001: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:5221 +//line sql.y:5245 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 998: + case 1002: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5225 +//line sql.y:5249 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 999: + case 1003: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5232 +//line sql.y:5256 { yyLOCAL = &OrExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1000: + case 1004: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5236 +//line sql.y:5260 { yyLOCAL = &XorExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1001: + case 1005: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5240 +//line sql.y:5264 { yyLOCAL = &AndExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1002: + case 1006: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5244 +//line sql.y:5268 { yyLOCAL = &NotExpr{Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1003: + case 1007: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5248 +//line sql.y:5272 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: yyDollar[3].isExprOperatorUnion()} } yyVAL.union = yyLOCAL - case 1004: + case 1008: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5252 +//line sql.y:5276 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1005: + case 1009: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5256 +//line sql.y:5280 { yyLOCAL = &AssignmentExpr{Left: yyDollar[1].variableUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1006: + case 1010: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5260 +//line sql.y:5284 { yyLOCAL = &MemberOfExpr{Value: yyDollar[1].exprUnion(), JSONArr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1007: + case 1011: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5266 +//line sql.y:5290 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: IsNullOp} } yyVAL.union = yyLOCAL - case 1008: + case 1012: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5270 +//line sql.y:5294 { yyLOCAL = &IsExpr{Left: yyDollar[1].exprUnion(), Right: IsNotNullOp} } yyVAL.union = yyLOCAL - case 1009: + case 1013: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5274 +//line sql.y:5298 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: yyDollar[2].comparisonExprOperatorUnion(), Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1010: + case 1014: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5278 +//line sql.y:5302 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1011: + case 1015: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5284 +//line sql.y:5308 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: InOp, Right: yyDollar[3].colTupleUnion()} } yyVAL.union = yyLOCAL - case 1012: + case 1016: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5288 +//line sql.y:5312 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotInOp, Right: yyDollar[4].colTupleUnion()} } yyVAL.union = yyLOCAL - case 1013: + case 1017: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5292 +//line sql.y:5316 { yyLOCAL = &BetweenExpr{Left: yyDollar[1].exprUnion(), IsBetween: true, From: yyDollar[3].exprUnion(), To: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1014: + case 1018: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5296 +//line sql.y:5320 { yyLOCAL = &BetweenExpr{Left: yyDollar[1].exprUnion(), IsBetween: false, From: yyDollar[4].exprUnion(), To: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1015: + case 1019: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5300 +//line sql.y:5324 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: LikeOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1016: + case 1020: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5304 +//line sql.y:5328 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotLikeOp, Right: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1017: + case 1021: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5308 +//line sql.y:5332 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: LikeOp, Right: yyDollar[3].exprUnion(), Escape: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1018: + case 1022: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5312 +//line sql.y:5336 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotLikeOp, Right: yyDollar[4].exprUnion(), Escape: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1019: + case 1023: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5316 +//line sql.y:5340 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: RegexpOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1020: + case 1024: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5320 +//line sql.y:5344 { yyLOCAL = &ComparisonExpr{Left: yyDollar[1].exprUnion(), Operator: NotRegexpOp, Right: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1021: + case 1025: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5324 +//line sql.y:5348 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1022: + case 1026: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5330 +//line sql.y:5354 { } - case 1023: + case 1027: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5333 +//line sql.y:5357 { } - case 1024: + case 1028: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5339 +//line sql.y:5363 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitOrOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1025: + case 1029: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5343 +//line sql.y:5367 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitAndOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1026: + case 1030: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5347 +//line sql.y:5371 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ShiftLeftOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1027: + case 1031: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5351 +//line sql.y:5375 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ShiftRightOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1028: + case 1032: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5355 +//line sql.y:5379 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: PlusOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1029: + case 1033: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5359 +//line sql.y:5383 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: MinusOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1030: + case 1034: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5363 +//line sql.y:5387 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinaryAdd, Date: yyDollar[1].exprUnion(), Unit: yyDollar[5].intervalTypeUnion(), Interval: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1031: + case 1035: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5367 +//line sql.y:5391 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinarySub, Date: yyDollar[1].exprUnion(), Unit: yyDollar[5].intervalTypeUnion(), Interval: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1032: + case 1036: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5371 +//line sql.y:5395 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: MultOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1033: + case 1037: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5375 +//line sql.y:5399 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: DivOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1034: + case 1038: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5379 +//line sql.y:5403 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ModOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1035: + case 1039: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5383 +//line sql.y:5407 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: IntDivOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1036: + case 1040: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5387 +//line sql.y:5411 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: ModOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1037: + case 1041: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5391 +//line sql.y:5415 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: BitXorOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1038: + case 1042: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5395 +//line sql.y:5419 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1039: + case 1043: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5401 +//line sql.y:5425 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1040: + case 1044: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5405 +//line sql.y:5429 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1041: + case 1045: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5409 +//line sql.y:5433 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1042: + case 1046: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5413 +//line sql.y:5437 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1043: + case 1047: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5417 +//line sql.y:5441 { yyLOCAL = &CollateExpr{Expr: yyDollar[1].exprUnion(), Collation: yyDollar[3].str} } yyVAL.union = yyLOCAL - case 1044: + case 1048: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5421 +//line sql.y:5445 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1045: + case 1049: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5425 +//line sql.y:5449 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1046: + case 1050: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5429 +//line sql.y:5453 { yyLOCAL = yyDollar[1].variableUnion() } yyVAL.union = yyLOCAL - case 1047: + case 1051: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5433 +//line sql.y:5457 { yyLOCAL = yyDollar[2].exprUnion() // TODO: do we really want to ignore unary '+' before any kind of literals? } yyVAL.union = yyLOCAL - case 1048: + case 1052: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5437 +//line sql.y:5461 { yyLOCAL = &UnaryExpr{Operator: UMinusOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1049: + case 1053: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5441 +//line sql.y:5465 { yyLOCAL = &UnaryExpr{Operator: TildaOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1050: + case 1054: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5445 +//line sql.y:5469 { yyLOCAL = &UnaryExpr{Operator: BangOp, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1051: + case 1055: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5449 +//line sql.y:5473 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1052: + case 1056: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5453 +//line sql.y:5477 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1053: + case 1057: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5457 +//line sql.y:5481 { yyLOCAL = &ExistsExpr{Subquery: yyDollar[2].subqueryUnion()} } yyVAL.union = yyLOCAL - case 1054: + case 1058: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:5461 +//line sql.y:5485 { yyLOCAL = &MatchExpr{Columns: yyDollar[2].colNamesUnion(), Expr: yyDollar[5].exprUnion(), Option: yyDollar[6].matchExprOptionUnion()} } yyVAL.union = yyLOCAL - case 1055: + case 1059: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:5465 +//line sql.y:5489 { yyLOCAL = &CastExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].convertTypeUnion(), Array: yyDollar[6].booleanUnion()} } yyVAL.union = yyLOCAL - case 1056: + case 1060: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5469 +//line sql.y:5493 { yyLOCAL = &ConvertExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1057: + case 1061: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5473 +//line sql.y:5497 { yyLOCAL = &ConvertUsingExpr{Expr: yyDollar[3].exprUnion(), Type: yyDollar[5].str} } yyVAL.union = yyLOCAL - case 1058: + case 1062: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5477 +//line sql.y:5501 { // From: https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html#operator_binary // To convert a string expression to a binary string, these constructs are equivalent: @@ -17818,3169 +17983,3177 @@ yydefault: yyLOCAL = &ConvertExpr{Expr: yyDollar[2].exprUnion(), Type: &ConvertType{Type: yyDollar[1].str}} } yyVAL.union = yyLOCAL - case 1059: + case 1063: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5485 +//line sql.y:5509 { yyLOCAL = &Default{ColName: yyDollar[2].str} } yyVAL.union = yyLOCAL - case 1060: + case 1064: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5489 +//line sql.y:5513 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprBinaryAddLeft, Date: yyDollar[5].exprUnion(), Unit: yyDollar[3].intervalTypeUnion(), Interval: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1061: + case 1065: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5493 +//line sql.y:5517 { yyLOCAL = &IntervalFuncExpr{Expr: yyDollar[3].exprUnion(), Exprs: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1062: + case 1066: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5497 +//line sql.y:5521 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: JSONExtractOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1063: + case 1067: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:5501 +//line sql.y:5525 { yyLOCAL = &BinaryExpr{Left: yyDollar[1].exprUnion(), Operator: JSONUnquoteExtractOp, Right: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1064: + case 1068: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5507 +//line sql.y:5531 { yyLOCAL = yyDollar[1].colNamesUnion() } yyVAL.union = yyLOCAL - case 1065: + case 1069: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5511 +//line sql.y:5535 { yyLOCAL = yyDollar[2].colNamesUnion() } yyVAL.union = yyLOCAL - case 1066: + case 1070: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*ColName -//line sql.y:5517 +//line sql.y:5541 { yyLOCAL = []*ColName{yyDollar[1].colNameUnion()} } yyVAL.union = yyLOCAL - case 1067: + case 1071: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5521 +//line sql.y:5545 { yySLICE := (*[]*ColName)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].colNameUnion()) } - case 1068: + case 1072: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5527 +//line sql.y:5551 { yyLOCAL = BothTrimType } yyVAL.union = yyLOCAL - case 1069: + case 1073: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5531 +//line sql.y:5555 { yyLOCAL = LeadingTrimType } yyVAL.union = yyLOCAL - case 1070: + case 1074: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL TrimType -//line sql.y:5535 +//line sql.y:5559 { yyLOCAL = TrailingTrimType } yyVAL.union = yyLOCAL - case 1071: + case 1075: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FrameUnitType -//line sql.y:5541 +//line sql.y:5565 { yyLOCAL = FrameRowsType } yyVAL.union = yyLOCAL - case 1072: + case 1076: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FrameUnitType -//line sql.y:5545 +//line sql.y:5569 { yyLOCAL = FrameRangeType } yyVAL.union = yyLOCAL - case 1073: + case 1077: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5552 +//line sql.y:5576 { yyLOCAL = CumeDistExprType } yyVAL.union = yyLOCAL - case 1074: + case 1078: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5556 +//line sql.y:5580 { yyLOCAL = DenseRankExprType } yyVAL.union = yyLOCAL - case 1075: + case 1079: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5560 +//line sql.y:5584 { yyLOCAL = PercentRankExprType } yyVAL.union = yyLOCAL - case 1076: + case 1080: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5564 +//line sql.y:5588 { yyLOCAL = RankExprType } yyVAL.union = yyLOCAL - case 1077: + case 1081: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ArgumentLessWindowExprType -//line sql.y:5568 +//line sql.y:5592 { yyLOCAL = RowNumberExprType } yyVAL.union = yyLOCAL - case 1078: + case 1082: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5574 +//line sql.y:5598 { yyLOCAL = &FramePoint{Type: CurrentRowType} } yyVAL.union = yyLOCAL - case 1079: + case 1083: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5578 +//line sql.y:5602 { yyLOCAL = &FramePoint{Type: UnboundedPrecedingType} } yyVAL.union = yyLOCAL - case 1080: + case 1084: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5582 +//line sql.y:5606 { yyLOCAL = &FramePoint{Type: UnboundedFollowingType} } yyVAL.union = yyLOCAL - case 1081: + case 1085: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5586 +//line sql.y:5610 { yyLOCAL = &FramePoint{Type: ExprPrecedingType, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1082: + case 1086: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5590 +//line sql.y:5614 { yyLOCAL = &FramePoint{Type: ExprPrecedingType, Expr: yyDollar[2].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1083: + case 1087: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5594 +//line sql.y:5618 { yyLOCAL = &FramePoint{Type: ExprFollowingType, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1084: + case 1088: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *FramePoint -//line sql.y:5598 +//line sql.y:5622 { yyLOCAL = &FramePoint{Type: ExprFollowingType, Expr: yyDollar[2].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1085: + case 1089: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5603 +//line sql.y:5627 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1086: + case 1090: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5607 +//line sql.y:5631 { yyLOCAL = yyDollar[1].frameClauseUnion() } yyVAL.union = yyLOCAL - case 1087: + case 1091: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5613 +//line sql.y:5637 { yyLOCAL = &FrameClause{Unit: yyDollar[1].frameUnitTypeUnion(), Start: yyDollar[2].framePointUnion()} } yyVAL.union = yyLOCAL - case 1088: + case 1092: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *FrameClause -//line sql.y:5617 +//line sql.y:5641 { yyLOCAL = &FrameClause{Unit: yyDollar[1].frameUnitTypeUnion(), Start: yyDollar[3].framePointUnion(), End: yyDollar[5].framePointUnion()} } yyVAL.union = yyLOCAL - case 1089: + case 1093: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Exprs -//line sql.y:5622 +//line sql.y:5646 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1090: + case 1094: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Exprs -//line sql.y:5626 +//line sql.y:5650 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1091: + case 1095: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5631 +//line sql.y:5655 { } - case 1092: + case 1096: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:5634 +//line sql.y:5658 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 1093: + case 1097: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *WindowSpecification -//line sql.y:5640 +//line sql.y:5664 { yyLOCAL = &WindowSpecification{Name: yyDollar[1].identifierCI, PartitionClause: yyDollar[2].exprsUnion(), OrderClause: yyDollar[3].orderByUnion(), FrameClause: yyDollar[4].frameClauseUnion()} } yyVAL.union = yyLOCAL - case 1094: + case 1098: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5646 +//line sql.y:5670 { yyLOCAL = &OverClause{WindowSpec: yyDollar[3].windowSpecificationUnion()} } yyVAL.union = yyLOCAL - case 1095: + case 1099: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *OverClause -//line sql.y:5650 +//line sql.y:5674 { yyLOCAL = &OverClause{WindowName: yyDollar[2].identifierCI} } yyVAL.union = yyLOCAL - case 1096: + case 1100: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *NullTreatmentClause -//line sql.y:5655 +//line sql.y:5679 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1098: + case 1102: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *NullTreatmentClause -//line sql.y:5662 +//line sql.y:5686 { yyLOCAL = &NullTreatmentClause{yyDollar[1].nullTreatmentTypeUnion()} } yyVAL.union = yyLOCAL - case 1099: + case 1103: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL NullTreatmentType -//line sql.y:5668 +//line sql.y:5692 { yyLOCAL = RespectNullsType } yyVAL.union = yyLOCAL - case 1100: + case 1104: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL NullTreatmentType -//line sql.y:5672 +//line sql.y:5696 { yyLOCAL = IgnoreNullsType } yyVAL.union = yyLOCAL - case 1101: + case 1105: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FirstOrLastValueExprType -//line sql.y:5678 +//line sql.y:5702 { yyLOCAL = FirstValueExprType } yyVAL.union = yyLOCAL - case 1102: + case 1106: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL FirstOrLastValueExprType -//line sql.y:5682 +//line sql.y:5706 { yyLOCAL = LastValueExprType } yyVAL.union = yyLOCAL - case 1103: + case 1107: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL FromFirstLastType -//line sql.y:5688 +//line sql.y:5712 { yyLOCAL = FromFirstType } yyVAL.union = yyLOCAL - case 1104: + case 1108: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL FromFirstLastType -//line sql.y:5692 +//line sql.y:5716 { yyLOCAL = FromLastType } yyVAL.union = yyLOCAL - case 1105: + case 1109: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *FromFirstLastClause -//line sql.y:5697 +//line sql.y:5721 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1107: + case 1111: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *FromFirstLastClause -//line sql.y:5704 +//line sql.y:5728 { yyLOCAL = &FromFirstLastClause{yyDollar[1].fromFirstLastTypeUnion()} } yyVAL.union = yyLOCAL - case 1108: + case 1112: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LagLeadExprType -//line sql.y:5710 +//line sql.y:5734 { yyLOCAL = LagExprType } yyVAL.union = yyLOCAL - case 1109: + case 1113: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL LagLeadExprType -//line sql.y:5714 +//line sql.y:5738 { yyLOCAL = LeadExprType } yyVAL.union = yyLOCAL - case 1110: + case 1114: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *WindowDefinition -//line sql.y:5720 +//line sql.y:5744 { yyLOCAL = &WindowDefinition{Name: yyDollar[1].identifierCI, WindowSpec: yyDollar[4].windowSpecificationUnion()} } yyVAL.union = yyLOCAL - case 1111: + case 1115: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL WindowDefinitions -//line sql.y:5726 +//line sql.y:5750 { yyLOCAL = WindowDefinitions{yyDollar[1].windowDefinitionUnion()} } yyVAL.union = yyLOCAL - case 1112: + case 1116: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5730 +//line sql.y:5754 { yySLICE := (*WindowDefinitions)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].windowDefinitionUnion()) } - case 1113: + case 1117: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:5736 +//line sql.y:5760 { yyVAL.str = "" } - case 1114: + case 1118: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5740 +//line sql.y:5764 { yyVAL.str = string(yyDollar[2].identifierCI.String()) } - case 1115: + case 1119: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL BoolVal -//line sql.y:5746 +//line sql.y:5770 { yyLOCAL = BoolVal(true) } yyVAL.union = yyLOCAL - case 1116: + case 1120: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL BoolVal -//line sql.y:5750 +//line sql.y:5774 { yyLOCAL = BoolVal(false) } yyVAL.union = yyLOCAL - case 1117: + case 1121: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5757 +//line sql.y:5781 { yyLOCAL = IsTrueOp } yyVAL.union = yyLOCAL - case 1118: + case 1122: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5761 +//line sql.y:5785 { yyLOCAL = IsNotTrueOp } yyVAL.union = yyLOCAL - case 1119: + case 1123: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5765 +//line sql.y:5789 { yyLOCAL = IsFalseOp } yyVAL.union = yyLOCAL - case 1120: + case 1124: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL IsExprOperator -//line sql.y:5769 +//line sql.y:5793 { yyLOCAL = IsNotFalseOp } yyVAL.union = yyLOCAL - case 1121: + case 1125: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5775 +//line sql.y:5799 { yyLOCAL = EqualOp } yyVAL.union = yyLOCAL - case 1122: + case 1126: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5779 +//line sql.y:5803 { yyLOCAL = LessThanOp } yyVAL.union = yyLOCAL - case 1123: + case 1127: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5783 +//line sql.y:5807 { yyLOCAL = GreaterThanOp } yyVAL.union = yyLOCAL - case 1124: + case 1128: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5787 +//line sql.y:5811 { yyLOCAL = LessEqualOp } yyVAL.union = yyLOCAL - case 1125: + case 1129: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5791 +//line sql.y:5815 { yyLOCAL = GreaterEqualOp } yyVAL.union = yyLOCAL - case 1126: + case 1130: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5795 +//line sql.y:5819 { yyLOCAL = NotEqualOp } yyVAL.union = yyLOCAL - case 1127: + case 1131: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ComparisonExprOperator -//line sql.y:5799 +//line sql.y:5823 { yyLOCAL = NullSafeEqualOp } yyVAL.union = yyLOCAL - case 1128: + case 1132: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5805 +//line sql.y:5829 { yyLOCAL = yyDollar[1].valTupleUnion() } yyVAL.union = yyLOCAL - case 1129: + case 1133: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5809 +//line sql.y:5833 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1130: + case 1134: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ColTuple -//line sql.y:5813 +//line sql.y:5837 { yyLOCAL = ListArg(yyDollar[1].str[2:]) markBindVariable(yylex, yyDollar[1].str[2:]) } yyVAL.union = yyLOCAL - case 1131: + case 1135: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Subquery -//line sql.y:5820 +//line sql.y:5844 { yyLOCAL = &Subquery{yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1132: + case 1136: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Exprs -//line sql.y:5826 +//line sql.y:5850 { yyLOCAL = Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1133: + case 1137: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:5830 +//line sql.y:5854 { yySLICE := (*Exprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].exprUnion()) } - case 1134: + case 1138: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5840 +//line sql.y:5864 { yyLOCAL = &FuncExpr{Name: yyDollar[1].identifierCI, Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1135: + case 1139: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5844 +//line sql.y:5868 { yyLOCAL = &FuncExpr{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCI, Exprs: yyDollar[5].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1136: + case 1140: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5854 +//line sql.y:5878 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("left"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1137: + case 1141: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5858 +//line sql.y:5882 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("right"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1138: + case 1142: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5862 +//line sql.y:5886 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1139: + case 1143: + yyDollar = yyS[yypt-8 : yypt+1] + var yyLOCAL Expr +//line sql.y:5890 + { + yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} + } + yyVAL.union = yyLOCAL + case 1144: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5866 +//line sql.y:5894 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1140: + case 1145: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5870 +//line sql.y:5898 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion(), To: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1141: + case 1146: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:5874 +//line sql.y:5902 { yyLOCAL = &SubstrExpr{Name: yyDollar[3].exprUnion(), From: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1142: + case 1147: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5878 +//line sql.y:5906 { yyLOCAL = &CaseExpr{Expr: yyDollar[2].exprUnion(), Whens: yyDollar[3].whensUnion(), Else: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1143: + case 1148: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5882 +//line sql.y:5910 { yyLOCAL = &ValuesFuncExpr{Name: yyDollar[3].colNameUnion()} } yyVAL.union = yyLOCAL - case 1144: + case 1149: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:5886 +//line sql.y:5914 { yyLOCAL = &InsertExpr{Str: yyDollar[3].exprUnion(), Pos: yyDollar[5].exprUnion(), Len: yyDollar[7].exprUnion(), NewStr: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1145: + case 1150: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5890 +//line sql.y:5918 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1146: + case 1151: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5901 +//line sql.y:5929 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("utc_date")} } yyVAL.union = yyLOCAL - case 1147: + case 1152: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:5905 +//line sql.y:5933 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1148: + case 1153: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5911 +//line sql.y:5939 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("current_date")} } yyVAL.union = yyLOCAL - case 1149: + case 1154: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5915 +//line sql.y:5943 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("curdate")} } yyVAL.union = yyLOCAL - case 1150: + case 1155: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5919 +//line sql.y:5947 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("utc_time"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1151: + case 1156: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5924 +//line sql.y:5952 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("curtime"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1152: + case 1157: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:5929 +//line sql.y:5957 { yyLOCAL = &CurTimeFuncExpr{Name: NewIdentifierCI("current_time"), Fsp: yyDollar[2].integerUnion()} } yyVAL.union = yyLOCAL - case 1153: + case 1158: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5933 +//line sql.y:5961 { yyLOCAL = &CountStar{} } yyVAL.union = yyLOCAL - case 1154: + case 1159: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5937 +//line sql.y:5965 { yyLOCAL = &Count{Distinct: yyDollar[3].booleanUnion(), Args: yyDollar[4].exprsUnion()} } yyVAL.union = yyLOCAL - case 1155: + case 1160: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5941 +//line sql.y:5969 { yyLOCAL = &Max{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1156: + case 1161: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5945 +//line sql.y:5973 { yyLOCAL = &Min{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1157: + case 1162: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5949 +//line sql.y:5977 { yyLOCAL = &Sum{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1158: + case 1163: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:5953 +//line sql.y:5981 { yyLOCAL = &Avg{Distinct: yyDollar[3].booleanUnion(), Arg: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1159: + case 1164: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5957 +//line sql.y:5985 { yyLOCAL = &BitAnd{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1160: + case 1165: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5961 +//line sql.y:5989 { yyLOCAL = &BitOr{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1161: + case 1166: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5965 +//line sql.y:5993 { yyLOCAL = &BitXor{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1162: + case 1167: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5969 +//line sql.y:5997 { yyLOCAL = &Std{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1163: + case 1168: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5973 +//line sql.y:6001 { yyLOCAL = &StdDev{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1164: + case 1169: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5977 +//line sql.y:6005 { yyLOCAL = &StdPop{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1165: + case 1170: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5981 +//line sql.y:6009 { yyLOCAL = &StdSamp{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1166: + case 1171: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5985 +//line sql.y:6013 { yyLOCAL = &VarPop{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1167: + case 1172: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5989 +//line sql.y:6017 { yyLOCAL = &VarSamp{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1168: + case 1173: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:5993 +//line sql.y:6021 { yyLOCAL = &Variance{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1169: + case 1174: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:5997 +//line sql.y:6025 { yyLOCAL = &GroupConcatExpr{Distinct: yyDollar[3].booleanUnion(), Exprs: yyDollar[4].exprsUnion(), OrderBy: yyDollar[5].orderByUnion(), Separator: yyDollar[6].str, Limit: yyDollar[7].limitUnion()} } yyVAL.union = yyLOCAL - case 1170: + case 1175: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6001 +//line sql.y:6029 { yyLOCAL = &AnyValue{Arg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1171: + case 1176: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6005 +//line sql.y:6033 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprTimestampadd, Date: yyDollar[7].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: yyDollar[3].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1172: + case 1177: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6009 +//line sql.y:6037 { yyLOCAL = &TimestampDiffExpr{Unit: yyDollar[3].intervalTypeUnion(), Expr1: yyDollar[5].exprUnion(), Expr2: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1173: + case 1178: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6013 +//line sql.y:6041 { yyLOCAL = &ExtractFuncExpr{IntervalType: yyDollar[3].intervalTypeUnion(), Expr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1174: + case 1179: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6017 +//line sql.y:6045 { yyLOCAL = &WeightStringFuncExpr{Expr: yyDollar[3].exprUnion(), As: yyDollar[4].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1175: + case 1180: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6021 +//line sql.y:6049 { yyLOCAL = &JSONPrettyExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1176: + case 1181: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6025 +//line sql.y:6053 { yyLOCAL = &JSONStorageFreeExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1177: + case 1182: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6029 +//line sql.y:6057 { yyLOCAL = &JSONStorageSizeExpr{JSONVal: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1178: + case 1183: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6033 +//line sql.y:6061 { yyLOCAL = &TrimFuncExpr{TrimFuncType: LTrimType, Type: LeadingTrimType, StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1179: + case 1184: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6037 +//line sql.y:6065 { yyLOCAL = &TrimFuncExpr{TrimFuncType: RTrimType, Type: TrailingTrimType, StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1180: + case 1185: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:6041 +//line sql.y:6069 { yyLOCAL = &TrimFuncExpr{Type: yyDollar[3].trimTypeUnion(), TrimArg: yyDollar[4].exprUnion(), StringArg: yyDollar[6].exprUnion()} } yyVAL.union = yyLOCAL - case 1181: + case 1186: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6045 +//line sql.y:6073 { yyLOCAL = &TrimFuncExpr{StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1182: + case 1187: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6049 +//line sql.y:6077 { yyLOCAL = &CharExpr{Exprs: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1183: + case 1188: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6053 +//line sql.y:6081 { yyLOCAL = &CharExpr{Exprs: yyDollar[3].exprsUnion(), Charset: yyDollar[5].str} } yyVAL.union = yyLOCAL - case 1184: + case 1189: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6057 +//line sql.y:6085 { yyLOCAL = &TrimFuncExpr{TrimArg: yyDollar[3].exprUnion(), StringArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1185: + case 1190: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6061 +//line sql.y:6089 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1186: + case 1191: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6065 +//line sql.y:6093 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion(), Pos: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1187: + case 1192: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6069 +//line sql.y:6097 { yyLOCAL = &LocateExpr{SubStr: yyDollar[3].exprUnion(), Str: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1188: + case 1193: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6073 +//line sql.y:6101 { yyLOCAL = &LockingFunc{Type: GetLock, Name: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1189: + case 1194: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6077 +//line sql.y:6105 { yyLOCAL = &LockingFunc{Type: IsFreeLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1190: + case 1195: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6081 +//line sql.y:6109 { yyLOCAL = &LockingFunc{Type: IsUsedLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1191: + case 1196: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:6085 +//line sql.y:6113 { yyLOCAL = &LockingFunc{Type: ReleaseAllLocks} } yyVAL.union = yyLOCAL - case 1192: + case 1197: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6089 +//line sql.y:6117 { yyLOCAL = &LockingFunc{Type: ReleaseLock, Name: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1193: + case 1198: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6093 +//line sql.y:6121 { yyLOCAL = &JSONSchemaValidFuncExpr{Schema: yyDollar[3].exprUnion(), Document: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1194: + case 1199: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6097 +//line sql.y:6125 { yyLOCAL = &JSONSchemaValidationReportFuncExpr{Schema: yyDollar[3].exprUnion(), Document: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1195: + case 1200: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6101 +//line sql.y:6129 { yyLOCAL = &JSONArrayExpr{Params: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1196: + case 1201: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6105 +//line sql.y:6133 { yyLOCAL = &GeomFormatExpr{FormatType: BinaryFormat, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1197: + case 1202: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6109 +//line sql.y:6137 { yyLOCAL = &GeomFormatExpr{FormatType: BinaryFormat, Geom: yyDollar[3].exprUnion(), AxisOrderOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1198: + case 1203: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6113 +//line sql.y:6141 { yyLOCAL = &GeomFormatExpr{FormatType: TextFormat, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1199: + case 1204: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6117 +//line sql.y:6145 { yyLOCAL = &GeomFormatExpr{FormatType: TextFormat, Geom: yyDollar[3].exprUnion(), AxisOrderOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1200: + case 1205: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6121 +//line sql.y:6149 { yyLOCAL = &GeomPropertyFuncExpr{Property: IsEmpty, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1201: + case 1206: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6125 +//line sql.y:6153 { yyLOCAL = &GeomPropertyFuncExpr{Property: IsSimple, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1202: + case 1207: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6129 +//line sql.y:6157 { yyLOCAL = &GeomPropertyFuncExpr{Property: Dimension, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1203: + case 1208: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6133 +//line sql.y:6161 { yyLOCAL = &GeomPropertyFuncExpr{Property: Envelope, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1204: + case 1209: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6137 +//line sql.y:6165 { yyLOCAL = &GeomPropertyFuncExpr{Property: GeometryType, Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1205: + case 1210: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6141 +//line sql.y:6169 { yyLOCAL = &PointPropertyFuncExpr{Property: Latitude, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1206: + case 1211: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6145 +//line sql.y:6173 { yyLOCAL = &PointPropertyFuncExpr{Property: Latitude, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1207: + case 1212: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6149 +//line sql.y:6177 { yyLOCAL = &PointPropertyFuncExpr{Property: Longitude, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1208: + case 1213: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6153 +//line sql.y:6181 { yyLOCAL = &PointPropertyFuncExpr{Property: Longitude, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1209: + case 1214: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6157 +//line sql.y:6185 { yyLOCAL = &LinestrPropertyFuncExpr{Property: EndPoint, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1210: + case 1215: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6161 +//line sql.y:6189 { yyLOCAL = &LinestrPropertyFuncExpr{Property: IsClosed, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1211: + case 1216: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6165 +//line sql.y:6193 { yyLOCAL = &LinestrPropertyFuncExpr{Property: Length, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1212: + case 1217: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6169 +//line sql.y:6197 { yyLOCAL = &LinestrPropertyFuncExpr{Property: Length, Linestring: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1213: + case 1218: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6173 +//line sql.y:6201 { yyLOCAL = &LinestrPropertyFuncExpr{Property: NumPoints, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1214: + case 1219: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6177 +//line sql.y:6205 { yyLOCAL = &LinestrPropertyFuncExpr{Property: PointN, Linestring: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1215: + case 1220: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6181 +//line sql.y:6209 { yyLOCAL = &LinestrPropertyFuncExpr{Property: StartPoint, Linestring: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1216: + case 1221: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6185 +//line sql.y:6213 { yyLOCAL = &PointPropertyFuncExpr{Property: XCordinate, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1217: + case 1222: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6189 +//line sql.y:6217 { yyLOCAL = &PointPropertyFuncExpr{Property: XCordinate, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1218: + case 1223: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6193 +//line sql.y:6221 { yyLOCAL = &PointPropertyFuncExpr{Property: YCordinate, Point: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1219: + case 1224: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6197 +//line sql.y:6225 { yyLOCAL = &PointPropertyFuncExpr{Property: YCordinate, Point: yyDollar[3].exprUnion(), ValueToSet: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1220: + case 1225: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6201 +//line sql.y:6229 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1221: + case 1226: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6205 +//line sql.y:6233 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1222: + case 1227: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6209 +//line sql.y:6237 { yyLOCAL = &GeomFromTextExpr{Type: GeometryFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1223: + case 1228: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6213 +//line sql.y:6241 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1224: + case 1229: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6217 +//line sql.y:6245 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1225: + case 1230: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6221 +//line sql.y:6249 { yyLOCAL = &GeomFromTextExpr{Type: GeometryCollectionFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1226: + case 1231: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6225 +//line sql.y:6253 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1227: + case 1232: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6229 +//line sql.y:6257 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1228: + case 1233: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6233 +//line sql.y:6261 { yyLOCAL = &GeomFromTextExpr{Type: LineStringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1229: + case 1234: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6237 +//line sql.y:6265 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1230: + case 1235: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6241 +//line sql.y:6269 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1231: + case 1236: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6245 +//line sql.y:6273 { yyLOCAL = &GeomFromTextExpr{Type: MultiLinestringFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1232: + case 1237: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6249 +//line sql.y:6277 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1233: + case 1238: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6253 +//line sql.y:6281 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1234: + case 1239: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6257 +//line sql.y:6285 { yyLOCAL = &GeomFromTextExpr{Type: MultiPointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1235: + case 1240: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6261 +//line sql.y:6289 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1236: + case 1241: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6265 +//line sql.y:6293 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1237: + case 1242: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6269 +//line sql.y:6297 { yyLOCAL = &GeomFromTextExpr{Type: MultiPolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1238: + case 1243: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6273 +//line sql.y:6301 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1239: + case 1244: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6277 +//line sql.y:6305 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1240: + case 1245: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6281 +//line sql.y:6309 { yyLOCAL = &GeomFromTextExpr{Type: PointFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1241: + case 1246: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6285 +//line sql.y:6313 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1242: + case 1247: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6289 +//line sql.y:6317 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1243: + case 1248: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6293 +//line sql.y:6321 { yyLOCAL = &GeomFromTextExpr{Type: PolygonFromText, WktText: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1244: + case 1249: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6297 +//line sql.y:6325 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1245: + case 1250: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6301 +//line sql.y:6329 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1246: + case 1251: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6305 +//line sql.y:6333 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1247: + case 1252: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6309 +//line sql.y:6337 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1248: + case 1253: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6313 +//line sql.y:6341 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1249: + case 1254: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6317 +//line sql.y:6345 { yyLOCAL = &GeomFromWKBExpr{Type: GeometryCollectionFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1250: + case 1255: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6321 +//line sql.y:6349 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1251: + case 1256: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6325 +//line sql.y:6353 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1252: + case 1257: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6329 +//line sql.y:6357 { yyLOCAL = &GeomFromWKBExpr{Type: LineStringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1253: + case 1258: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6333 +//line sql.y:6361 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1254: + case 1259: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6337 +//line sql.y:6365 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1255: + case 1260: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6341 +//line sql.y:6369 { yyLOCAL = &GeomFromWKBExpr{Type: MultiLinestringFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1256: + case 1261: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6345 +//line sql.y:6373 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1257: + case 1262: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6349 +//line sql.y:6377 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1258: + case 1263: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6353 +//line sql.y:6381 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1259: + case 1264: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6357 +//line sql.y:6385 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1260: + case 1265: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6361 +//line sql.y:6389 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1261: + case 1266: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6365 +//line sql.y:6393 { yyLOCAL = &GeomFromWKBExpr{Type: MultiPolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1262: + case 1267: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6369 +//line sql.y:6397 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1263: + case 1268: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6373 +//line sql.y:6401 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1264: + case 1269: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6377 +//line sql.y:6405 { yyLOCAL = &GeomFromWKBExpr{Type: PointFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1265: + case 1270: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6381 +//line sql.y:6409 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1266: + case 1271: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6385 +//line sql.y:6413 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1267: + case 1272: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6389 +//line sql.y:6417 { yyLOCAL = &GeomFromWKBExpr{Type: PolygonFromWKB, WkbBlob: yyDollar[3].exprUnion(), Srid: yyDollar[5].exprUnion(), AxisOrderOpt: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1268: + case 1273: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6393 +//line sql.y:6421 { yyLOCAL = &PolygonPropertyFuncExpr{Property: Area, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1269: + case 1274: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6397 +//line sql.y:6425 { yyLOCAL = &PolygonPropertyFuncExpr{Property: Centroid, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1270: + case 1275: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6401 +//line sql.y:6429 { yyLOCAL = &PolygonPropertyFuncExpr{Property: ExteriorRing, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1271: + case 1276: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6405 +//line sql.y:6433 { yyLOCAL = &PolygonPropertyFuncExpr{Property: InteriorRingN, Polygon: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1272: + case 1277: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6409 +//line sql.y:6437 { yyLOCAL = &PolygonPropertyFuncExpr{Property: NumInteriorRings, Polygon: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1273: + case 1278: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6413 +//line sql.y:6441 { yyLOCAL = &GeomCollPropertyFuncExpr{Property: GeometryN, GeomColl: yyDollar[3].exprUnion(), PropertyDefArg: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1274: + case 1279: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6417 +//line sql.y:6445 { yyLOCAL = &GeomCollPropertyFuncExpr{Property: NumGeometries, GeomColl: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1275: + case 1280: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6421 +//line sql.y:6449 { yyLOCAL = &GeoHashFromLatLongExpr{Longitude: yyDollar[3].exprUnion(), Latitude: yyDollar[5].exprUnion(), MaxLength: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1276: + case 1281: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6425 +//line sql.y:6453 { yyLOCAL = &GeoHashFromPointExpr{Point: yyDollar[3].exprUnion(), MaxLength: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1277: + case 1282: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6429 +//line sql.y:6457 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: LatitudeFromHash, GeoHash: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1278: + case 1283: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6433 +//line sql.y:6461 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: LongitudeFromHash, GeoHash: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1279: + case 1284: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6437 +//line sql.y:6465 { yyLOCAL = &GeomFromGeoHashExpr{GeomType: PointFromHash, GeoHash: yyDollar[3].exprUnion(), SridOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1280: + case 1285: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6441 +//line sql.y:6469 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1281: + case 1286: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6445 +//line sql.y:6473 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion(), HigherDimHandlerOpt: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1282: + case 1287: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6449 +//line sql.y:6477 { yyLOCAL = &GeomFromGeoJSONExpr{GeoJSON: yyDollar[3].exprUnion(), HigherDimHandlerOpt: yyDollar[5].exprUnion(), Srid: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1283: + case 1288: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6453 +//line sql.y:6481 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1284: + case 1289: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6457 +//line sql.y:6485 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion(), MaxDecimalDigits: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1285: + case 1290: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6461 +//line sql.y:6489 { yyLOCAL = &GeoJSONFromGeomExpr{Geom: yyDollar[3].exprUnion(), MaxDecimalDigits: yyDollar[5].exprUnion(), Bitmask: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1286: + case 1291: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6465 +//line sql.y:6493 { yyLOCAL = &JSONObjectExpr{Params: yyDollar[3].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1287: + case 1292: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6469 +//line sql.y:6497 { yyLOCAL = &JSONQuoteExpr{StringArg: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1288: + case 1293: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6473 +//line sql.y:6501 { yyLOCAL = &JSONContainsExpr{Target: yyDollar[3].exprUnion(), Candidate: yyDollar[5].exprsUnion()[0], PathList: yyDollar[5].exprsUnion()[1:]} } yyVAL.union = yyLOCAL - case 1289: + case 1294: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6477 +//line sql.y:6505 { yyLOCAL = &JSONContainsPathExpr{JSONDoc: yyDollar[3].exprUnion(), OneOrAll: yyDollar[5].exprUnion(), PathList: yyDollar[7].exprsUnion()} } yyVAL.union = yyLOCAL - case 1290: + case 1295: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6481 +//line sql.y:6509 { yyLOCAL = &JSONExtractExpr{JSONDoc: yyDollar[3].exprUnion(), PathList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1291: + case 1296: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6485 +//line sql.y:6513 { yyLOCAL = &JSONKeysExpr{JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1292: + case 1297: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6489 +//line sql.y:6517 { yyLOCAL = &JSONKeysExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1293: + case 1298: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6493 +//line sql.y:6521 { yyLOCAL = &JSONOverlapsExpr{JSONDoc1: yyDollar[3].exprUnion(), JSONDoc2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1294: + case 1299: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6497 +//line sql.y:6525 { yyLOCAL = &JSONSearchExpr{JSONDoc: yyDollar[3].exprUnion(), OneOrAll: yyDollar[5].exprUnion(), SearchStr: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1295: + case 1300: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6501 +//line sql.y:6529 { yyLOCAL = &JSONSearchExpr{JSONDoc: yyDollar[3].exprUnion(), OneOrAll: yyDollar[5].exprUnion(), SearchStr: yyDollar[7].exprUnion(), EscapeChar: yyDollar[9].exprsUnion()[0], PathList: yyDollar[9].exprsUnion()[1:]} } yyVAL.union = yyLOCAL - case 1296: + case 1301: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL Expr -//line sql.y:6505 +//line sql.y:6533 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion()} } yyVAL.union = yyLOCAL - case 1297: + case 1302: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6509 +//line sql.y:6537 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion(), EmptyOnResponse: yyDollar[7].jtOnResponseUnion()} } yyVAL.union = yyLOCAL - case 1298: + case 1303: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6513 +//line sql.y:6541 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion(), ErrorOnResponse: yyDollar[7].jtOnResponseUnion()} } yyVAL.union = yyLOCAL - case 1299: + case 1304: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6517 +//line sql.y:6545 { yyLOCAL = &JSONValueExpr{JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion(), ReturningType: yyDollar[6].convertTypeUnion(), EmptyOnResponse: yyDollar[7].jtOnResponseUnion(), ErrorOnResponse: yyDollar[8].jtOnResponseUnion()} } yyVAL.union = yyLOCAL - case 1300: + case 1305: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6521 +//line sql.y:6549 { yyLOCAL = &JSONAttributesExpr{Type: DepthAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1301: + case 1306: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6525 +//line sql.y:6553 { yyLOCAL = &JSONAttributesExpr{Type: ValidAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1302: + case 1307: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6529 +//line sql.y:6557 { yyLOCAL = &JSONAttributesExpr{Type: TypeAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1303: + case 1308: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6533 +//line sql.y:6561 { yyLOCAL = &JSONAttributesExpr{Type: LengthAttributeType, JSONDoc: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1304: + case 1309: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6537 +//line sql.y:6565 { yyLOCAL = &JSONAttributesExpr{Type: LengthAttributeType, JSONDoc: yyDollar[3].exprUnion(), Path: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1305: + case 1310: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6541 +//line sql.y:6569 { yyLOCAL = &JSONValueModifierExpr{Type: JSONArrayAppendType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1306: + case 1311: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6545 +//line sql.y:6573 { yyLOCAL = &JSONValueModifierExpr{Type: JSONArrayInsertType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1307: + case 1312: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6549 +//line sql.y:6577 { yyLOCAL = &JSONValueModifierExpr{Type: JSONInsertType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1308: + case 1313: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6553 +//line sql.y:6581 { yyLOCAL = &JSONValueModifierExpr{Type: JSONReplaceType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1309: + case 1314: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6557 +//line sql.y:6585 { yyLOCAL = &JSONValueModifierExpr{Type: JSONSetType, JSONDoc: yyDollar[3].exprUnion(), Params: yyDollar[5].jsonObjectParamsUnion()} } yyVAL.union = yyLOCAL - case 1310: + case 1315: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6561 +//line sql.y:6589 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergeType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1311: + case 1316: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6565 +//line sql.y:6593 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergePatchType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1312: + case 1317: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6569 +//line sql.y:6597 { yyLOCAL = &JSONValueMergeExpr{Type: JSONMergePreserveType, JSONDoc: yyDollar[3].exprUnion(), JSONDocList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1313: + case 1318: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6573 +//line sql.y:6601 { yyLOCAL = &JSONRemoveExpr{JSONDoc: yyDollar[3].exprUnion(), PathList: yyDollar[5].exprsUnion()} } yyVAL.union = yyLOCAL - case 1314: + case 1319: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6577 +//line sql.y:6605 { yyLOCAL = &JSONUnquoteExpr{JSONValue: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1315: + case 1320: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6581 +//line sql.y:6609 { yyLOCAL = &MultiPolygonExpr{PolygonParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1316: + case 1321: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6585 +//line sql.y:6613 { yyLOCAL = &MultiPointExpr{PointParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1317: + case 1322: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6589 +//line sql.y:6617 { yyLOCAL = &MultiLinestringExpr{LinestringParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1318: + case 1323: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6593 +//line sql.y:6621 { yyLOCAL = &PolygonExpr{LinestringParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1319: + case 1324: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6597 +//line sql.y:6625 { yyLOCAL = &LineStringExpr{PointParams: yyDollar[3].exprsUnion()} } yyVAL.union = yyLOCAL - case 1320: + case 1325: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6601 +//line sql.y:6629 { yyLOCAL = &PointExpr{XCordinate: yyDollar[3].exprUnion(), YCordinate: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1321: + case 1326: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6605 +//line sql.y:6633 { yyLOCAL = &ArgumentLessWindowExpr{Type: yyDollar[1].argumentLessWindowExprTypeUnion(), OverClause: yyDollar[4].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1322: + case 1327: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6609 +//line sql.y:6637 { yyLOCAL = &FirstOrLastValueExpr{Type: yyDollar[1].firstOrLastValueExprTypeUnion(), Expr: yyDollar[3].exprUnion(), NullTreatmentClause: yyDollar[5].nullTreatmentClauseUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1323: + case 1328: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Expr -//line sql.y:6613 +//line sql.y:6641 { yyLOCAL = &NtileExpr{N: yyDollar[3].exprUnion(), OverClause: yyDollar[5].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1324: + case 1329: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6617 +//line sql.y:6645 { yyLOCAL = &NTHValueExpr{Expr: yyDollar[3].exprUnion(), N: yyDollar[5].exprUnion(), FromFirstLastClause: yyDollar[7].fromFirstLastClauseUnion(), NullTreatmentClause: yyDollar[8].nullTreatmentClauseUnion(), OverClause: yyDollar[9].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1325: + case 1330: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6621 +//line sql.y:6649 { yyLOCAL = &LagLeadExpr{Type: yyDollar[1].lagLeadExprTypeUnion(), Expr: yyDollar[3].exprUnion(), NullTreatmentClause: yyDollar[5].nullTreatmentClauseUnion(), OverClause: yyDollar[6].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1326: + case 1331: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL Expr -//line sql.y:6625 +//line sql.y:6653 { yyLOCAL = &LagLeadExpr{Type: yyDollar[1].lagLeadExprTypeUnion(), Expr: yyDollar[3].exprUnion(), N: yyDollar[5].exprUnion(), Default: yyDollar[6].exprUnion(), NullTreatmentClause: yyDollar[8].nullTreatmentClauseUnion(), OverClause: yyDollar[9].overClauseUnion()} } yyVAL.union = yyLOCAL - case 1327: + case 1332: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6629 +//line sql.y:6657 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprAdddate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1328: + case 1333: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6633 +//line sql.y:6661 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprAdddate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: IntervalNone} } yyVAL.union = yyLOCAL - case 1329: + case 1334: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6637 +//line sql.y:6665 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprDateAdd, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1330: + case 1335: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6641 +//line sql.y:6669 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprDateSub, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1331: + case 1336: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6645 +//line sql.y:6673 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprSubdate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[6].exprUnion(), Unit: yyDollar[7].intervalTypeUnion()} } yyVAL.union = yyLOCAL - case 1332: + case 1337: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6649 +//line sql.y:6677 { yyLOCAL = &IntervalDateExpr{Syntax: IntervalDateExprSubdate, Date: yyDollar[3].exprUnion(), Interval: yyDollar[5].exprUnion(), Unit: IntervalNone} } yyVAL.union = yyLOCAL - case 1337: + case 1342: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6659 +//line sql.y:6687 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1338: + case 1343: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6663 +//line sql.y:6691 { yyLOCAL = NewIntLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1339: + case 1344: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6667 +//line sql.y:6695 { yyLOCAL = yyDollar[1].variableUnion() } yyVAL.union = yyLOCAL - case 1340: + case 1345: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:6671 +//line sql.y:6699 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } yyVAL.union = yyLOCAL - case 1341: + case 1346: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:6676 +//line sql.y:6704 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1342: + case 1347: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:6680 +//line sql.y:6708 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1343: + case 1348: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6686 +//line sql.y:6714 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1344: + case 1349: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6690 +//line sql.y:6718 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1345: + case 1350: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6694 +//line sql.y:6722 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1346: + case 1351: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6698 +//line sql.y:6726 { yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion(), ReturnOption: yyDollar[11].exprUnion()} } yyVAL.union = yyLOCAL - case 1347: + case 1352: yyDollar = yyS[yypt-14 : yypt+1] var yyLOCAL Expr -//line sql.y:6702 +//line sql.y:6730 { // Match type is kept expression as TRIM( ' m ') is accepted yyLOCAL = &RegexpInstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion(), ReturnOption: yyDollar[11].exprUnion(), MatchType: yyDollar[13].exprUnion()} } yyVAL.union = yyLOCAL - case 1348: + case 1353: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6707 +//line sql.y:6735 { yyLOCAL = &RegexpLikeExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1349: + case 1354: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6711 +//line sql.y:6739 { yyLOCAL = &RegexpLikeExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), MatchType: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1350: + case 1355: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6715 +//line sql.y:6743 { yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1351: + case 1356: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6719 +//line sql.y:6747 { yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion(), Position: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1352: + case 1357: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6723 +//line sql.y:6751 { yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion(), Position: yyDollar[9].exprUnion(), Occurrence: yyDollar[11].exprUnion()} } yyVAL.union = yyLOCAL - case 1353: + case 1358: yyDollar = yyS[yypt-14 : yypt+1] var yyLOCAL Expr -//line sql.y:6727 +//line sql.y:6755 { // Match type is kept expression as TRIM( ' m ') is accepted yyLOCAL = &RegexpReplaceExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Repl: yyDollar[7].exprUnion(), Position: yyDollar[9].exprUnion(), Occurrence: yyDollar[11].exprUnion(), MatchType: yyDollar[13].exprUnion()} } yyVAL.union = yyLOCAL - case 1354: + case 1359: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6732 +//line sql.y:6760 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1355: + case 1360: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6736 +//line sql.y:6764 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1356: + case 1361: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL Expr -//line sql.y:6740 +//line sql.y:6768 { yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion()} } yyVAL.union = yyLOCAL - case 1357: + case 1362: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL Expr -//line sql.y:6744 +//line sql.y:6772 { // Match type is kept expression as TRIM( ' m ') is accepted yyLOCAL = &RegexpSubstrExpr{Expr: yyDollar[3].exprUnion(), Pattern: yyDollar[5].exprUnion(), Position: yyDollar[7].exprUnion(), Occurrence: yyDollar[9].exprUnion(), MatchType: yyDollar[11].exprUnion()} } yyVAL.union = yyLOCAL - case 1358: + case 1363: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6751 +//line sql.y:6779 { yyLOCAL = &ExtractValueExpr{Fragment: yyDollar[3].exprUnion(), XPathExpr: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1359: + case 1364: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6755 +//line sql.y:6783 { yyLOCAL = &UpdateXMLExpr{Target: yyDollar[3].exprUnion(), XPathExpr: yyDollar[5].exprUnion(), NewXML: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1360: + case 1365: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6761 +//line sql.y:6789 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: FormatBytesType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1361: + case 1366: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6765 +//line sql.y:6793 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: FormatPicoTimeType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1362: + case 1367: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Expr -//line sql.y:6769 +//line sql.y:6797 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: PsCurrentThreadIDType} } yyVAL.union = yyLOCAL - case 1363: + case 1368: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6773 +//line sql.y:6801 { yyLOCAL = &PerformanceSchemaFuncExpr{Type: PsThreadIDType, Argument: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1364: + case 1369: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6779 +//line sql.y:6807 { yyLOCAL = >IDFuncExpr{Type: GTIDSubsetType, Set1: yyDollar[3].exprUnion(), Set2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1365: + case 1370: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6783 +//line sql.y:6811 { yyLOCAL = >IDFuncExpr{Type: GTIDSubtractType, Set1: yyDollar[3].exprUnion(), Set2: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1366: + case 1371: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6787 +//line sql.y:6815 { yyLOCAL = >IDFuncExpr{Type: WaitForExecutedGTIDSetType, Set1: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1367: + case 1372: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6791 +//line sql.y:6819 { yyLOCAL = >IDFuncExpr{Type: WaitForExecutedGTIDSetType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1368: + case 1373: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6795 +//line sql.y:6823 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1369: + case 1374: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL Expr -//line sql.y:6799 +//line sql.y:6827 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion()} } yyVAL.union = yyLOCAL - case 1370: + case 1375: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL Expr -//line sql.y:6803 +//line sql.y:6831 { yyLOCAL = >IDFuncExpr{Type: WaitUntilSQLThreadAfterGTIDSType, Set1: yyDollar[3].exprUnion(), Timeout: yyDollar[5].exprUnion(), Channel: yyDollar[7].exprUnion()} } yyVAL.union = yyLOCAL - case 1371: + case 1376: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:6808 +//line sql.y:6836 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1372: + case 1377: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:6812 +//line sql.y:6840 { yyLOCAL = yyDollar[2].convertTypeUnion() } yyVAL.union = yyLOCAL - case 1373: + case 1378: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6818 +//line sql.y:6846 { yyLOCAL = IntervalDayHour } yyVAL.union = yyLOCAL - case 1374: + case 1379: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6822 +//line sql.y:6850 { yyLOCAL = IntervalDayMicrosecond } yyVAL.union = yyLOCAL - case 1375: + case 1380: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6826 +//line sql.y:6854 { yyLOCAL = IntervalDayMinute } yyVAL.union = yyLOCAL - case 1376: + case 1381: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6830 +//line sql.y:6858 { yyLOCAL = IntervalDaySecond } yyVAL.union = yyLOCAL - case 1377: + case 1382: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6834 +//line sql.y:6862 { yyLOCAL = IntervalHourMicrosecond } yyVAL.union = yyLOCAL - case 1378: + case 1383: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6838 +//line sql.y:6866 { yyLOCAL = IntervalHourMinute } yyVAL.union = yyLOCAL - case 1379: + case 1384: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6842 +//line sql.y:6870 { yyLOCAL = IntervalHourSecond } yyVAL.union = yyLOCAL - case 1380: + case 1385: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6846 +//line sql.y:6874 { yyLOCAL = IntervalMinuteMicrosecond } yyVAL.union = yyLOCAL - case 1381: + case 1386: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6850 +//line sql.y:6878 { yyLOCAL = IntervalMinuteSecond } yyVAL.union = yyLOCAL - case 1382: + case 1387: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6854 +//line sql.y:6882 { yyLOCAL = IntervalSecondMicrosecond } yyVAL.union = yyLOCAL - case 1383: + case 1388: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6858 +//line sql.y:6886 { yyLOCAL = IntervalYearMonth } yyVAL.union = yyLOCAL - case 1384: + case 1389: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6862 +//line sql.y:6890 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1385: + case 1390: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6866 +//line sql.y:6894 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1386: + case 1391: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6870 +//line sql.y:6898 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1387: + case 1392: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6874 +//line sql.y:6902 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1388: + case 1393: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6878 +//line sql.y:6906 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1389: + case 1394: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6882 +//line sql.y:6910 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1390: + case 1395: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6886 +//line sql.y:6914 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1391: + case 1396: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6890 +//line sql.y:6918 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1392: + case 1397: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6894 +//line sql.y:6922 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1393: + case 1398: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6900 +//line sql.y:6928 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1394: + case 1399: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6904 +//line sql.y:6932 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1395: + case 1400: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6908 +//line sql.y:6936 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1396: + case 1401: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6912 +//line sql.y:6940 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1397: + case 1402: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6916 +//line sql.y:6944 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1398: + case 1403: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6920 +//line sql.y:6948 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1399: + case 1404: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6924 +//line sql.y:6952 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1400: + case 1405: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6928 +//line sql.y:6956 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1401: + case 1406: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6932 +//line sql.y:6960 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1402: + case 1407: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6936 +//line sql.y:6964 { yyLOCAL = IntervalDay } yyVAL.union = yyLOCAL - case 1403: + case 1408: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6940 +//line sql.y:6968 { yyLOCAL = IntervalWeek } yyVAL.union = yyLOCAL - case 1404: + case 1409: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6944 +//line sql.y:6972 { yyLOCAL = IntervalHour } yyVAL.union = yyLOCAL - case 1405: + case 1410: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6948 +//line sql.y:6976 { yyLOCAL = IntervalMinute } yyVAL.union = yyLOCAL - case 1406: + case 1411: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6952 +//line sql.y:6980 { yyLOCAL = IntervalMonth } yyVAL.union = yyLOCAL - case 1407: + case 1412: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6956 +//line sql.y:6984 { yyLOCAL = IntervalQuarter } yyVAL.union = yyLOCAL - case 1408: + case 1413: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6960 +//line sql.y:6988 { yyLOCAL = IntervalSecond } yyVAL.union = yyLOCAL - case 1409: + case 1414: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6964 +//line sql.y:6992 { yyLOCAL = IntervalMicrosecond } yyVAL.union = yyLOCAL - case 1410: + case 1415: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL IntervalType -//line sql.y:6968 +//line sql.y:6996 { yyLOCAL = IntervalYear } yyVAL.union = yyLOCAL - case 1413: + case 1418: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int -//line sql.y:6978 +//line sql.y:7006 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1414: + case 1419: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int -//line sql.y:6982 +//line sql.y:7010 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1415: + case 1420: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int -//line sql.y:6986 +//line sql.y:7014 { yyLOCAL = convertStringToInt(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1416: + case 1421: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:6996 +//line sql.y:7024 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("if"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1417: + case 1422: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7000 +//line sql.y:7028 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("database"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1418: + case 1423: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7004 +//line sql.y:7032 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("schema"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1419: + case 1424: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7008 +//line sql.y:7036 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("mod"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1420: + case 1425: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Expr -//line sql.y:7012 +//line sql.y:7040 { yyLOCAL = &FuncExpr{Name: NewIdentifierCI("replace"), Exprs: yyDollar[3].selectExprsUnion()} } yyVAL.union = yyLOCAL - case 1421: + case 1426: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7018 +//line sql.y:7046 { yyLOCAL = NoOption } yyVAL.union = yyLOCAL - case 1422: + case 1427: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7022 +//line sql.y:7050 { yyLOCAL = BooleanModeOpt } yyVAL.union = yyLOCAL - case 1423: + case 1428: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7026 +//line sql.y:7054 { yyLOCAL = NaturalLanguageModeOpt } yyVAL.union = yyLOCAL - case 1424: + case 1429: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7030 +//line sql.y:7058 { yyLOCAL = NaturalLanguageModeWithQueryExpansionOpt } yyVAL.union = yyLOCAL - case 1425: + case 1430: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL MatchExprOption -//line sql.y:7034 +//line sql.y:7062 { yyLOCAL = QueryExpansionOpt } yyVAL.union = yyLOCAL - case 1426: + case 1431: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7040 +//line sql.y:7068 { yyVAL.str = string(yyDollar[1].identifierCI.String()) } - case 1427: + case 1432: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7044 +//line sql.y:7072 { yyVAL.str = string(yyDollar[1].str) } - case 1428: + case 1433: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7048 +//line sql.y:7076 { yyVAL.str = string(yyDollar[1].str) } - case 1429: + case 1434: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7054 +//line sql.y:7082 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1430: + case 1435: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7058 +//line sql.y:7086 { - yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: NewIntLiteral(yyDollar[4].str)} + yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: ptr.Of(convertStringToInt(yyDollar[4].str))} } yyVAL.union = yyLOCAL - case 1431: + case 1436: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7062 +//line sql.y:7090 { - yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: NewIntLiteral(yyDollar[4].str)} + yyLOCAL = &ConvertType{Type: string(yyDollar[2].str), Length: ptr.Of(convertStringToInt(yyDollar[4].str))} } yyVAL.union = yyLOCAL - case 1432: + case 1437: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7068 +//line sql.y:7096 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1433: + case 1438: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7072 +//line sql.y:7100 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion(), Charset: yyDollar[3].columnCharset} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion(), Charset: yyDollar[3].columnCharset} } yyVAL.union = yyLOCAL - case 1434: + case 1439: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7076 +//line sql.y:7104 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1435: + case 1440: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7080 +//line sql.y:7108 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1436: + case 1441: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7084 +//line sql.y:7112 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} yyLOCAL.Length = yyDollar[2].LengthScaleOption.Length yyLOCAL.Scale = yyDollar[2].LengthScaleOption.Scale } yyVAL.union = yyLOCAL - case 1437: + case 1442: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7090 +//line sql.y:7118 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1438: + case 1443: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7094 +//line sql.y:7122 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1439: + case 1444: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7098 +//line sql.y:7126 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1440: + case 1445: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7102 +//line sql.y:7130 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1441: + case 1446: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7106 +//line sql.y:7134 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1442: + case 1447: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7110 +//line sql.y:7138 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1443: + case 1448: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7114 +//line sql.y:7142 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1444: + case 1449: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7118 +//line sql.y:7146 { - yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].literalUnion()} + yyLOCAL = &ConvertType{Type: string(yyDollar[1].str), Length: yyDollar[2].intPtrUnion()} } yyVAL.union = yyLOCAL - case 1445: + case 1450: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7122 +//line sql.y:7150 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1446: + case 1451: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ConvertType -//line sql.y:7126 +//line sql.y:7154 { yyLOCAL = &ConvertType{Type: string(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1447: + case 1452: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7132 +//line sql.y:7160 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1448: + case 1453: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:7136 +//line sql.y:7164 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1449: + case 1454: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7141 +//line sql.y:7169 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1450: + case 1455: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7145 +//line sql.y:7173 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1451: + case 1456: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7150 +//line sql.y:7178 { yyVAL.str = string("") } - case 1452: + case 1457: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7154 +//line sql.y:7182 { yyVAL.str = encodeSQLString(yyDollar[2].str) } - case 1453: + case 1458: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*When -//line sql.y:7160 +//line sql.y:7188 { yyLOCAL = []*When{yyDollar[1].whenUnion()} } yyVAL.union = yyLOCAL - case 1454: + case 1459: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7164 +//line sql.y:7192 { yySLICE := (*[]*When)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[2].whenUnion()) } - case 1455: + case 1460: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *When -//line sql.y:7170 +//line sql.y:7198 { yyLOCAL = &When{Cond: yyDollar[2].exprUnion(), Val: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1456: + case 1461: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7175 +//line sql.y:7203 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1457: + case 1462: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7179 +//line sql.y:7207 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1458: + case 1463: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ColName -//line sql.y:7185 +//line sql.y:7213 { yyLOCAL = &ColName{Name: yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 1459: + case 1464: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *ColName -//line sql.y:7189 +//line sql.y:7217 { yyLOCAL = &ColName{Name: NewIdentifierCI(string(yyDollar[1].str))} } yyVAL.union = yyLOCAL - case 1460: + case 1465: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *ColName -//line sql.y:7193 +//line sql.y:7221 { yyLOCAL = &ColName{Qualifier: TableName{Name: yyDollar[1].identifierCS}, Name: yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 1461: + case 1466: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *ColName -//line sql.y:7197 +//line sql.y:7225 { yyLOCAL = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].identifierCS, Name: yyDollar[3].identifierCS}, Name: yyDollar[5].identifierCI} } yyVAL.union = yyLOCAL - case 1462: + case 1467: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7203 +//line sql.y:7231 { yyLOCAL = yyDollar[1].colNameUnion() } yyVAL.union = yyLOCAL - case 1463: + case 1468: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7207 +//line sql.y:7235 { yyLOCAL = &Offset{V: convertStringToInt(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 1464: + case 1469: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7213 +//line sql.y:7241 { // TODO(sougou): Deprecate this construct. if yyDollar[1].identifierCI.Lowered() != "value" { @@ -20990,426 +21163,426 @@ yydefault: yyLOCAL = NewIntLiteral("1") } yyVAL.union = yyLOCAL - case 1465: + case 1470: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7222 +//line sql.y:7250 { yyLOCAL = NewIntLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1466: + case 1471: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7226 +//line sql.y:7254 { yyLOCAL = parseBindVariable(yylex, yyDollar[1].str[1:]) } yyVAL.union = yyLOCAL - case 1467: + case 1472: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Exprs -//line sql.y:7231 +//line sql.y:7259 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1468: + case 1473: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Exprs -//line sql.y:7235 +//line sql.y:7263 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1469: + case 1474: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Expr -//line sql.y:7240 +//line sql.y:7268 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1470: + case 1475: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Expr -//line sql.y:7244 +//line sql.y:7272 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1471: + case 1476: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *NamedWindow -//line sql.y:7250 +//line sql.y:7278 { yyLOCAL = &NamedWindow{yyDollar[2].windowDefinitionsUnion()} } yyVAL.union = yyLOCAL - case 1472: + case 1477: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7256 +//line sql.y:7284 { yyLOCAL = NamedWindows{yyDollar[1].namedWindowUnion()} } yyVAL.union = yyLOCAL - case 1473: + case 1478: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7260 +//line sql.y:7288 { yySLICE := (*NamedWindows)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].namedWindowUnion()) } - case 1474: + case 1479: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7265 +//line sql.y:7293 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1475: + case 1480: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL NamedWindows -//line sql.y:7269 +//line sql.y:7297 { yyLOCAL = yyDollar[1].namedWindowsUnion() } yyVAL.union = yyLOCAL - case 1476: + case 1481: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7274 +//line sql.y:7302 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1477: + case 1482: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7278 +//line sql.y:7306 { yyLOCAL = yyDollar[1].orderByUnion() } yyVAL.union = yyLOCAL - case 1478: + case 1483: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7284 +//line sql.y:7312 { yyLOCAL = yyDollar[3].orderByUnion() } yyVAL.union = yyLOCAL - case 1479: + case 1484: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderBy -//line sql.y:7290 +//line sql.y:7318 { yyLOCAL = OrderBy{yyDollar[1].orderUnion()} } yyVAL.union = yyLOCAL - case 1480: + case 1485: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7294 +//line sql.y:7322 { yySLICE := (*OrderBy)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].orderUnion()) } - case 1481: + case 1486: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Order -//line sql.y:7300 +//line sql.y:7328 { yyLOCAL = &Order{Expr: yyDollar[1].exprUnion(), Direction: yyDollar[2].orderDirectionUnion()} } yyVAL.union = yyLOCAL - case 1482: + case 1487: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7305 +//line sql.y:7333 { yyLOCAL = AscOrder } yyVAL.union = yyLOCAL - case 1483: + case 1488: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7309 +//line sql.y:7337 { yyLOCAL = AscOrder } yyVAL.union = yyLOCAL - case 1484: + case 1489: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL OrderDirection -//line sql.y:7313 +//line sql.y:7341 { yyLOCAL = DescOrder } yyVAL.union = yyLOCAL - case 1485: + case 1490: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *Limit -//line sql.y:7318 +//line sql.y:7346 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1486: + case 1491: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Limit -//line sql.y:7322 +//line sql.y:7350 { yyLOCAL = yyDollar[1].limitUnion() } yyVAL.union = yyLOCAL - case 1487: + case 1492: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Limit -//line sql.y:7328 +//line sql.y:7356 { yyLOCAL = &Limit{Rowcount: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1488: + case 1493: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Limit -//line sql.y:7332 +//line sql.y:7360 { yyLOCAL = &Limit{Offset: yyDollar[2].exprUnion(), Rowcount: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 1489: + case 1494: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Limit -//line sql.y:7336 +//line sql.y:7364 { yyLOCAL = &Limit{Offset: yyDollar[4].exprUnion(), Rowcount: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1490: + case 1495: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7341 +//line sql.y:7369 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1491: + case 1496: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7345 +//line sql.y:7373 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion(), yyDollar[2].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1492: + case 1497: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7349 +//line sql.y:7377 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion(), yyDollar[2].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1493: + case 1498: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7353 +//line sql.y:7381 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1494: + case 1499: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []AlterOption -//line sql.y:7357 +//line sql.y:7385 { yyLOCAL = []AlterOption{yyDollar[1].alterOptionUnion()} } yyVAL.union = yyLOCAL - case 1495: + case 1500: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7364 +//line sql.y:7392 { yyLOCAL = &LockOption{Type: DefaultType} } yyVAL.union = yyLOCAL - case 1496: + case 1501: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7368 +//line sql.y:7396 { yyLOCAL = &LockOption{Type: NoneType} } yyVAL.union = yyLOCAL - case 1497: + case 1502: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7372 +//line sql.y:7400 { yyLOCAL = &LockOption{Type: SharedType} } yyVAL.union = yyLOCAL - case 1498: + case 1503: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7376 +//line sql.y:7404 { yyLOCAL = &LockOption{Type: ExclusiveType} } yyVAL.union = yyLOCAL - case 1499: + case 1504: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7382 +//line sql.y:7410 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1500: + case 1505: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7386 +//line sql.y:7414 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1501: + case 1506: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7390 +//line sql.y:7418 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1502: + case 1507: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL AlterOption -//line sql.y:7394 +//line sql.y:7422 { yyLOCAL = AlgorithmValue(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1503: + case 1508: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7399 +//line sql.y:7427 { yyVAL.str = "" } - case 1504: + case 1509: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7403 +//line sql.y:7431 { yyVAL.str = string(yyDollar[3].str) } - case 1505: + case 1510: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7407 +//line sql.y:7435 { yyVAL.str = string(yyDollar[3].str) } - case 1506: + case 1511: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7411 +//line sql.y:7439 { yyVAL.str = string(yyDollar[3].str) } - case 1507: + case 1512: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7416 +//line sql.y:7444 { yyVAL.str = "" } - case 1508: + case 1513: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7420 +//line sql.y:7448 { yyVAL.str = yyDollar[3].str } - case 1509: + case 1514: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7426 +//line sql.y:7454 { yyVAL.str = string(yyDollar[1].str) } - case 1510: + case 1515: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7430 +//line sql.y:7458 { yyVAL.str = string(yyDollar[1].str) } - case 1511: + case 1516: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7435 +//line sql.y:7463 { yyVAL.str = "" } - case 1512: + case 1517: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:7439 +//line sql.y:7467 { yyVAL.str = yyDollar[2].str } - case 1513: + case 1518: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7444 +//line sql.y:7472 { yyVAL.str = "cascaded" } - case 1514: + case 1519: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7448 +//line sql.y:7476 { yyVAL.str = string(yyDollar[1].str) } - case 1515: + case 1520: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7452 +//line sql.y:7480 { yyVAL.str = string(yyDollar[1].str) } - case 1516: + case 1521: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *Definer -//line sql.y:7457 +//line sql.y:7485 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1517: + case 1522: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Definer -//line sql.y:7461 +//line sql.y:7489 { yyLOCAL = yyDollar[3].definerUnion() } yyVAL.union = yyLOCAL - case 1518: + case 1523: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Definer -//line sql.y:7467 +//line sql.y:7495 { yyLOCAL = &Definer{ Name: string(yyDollar[1].str), } } yyVAL.union = yyLOCAL - case 1519: + case 1524: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *Definer -//line sql.y:7473 +//line sql.y:7501 { yyLOCAL = &Definer{ Name: string(yyDollar[1].str), } } yyVAL.union = yyLOCAL - case 1520: + case 1525: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Definer -//line sql.y:7479 +//line sql.y:7507 { yyLOCAL = &Definer{ Name: yyDollar[1].str, @@ -21417,369 +21590,409 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1521: + case 1526: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7488 +//line sql.y:7516 { yyVAL.str = encodeSQLString(yyDollar[1].str) } - case 1522: + case 1527: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7492 +//line sql.y:7520 { yyVAL.str = formatIdentifier(yyDollar[1].str) } - case 1523: + case 1528: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7497 +//line sql.y:7525 { yyVAL.str = "" } - case 1524: + case 1529: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7501 +//line sql.y:7529 { yyVAL.str = formatAddress(yyDollar[1].str) } - case 1525: + case 1530: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL Lock -//line sql.y:7507 +//line sql.y:7535 { yyLOCAL = ForUpdateLock } yyVAL.union = yyLOCAL - case 1526: + case 1531: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL Lock +//line sql.y:7539 + { + yyLOCAL = ForUpdateLockNoWait + } + yyVAL.union = yyLOCAL + case 1532: + yyDollar = yyS[yypt-4 : yypt+1] + var yyLOCAL Lock +//line sql.y:7543 + { + yyLOCAL = ForUpdateLockSkipLocked + } + yyVAL.union = yyLOCAL + case 1533: + yyDollar = yyS[yypt-2 : yypt+1] + var yyLOCAL Lock +//line sql.y:7547 + { + yyLOCAL = ForShareLock + } + yyVAL.union = yyLOCAL + case 1534: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL Lock +//line sql.y:7551 + { + yyLOCAL = ForShareLockNoWait + } + yyVAL.union = yyLOCAL + case 1535: + yyDollar = yyS[yypt-4 : yypt+1] + var yyLOCAL Lock +//line sql.y:7555 + { + yyLOCAL = ForShareLockSkipLocked + } + yyVAL.union = yyLOCAL + case 1536: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL Lock -//line sql.y:7511 +//line sql.y:7559 { yyLOCAL = ShareModeLock } yyVAL.union = yyLOCAL - case 1527: + case 1537: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7517 +//line sql.y:7565 { yyLOCAL = &SelectInto{Type: IntoOutfileS3, FileName: encodeSQLString(yyDollar[4].str), Charset: yyDollar[5].columnCharset, FormatOption: yyDollar[6].str, ExportOption: yyDollar[7].str, Manifest: yyDollar[8].str, Overwrite: yyDollar[9].str} } yyVAL.union = yyLOCAL - case 1528: + case 1538: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7521 +//line sql.y:7569 { yyLOCAL = &SelectInto{Type: IntoDumpfile, FileName: encodeSQLString(yyDollar[3].str), Charset: ColumnCharset{}, FormatOption: "", ExportOption: "", Manifest: "", Overwrite: ""} } yyVAL.union = yyLOCAL - case 1529: + case 1539: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *SelectInto -//line sql.y:7525 +//line sql.y:7573 { yyLOCAL = &SelectInto{Type: IntoOutfile, FileName: encodeSQLString(yyDollar[3].str), Charset: yyDollar[4].columnCharset, FormatOption: "", ExportOption: yyDollar[5].str, Manifest: "", Overwrite: ""} } yyVAL.union = yyLOCAL - case 1530: + case 1540: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7530 +//line sql.y:7578 { yyVAL.str = "" } - case 1531: + case 1541: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7534 +//line sql.y:7582 { yyVAL.str = " format csv" + yyDollar[3].str } - case 1532: + case 1542: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7538 +//line sql.y:7586 { yyVAL.str = " format text" + yyDollar[3].str } - case 1533: + case 1543: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7543 +//line sql.y:7591 { yyVAL.str = "" } - case 1534: + case 1544: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7547 +//line sql.y:7595 { yyVAL.str = " header" } - case 1535: + case 1545: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7552 +//line sql.y:7600 { yyVAL.str = "" } - case 1536: + case 1546: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7556 +//line sql.y:7604 { yyVAL.str = " manifest on" } - case 1537: + case 1547: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7560 +//line sql.y:7608 { yyVAL.str = " manifest off" } - case 1538: + case 1548: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7565 +//line sql.y:7613 { yyVAL.str = "" } - case 1539: + case 1549: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7569 +//line sql.y:7617 { yyVAL.str = " overwrite on" } - case 1540: + case 1550: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7573 +//line sql.y:7621 { yyVAL.str = " overwrite off" } - case 1541: + case 1551: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7579 +//line sql.y:7627 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1542: + case 1552: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7584 +//line sql.y:7632 { yyVAL.str = "" } - case 1543: + case 1553: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7588 +//line sql.y:7636 { yyVAL.str = " lines" + yyDollar[2].str } - case 1544: + case 1554: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7594 +//line sql.y:7642 { yyVAL.str = yyDollar[1].str } - case 1545: + case 1555: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7598 +//line sql.y:7646 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1546: + case 1556: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7604 +//line sql.y:7652 { yyVAL.str = " starting by " + encodeSQLString(yyDollar[3].str) } - case 1547: + case 1557: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7608 +//line sql.y:7656 { yyVAL.str = " terminated by " + encodeSQLString(yyDollar[3].str) } - case 1548: + case 1558: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7613 +//line sql.y:7661 { yyVAL.str = "" } - case 1549: + case 1559: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7617 +//line sql.y:7665 { yyVAL.str = " " + yyDollar[1].str + yyDollar[2].str } - case 1550: + case 1560: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7623 +//line sql.y:7671 { yyVAL.str = yyDollar[1].str } - case 1551: + case 1561: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7627 +//line sql.y:7675 { yyVAL.str = yyDollar[1].str + yyDollar[2].str } - case 1552: + case 1562: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7633 +//line sql.y:7681 { yyVAL.str = " terminated by " + encodeSQLString(yyDollar[3].str) } - case 1553: + case 1563: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:7637 +//line sql.y:7685 { yyVAL.str = yyDollar[1].str + " enclosed by " + encodeSQLString(yyDollar[4].str) } - case 1554: + case 1564: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7641 +//line sql.y:7689 { yyVAL.str = " escaped by " + encodeSQLString(yyDollar[3].str) } - case 1555: + case 1565: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7646 +//line sql.y:7694 { yyVAL.str = "" } - case 1556: + case 1566: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7650 +//line sql.y:7698 { yyVAL.str = " optionally" } - case 1557: + case 1567: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *Insert -//line sql.y:7663 +//line sql.y:7711 { yyLOCAL = &Insert{Rows: yyDollar[2].valuesUnion()} } yyVAL.union = yyLOCAL - case 1558: + case 1568: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *Insert -//line sql.y:7667 +//line sql.y:7715 { yyLOCAL = &Insert{Rows: yyDollar[1].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1559: + case 1569: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *Insert -//line sql.y:7671 +//line sql.y:7719 { yyLOCAL = &Insert{Columns: yyDollar[2].columnsUnion(), Rows: yyDollar[5].valuesUnion()} } yyVAL.union = yyLOCAL - case 1560: + case 1570: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Insert -//line sql.y:7675 +//line sql.y:7723 { yyLOCAL = &Insert{Columns: []IdentifierCI{}, Rows: yyDollar[4].valuesUnion()} } yyVAL.union = yyLOCAL - case 1561: + case 1571: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *Insert -//line sql.y:7679 +//line sql.y:7727 { yyLOCAL = &Insert{Columns: yyDollar[2].columnsUnion(), Rows: yyDollar[4].selStmtUnion()} } yyVAL.union = yyLOCAL - case 1562: + case 1572: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Columns -//line sql.y:7685 +//line sql.y:7733 { yyLOCAL = Columns{yyDollar[1].identifierCI} } yyVAL.union = yyLOCAL - case 1563: + case 1573: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Columns -//line sql.y:7689 +//line sql.y:7737 { yyLOCAL = Columns{yyDollar[3].identifierCI} } yyVAL.union = yyLOCAL - case 1564: + case 1574: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7693 +//line sql.y:7741 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].identifierCI) } - case 1565: + case 1575: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:7697 +//line sql.y:7745 { yySLICE := (*Columns)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[5].identifierCI) } - case 1566: + case 1576: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7702 +//line sql.y:7750 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1567: + case 1577: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7706 +//line sql.y:7754 { yyLOCAL = yyDollar[5].updateExprsUnion() } yyVAL.union = yyLOCAL - case 1568: + case 1578: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Values -//line sql.y:7712 +//line sql.y:7760 { yyLOCAL = Values{yyDollar[1].valTupleUnion()} } yyVAL.union = yyLOCAL - case 1569: + case 1579: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7716 +//line sql.y:7764 { yySLICE := (*Values)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].valTupleUnion()) } - case 1570: + case 1580: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7722 +//line sql.y:7770 { yyLOCAL = yyDollar[1].valTupleUnion() } yyVAL.union = yyLOCAL - case 1571: + case 1581: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7726 +//line sql.y:7774 { yyLOCAL = ValTuple{} } yyVAL.union = yyLOCAL - case 1572: + case 1582: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7732 +//line sql.y:7780 { yyLOCAL = ValTuple(yyDollar[2].exprsUnion()) } yyVAL.union = yyLOCAL - case 1573: + case 1583: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL ValTuple -//line sql.y:7736 +//line sql.y:7784 { yyLOCAL = ValTuple(yyDollar[3].exprsUnion()) } yyVAL.union = yyLOCAL - case 1574: + case 1584: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7741 +//line sql.y:7789 { if len(yyDollar[1].valTupleUnion()) == 1 { yyLOCAL = yyDollar[1].valTupleUnion()[0] @@ -21788,300 +22001,300 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1575: + case 1585: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL UpdateExprs -//line sql.y:7751 +//line sql.y:7799 { yyLOCAL = UpdateExprs{yyDollar[1].updateExprUnion()} } yyVAL.union = yyLOCAL - case 1576: + case 1586: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:7755 +//line sql.y:7803 { yySLICE := (*UpdateExprs)(yyIaddr(yyVAL.union)) *yySLICE = append(*yySLICE, yyDollar[3].updateExprUnion()) } - case 1577: + case 1587: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *UpdateExpr -//line sql.y:7761 +//line sql.y:7809 { yyLOCAL = &UpdateExpr{Name: yyDollar[1].colNameUnion(), Expr: yyDollar[3].exprUnion()} } yyVAL.union = yyLOCAL - case 1579: + case 1589: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:7768 +//line sql.y:7816 { yyVAL.str = "charset" } - case 1582: + case 1592: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7778 +//line sql.y:7826 { yyLOCAL = NewStrLiteral(yyDollar[1].identifierCI.String()) } yyVAL.union = yyLOCAL - case 1583: + case 1593: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7782 +//line sql.y:7830 { yyLOCAL = NewStrLiteral(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1584: + case 1594: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Expr -//line sql.y:7786 +//line sql.y:7834 { yyLOCAL = &Default{} } yyVAL.union = yyLOCAL - case 1587: + case 1597: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7795 +//line sql.y:7843 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1588: + case 1598: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line sql.y:7797 +//line sql.y:7845 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1589: + case 1599: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7800 +//line sql.y:7848 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1590: + case 1600: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line sql.y:7802 +//line sql.y:7850 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1591: + case 1601: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line sql.y:7805 +//line sql.y:7853 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1592: + case 1602: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line sql.y:7807 +//line sql.y:7855 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1593: + case 1603: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Ignore -//line sql.y:7810 +//line sql.y:7858 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1594: + case 1604: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Ignore -//line sql.y:7812 +//line sql.y:7860 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1595: + case 1605: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7815 +//line sql.y:7863 { yyVAL.empty = struct{}{} } - case 1596: + case 1606: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7817 +//line sql.y:7865 { yyVAL.empty = struct{}{} } - case 1597: + case 1607: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7819 +//line sql.y:7867 { yyVAL.empty = struct{}{} } - case 1598: + case 1608: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL Statement -//line sql.y:7823 +//line sql.y:7871 { yyLOCAL = &CallProc{Name: yyDollar[2].tableName, Params: yyDollar[4].exprsUnion()} } yyVAL.union = yyLOCAL - case 1599: + case 1609: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL Exprs -//line sql.y:7828 +//line sql.y:7876 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1600: + case 1610: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL Exprs -//line sql.y:7832 +//line sql.y:7880 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 1601: + case 1611: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:7837 +//line sql.y:7885 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1602: + case 1612: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*IndexOption -//line sql.y:7839 +//line sql.y:7887 { yyLOCAL = []*IndexOption{yyDollar[1].indexOptionUnion()} } yyVAL.union = yyLOCAL - case 1603: + case 1613: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *IndexOption -//line sql.y:7843 +//line sql.y:7891 { yyLOCAL = &IndexOption{Name: string(yyDollar[1].str), String: string(yyDollar[2].identifierCI.String())} } yyVAL.union = yyLOCAL - case 1604: + case 1614: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7849 +//line sql.y:7897 { yyVAL.identifierCI = yyDollar[1].identifierCI } - case 1605: + case 1615: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7853 +//line sql.y:7901 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 1607: + case 1617: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7860 +//line sql.y:7908 { yyVAL.identifierCI = NewIdentifierCI(string(yyDollar[1].str)) } - case 1608: + case 1618: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7866 +//line sql.y:7914 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1609: + case 1619: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7870 +//line sql.y:7918 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1610: + case 1620: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:7876 +//line sql.y:7924 { yyVAL.identifierCS = NewIdentifierCS("") } - case 1611: + case 1621: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7880 +//line sql.y:7928 { yyVAL.identifierCS = yyDollar[1].identifierCS } - case 1613: + case 1623: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:7887 +//line sql.y:7935 { yyVAL.identifierCS = NewIdentifierCS(string(yyDollar[1].str)) } - case 1614: + case 1624: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL Statement -//line sql.y:7893 +//line sql.y:7941 { yyLOCAL = &Kill{Type: yyDollar[2].killTypeUnion(), ProcesslistID: convertStringToUInt64(yyDollar[3].str)} } yyVAL.union = yyLOCAL - case 1615: + case 1625: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL KillType -//line sql.y:7899 +//line sql.y:7947 { yyLOCAL = ConnectionType } yyVAL.union = yyLOCAL - case 1616: + case 1626: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL KillType -//line sql.y:7903 +//line sql.y:7951 { yyLOCAL = ConnectionType } yyVAL.union = yyLOCAL - case 1617: + case 1627: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL KillType -//line sql.y:7907 +//line sql.y:7955 { yyLOCAL = QueryType } yyVAL.union = yyLOCAL - case 2232: + case 2244: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8550 +//line sql.y:8600 { } - case 2233: + case 2245: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8555 +//line sql.y:8605 { } - case 2234: + case 2246: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:8559 +//line sql.y:8609 { skipToEnd(yylex) } - case 2235: + case 2247: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:8564 +//line sql.y:8614 { skipToEnd(yylex) } - case 2236: + case 2248: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8568 +//line sql.y:8618 { skipToEnd(yylex) } - case 2237: + case 2249: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:8572 +//line sql.y:8622 { skipToEnd(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index fcb481725e9..38af54c7323 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -16,6 +16,8 @@ limitations under the License. %{ package sqlparser +import "vitess.io/vitess/go/ptr" + func setParseTree(yylex yyLexer, stmt Statement) { yylex.(*Tokenizer).ParseTree = stmt } @@ -191,6 +193,7 @@ func markBindVariable(yylex yyLexer, bvar string) { partitionByType PartitionByType definer *Definer integer int + intPtr *int JSONTableExpr *JSONTableExpr jtColumnDefinition *JtColumnDefinition @@ -202,6 +205,15 @@ func markBindVariable(yylex yyLexer, bvar string) { // These precedence rules are there to handle shift-reduce conflicts. %nonassoc MEMBER +// MULTIPLE_TEXT_LITERAL is used to resolve shift-reduce conflicts occuring due to multiple STRING symbols occuring one after the other. +// According to the ANSI standard, these strings should be concatenated together. +// The shift-reduce conflict occurrs because after seeing a STRING, if we see another one, then we can either shift to concatenate them or +// reduce the STRING into a text_literal, eventually into a simple_expr and use the coming string as an alias. +// The way to fix this conflict is to give shifting higher precedence than reducing. +// Adding no precedence also works, since shifting is the default, but it reports a conflict which we can avoid by adding this precedence rule. +// In order to ensure lower precedence of reduction, this rule has to come before the precedence declaration of STRING. +// This precedence should not be used anywhere else other than with rules where text_literal is being reduced. +%nonassoc MULTIPLE_TEXT_LITERAL // FUNCTION_CALL_NON_KEYWORD is used to resolve shift-reduce conflicts occuring due to function_call_generic symbol and // having special parsing for functions whose names are non-reserved keywords. The shift-reduce conflict occurrs because // after seeing a non-reserved keyword, if we see '(', then we can either shift to use the special parsing grammar rule or @@ -303,7 +315,7 @@ func markBindVariable(yylex yyLexer, bvar string) { %token SEQUENCE MERGE TEMPORARY TEMPTABLE INVOKER SECURITY FIRST AFTER LAST // Migration tokens -%token VITESS_MIGRATION CANCEL RETRY LAUNCH COMPLETE CLEANUP THROTTLE UNTHROTTLE EXPIRE RATIO +%token VITESS_MIGRATION CANCEL RETRY LAUNCH COMPLETE CLEANUP THROTTLE UNTHROTTLE FORCE_CUTOVER EXPIRE RATIO // Throttler tokens %token VITESS_THROTTLER @@ -343,7 +355,7 @@ func markBindVariable(yylex yyLexer, bvar string) { %token SQL_TSI_DAY SQL_TSI_WEEK SQL_TSI_HOUR SQL_TSI_MINUTE SQL_TSI_MONTH SQL_TSI_QUARTER SQL_TSI_SECOND SQL_TSI_MICROSECOND SQL_TSI_YEAR %token REPLACE %token CONVERT CAST -%token SUBSTR SUBSTRING +%token SUBSTR SUBSTRING MID %token SEPARATOR %token TIMESTAMPADD TIMESTAMPDIFF %token WEIGHT_STRING @@ -459,12 +471,12 @@ func markBindVariable(yylex yyLexer, bvar string) { %type distinct_opt union_op replace_opt local_opt %type select_expression_list select_expression_list_opt %type select_expression -%type select_options flush_option_list +%type select_options select_options_opt flush_option_list %type select_option algorithm_view security_view security_view_opt %type generated_always_opt user_username address_opt %type definer_opt user %type expression signed_literal signed_literal_or_null null_as_literal now_or_signed_literal signed_literal bit_expr regular_expressions xml_expressions -%type simple_expr literal NUM_literal text_literal text_literal_or_arg bool_pri literal_or_null now predicate tuple_expression null_int_variable_arg performance_schema_function_expressions gtid_function_expressions +%type simple_expr literal NUM_literal text_start text_literal text_literal_or_arg bool_pri literal_or_null now predicate tuple_expression null_int_variable_arg performance_schema_function_expressions gtid_function_expressions %type from_opt table_references from_clause %type table_reference table_factor join_table json_table_function %type jt_column @@ -537,7 +549,8 @@ func markBindVariable(yylex yyLexer, bvar string) { %type array_opt %type column_type %type int_type decimal_type numeric_type time_type char_type spatial_type -%type length_opt partition_comment partition_data_directory partition_index_directory +%type partition_comment partition_data_directory partition_index_directory +%type length_opt %type func_datetime_precision %type charset_opt %type collate_opt @@ -897,11 +910,11 @@ vstream_statement: // query_primary is an unparenthesized SELECT with no order by clause or beyond. query_primary: // 1 2 3 4 5 6 7 8 9 10 - SELECT comment_opt select_options select_expression_list into_clause from_opt where_expression_opt group_by_opt having_opt named_windows_list_opt + SELECT comment_opt select_options_opt select_expression_list into_clause from_opt where_expression_opt group_by_opt having_opt named_windows_list_opt { $$ = NewSelect(Comments($2), $4/*SelectExprs*/, $3/*options*/, $5/*into*/, $6/*from*/, NewWhere(WhereClause, $7), GroupBy($8), NewWhere(HavingClause, $9), $10) } -| SELECT comment_opt select_options select_expression_list from_opt where_expression_opt group_by_opt having_opt named_windows_list_opt +| SELECT comment_opt select_options_opt select_expression_list from_opt where_expression_opt group_by_opt having_opt named_windows_list_opt { $$ = NewSelect(Comments($2), $4/*SelectExprs*/, $3/*options*/, nil, $5/*from*/, NewWhere(WhereClause, $6), GroupBy($7), NewWhere(HavingClause, $8), $9) } @@ -1464,14 +1477,12 @@ column_attribute_list_opt: } | column_attribute_list_opt NULL { - val := true - $1.Null = &val + $1.Null = ptr.Of(true) $$ = $1 } | column_attribute_list_opt NOT NULL { - val := false - $1.Null = &val + $1.Null = ptr.Of(false) $$ = $1 } | column_attribute_list_opt DEFAULT openb expression closeb @@ -1525,14 +1536,12 @@ column_attribute_list_opt: } | column_attribute_list_opt VISIBLE { - val := false - $1.Invisible = &val + $1.Invisible = ptr.Of(false) $$ = $1 } | column_attribute_list_opt INVISIBLE { - val := true - $1.Invisible = &val + $1.Invisible = ptr.Of(true) $$ = $1 } | column_attribute_list_opt ENGINE_ATTRIBUTE equal_opt STRING @@ -1579,14 +1588,12 @@ generated_column_attribute_list_opt: } | generated_column_attribute_list_opt NULL { - val := true - $1.Null = &val + $1.Null = ptr.Of(true) $$ = $1 } | generated_column_attribute_list_opt NOT NULL { - val := false - $1.Null = &val + $1.Null = ptr.Of(false) $$ = $1 } | generated_column_attribute_list_opt COMMENT_KEYWORD STRING @@ -1601,14 +1608,12 @@ generated_column_attribute_list_opt: } | generated_column_attribute_list_opt VISIBLE { - val := false - $1.Invisible = &val + $1.Invisible = ptr.Of(false) $$ = $1 } | generated_column_attribute_list_opt INVISIBLE { - val := true - $1.Invisible = &val + $1.Invisible = ptr.Of(true) $$ = $1 } @@ -1667,7 +1672,7 @@ NULL } literal: -text_literal +text_literal %prec MULTIPLE_TEXT_LITERAL { $$= $1 } @@ -1926,6 +1931,16 @@ INTEGRAL } text_literal: +text_start + { + $$ = $1 + } +| text_literal STRING + { + $$ = AppendString($1, $2) + } + +text_start: STRING { $$ = NewStrLiteral($1) @@ -1940,7 +1955,7 @@ STRING } text_literal_or_arg: - text_literal + text_literal %prec MULTIPLE_TEXT_LITERAL { $$ = $1 } @@ -2213,7 +2228,7 @@ length_opt: } | '(' INTEGRAL ')' { - $$ = NewIntLiteral($2) + $$ = ptr.Of(convertStringToInt($2)) } double_length_opt: @@ -2223,8 +2238,8 @@ double_length_opt: | '(' INTEGRAL ',' INTEGRAL ')' { $$ = LengthScaleOption{ - Length: NewIntLiteral($2), - Scale: NewIntLiteral($4), + Length: ptr.Of(convertStringToInt($2)), + Scale: ptr.Of(convertStringToInt($4)), } } @@ -2236,7 +2251,7 @@ double_length_opt | '(' INTEGRAL ')' { $$ = LengthScaleOption{ - Length: NewIntLiteral($2), + Length: ptr.Of(convertStringToInt($2)), } } @@ -2247,14 +2262,14 @@ decimal_length_opt: | '(' INTEGRAL ')' { $$ = LengthScaleOption{ - Length: NewIntLiteral($2), + Length: ptr.Of(convertStringToInt($2)), } } | '(' INTEGRAL ',' INTEGRAL ')' { $$ = LengthScaleOption{ - Length: NewIntLiteral($2), - Scale: NewIntLiteral($4), + Length: ptr.Of(convertStringToInt($2)), + Scale: ptr.Of(convertStringToInt($4)), } } @@ -3002,13 +3017,11 @@ alter_option: } | ALTER column_opt column_name SET VISIBLE { - val := false - $$ = &AlterColumn{Column: $3, Invisible:&val} + $$ = &AlterColumn{Column: $3, Invisible: ptr.Of(false)} } | ALTER column_opt column_name SET INVISIBLE { - val := true - $$ = &AlterColumn{Column: $3, Invisible:&val} + $$ = &AlterColumn{Column: $3, Invisible: ptr.Of(true)} } | ALTER CHECK ci_identifier enforced { @@ -3365,6 +3378,19 @@ alter_statement: Type: UnthrottleAllMigrationType, } } +| ALTER comment_opt VITESS_MIGRATION STRING FORCE_CUTOVER + { + $$ = &AlterMigration{ + Type: ForceCutOverMigrationType, + UUID: string($4), + } + } +| ALTER comment_opt VITESS_MIGRATION FORCE_CUTOVER ALL + { + $$ = &AlterMigration{ + Type: ForceCutOverAllMigrationType, + } + } partitions_options_opt: { @@ -3740,14 +3766,12 @@ partition_definition_attribute_list_opt: } | partition_definition_attribute_list_opt partition_max_rows { - val := $2 - $1.MaxRows = &val + $1.MaxRows = ptr.Of($2) $$ = $1 } | partition_definition_attribute_list_opt partition_min_rows { - val := $2 - $1.MinRows = &val + $1.MinRows = ptr.Of($2) $$ = $1 } | partition_definition_attribute_list_opt partition_tablespace_name @@ -3808,14 +3832,12 @@ subpartition_definition_attribute_list_opt: } | subpartition_definition_attribute_list_opt partition_max_rows { - val := $2 - $1.MaxRows = &val + $1.MaxRows = ptr.Of($2) $$ = $1 } | subpartition_definition_attribute_list_opt partition_min_rows { - val := $2 - $1.MinRows = &val + $1.MinRows = ptr.Of($2) $$ = $1 } | subpartition_definition_attribute_list_opt partition_tablespace_name @@ -4128,6 +4150,10 @@ show_statement: { $$ = &Show{&ShowBasic{Command: VschemaTables}} } +| SHOW VSCHEMA KEYSPACES + { + $$ = &Show{&ShowBasic{Command: VschemaKeyspaces}} + } | SHOW VSCHEMA VINDEXES { $$ = &Show{&ShowBasic{Command: VschemaVindexes}} @@ -4412,14 +4438,6 @@ explain_format_opt: { $$ = TreeType } -| FORMAT '=' VITESS - { - $$ = VitessType - } -| FORMAT '=' VTEXPLAIN - { - $$ = VTExplainType - } | FORMAT '=' TRADITIONAL { $$ = TraditionalType @@ -4784,25 +4802,23 @@ select_expression_list_opt: $$ = $1 } -select_options: +select_options_opt: { $$ = nil } -| select_option +| select_options { - $$ = []string{$1} - } -| select_option select_option // TODO: figure out a way to do this recursively instead. - { // TODO: This is a hack since I couldn't get it to work in a nicer way. I got 'conflicts: 8 shift/reduce' - $$ = []string{$1, $2} + $$ = $1 } -| select_option select_option select_option + +select_options: +select_option { - $$ = []string{$1, $2, $3} + $$ = []string{$1} } -| select_option select_option select_option select_option +| select_options select_option { - $$ = []string{$1, $2, $3, $4} + $$ = append($1, $2) } select_option: @@ -5198,6 +5214,14 @@ index_hint: { $$ = &IndexHint{Type: ForceOp, ForType: $3, Indexes: $5} } +| USE VINDEX openb index_list closeb + { + $$ = &IndexHint{Type: UseVindexOp, Indexes: $4 } + } +| IGNORE VINDEX openb index_list closeb + { + $$ = &IndexHint{Type: IgnoreVindexOp, Indexes: $4} + } index_hint_for_opt: { @@ -5862,6 +5886,10 @@ function_call_keyword: { $$ = &SubstrExpr{Name: $3, From: $5, To: $7} } +| MID openb expression ',' expression ',' expression closeb + { + $$ = &SubstrExpr{Name: $3, From: $5, To: $7} + } | SUBSTRING openb expression ',' expression closeb { $$ = &SubstrExpr{Name: $3, From: $5} @@ -7056,11 +7084,11 @@ convert_type_weight_string: } | AS BINARY '(' INTEGRAL ')' { - $$ = &ConvertType{Type: string($2), Length: NewIntLiteral($4)} + $$ = &ConvertType{Type: string($2), Length: ptr.Of(convertStringToInt($4))} } | AS CHAR '(' INTEGRAL ')' { - $$ = &ConvertType{Type: string($2), Length: NewIntLiteral($4)} + $$ = &ConvertType{Type: string($2), Length: ptr.Of(convertStringToInt($4))} } convert_type: @@ -7507,6 +7535,26 @@ FOR UPDATE { $$ = ForUpdateLock } +| FOR UPDATE NOWAIT + { + $$ = ForUpdateLockNoWait + } +| FOR UPDATE SKIP LOCKED + { + $$ = ForUpdateLockSkipLocked + } +| FOR SHARE + { + $$ = ForShareLock + } +| FOR SHARE NOWAIT + { + $$ = ForShareLockNoWait + } +| FOR SHARE SKIP LOCKED + { + $$ = ForShareLockSkipLocked + } | LOCK IN SHARE MODE { $$ = ShareModeLock @@ -8180,6 +8228,7 @@ non_reserved_keyword: | FIXED | FLUSH | FOLLOWING +| FORCE_CUTOVER | FORMAT | FORMAT_BYTES %prec FUNCTION_CALL_NON_KEYWORD | FORMAT_PICO_TIME %prec FUNCTION_CALL_NON_KEYWORD @@ -8276,6 +8325,7 @@ non_reserved_keyword: | MEMORY | MEMBER | MERGE +| MID %prec FUNCTION_CALL_NON_KEYWORD | MIN %prec FUNCTION_CALL_NON_KEYWORD | MIN_ROWS | MODE diff --git a/go/vt/sqlparser/testdata/select_cases.txt b/go/vt/sqlparser/testdata/select_cases.txt index 661045add7d..b7d1a32c894 100644 --- a/go/vt/sqlparser/testdata/select_cases.txt +++ b/go/vt/sqlparser/testdata/select_cases.txt @@ -710,7 +710,7 @@ INPUT select substr(null,null,null),mid(null,null,null); END OUTPUT -select substr(null, null, null), mid(null, null, null) from dual +select substr(null, null, null), substr(null, null, null) from dual END INPUT select * from t1 where a=if(b<10,_ucs2 0x00C0,_ucs2 0x0062); @@ -1040,7 +1040,7 @@ INPUT select t1.a, group_concat(c order by (select mid(group_concat(c order by a),1,5) from t2 where t2.a=t1.a) desc) as grp from t1 group by 1; END OUTPUT -select t1.a, group_concat(c order by (select mid(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) desc) as grp from t1 group by 1 +select t1.a, group_concat(c order by (select substr(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) desc) as grp from t1 group by 1 END INPUT select a as like_lll from t1 where a like 'lll%'; @@ -1166,7 +1166,7 @@ INPUT select a from t1 where mid(a+0,6,3) in ( mid(20040106123400,6,3) ); END OUTPUT -select a from t1 where mid(a + 0, 6, 3) in (mid(20040106123400, 6, 3)) +select a from t1 where substr(a + 0, 6, 3) in (substr(20040106123400, 6, 3)) END INPUT select word, word=binary 0xdf as t from t1 having t > 0; @@ -2996,7 +2996,7 @@ INPUT select concat(a, if(b>10, 'x' 'æ', 'y' 'ß')) from t1; END OUTPUT -select concat(a, if(b > 10, 'x' as `æ`, 'y' as `ß`)) from t1 +select concat(a, if(b > 10, 'xæ', 'yß')) from t1 END INPUT select * from (t1 natural join t2) natural join (t3 join (t4 natural join t5) on (b < z)); @@ -3596,7 +3596,7 @@ INPUT select mid(@my_uuid,15,1); END OUTPUT -select mid(@my_uuid, 15, 1) from dual +select substr(@my_uuid, 15, 1) from dual END INPUT select collation(concat_ws(_latin2'a',_latin2'b')), coercibility(concat_ws(_latin2'a',_latin2'b')); @@ -3806,7 +3806,7 @@ INPUT select format('f','')<=replace(1,1,mid(0xd9,2,1)); END OUTPUT -select format('f', '') <= replace(1, 1, mid(0xd9, 2, 1)) from dual +select format('f', '') <= replace(1, 1, substr(0xd9, 2, 1)) from dual END INPUT select substring('hello', 4294967295, 4294967295); @@ -3866,7 +3866,7 @@ INPUT select group_concat(c order by (select mid(group_concat(c order by a),1,5) from t2 where t2.a=t1.a)) as grp from t1; END OUTPUT -select group_concat(c order by (select mid(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) asc) as grp from t1 +select group_concat(c order by (select substr(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) asc) as grp from t1 END INPUT select a1,a2,b,min(c) from t1 where ((a1 > 'a') or (a1 < '9')) and ((a2 >= 'b') and (a2 < 'z')) and (b = 'a') and ((c < 'h112') or (c = 'j121') or (c > 'k121' and c < 'm122') or (c > 'o122')) group by a1,a2,b; @@ -10376,7 +10376,7 @@ INPUT select concat(a, if(b>10, 'x' 'x', 'y' 'y')) from t1; END OUTPUT -select concat(a, if(b > 10, 'x' as x, 'y' as y)) from t1 +select concat(a, if(b > 10, 'xx', 'yy')) from t1 END INPUT select * from `information_schema`.`COLUMNS` where `TABLE_NAME` = NULL; @@ -12014,7 +12014,7 @@ INPUT select mid('hello',1,null),mid('hello',null,1),mid(null,1,1); END OUTPUT -select mid('hello', 1, null), mid('hello', null, 1), mid(null, 1, 1) from dual +select substr('hello', 1, null), substr('hello', null, 1), substr(null, 1, 1) from dual END INPUT select locate(_ujis 0xa2a1,_ujis 0xa1a2a1a3); @@ -12380,7 +12380,7 @@ INPUT select group_concat(c order by (select mid(group_concat(c order by a),1,5) from t2 where t2.a=t1.a) desc) as grp from t1; END OUTPUT -select group_concat(c order by (select mid(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) desc) as grp from t1 +select group_concat(c order by (select substr(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) desc) as grp from t1 END INPUT select a.text, b.id, b.betreff from t2 a inner join t3 b on a.id = b.forum inner join t1 c on b.id = c.thread where match(b.betreff) against ('+abc' in boolean mode) union select a.text, b.id, b.betreff from t2 a inner join t3 b on a.id = b.forum inner join t1 c on b.id = c.thread where match(c.beitrag) against ('+abc' in boolean mode) order by match(betreff) against ('+abc' in boolean mode) desc; @@ -12656,7 +12656,7 @@ INPUT select a from t1 where mid(a+0,6,3) = ( mid(20040106123400,6,3) ); END OUTPUT -select a from t1 where mid(a + 0, 6, 3) = mid(20040106123400, 6, 3) +select a from t1 where substr(a + 0, 6, 3) = substr(20040106123400, 6, 3) END INPUT select concat('',str_to_date('8:11:2.123456 03-01-02','%H:%i:%S.%f %y-%m-%d')); @@ -16772,7 +16772,7 @@ INPUT select 'The cost of accessing t1 (dont care if it changes' '^'; END OUTPUT -select 'The cost of accessing t1 (dont care if it changes' as `^` from dual +select 'The cost of accessing t1 (dont care if it changes^' from dual END INPUT select * from t1 natural left join t2 where (i is not null)=0; @@ -18872,7 +18872,7 @@ INPUT select 'hello' 'monty'; END OUTPUT -select 'hello' as monty from dual +select 'hellomonty' from dual END INPUT select collation(bin(130)), coercibility(bin(130)); @@ -21266,7 +21266,7 @@ INPUT select t1.a, group_concat(c order by (select mid(group_concat(c order by a),1,5) from t2 where t2.a=t1.a)) as grp from t1 group by 1; END OUTPUT -select t1.a, group_concat(c order by (select mid(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) asc) as grp from t1 group by 1 +select t1.a, group_concat(c order by (select substr(group_concat(c order by a asc), 1, 5) from t2 where t2.a = t1.a) asc) as grp from t1 group by 1 END INPUT select hex(inet_aton('127')); @@ -21296,7 +21296,7 @@ INPUT select left('hello',2),right('hello',2),substring('hello',2,2),mid('hello',1,5); END OUTPUT -select left('hello', 2), right('hello', 2), substr('hello', 2, 2), mid('hello', 1, 5) from dual +select left('hello', 2), right('hello', 2), substr('hello', 2, 2), substr('hello', 1, 5) from dual END INPUT select date_add("1997-12-31",INTERVAL "1 1" YEAR_MONTH); diff --git a/go/vt/sqlparser/testdata/union_cases.txt b/go/vt/sqlparser/testdata/union_cases.txt index 8e2def0e04e..d5b620cb246 100644 --- a/go/vt/sqlparser/testdata/union_cases.txt +++ b/go/vt/sqlparser/testdata/union_cases.txt @@ -1004,7 +1004,7 @@ INPUT SELECT 1 FOR SHARE UNION SELECT 2; END ERROR -syntax error at position 19 near 'SHARE' +syntax error at position 25 near 'UNION' END INPUT SELECT ST_AsText(ST_Union(shore, boundary)) FROM lakes, named_places WHERE lakes.name = 'Blue Lake' AND named_places.name = 'Goose Island'; diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index 2b82e619445..58f575f8642 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -44,18 +44,18 @@ type Tokenizer struct { multi bool specialComment *Tokenizer - Pos int - buf string + Pos int + buf string + parser *Parser } // NewStringTokenizer creates a new Tokenizer for the // sql string. -func NewStringTokenizer(sql string) *Tokenizer { - checkParserVersionFlag() - +func (p *Parser) NewStringTokenizer(sql string) *Tokenizer { return &Tokenizer{ buf: sql, BindVars: make(map[string]struct{}), + parser: p, } } @@ -680,9 +680,9 @@ func (tkn *Tokenizer) scanMySQLSpecificComment() (int, string) { commentVersion, sql := ExtractMysqlComment(tkn.buf[start:tkn.Pos]) - if mySQLParserVersion >= commentVersion { + if tkn.parser.version >= commentVersion { // Only add the special comment to the tokenizer if the version of MySQL is higher or equal to the comment version - tkn.specialComment = NewStringTokenizer(sql) + tkn.specialComment = tkn.parser.NewStringTokenizer(sql) } return tkn.Scan() diff --git a/go/vt/sqlparser/token_test.go b/go/vt/sqlparser/token_test.go index 0fd43b8f86c..b6848d35f06 100644 --- a/go/vt/sqlparser/token_test.go +++ b/go/vt/sqlparser/token_test.go @@ -74,9 +74,10 @@ func TestLiteralID(t *testing.T) { out: "@x @y", }} + parser := NewTestParser() for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - tkn := NewStringTokenizer(tcase.in) + tkn := parser.NewStringTokenizer(tcase.in) id, out := tkn.Scan() require.Equal(t, tcase.id, id) require.Equal(t, tcase.out, string(out)) @@ -148,9 +149,10 @@ func TestString(t *testing.T) { want: "hello", }} + parser := NewTestParser() for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - id, got := NewStringTokenizer(tcase.in).Scan() + id, got := parser.NewStringTokenizer(tcase.in).Scan() require.Equal(t, tcase.id, id, "Scan(%q) = (%s), want (%s)", tcase.in, tokenName(id), tokenName(tcase.id)) require.Equal(t, tcase.want, string(got)) }) @@ -193,9 +195,10 @@ func TestSplitStatement(t *testing.T) { sql: "", }} + parser := NewTestParser() for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - sql, rem, err := SplitStatement(tcase.in) + sql, rem, err := parser.SplitStatement(tcase.in) if err != nil { t.Errorf("EndOfStatementPosition(%s): ERROR: %v", tcase.in, err) return @@ -218,27 +221,28 @@ func TestVersion(t *testing.T) { in string id []int }{{ - version: "50709", + version: "5.7.9", in: "/*!80102 SELECT*/ FROM IN EXISTS", id: []int{FROM, IN, EXISTS, 0}, }, { - version: "80101", + version: "8.1.1", in: "/*!80102 SELECT*/ FROM IN EXISTS", id: []int{FROM, IN, EXISTS, 0}, }, { - version: "80201", + version: "8.2.1", in: "/*!80102 SELECT*/ FROM IN EXISTS", id: []int{SELECT, FROM, IN, EXISTS, 0}, }, { - version: "80102", + version: "8.1.2", in: "/*!80102 SELECT*/ FROM IN EXISTS", id: []int{SELECT, FROM, IN, EXISTS, 0}, }} for _, tcase := range testcases { t.Run(tcase.version+"_"+tcase.in, func(t *testing.T) { - mySQLParserVersion = tcase.version - tok := NewStringTokenizer(tcase.in) + parser, err := New(Options{MySQLServerVersion: tcase.version}) + require.NoError(t, err) + tok := parser.NewStringTokenizer(tcase.in) for _, expectedID := range tcase.id { id, _ := tok.Scan() require.Equal(t, expectedID, id) @@ -306,9 +310,10 @@ func TestIntegerAndID(t *testing.T) { out: "3.2", }} + parser := NewTestParser() for _, tcase := range testcases { t.Run(tcase.in, func(t *testing.T) { - tkn := NewStringTokenizer(tcase.in) + tkn := parser.NewStringTokenizer(tcase.in) id, out := tkn.Scan() require.Equal(t, tcase.id, id) expectedOut := tcase.out diff --git a/go/vt/sqlparser/tracked_buffer.go b/go/vt/sqlparser/tracked_buffer.go index aab0c1a1331..aec206f3b3d 100644 --- a/go/vt/sqlparser/tracked_buffer.go +++ b/go/vt/sqlparser/tracked_buffer.go @@ -34,7 +34,7 @@ type NodeFormatter func(buf *TrackedBuffer, node SQLNode) // want to generate a query that's different from the default. type TrackedBuffer struct { *strings.Builder - bindLocations []bindLocation + bindLocations []BindLocation nodeFormatter NodeFormatter literal func(string) (int, error) fast bool @@ -288,9 +288,9 @@ func areBothISExpr(op Expr, val Expr) bool { // WriteArg writes a value argument into the buffer along with // tracking information for future substitutions. func (buf *TrackedBuffer) WriteArg(prefix, arg string) { - buf.bindLocations = append(buf.bindLocations, bindLocation{ - offset: buf.Len(), - length: len(prefix) + len(arg), + buf.bindLocations = append(buf.bindLocations, BindLocation{ + Offset: buf.Len(), + Length: len(prefix) + len(arg), }) buf.WriteString(prefix) buf.WriteString(arg) diff --git a/go/vt/sqlparser/tracked_buffer_test.go b/go/vt/sqlparser/tracked_buffer_test.go index 2375441b34e..4dff65634e8 100644 --- a/go/vt/sqlparser/tracked_buffer_test.go +++ b/go/vt/sqlparser/tracked_buffer_test.go @@ -278,16 +278,17 @@ func TestCanonicalOutput(t *testing.T) { }, } + parser := NewTestParser() for _, tc := range testcases { t.Run(tc.input, func(t *testing.T) { - tree, err := Parse(tc.input) + tree, err := parser.Parse(tc.input) require.NoError(t, err, tc.input) out := CanonicalString(tree) require.Equal(t, tc.canonical, out, "bad serialization") // Make sure we've generated a valid query! - rereadStmt, err := Parse(out) + rereadStmt, err := parser.Parse(out) require.NoError(t, err, out) out = CanonicalString(rereadStmt) require.Equal(t, tc.canonical, out, "bad serialization") diff --git a/go/vt/sqlparser/truncate_query.go b/go/vt/sqlparser/truncate_query.go index 4bb63730fd2..996ceeb20cf 100644 --- a/go/vt/sqlparser/truncate_query.go +++ b/go/vt/sqlparser/truncate_query.go @@ -16,73 +16,36 @@ limitations under the License. package sqlparser -import ( - "github.com/spf13/pflag" - - "vitess.io/vitess/go/vt/servenv" -) - -var ( - // truncateUILen truncate queries in debug UIs to the given length. 0 means unlimited. - truncateUILen = 512 - - // truncateErrLen truncate queries in error logs to the given length. 0 means unlimited. - truncateErrLen = 0 -) - const TruncationText = "[TRUNCATED]" -func registerQueryTruncationFlags(fs *pflag.FlagSet) { - fs.IntVar(&truncateUILen, "sql-max-length-ui", truncateUILen, "truncate queries in debug UIs to the given length (default 512)") - fs.IntVar(&truncateErrLen, "sql-max-length-errors", truncateErrLen, "truncate queries in error logs to the given length (default unlimited)") -} - -func init() { - for _, cmd := range []string{ - "vtgate", - "vttablet", - "vtcombo", - "vtctld", - "vtctl", - "vtexplain", - "vtbackup", - "vttestserver", - "vtbench", - } { - servenv.OnParseFor(cmd, registerQueryTruncationFlags) - } -} - // GetTruncateErrLen is a function used to read the value of truncateErrLen -func GetTruncateErrLen() int { - return truncateErrLen -} - -// SetTruncateErrLen is a function used to override the value of truncateErrLen -// It is only meant to be used from tests and not from production code. -func SetTruncateErrLen(errLen int) { - truncateErrLen = errLen +func (p *Parser) GetTruncateErrLen() int { + return p.truncateErrLen } -func truncateQuery(query string, max int) string { +func TruncateQuery(query string, max int) string { sql, comments := SplitMarginComments(query) - if max == 0 || len(sql) <= max { + if max == 0 || len(sql) <= max || len(sql) < len(TruncationText) { return comments.Leading + sql + comments.Trailing } + if max < len(TruncationText)+1 { + max = len(TruncationText) + 1 + } + return comments.Leading + sql[:max-(len(TruncationText)+1)] + " " + TruncationText + comments.Trailing } // TruncateForUI is used when displaying queries on various Vitess status pages // to keep the pages small enough to load and render properly -func TruncateForUI(query string) string { - return truncateQuery(query, truncateUILen) +func (p *Parser) TruncateForUI(query string) string { + return TruncateQuery(query, p.truncateUILen) } // TruncateForLog is used when displaying queries as part of error logs // to avoid overwhelming logging systems with potentially long queries and // bind value data. -func TruncateForLog(query string) string { - return truncateQuery(query, truncateErrLen) +func (p *Parser) TruncateForLog(query string) string { + return TruncateQuery(query, p.truncateErrLen) } diff --git a/go/vt/sqlparser/truncate_query_test.go b/go/vt/sqlparser/truncate_query_test.go index e5fc2fc0a9c..64d53ae7b10 100644 --- a/go/vt/sqlparser/truncate_query_test.go +++ b/go/vt/sqlparser/truncate_query_test.go @@ -13,6 +13,21 @@ func TestTruncateQuery(t *testing.T) { max int want string }{ + { + query: "select 111", + max: 2, + want: "select 111", + }, + { + query: "select 1111", + max: 2, + want: " [TRUNCATED]", + }, + { + query: "select 11111", + max: 2, + want: " [TRUNCATED]", + }, { query: "select * from test where name = 'abc'", max: 30, @@ -26,7 +41,7 @@ func TestTruncateQuery(t *testing.T) { } for _, tt := range tests { t.Run(fmt.Sprintf("%s-%d", tt.query, tt.max), func(t *testing.T) { - assert.Equalf(t, tt.want, truncateQuery(tt.query, tt.max), "truncateQuery(%v, %v)", tt.query, tt.max) + assert.Equalf(t, tt.want, TruncateQuery(tt.query, tt.max), "TruncateQuery(%v, %v)", tt.query, tt.max) }) } } diff --git a/go/vt/sqlparser/utils.go b/go/vt/sqlparser/utils.go index 0f3c66f2ea3..b785128917f 100644 --- a/go/vt/sqlparser/utils.go +++ b/go/vt/sqlparser/utils.go @@ -19,24 +19,25 @@ package sqlparser import ( "fmt" "sort" + "strings" querypb "vitess.io/vitess/go/vt/proto/query" ) // QueryMatchesTemplates sees if the given query has the same fingerprint as one of the given templates // (one is enough) -func QueryMatchesTemplates(query string, queryTemplates []string) (match bool, err error) { +func (p *Parser) QueryMatchesTemplates(query string, queryTemplates []string) (match bool, err error) { if len(queryTemplates) == 0 { return false, fmt.Errorf("No templates found") } bv := make(map[string]*querypb.BindVariable) normalize := func(q string) (string, error) { - q, err := NormalizeAlphabetically(q) + q, err := p.NormalizeAlphabetically(q) if err != nil { return "", err } - stmt, reservedVars, err := Parse2(q) + stmt, reservedVars, err := p.Parse2(q) if err != nil { return "", err } @@ -69,8 +70,8 @@ func QueryMatchesTemplates(query string, queryTemplates []string) (match bool, e // NormalizeAlphabetically rewrites given query such that: // - WHERE 'AND' expressions are reordered alphabetically -func NormalizeAlphabetically(query string) (normalized string, err error) { - stmt, err := Parse(query) +func (p *Parser) NormalizeAlphabetically(query string) (normalized string, err error) { + stmt, err := p.Parse(query) if err != nil { return normalized, err } @@ -118,12 +119,12 @@ func NormalizeAlphabetically(query string) (normalized string, err error) { // replaces any cases of the provided database name with the // specified replacement name. // Note: both database names provided should be unescaped strings. -func ReplaceTableQualifiers(query, olddb, newdb string) (string, error) { +func (p *Parser) ReplaceTableQualifiers(query, olddb, newdb string) (string, error) { if newdb == olddb { // Nothing to do here. return query, nil } - in, err := Parse(query) + in, err := p.Parse(query) if err != nil { return "", err } @@ -135,14 +136,14 @@ func ReplaceTableQualifiers(query, olddb, newdb string) (string, error) { upd := Rewrite(in, func(cursor *Cursor) bool { switch node := cursor.Node().(type) { case TableName: - if !node.Qualifier.IsEmpty() && + if node.Qualifier.NotEmpty() && node.Qualifier.String() == oldQualifier.String() { node.Qualifier = newQualifier cursor.Replace(node) modified = true } case *ShowBasic: // for things like 'show tables from _vt' - if !node.DbName.IsEmpty() && + if node.DbName.NotEmpty() && node.DbName.String() == oldQualifier.String() { node.DbName = newQualifier cursor.Replace(node) @@ -160,3 +161,22 @@ func ReplaceTableQualifiers(query, olddb, newdb string) (string, error) { } return query, nil } + +// ReplaceTableQualifiersMultiQuery accepts a multi-query string and modifies it +// via ReplaceTableQualifiers, one query at a time. +func (p *Parser) ReplaceTableQualifiersMultiQuery(multiQuery, olddb, newdb string) (string, error) { + queries, err := p.SplitStatementToPieces(multiQuery) + if err != nil { + return multiQuery, err + } + var modifiedQueries []string + for _, query := range queries { + // Replace any provided sidecar database qualifiers with the correct one. + query, err := p.ReplaceTableQualifiers(query, olddb, newdb) + if err != nil { + return query, err + } + modifiedQueries = append(modifiedQueries, query) + } + return strings.Join(modifiedQueries, ";"), nil +} diff --git a/go/vt/sqlparser/utils_test.go b/go/vt/sqlparser/utils_test.go index 63c9b10ba43..64339211917 100644 --- a/go/vt/sqlparser/utils_test.go +++ b/go/vt/sqlparser/utils_test.go @@ -47,8 +47,9 @@ func TestNormalizeAlphabetically(t *testing.T) { out: "select * from tbl where b = 4 or a = 3", }} + parser := NewTestParser() for _, tc := range testcases { - normalized, err := NormalizeAlphabetically(tc.in) + normalized, err := parser.NormalizeAlphabetically(tc.in) assert.NoError(t, err) assert.Equal(t, tc.out, normalized) } @@ -173,9 +174,10 @@ func TestQueryMatchesTemplates(t *testing.T) { out: true, }, } + parser := NewTestParser() for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - match, err := QueryMatchesTemplates(tc.q, tc.tmpl) + match, err := parser.QueryMatchesTemplates(tc.q, tc.tmpl) assert.NoError(t, err) assert.Equal(t, tc.out, match) }) @@ -263,9 +265,79 @@ func TestReplaceTableQualifiers(t *testing.T) { out: "set names 'binary'", }, } + parser := NewTestParser() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := ReplaceTableQualifiers(tt.in, origDB, tt.newdb) + got, err := parser.ReplaceTableQualifiers(tt.in, origDB, tt.newdb) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.out, got, "RemoveTableQualifiers(); in: %s, out: %s", tt.in, got) + }) + } +} + +func TestReplaceTableQualifiersMultiQuery(t *testing.T) { + origDB := "_vt" + tests := []struct { + name string + in string + newdb string + out string + wantErr bool + }{ + { + name: "invalid select", + in: "select frog bar person", + out: "", + wantErr: true, + }, + { + name: "simple select", + in: "select * from _vt.foo", + out: "select * from foo", + }, + { + name: "simple select with new db", + in: "select * from _vt.foo", + newdb: "_vt_test", + out: "select * from _vt_test.foo", + }, + { + name: "simple select with new db same", + in: "select * from _vt.foo where id=1", // should be unchanged + newdb: "_vt", + out: "select * from _vt.foo where id=1", + }, + { + name: "simple select with new db needing escaping", + in: "select * from _vt.foo", + newdb: "1_vt-test", + out: "select * from `1_vt-test`.foo", + }, + { + name: "multi query", + in: "select * from _vt.foo ; select * from _vt.bar", + out: "select * from foo;select * from bar", + }, + { + name: "multi query with new db", + in: "select * from _vt.foo ; select * from _vt.bar", + newdb: "_vt_test", + out: "select * from _vt_test.foo;select * from _vt_test.bar", + }, + { + name: "multi query with error", + in: "select * from _vt.foo ; select * from _vt.bar ; sel ect fr om wh at", + wantErr: true, + }, + } + parser := NewTestParser() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.ReplaceTableQualifiersMultiQuery(tt.in, origDB, tt.newdb) if tt.wantErr { require.Error(t, err) } else { diff --git a/go/vt/sqlparser/version_test.go b/go/vt/sqlparser/version_test.go index 718b5804aad..8b570404cee 100644 --- a/go/vt/sqlparser/version_test.go +++ b/go/vt/sqlparser/version_test.go @@ -49,7 +49,7 @@ func TestConvertMySQLVersion(t *testing.T) { for _, tcase := range testcases { t.Run(tcase.version, func(t *testing.T) { - output, err := convertMySQLVersionToCommentVersion(tcase.version) + output, err := ConvertMySQLVersionToCommentVersion(tcase.version) if tcase.error != "" { require.EqualError(t, err, tcase.error) } else { diff --git a/go/vt/srvtopo/resilient_server.go b/go/vt/srvtopo/resilient_server.go index d1521952ab0..0cbccbdb31c 100644 --- a/go/vt/srvtopo/resilient_server.go +++ b/go/vt/srvtopo/resilient_server.go @@ -84,11 +84,9 @@ func NewResilientServer(ctx context.Context, base *topo.Server, counterPrefix st log.Fatalf("srv_topo_cache_refresh must be less than or equal to srv_topo_cache_ttl") } - var metric string - if counterPrefix == "" { + metric := "" + if counterPrefix != "" { metric = counterPrefix + "Counts" - } else { - metric = "" } counts := stats.NewCountersWithSingleLabel(metric, "Resilient srvtopo server operations", "type") diff --git a/go/vt/srvtopo/resilient_server_test.go b/go/vt/srvtopo/resilient_server_test.go index c237d43f300..fe248f56087 100644 --- a/go/vt/srvtopo/resilient_server_test.go +++ b/go/vt/srvtopo/resilient_server_test.go @@ -811,7 +811,7 @@ func TestSrvKeyspaceListener(t *testing.T) { srvTopoCacheRefresh = 1 * time.Second }() - rs := NewResilientServer(ctx, ts, "TestGetSrvKeyspaceWatcher") + rs := NewResilientServer(ctx, ts, "TestGetSrvKeyspaceListener") cancelCtx, cancelFunc := context.WithCancel(context.Background()) var callbackCount atomic.Int32 diff --git a/go/vt/srvtopo/resolver.go b/go/vt/srvtopo/resolver.go index 98d77e259ef..042e291c0a6 100644 --- a/go/vt/srvtopo/resolver.go +++ b/go/vt/srvtopo/resolver.go @@ -83,24 +83,6 @@ type ResolvedShard struct { Gateway Gateway } -// ResolvedShardEqual is an equality check on *ResolvedShard. -func ResolvedShardEqual(rs1, rs2 *ResolvedShard) bool { - return proto.Equal(rs1.Target, rs2.Target) -} - -// ResolvedShardsEqual is an equality check on []*ResolvedShard. -func ResolvedShardsEqual(rss1, rss2 []*ResolvedShard) bool { - if len(rss1) != len(rss2) { - return false - } - for i, rs1 := range rss1 { - if !ResolvedShardEqual(rs1, rss2[i]) { - return false - } - } - return true -} - // WithKeyspace returns a ResolvedShard with a new keyspace keeping other parameters the same func (rs *ResolvedShard) WithKeyspace(newKeyspace string) *ResolvedShard { return &ResolvedShard{ @@ -114,8 +96,7 @@ func (rs *ResolvedShard) WithKeyspace(newKeyspace string) *ResolvedShard { } } -// GetKeyspaceShards return all the shards in a keyspace. It follows -// redirection if ServedFrom is set. It is only valid for the local cell. +// GetKeyspaceShards return all the shards in a keyspace. It is only valid for the local cell. // Do not use it to further resolve shards, instead use the Resolve* methods. func (r *Resolver) GetKeyspaceShards(ctx context.Context, keyspace string, tabletType topodatapb.TabletType) (string, *topodatapb.SrvKeyspace, []*topodatapb.ShardReference, error) { srvKeyspace, err := r.topoServ.GetSrvKeyspace(ctx, r.localCell, keyspace) @@ -123,17 +104,6 @@ func (r *Resolver) GetKeyspaceShards(ctx context.Context, keyspace string, table return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "keyspace %v fetch error: %v", keyspace, err) } - // check if the keyspace has been redirected for this tabletType. - for _, sf := range srvKeyspace.ServedFrom { - if sf.TabletType == tabletType { - keyspace = sf.Keyspace - srvKeyspace, err = r.topoServ.GetSrvKeyspace(ctx, r.localCell, keyspace) - if err != nil { - return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "keyspace %v fetch error: %v", keyspace, err) - } - } - } - partition := topoproto.SrvKeyspaceGetPartition(srvKeyspace, tabletType) if partition == nil { return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "No partition found for tabletType %v in keyspace %v", topoproto.TabletTypeLString(tabletType), keyspace) diff --git a/go/vt/srvtopo/status.go b/go/vt/srvtopo/status.go index b3069be6c38..9a5e8684b7f 100644 --- a/go/vt/srvtopo/status.go +++ b/go/vt/srvtopo/status.go @@ -133,12 +133,6 @@ var partitions = template.Must(template.New("partitions").Parse(` {{ end }}
{{ end }} -{{if .ServedFrom }} -ServedFrom:
-{{ range .ServedFrom }} - {{ .TabletType }}: {{ .Keyspace}}
-{{ end }} -{{ end }} `)) // StatusAsHTML returns an HTML version of our status. diff --git a/go/vt/srvtopo/watch.go b/go/vt/srvtopo/watch.go index 36d8fd428bd..4a0ccda2d59 100644 --- a/go/vt/srvtopo/watch.go +++ b/go/vt/srvtopo/watch.go @@ -23,6 +23,7 @@ import ( "time" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo" ) @@ -204,8 +205,11 @@ func (entry *watchEntry) onErrorLocked(ctx context.Context, err error, init bool entry.value = nil } } else { - entry.lastError = fmt.Errorf("ResilientWatch stream failed for %v: %w", entry.key, err) - log.Errorf("%v", entry.lastError) + if !topo.IsErrType(err, topo.Interrupted) { + // No need to log if we're explicitly interrupted. + entry.lastError = fmt.Errorf("ResilientWatch stream failed for %v: %w", entry.key, err) + log.Errorf("%v", entry.lastError) + } // Even though we didn't get a new value, update the lastValueTime // here since the watch was successfully running before and we want @@ -224,8 +228,7 @@ func (entry *watchEntry) onErrorLocked(ctx context.Context, err error, init bool if len(entry.listeners) > 0 && !topo.IsErrType(err, topo.Interrupted) { go func() { - time.Sleep(entry.rw.cacheRefreshInterval) - + _ = timer.SleepContext(ctx, entry.rw.cacheRefreshInterval) entry.mutex.Lock() entry.ensureWatchingLocked(ctx) entry.mutex.Unlock() diff --git a/go/vt/srvtopo/watch_srvkeyspace.go b/go/vt/srvtopo/watch_srvkeyspace.go index cefe95c6951..ac2d8c0bac1 100644 --- a/go/vt/srvtopo/watch_srvkeyspace.go +++ b/go/vt/srvtopo/watch_srvkeyspace.go @@ -40,7 +40,7 @@ func (k *srvKeyspaceKey) String() string { func NewSrvKeyspaceWatcher(ctx context.Context, topoServer *topo.Server, counts *stats.CountersWithSingleLabel, cacheRefresh, cacheTTL time.Duration) *SrvKeyspaceWatcher { watch := func(entry *watchEntry) { key := entry.key.(*srvKeyspaceKey) - requestCtx, requestCancel := context.WithCancel(context.Background()) + requestCtx, requestCancel := context.WithCancel(ctx) defer requestCancel() current, changes, err := topoServer.WatchSrvKeyspace(requestCtx, key.cell, key.keyspace) diff --git a/go/vt/sysvars/sysvars.go b/go/vt/sysvars/sysvars.go index 98da8ff07b7..c8037563ca1 100644 --- a/go/vt/sysvars/sysvars.go +++ b/go/vt/sysvars/sysvars.go @@ -57,6 +57,8 @@ var ( off = "0" utf8mb4 = "'utf8mb4'" + ForeignKeyChecks = "foreign_key_checks" + Autocommit = SystemVariable{Name: "autocommit", IsBoolean: true, Default: on} Charset = SystemVariable{Name: "charset", Default: utf8mb4, IdentifierAsString: true} ClientFoundRows = SystemVariable{Name: "client_found_rows", IsBoolean: true, Default: off} @@ -186,7 +188,7 @@ var ( {Name: "end_markers_in_json", IsBoolean: true, SupportSetVar: true}, {Name: "eq_range_index_dive_limit", SupportSetVar: true}, {Name: "explicit_defaults_for_timestamp"}, - {Name: "foreign_key_checks", IsBoolean: true, SupportSetVar: true}, + {Name: ForeignKeyChecks, IsBoolean: true, SupportSetVar: true}, {Name: "group_concat_max_len", SupportSetVar: true}, {Name: "information_schema_stats_expiry"}, {Name: "max_heap_table_size", SupportSetVar: true}, diff --git a/go/vt/throttler/demo/throttler_demo.go b/go/vt/throttler/demo/throttler_demo.go index 126b9098236..15228475bfb 100644 --- a/go/vt/throttler/demo/throttler_demo.go +++ b/go/vt/throttler/demo/throttler_demo.go @@ -26,6 +26,8 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" @@ -114,9 +116,9 @@ type replica struct { wg sync.WaitGroup } -func newReplica(lagUpdateInterval, degrationInterval, degrationDuration time.Duration, ts *topo.Server) *replica { +func newReplica(env *vtenv.Environment, lagUpdateInterval, degrationInterval, degrationDuration time.Duration, ts *topo.Server) *replica { t := &testing.T{} - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(env, logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) fakeTablet := testlib.NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_REPLICA, nil, testlib.TabletKeyspaceShard(t, "ks", "-80")) fakeTablet.StartActionLoop(t, wr) @@ -308,7 +310,15 @@ func main() { log.Infof("start rate set to: %v", rate) ts := memorytopo.NewServer(context.Background(), "cell1") - replica := newReplica(lagUpdateInterval, replicaDegrationInterval, replicaDegrationDuration, ts) + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: servenv.MySQLServerVersion(), + TruncateUILen: servenv.TruncateUILen, + TruncateErrLen: servenv.TruncateErrLen, + }) + if err != nil { + log.Fatal(err) + } + replica := newReplica(env, lagUpdateInterval, replicaDegrationInterval, replicaDegrationDuration, ts) primary := &primary{replica: replica} client := newClient(context.Background(), primary, replica, ts) client.run() diff --git a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go b/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go deleted file mode 100644 index 1518d7ea8d8..00000000000 --- a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package grpcthrottlerclient contains the gRPC version of the throttler client protocol. -package grpcthrottlerclient - -import ( - "flag" - - "context" - - "google.golang.org/grpc" - - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/throttler/throttlerclient" - "vitess.io/vitess/go/vt/vterrors" - - throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata" - throttlerservicepb "vitess.io/vitess/go/vt/proto/throttlerservice" -) - -var ( - cert = flag.String("throttler_client_grpc_cert", "", "the cert to use to connect") - key = flag.String("throttler_client_grpc_key", "", "the key to use to connect") - ca = flag.String("throttler_client_grpc_ca", "", "the server ca to use to validate servers when connecting") - crl = flag.String("throttler_client_grpc_crl", "", "the server crl to use to validate server certificates when connecting") - name = flag.String("throttler_client_grpc_server_name", "", "the server name to use to validate server certificate") -) - -type client struct { - conn *grpc.ClientConn - gRPCClient throttlerservicepb.ThrottlerClient -} - -func factory(addr string) (throttlerclient.Client, error) { - opt, err := grpcclient.SecureDialOption(*cert, *key, *ca, *crl, *name) - if err != nil { - return nil, err - } - conn, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) - if err != nil { - return nil, err - } - gRPCClient := throttlerservicepb.NewThrottlerClient(conn) - - return &client{conn, gRPCClient}, nil -} - -// MaxRates is part of the throttlerclient.Client interface and returns the -// current max rate for each throttler of the process. -func (c *client) MaxRates(ctx context.Context) (map[string]int64, error) { - response, err := c.gRPCClient.MaxRates(ctx, &throttlerdatapb.MaxRatesRequest{}) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Rates, nil -} - -// SetMaxRate is part of the throttlerclient.Client interface and sets the rate -// on all throttlers of the server. -func (c *client) SetMaxRate(ctx context.Context, rate int64) ([]string, error) { - request := &throttlerdatapb.SetMaxRateRequest{ - Rate: rate, - } - - response, err := c.gRPCClient.SetMaxRate(ctx, request) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Names, nil -} - -// GetConfiguration is part of the throttlerclient.Client interface. -func (c *client) GetConfiguration(ctx context.Context, throttlerName string) (map[string]*throttlerdatapb.Configuration, error) { - response, err := c.gRPCClient.GetConfiguration(ctx, &throttlerdatapb.GetConfigurationRequest{ - ThrottlerName: throttlerName, - }) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Configurations, nil -} - -// UpdateConfiguration is part of the throttlerclient.Client interface. -func (c *client) UpdateConfiguration(ctx context.Context, throttlerName string, configuration *throttlerdatapb.Configuration, copyZeroValues bool) ([]string, error) { - response, err := c.gRPCClient.UpdateConfiguration(ctx, &throttlerdatapb.UpdateConfigurationRequest{ - ThrottlerName: throttlerName, - Configuration: configuration, - CopyZeroValues: copyZeroValues, - }) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Names, nil -} - -// ResetConfiguration is part of the throttlerclient.Client interface. -func (c *client) ResetConfiguration(ctx context.Context, throttlerName string) ([]string, error) { - response, err := c.gRPCClient.ResetConfiguration(ctx, &throttlerdatapb.ResetConfigurationRequest{ - ThrottlerName: throttlerName, - }) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Names, nil -} - -// Close is part of the throttlerclient.Client interface. -func (c *client) Close() { - c.conn.Close() -} - -func init() { - throttlerclient.RegisterFactory("grpc", factory) -} diff --git a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient_test.go b/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient_test.go deleted file mode 100644 index d3ae3c40a33..00000000000 --- a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient_test.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package grpcthrottlerclient - -import ( - "fmt" - "net" - "testing" - - "google.golang.org/grpc" - - "vitess.io/vitess/go/vt/throttler" - "vitess.io/vitess/go/vt/throttler/grpcthrottlerserver" - "vitess.io/vitess/go/vt/throttler/throttlerclienttest" -) - -// TestThrottlerServer tests the gRPC implementation using a throttler client -// and server. -func TestThrottlerServer(t *testing.T) { - // Use the global manager which is a singleton. - port := startGRPCServer(t, throttler.GlobalManager) - - // Create a ThrottlerClient gRPC client to talk to the throttler. - client, err := factory(fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatalf("Cannot create client: %v", err) - } - defer client.Close() - - throttlerclienttest.TestSuite(t, client) -} - -// TestThrottlerServerPanics tests the panic handling of the gRPC throttler -// server implementation. -func TestThrottlerServerPanics(t *testing.T) { - // For testing the panic handling, use a fake Manager instead. - port := startGRPCServer(t, &throttlerclienttest.FakeManager{}) - - // Create a ThrottlerClient gRPC client to talk to the throttler. - client, err := factory(fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatalf("Cannot create client: %v", err) - } - defer client.Close() - - throttlerclienttest.TestSuitePanics(t, client) -} - -func startGRPCServer(t *testing.T, m throttler.Manager) int { - // Listen on a random port. - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Cannot listen: %v", err) - } - - s := grpc.NewServer() - grpcthrottlerserver.RegisterServer(s, m) - // Call Serve() after our service has been registered. Otherwise, the test - // will fail with the error "grpc: Server.RegisterService after Server.Serve". - go s.Serve(listener) - return listener.Addr().(*net.TCPAddr).Port -} diff --git a/go/vt/throttler/max_replication_lag_module.go b/go/vt/throttler/max_replication_lag_module.go index f08c9211205..ac184fe7be8 100644 --- a/go/vt/throttler/max_replication_lag_module.go +++ b/go/vt/throttler/max_replication_lag_module.go @@ -382,7 +382,6 @@ logResult: r.Reason += clearReason } - log.Infof("%v", r) m.results.add(r) } diff --git a/go/vt/throttler/max_replication_lag_module_test.go b/go/vt/throttler/max_replication_lag_module_test.go index 6379b067412..77be6501e4c 100644 --- a/go/vt/throttler/max_replication_lag_module_test.go +++ b/go/vt/throttler/max_replication_lag_module_test.go @@ -23,10 +23,10 @@ import ( "time" "github.com/stretchr/testify/assert" - - "vitess.io/vitess/go/vt/log" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/vt/discovery" + "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -51,32 +51,33 @@ const ( ) type testFixture struct { + tb testing.TB m *MaxReplicationLagModule ratesHistory *fakeRatesHistory } -func newTestFixtureWithMaxReplicationLag(maxReplicationLag int64) (*testFixture, error) { +func newTestFixtureWithMaxReplicationLag(tb testing.TB, maxReplicationLag int64) *testFixture { config := NewMaxReplicationLagModuleConfig(maxReplicationLag) - return newTestFixture(config) + return newTestFixture(tb, config) } -func newTestFixture(config MaxReplicationLagModuleConfig) (*testFixture, error) { +func newTestFixture(tb testing.TB, config MaxReplicationLagModuleConfig) *testFixture { ratesHistory := newFakeRatesHistory() fc := &fakeClock{} // Do not start at 0*time.Second because than the code cannot distinguish // between a legimate value and a zero time.Time value. fc.setNow(1 * time.Second) m, err := NewMaxReplicationLagModule(config, ratesHistory.aggregatedIntervalHistory, fc.now) - if err != nil { - return nil, err - } + require.NoError(tb, err) + // Updates for the throttler go into a big channel and will be ignored. m.rateUpdateChan = make(chan<- struct{}, 1000) return &testFixture{ + tb: tb, m: m, ratesHistory: ratesHistory, - }, nil + } } // process does the same thing as MaxReplicationLagModule.ProcessRecords() does @@ -91,17 +92,10 @@ func (tf *testFixture) recalculateRate(lagRecord replicationLagRecord) { tf.m.recalculateRate(lagRecord) } -func (tf *testFixture) checkState(state state, rate int64, lastRateChange time.Time) error { - if got, want := tf.m.currentState, state; got != want { - return fmt.Errorf("module in wrong state. got = %v, want = %v", got, want) - } - if got, want := tf.m.MaxRate(), rate; got != want { - return fmt.Errorf("module has wrong MaxRate(). got = %v, want = %v", got, want) - } - if got, want := tf.m.lastRateChange, lastRateChange; got != want { - return fmt.Errorf("module has wrong lastRateChange time. got = %v, want = %v", got, want) - } - return nil +func (tf *testFixture) checkState(state state, rate int64, lastRateChange time.Time) { + require.Equal(tf.tb, state, tf.m.currentState, "module in wrong state") + require.Equal(tf.tb, rate, tf.m.MaxRate(), "module has wrong MaxRate()") + require.Equal(tf.tb, lastRateChange, tf.m.lastRateChange, "module has wrong lastRateChange time") } func TestNewMaxReplicationLagModule_recalculateRate(t *testing.T) { @@ -128,14 +122,11 @@ func TestNewMaxReplicationLagModule_recalculateRate(t *testing.T) { }, } - for _, aTestCase := range testCases { - theCase := aTestCase - + for _, theCase := range testCases { t.Run(theCase.name, func(t *testing.T) { t.Parallel() - fixture, err := newTestFixtureWithMaxReplicationLag(5) - assert.NoError(t, err) + fixture := newTestFixtureWithMaxReplicationLag(t, 5) if theCase.expectPanic { assert.Panics(t, func() { fixture.recalculateRate(theCase.lagRecord) }) @@ -146,15 +137,9 @@ func TestNewMaxReplicationLagModule_recalculateRate(t *testing.T) { } func TestMaxReplicationLagModule_RateNotZeroWhenDisabled(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(ReplicationLagModuleDisabled) - if err != nil { - t.Fatal(err) - } - + tf := newTestFixtureWithMaxReplicationLag(t, ReplicationLagModuleDisabled) // Initial rate must not be zero. It's ReplicationLagModuleDisabled instead. - if err := tf.checkState(stateIncreaseRate, ReplicationLagModuleDisabled, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, ReplicationLagModuleDisabled, sinceZero(1*time.Second)) } func TestMaxReplicationLagModule_InitialStateAndWait(t *testing.T) { @@ -162,34 +147,24 @@ func TestMaxReplicationLagModule_InitialStateAndWait(t *testing.T) { // Overwrite the default config to make sure we test a non-default value. config.InitialRate = 123 config.MaxDurationBetweenIncreasesSec = 23 - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // Initial rate must be config.InitialRate. - if err := tf.checkState(stateIncreaseRate, config.InitialRate, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, config.InitialRate, sinceZero(1*time.Second)) + // After startup, the next increment won't happen until // config.MaxDurationBetweenIncreasesSec elapsed. - if got, want := tf.m.nextAllowedChangeAfterInit, sinceZero(config.MaxDurationBetweenIncreases()+1*time.Second); got != want { - t.Fatalf("got = %v, want = %v", got, want) - } + require.Equal(t, sinceZero(config.MaxDurationBetweenIncreases()+1*time.Second), tf.m.nextAllowedChangeAfterInit) } // TestMaxReplicationLagModule_Increase tests only the continuous increase of the // rate and assumes that we are well below the replica capacity. func TestMaxReplicationLagModule_Increase(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // We start at config.InitialRate. - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) + // After the initial wait period of 62s // (config.MaxDurationBetweenIncreasesSec), regular increments start. @@ -200,31 +175,25 @@ func TestMaxReplicationLagModule_Increase(t *testing.T) { tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. // r2 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) + // We have to wait at least config.MinDurationBetweenIncreasesSec (40s) before // the next increase. - if got, want := tf.m.replicaUnderTest.nextAllowedChange, sinceZero(70*time.Second).Add(tf.m.config.MinDurationBetweenIncreases()); got != want { - t.Fatalf("got = %v, want = %v", got, want) - } + require.Equal(t, sinceZero(70*time.Second).Add(tf.m.config.MinDurationBetweenIncreases()), tf.m.replicaUnderTest.nextAllowedChange) + // r2 @ 75s, 0s lag tf.ratesHistory.add(sinceZero(70*time.Second), 100) tf.ratesHistory.add(sinceZero(74*time.Second), 200) tf.process(lagRecord(sinceZero(75*time.Second), r2, 0)) // Lag record was ignored because it's within the wait period. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 80s, 0s lag tf.ratesHistory.add(sinceZero(79*time.Second), 200) tf.process(lagRecord(sinceZero(80*time.Second), r1, 0)) // The r1 lag update was ignored because an increment "under test" is always // locked in with the replica which triggered the increase (r2 this time). - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // No increase is possible for the next 20 seconds. @@ -232,25 +201,19 @@ func TestMaxReplicationLagModule_Increase(t *testing.T) { tf.ratesHistory.add(sinceZero(80*time.Second), 200) tf.ratesHistory.add(sinceZero(89*time.Second), 200) tf.process(lagRecord(sinceZero(90*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 100s, 0s lag tf.ratesHistory.add(sinceZero(99*time.Second), 200) tf.process(lagRecord(sinceZero(100*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // Next rate increase is possible after testing the rate for 40s. // r2 @ 110s, 0s lag tf.ratesHistory.add(sinceZero(109*time.Second), 200) tf.process(lagRecord(sinceZero(110*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)) } // TestMaxReplicationLagModule_ReplicaUnderTest_LastErrorOrNotUp is @@ -258,19 +221,14 @@ func TestMaxReplicationLagModule_Increase(t *testing.T) { // test that the system makes progress if the currently tracked replica has // LastError set or is no longer tracked. func TestMaxReplicationLagModule_ReplicaUnderTest_LastErrorOrNotUp(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. // r2 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 @ 75s, 0s lag, LastError set rError := lagRecord(sinceZero(75*time.Second), r2, 0) @@ -284,9 +242,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_LastErrorOrNotUp(t *testing.T) // We ignore r2 as "replica under test" because it has LastError set. // Instead, we act on r1. // r1 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)) // We'll simulate a shutdown of r1 i.e. we're no longer tracking it. // r1 @ 115s, 0s lag, !Up @@ -302,36 +258,27 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_LastErrorOrNotUp(t *testing.T) // We ignore r1 as "replica under test" because it has !Up set. // Instead, we act on r2. // r2 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 800, sinceZero(150*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 800, sinceZero(150*time.Second)) } // TestMaxReplicationLagModule_ReplicaUnderTest_Timeout tests the safe guard // that a "replica under test" which didn't report its lag for a while will be // cleared such that any other replica can become the new "replica under test". func TestMaxReplicationLagModule_ReplicaUnderTest_Timeout(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. // r2 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 80s, 0s lag (ignored because r2 is the "replica under test") tf.ratesHistory.add(sinceZero(70*time.Second), 100) tf.ratesHistory.add(sinceZero(79*time.Second), 200) tf.process(lagRecord(sinceZero(80*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 as "replica under test" did not report its lag for too long. // We'll ignore it from now and let other replicas trigger rate changes. @@ -340,28 +287,21 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_Timeout(t *testing.T) { // (last rate change + test duration + max duration between increases). tf.ratesHistory.add(sinceZero(172*time.Second), 200) tf.process(lagRecord(sinceZero(173*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 400, sinceZero(173*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 400, sinceZero(173*time.Second)) } // TestMaxReplicationLagModule_ReplicaUnderTest_IncreaseToDecrease verifies that // the current "replica under test" is ignored when our state changes from // "stateIncreaseRate" to "stateDecreaseAndGuessRate". func TestMaxReplicationLagModule_ReplicaUnderTest_IncreaseToDecrease(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // r2 @ 70s, 0s lag (triggers the increase state) tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. // r2 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 80s, 0s lag // This lag record is required in the next step to correctly calculate how @@ -370,16 +310,12 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_IncreaseToDecrease(t *testing. tf.ratesHistory.add(sinceZero(79*time.Second), 200) tf.process(lagRecord(sinceZero(80*time.Second), r1, 0)) // r1 remains the "replica under test". - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 @ 90s, 0s lag (ignored because the test duration is not up yet) tf.ratesHistory.add(sinceZero(89*time.Second), 200) tf.process(lagRecord(sinceZero(90*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 100s, 3s lag (above target, provokes a decrease) tf.ratesHistory.add(sinceZero(99*time.Second), 200) @@ -387,18 +323,14 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_IncreaseToDecrease(t *testing. // r1 becomes the "replica under test". // r1's high lag triggered the decrease state and therefore we did not wait // for the pending increase of "replica under test" r2. - if err := tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(100*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(100*time.Second)) // r2 lag records are ignored while r1 is the "replica under test". // r2 @ 110s, 0s lag tf.ratesHistory.add(sinceZero(100*time.Second), 200) tf.ratesHistory.add(sinceZero(109*time.Second), 140) tf.process(lagRecord(sinceZero(110*time.Second), r2, 0)) - if err := tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(100*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(100*time.Second)) // r1 leaves the "replica under test" as soon as the test duration is up // or its lag improved to a better state. @@ -408,19 +340,14 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_IncreaseToDecrease(t *testing. tf.ratesHistory.add(sinceZero(118*time.Second), 140) tf.process(lagRecord(sinceZero(119*time.Second), r1, 0)) // Rate increases to 170, the middle of: [good, bad] = [140, 200]. - if err := tf.checkState(stateIncreaseRate, 170, sinceZero(119*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 170, sinceZero(119*time.Second)) } // TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency verifies // that the current "replica under test" is ignored when our state changes from // "stateDecreaseAndGuessRate" to "stateEmergency". func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // INCREASE @@ -429,9 +356,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing // much r1 lags behind due to the rate increase. tf.ratesHistory.add(sinceZero(19*time.Second), 100) tf.process(lagRecord(sinceZero(20*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) // DECREASE @@ -439,9 +364,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing tf.ratesHistory.add(sinceZero(39*time.Second), 100) tf.process(lagRecord(sinceZero(40*time.Second), r1, 3)) // r1 becomes the "replica under test". - if err := tf.checkState(stateDecreaseAndGuessRate, 70, sinceZero(40*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 70, sinceZero(40*time.Second)) // EMERGENCY @@ -451,9 +374,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing tf.ratesHistory.add(sinceZero(49*time.Second), 70) tf.process(lagRecord(sinceZero(50*time.Second), r2, 10)) // r1 overrides r2 as new "replica under test". - if err := tf.checkState(stateEmergency, 35, sinceZero(50*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 35, sinceZero(50*time.Second)) // r1 lag becomes worse than the r1 lag now. We don't care and keep r1 as // "replica under test" for now. @@ -461,9 +382,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing tf.ratesHistory.add(sinceZero(50*time.Second), 70) tf.ratesHistory.add(sinceZero(59*time.Second), 35) tf.process(lagRecord(sinceZero(60*time.Second), r1, 15)) - if err := tf.checkState(stateEmergency, 35, sinceZero(50*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 35, sinceZero(50*time.Second)) // INCREASE @@ -472,9 +391,7 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing tf.ratesHistory.add(sinceZero(69*time.Second), 35) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // r2 becomes the new "replica under test". - if err := tf.checkState(stateIncreaseRate, 70, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 70, sinceZero(70*time.Second)) // EMERGENCY @@ -484,23 +401,16 @@ func TestMaxReplicationLagModule_ReplicaUnderTest_DecreaseToEmergency(t *testing tf.ratesHistory.add(sinceZero(79*time.Second), 70) tf.process(lagRecord(sinceZero(80*time.Second), r1, 15)) // r1 becomes the new "replica under test". - if err := tf.checkState(stateEmergency, 35, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 35, sinceZero(80*time.Second)) } // TestMaxReplicationLagModule_Increase_BadRateUpperBound verifies that a // known bad rate is always the upper bound for any rate increase. func TestMaxReplicationLagModule_Increase_BadRateUpperBound(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // We start at config.InitialRate. - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) // Assume that a bad value of 150 was set @ 30s and log error if err := tf.m.memory.markBad(150, sinceZero(30*time.Second)); err != nil { @@ -514,24 +424,17 @@ func TestMaxReplicationLagModule_Increase_BadRateUpperBound(t *testing.T) { // [0s, 69s]. // However, this would go over the bad rate. Therefore, the new rate will be // the middle of [100, 150] ([actual rate, bad rate]). - if err := tf.checkState(stateIncreaseRate, 125, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 125, sinceZero(70*time.Second)) } // TestMaxReplicationLagModule_Increase_MinimumProgress verifies that the // calculated new rate is never identical to the current rate and at least by // "memoryGranularity" higher. func TestMaxReplicationLagModule_Increase_MinimumProgress(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // We start at config.InitialRate. - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) // Assume that a bad value of 105 was set @ 30s. tf.m.memory.markBad(105, sinceZero(30*time.Second)) @@ -547,26 +450,19 @@ func TestMaxReplicationLagModule_Increase_MinimumProgress(t *testing.T) { // But then the new rate is identical to the old set rate of 100. // In such a case, we always advance the new rate by "memoryGranularity" // (which is currently 5). - if err := tf.checkState(stateIncreaseRate, 105, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 105, sinceZero(70*time.Second)) } // TestMaxReplicationLagModule_Decrease verifies that we correctly calculate the // replication rate in the decreaseAndGuessRate state. func TestMaxReplicationLagModule_Decrease(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(5) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 5) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 @ 90s, 3s lag (above target, provokes a decrease) tf.ratesHistory.add(sinceZero(70*time.Second), 100) @@ -581,9 +477,7 @@ func TestMaxReplicationLagModule_Decrease(t *testing.T) { // Since this backlog is spread across SpreadBacklogAcrossSec (20s), // the guessed rate gets further reduced by 30 QPS (600 queries / 20s). // Hence, the rate is set to 140 QPS (170 - 30). - if err := tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(90*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 140, sinceZero(90*time.Second)) } // TestMaxReplicationLagModule_Decrease_NoReplicaHistory skips decreasing the @@ -591,44 +485,33 @@ func TestMaxReplicationLagModule_Decrease(t *testing.T) { // replication lag value since the last rate change for r2. Therefore, we cannot // reliably guess its rate and wait for the next available record. func TestMaxReplicationLagModule_Decrease_NoReplicaHistory(t *testing.T) { - tf, err := newTestFixtureWithMaxReplicationLag(10) - if err != nil { - t.Fatal(err) - } + tf := newTestFixtureWithMaxReplicationLag(t, 10) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 80s, 3s lag (above target, but no decrease triggered) tf.ratesHistory.add(sinceZero(70*time.Second), 100) tf.ratesHistory.add(sinceZero(79*time.Second), 200) tf.process(lagRecord(sinceZero(80*time.Second), r1, 3)) // Rate was decreased by 25% (half the emergency decrease) as safety measure. - if err := tf.checkState(stateDecreaseAndGuessRate, 150, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 150, sinceZero(80*time.Second)) // r2 @ 90s, 0s lag tf.ratesHistory.add(sinceZero(80*time.Second), 200) tf.ratesHistory.add(sinceZero(89*time.Second), 150) tf.process(lagRecord(sinceZero(90*time.Second), r2, 0)) // r2 is ignored because r1 is the "replica under test". - if err := tf.checkState(stateDecreaseAndGuessRate, 150, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateDecreaseAndGuessRate, 150, sinceZero(80*time.Second)) // r1 recovers after the rate decrease and triggers a new increase. // r1 @ 100s, 0s lag tf.ratesHistory.add(sinceZero(99*time.Second), 150) tf.process(lagRecord(sinceZero(100*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 300, sinceZero(100*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 300, sinceZero(100*time.Second)) } func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_REPLICA(t *testing.T) { @@ -648,33 +531,22 @@ func testIgnoreNSlowestReplicas(t *testing.T, r1UID, r2UID uint32) { config.IgnoreNSlowestRdonlys = 1 typ = "RDONLY" } - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // r1 @ 80s, 0s lag tf.ratesHistory.add(sinceZero(79*time.Second), 100) tf.process(lagRecord(sinceZero(80*time.Second), r1UID, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) // r2 @ 90s, 10s lag tf.ratesHistory.add(sinceZero(80*time.Second), 100) tf.ratesHistory.add(sinceZero(90*time.Second), 200) tf.process(lagRecord(sinceZero(90*time.Second), r2UID, 10)) // Although r2's lag is high, it's ignored because it's the 1 slowest replica. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) results := tf.m.results.latestValues() - if got, want := len(results), 2; got != want { - t.Fatalf("skipped replica should have been recorded on the results page. got = %v, want = %v", got, want) - } - if got, want := results[0].Reason, fmt.Sprintf("skipping this replica because it's among the 1 slowest %v tablets", typ); got != want { - t.Fatalf("skipped replica should have been recorded as skipped on the results page. got = %v, want = %v", got, want) - } + require.Len(t, results, 2, "skipped replica should have been recorded on the results page") + require.Equal(t, fmt.Sprintf("skipping this replica because it's among the 1 slowest %v tablets", typ), results[0].Reason, "skipped replica should have been recorded as skipped on the results page.") // r1 @ 100s, 20s lag tf.ratesHistory.add(sinceZero(99*time.Second), 200) @@ -682,27 +554,20 @@ func testIgnoreNSlowestReplicas(t *testing.T, r1UID, r2UID uint32) { // r1 would become the new 1 slowest replica. However, we do not ignore it // because then we would ignore all known replicas in a row. // => react to the high lag and reduce the rate by 50% from 200 to 100. - if err := tf.checkState(stateEmergency, 100, sinceZero(100*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 100, sinceZero(100*time.Second)) } func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_NotEnoughReplicas(t *testing.T) { config := NewMaxReplicationLagModuleConfig(5) config.IgnoreNSlowestReplicas = 1 - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // r2 @ 70s, 10s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 10)) // r2 is the 1 slowest replica. However, it's not ignored because then we // would ignore all replicas. Therefore, we react to its lag increase. - if err := tf.checkState(stateEmergency, 50, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 50, sinceZero(70*time.Second)) } // TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IsIgnoredDuringIncrease @@ -715,45 +580,34 @@ func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_NotEnoughReplicas(t *tes func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IsIgnoredDuringIncrease(t *testing.T) { config := NewMaxReplicationLagModuleConfig(5) config.IgnoreNSlowestReplicas = 1 - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 200 based on actual rate of 100 within [0s, 69s]. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 80s, 0s lag tf.ratesHistory.add(sinceZero(70*time.Second), 100) tf.ratesHistory.add(sinceZero(79*time.Second), 200) tf.process(lagRecord(sinceZero(80*time.Second), r1, 0)) // Lag record was ignored because it's within the wait period. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 becomes slow and will be ignored now. // r2 @ 90s, 10s lag tf.ratesHistory.add(sinceZero(89*time.Second), 200) tf.m.replicaLagCache.add(lagRecord(sinceZero(90*time.Second), r2, 10)) // We ignore the 1 slowest replica and do not decrease despite r2's high lag. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r1 @ 110s, 0s lag tf.ratesHistory.add(sinceZero(109*time.Second), 200) tf.process(lagRecord(sinceZero(110*time.Second), r1, 0)) // Meanwhile, r1 is doing fine and will trigger the next increase because // we're no longer waiting for the ignored r2. - if err := tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)) } // TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IncludeRdonly is the same @@ -764,55 +618,36 @@ func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IncludeRdonly(t *testing // We ignore up to 1 REPLICA and 1 RDONLY tablet. config.IgnoreNSlowestReplicas = 1 config.IgnoreNSlowestRdonlys = 1 - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // r1 @ 80s, 0s lag tf.ratesHistory.add(sinceZero(79*time.Second), 100) tf.process(lagRecord(sinceZero(80*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) // rdonly1 @ 85s, 0s lag tf.ratesHistory.add(sinceZero(80*time.Second), 100) tf.ratesHistory.add(sinceZero(84*time.Second), 200) tf.process(lagRecord(sinceZero(85*time.Second), rdonly1, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) // r2 @ 90s, 10s lag tf.ratesHistory.add(sinceZero(89*time.Second), 200) tf.process(lagRecord(sinceZero(90*time.Second), r2, 10)) // Although r2's lag is high, it's ignored because it's the 1 slowest REPLICA tablet. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) results := tf.m.results.latestValues() - if got, want := len(results), 3; got != want { - t.Fatalf("skipped replica should have been recorded on the results page. got = %v, want = %v", got, want) - } - if got, want := results[0].Reason, "skipping this replica because it's among the 1 slowest REPLICA tablets"; got != want { - t.Fatalf("skipped replica should have been recorded as skipped on the results page. got = %v, want = %v", got, want) - } + require.Len(t, results, 3, "skipped replica should have been recorded on the results page") + require.Equal(t, "skipping this replica because it's among the 1 slowest REPLICA tablets", results[0].Reason, "skipped replica should have been recorded as skipped on the results page") // rdonly2 @ 95s, 10s lag tf.ratesHistory.add(sinceZero(94*time.Second), 200) tf.process(lagRecord(sinceZero(95*time.Second), rdonly2, 10)) // Although rdonly2's lag is high, it's ignored because it's the 1 slowest RDONLY tablet. - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(80*time.Second)) results = tf.m.results.latestValues() - if got, want := len(results), 4; got != want { - t.Fatalf("skipped replica should have been recorded on the results page. got = %v, want = %v", got, want) - } - if got, want := results[0].Reason, "skipping this replica because it's among the 1 slowest RDONLY tablets"; got != want { - t.Fatalf("skipped replica should have been recorded as skipped on the results page. got = %v, want = %v", got, want) - } + require.Len(t, results, 4, "skipped replica should have been recorded on the results page") + require.Equal(t, "skipping this replica because it's among the 1 slowest RDONLY tablets", results[0].Reason, "skipped replica should have been recorded as skipped on the results page") // r1 @ 100s, 11s lag tf.ratesHistory.add(sinceZero(99*time.Second), 200) @@ -820,9 +655,7 @@ func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IncludeRdonly(t *testing // r1 would become the new 1 slowest REPLICA tablet. However, we do not ignore // it because then we would ignore all known replicas in a row. // => react to the high lag and reduce the rate by 50% from 200 to 100. - if err := tf.checkState(stateEmergency, 100, sinceZero(100*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 100, sinceZero(100*time.Second)) // r2 and rdonly are omitted here for brevity. @@ -831,9 +664,7 @@ func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IncludeRdonly(t *testing // r1 @ 120s, 0s lag tf.ratesHistory.add(sinceZero(119*time.Second), 100) tf.process(lagRecord(sinceZero(120*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(120*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(120*time.Second)) // rdonly1 @ 125s, 11s lag tf.ratesHistory.add(sinceZero(120*time.Second), 100) @@ -842,9 +673,7 @@ func TestMaxReplicationLagModule_IgnoreNSlowestReplicas_IncludeRdonly(t *testing // rdonly1 would become the new 1 slowest RDONLY tablet. However, we do not // ignore it because then we would ignore all known replicas in a row. // => react to the high lag and reduce the rate by 50% from 200 to 100. - if err := tf.checkState(stateEmergency, 100, sinceZero(125*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 100, sinceZero(125*time.Second)) } // TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues verifies that a @@ -863,30 +692,21 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { // Use a very aggressive aging rate to verify that bad rates do not age while // we're in the "emergency" state. config.AgeBadRateAfterSec = 21 - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // INCREASE (necessary to set a "good" rate in the memory) // r2 @ 70s, 0s lag tf.ratesHistory.add(sinceZero(69*time.Second), 100) tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 200, sinceZero(70*time.Second)) // r2 @ 110s, 0s lag tf.ratesHistory.add(sinceZero(70*time.Second), 100) tf.ratesHistory.add(sinceZero(109*time.Second), 200) tf.process(lagRecord(sinceZero(110*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)); err != nil { - t.Fatal(err) - } - if got, want := tf.m.memory.highestGood(), int64(200); got != want { - t.Fatalf("wrong good rate: got = %v, want = %v", got, want) - } + tf.checkState(stateIncreaseRate, 400, sinceZero(110*time.Second)) + require.Equal(t, int64(200), tf.m.memory.highestGood(), "wrong good rate") // DECREASE (necessary to set a "bad" rate in the memory) @@ -894,12 +714,8 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { tf.ratesHistory.add(sinceZero(110*time.Second), 200) tf.ratesHistory.add(sinceZero(129*time.Second), 400) tf.process(lagRecord(sinceZero(130*time.Second), r2, 3)) - if err := tf.checkState(stateDecreaseAndGuessRate, 280, sinceZero(130*time.Second)); err != nil { - t.Fatal(err) - } - if got, want := tf.m.memory.lowestBad(), int64(400); got != want { - t.Fatalf("wrong bad rate: got = %v, want = %v", got, want) - } + tf.checkState(stateDecreaseAndGuessRate, 280, sinceZero(130*time.Second)) + require.Equal(t, int64(400), tf.m.memory.lowestBad(), "wrong bad rate") // memory: [good, bad] now is [200, 400]. @@ -910,12 +726,8 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { tf.ratesHistory.add(sinceZero(130*time.Second), 400) tf.ratesHistory.add(sinceZero(139*time.Second), 280) tf.process(lagRecord(sinceZero(140*time.Second), r1, 3600)) - if err := tf.checkState(stateEmergency, 140, sinceZero(140*time.Second)); err != nil { - t.Fatal(err) - } - if got, want := tf.m.memory.lowestBad(), int64(280); got != want { - t.Fatalf("bad rate should change when we transition to the emergency state: got = %v, want = %v", got, want) - } + tf.checkState(stateEmergency, 140, sinceZero(140*time.Second)) + require.Equal(t, int64(280), tf.m.memory.lowestBad(), "bad rate should change when we transition to the emergency state") // memory: [good, bad] now is [200, 280]. @@ -923,9 +735,7 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { tf.ratesHistory.add(sinceZero(140*time.Second), 280) tf.ratesHistory.add(sinceZero(149*time.Second), 140) tf.process(lagRecord(sinceZero(150*time.Second), r2, 0)) - if err := tf.checkState(stateEmergency, 140, sinceZero(140*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateEmergency, 140, sinceZero(140*time.Second)) tf.ratesHistory.add(sinceZero(160*time.Second), 140) // r1 keeps to drive the throttler rate down, but not the bad rate. @@ -940,20 +750,14 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { tf.ratesHistory.add(r1Time, int64(rates[i-1])) } tf.process(lagRecord(r1Time, r1, 3600)) - if err := tf.checkState(stateEmergency, int64(rates[i]), r1Time); err != nil { - t.Fatalf("time=%d: %v", tm, err) - } - if got, want := tf.m.memory.lowestBad(), int64(280); got != want { - t.Fatalf("time=%d: bad rate must not change when the old state is the emergency state: got = %v, want = %v", tm, got, want) - } + tf.checkState(stateEmergency, int64(rates[i]), r1Time) + require.Equal(t, int64(280), tf.m.memory.lowestBad(), "bad rate must not change when the old state is the emergency state") // r2 @ s, 0s lag (ignored because r1 is the "replica under test") r2Time := sinceZero(time.Duration(tm+10) * time.Second) tf.ratesHistory.add(r2Time, int64(rates[i])) tf.process(lagRecord(r2Time, r2, 0)) - if err := tf.checkState(stateEmergency, int64(rates[i]), r1Time); err != nil { - t.Fatalf("time=%d: %v", tm, err) - } + tf.checkState(stateEmergency, int64(rates[i]), r1Time) } // INCREASE @@ -966,25 +770,18 @@ func TestMaxReplicationLagModule_EmergencyDoesNotChangeBadValues(t *testing.T) { tf.ratesHistory.add(sinceZero(339*time.Second), 1) tf.process(lagRecord(sinceZero(340*time.Second), r1, 0)) // New rate is 240, the middle of [good, bad] = [200, 240]. - if err := tf.checkState(stateIncreaseRate, 240, sinceZero(340*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 240, sinceZero(340*time.Second)) } func TestMaxReplicationLagModule_NoIncreaseIfMaxRateWasNotApproached(t *testing.T) { config := NewMaxReplicationLagModuleConfig(5) - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // r1 @ 20s, 0s lag // This lag record is required in the next step to correctly calculate how // much r1 lags behind due to the rate increase. tf.process(lagRecord(sinceZero(20*time.Second), r1, 0)) - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) // Master gets 10 QPS in second 69. // r1 @ 70s, 0s lag. @@ -993,9 +790,7 @@ func TestMaxReplicationLagModule_NoIncreaseIfMaxRateWasNotApproached(t *testing. tf.ratesHistory.add(sinceZero(69*time.Second), 10) tf.process(lagRecord(sinceZero(70*time.Second), r1, 0)) // r1 becomes the "replica under test". - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) } // lagRecord creates a fake record using a fake LegacyTabletStats object. @@ -1034,15 +829,10 @@ func tabletStats(uid, lag uint32) discovery.TabletHealth { func TestApplyLatestConfig(t *testing.T) { config := NewMaxReplicationLagModuleConfig(5) - tf, err := newTestFixture(config) - if err != nil { - t.Fatal(err) - } + tf := newTestFixture(t, config) // We start at config.InitialRate. - if err := tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 100, sinceZero(1*time.Second)) // Change the default MaxIncrease from 100% to 200% and test that it's // correctly propagated. config.MaxIncrease = 2 @@ -1053,9 +843,7 @@ func TestApplyLatestConfig(t *testing.T) { tf.process(lagRecord(sinceZero(70*time.Second), r2, 0)) // Rate was increased to 300 based on an actual rate of 100 within [0s, 69s]. // That's a 200% increase. - if err := tf.checkState(stateIncreaseRate, 300, sinceZero(70*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 300, sinceZero(70*time.Second)) // Now reset the config to its default values. tf.m.resetConfiguration() @@ -1066,7 +854,5 @@ func TestApplyLatestConfig(t *testing.T) { tf.ratesHistory.add(sinceZero(80*time.Second), 300) tf.ratesHistory.add(sinceZero(109*time.Second), 300) tf.process(lagRecord(sinceZero(110*time.Second), r2, 0)) - if err := tf.checkState(stateIncreaseRate, 600, sinceZero(110*time.Second)); err != nil { - t.Fatal(err) - } + tf.checkState(stateIncreaseRate, 600, sinceZero(110*time.Second)) } diff --git a/go/vt/throttler/result.go b/go/vt/throttler/result.go index 179711116a3..0976a180877 100644 --- a/go/vt/throttler/result.go +++ b/go/vt/throttler/result.go @@ -17,8 +17,8 @@ limitations under the License. package throttler import ( - "bytes" "fmt" + "strings" "sync" "text/template" "time" @@ -81,7 +81,7 @@ type result struct { } func (r result) String() string { - var b bytes.Buffer + var b strings.Builder if err := resultStringTemplate.Execute(&b, r); err != nil { panic(fmt.Sprintf("failed to Execute() template: %v", err)) } diff --git a/go/vt/throttler/throttler.go b/go/vt/throttler/throttler.go index 83a1c52225e..909888bd0d4 100644 --- a/go/vt/throttler/throttler.go +++ b/go/vt/throttler/throttler.go @@ -35,6 +35,7 @@ import ( "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/proto/topodata" throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata" ) @@ -50,7 +51,7 @@ const ( // MaxRateModuleDisabled can be set in NewThrottler() to disable throttling // by a fixed rate. - MaxRateModuleDisabled = math.MaxInt64 + MaxRateModuleDisabled = int64(math.MaxInt64) // InvalidMaxRate is a constant which will fail in a NewThrottler() call. // It should be used when returning maxRate in an error case. @@ -58,7 +59,7 @@ const ( // ReplicationLagModuleDisabled can be set in NewThrottler() to disable // throttling based on the MySQL replication lag. - ReplicationLagModuleDisabled = math.MaxInt64 + ReplicationLagModuleDisabled = int64(math.MaxInt64) // InvalidMaxReplicationLag is a constant which will fail in a NewThrottler() // call. It should be used when returning maxReplicationlag in an error case. @@ -224,6 +225,28 @@ func (t *Throttler) Throttle(threadID int) time.Duration { return t.threadThrottlers[threadID].throttle(t.nowFunc()) } +// MaxLag returns the max of all the last replication lag values seen across all tablets of +// the provided type, excluding ignored tablets. +func (t *Throttler) MaxLag(tabletType topodata.TabletType) uint32 { + cache := t.maxReplicationLagModule.lagCacheByType(tabletType) + + var maxLag uint32 + cacheEntries := cache.entries + + for key := range cacheEntries { + if cache.isIgnored(key) { + continue + } + + lag := cache.latest(key).Stats.ReplicationLagSeconds + if lag > maxLag { + maxLag = lag + } + } + + return maxLag +} + // ThreadFinished marks threadID as finished and redistributes the thread's // rate allotment across the other threads. // After ThreadFinished() is called, Throttle() must not be called anymore. diff --git a/go/vt/throttler/throttlerclient/throttlerclient.go b/go/vt/throttler/throttlerclient/throttlerclient.go deleted file mode 100644 index cf01ccb1239..00000000000 --- a/go/vt/throttler/throttlerclient/throttlerclient.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package throttlerclient defines the generic RPC client interface for the -// throttler service. It has to be implemented for the different RPC frameworks -// e.g. gRPC. -package throttlerclient - -import ( - "fmt" - "log" - - "github.com/spf13/pflag" - - "vitess.io/vitess/go/vt/servenv" - - "context" - - throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata" -) - -// protocol specifics which RPC client implementation should be used. -var protocol = "grpc" - -func init() { - servenv.OnParseFor("vttablet", registerFlags) -} - -func registerFlags(fs *pflag.FlagSet) { - fs.StringVar(&protocol, "throttler_client_protocol", protocol, "the protocol to use to talk to the integrated throttler service") -} - -// Client defines the generic RPC interface for the throttler service. -type Client interface { - // MaxRates returns the current max rate for each throttler of the process. - MaxRates(ctx context.Context) (map[string]int64, error) - - // SetMaxRate allows to change the current max rate for all throttlers - // of the process. - // It returns the names of the updated throttlers. - SetMaxRate(ctx context.Context, rate int64) ([]string, error) - - // GetConfiguration returns the configuration of the MaxReplicationlag module - // for the given throttler or all throttlers if "throttlerName" is empty. - GetConfiguration(ctx context.Context, throttlerName string) (map[string]*throttlerdatapb.Configuration, error) - - // UpdateConfiguration (partially) updates the configuration of the - // MaxReplicationlag module for the given throttler or all throttlers if - // "throttlerName" is empty. - // If "copyZeroValues" is true, fields with zero values will be copied - // as well. - // The function returns the names of the updated throttlers. - UpdateConfiguration(ctx context.Context, throttlerName string, configuration *throttlerdatapb.Configuration, copyZeroValues bool) ([]string, error) - - // ResetConfiguration resets the configuration of the MaxReplicationlag module - // to the initial configuration for the given throttler or all throttlers if - // "throttlerName" is empty. - // The function returns the names of the updated throttlers. - ResetConfiguration(ctx context.Context, throttlerName string) ([]string, error) - - // Close will terminate the connection and free resources. - Close() -} - -// Factory has to be implemented and must create a new RPC client for a given -// "addr". -type Factory func(addr string) (Client, error) - -var factories = make(map[string]Factory) - -// RegisterFactory allows a client implementation to register itself. -func RegisterFactory(name string, factory Factory) { - if _, ok := factories[name]; ok { - log.Fatalf("RegisterFactory: %s already exists", name) - } - factories[name] = factory -} - -// New will return a client for the selected RPC implementation. -func New(addr string) (Client, error) { - factory, ok := factories[protocol] - if !ok { - return nil, fmt.Errorf("unknown throttler client protocol: %v", protocol) - } - return factory(addr) -} diff --git a/go/vt/throttler/throttlerclienttest/throttlerclient_testsuite.go b/go/vt/throttler/throttlerclienttest/throttlerclient_testsuite.go deleted file mode 100644 index 38fd9d76286..00000000000 --- a/go/vt/throttler/throttlerclienttest/throttlerclient_testsuite.go +++ /dev/null @@ -1,262 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package throttlerclienttest contains the testsuite against which each -// RPC implementation of the throttlerclient interface must be tested. -package throttlerclienttest - -// NOTE: This file is not test-only code because it is referenced by -// tests in other packages and therefore it has to be regularly -// visible. - -// NOTE: This code is in its own package such that its dependencies -// (e.g. zookeeper) won't be drawn into production binaries as well. - -import ( - "reflect" - "strings" - "testing" - - "context" - - "google.golang.org/protobuf/proto" - - "vitess.io/vitess/go/vt/throttler" - "vitess.io/vitess/go/vt/throttler/throttlerclient" - - throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata" -) - -// TestSuite runs the test suite on the given throttlerclient and throttlerserver. -func TestSuite(t *testing.T, c throttlerclient.Client) { - tf := &testFixture{} - if err := tf.setUp(); err != nil { - t.Fatal(err) - } - defer tf.tearDown() - - tf.maxRates(t, c) - - tf.setMaxRate(t, c) - - tf.configuration(t, c) -} - -// TestSuitePanics tests the panic handling of each RPC method. Unlike TestSuite -// it does not use the real throttler.managerImpl. Instead, it uses FakeManager -// which allows us to panic on each RPC. -func TestSuitePanics(t *testing.T, c throttlerclient.Client) { - maxRatesPanics(t, c) - - setMaxRatePanics(t, c) - - getConfigurationPanics(t, c) - - updateConfigurationPanics(t, c) - - resetConfigurationPanics(t, c) -} - -var throttlerNames = []string{"t1", "t2"} - -type testFixture struct { - throttlers []*throttler.Throttler -} - -func (tf *testFixture) setUp() error { - for _, name := range throttlerNames { - t, err := throttler.NewThrottler(name, "TPS", 1 /* threadCount */, 1, throttler.ReplicationLagModuleDisabled) - if err != nil { - return err - } - tf.throttlers = append(tf.throttlers, t) - } - return nil -} - -func (tf *testFixture) tearDown() { - for _, t := range tf.throttlers { - t.Close() - } -} - -func (tf *testFixture) maxRates(t *testing.T, client throttlerclient.Client) { - _, err := client.SetMaxRate(context.Background(), 23) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - - got, err := client.MaxRates(context.Background()) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - want := map[string]int64{ - "t1": 23, - "t2": 23, - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("rate was not updated on all registered throttlers. got = %v, want = %v", got, throttlerNames) - } -} - -func (tf *testFixture) setMaxRate(t *testing.T, client throttlerclient.Client) { - got, err := client.SetMaxRate(context.Background(), 23) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - - if !reflect.DeepEqual(got, throttlerNames) { - t.Fatalf("rate was not updated on all registered throttlers. got = %v, want = %v", got, throttlerNames) - } -} - -func (tf *testFixture) configuration(t *testing.T, client throttlerclient.Client) { - initialConfigs, err := client.GetConfiguration(context.Background(), "" /* all */) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - - // Test UpdateConfiguration. - config := &throttlerdatapb.Configuration{ - TargetReplicationLagSec: 1, - MaxReplicationLagSec: 2, - InitialRate: 3, - MaxIncrease: 0.4, - EmergencyDecrease: 0.5, - MinDurationBetweenIncreasesSec: 6, - MaxDurationBetweenIncreasesSec: 7, - MinDurationBetweenDecreasesSec: 8, - SpreadBacklogAcrossSec: 9, - IgnoreNSlowestReplicas: 10, - IgnoreNSlowestRdonlys: 11, - AgeBadRateAfterSec: 12, - BadRateIncrease: 0.13, - MaxRateApproachThreshold: 0.9, - } - names, err := client.UpdateConfiguration(context.Background(), "t2", config /* false */, true /* copyZeroValues */) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - if got, want := names, []string{"t2"}; !reflect.DeepEqual(got, want) { - t.Fatalf("returned names of updated throttlers is wrong. got = %v, want = %v", got, want) - } - - // Test GetConfiguration. - configs, err := client.GetConfiguration(context.Background(), "t2") - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - if len(configs) != 1 || configs["t2"] == nil { - t.Fatalf("wrong named configuration returned. got = %v, want configuration for t2", configs) - } - if got, want := configs["t2"], config; !proto.Equal(got, want) { - t.Fatalf("did not read updated config. got = %v, want = %v", got, want) - } - - // Reset should return the initial configs. - namesForReset, err := client.ResetConfiguration(context.Background(), "" /* all */) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - if got, want := namesForReset, throttlerNames; !reflect.DeepEqual(got, want) { - t.Fatalf("returned names of reset throttlers is wrong. got = %v, want = %v", got, want) - } - - // Verify that it was correctly set. - configsAfterReset, err := client.GetConfiguration(context.Background(), "" /* all */) - if err != nil { - t.Fatalf("Cannot execute remote command: %v", err) - } - if got, want := configsAfterReset, initialConfigs; !reflect.DeepEqual(got, want) { - t.Fatalf("wrong configurations after reset. got = %v, want = %v", got, want) - } -} - -// FakeManager implements the throttler.Manager interface and panics on all -// methods defined in the interface. -type FakeManager struct { -} - -const panicMsg = "RPC server implementation should handle this" - -// MaxRates implements the throttler.Manager interface. It always panics. -func (fm *FakeManager) MaxRates() map[string]int64 { - panic(panicMsg) -} - -// SetMaxRate implements the throttler.Manager interface. It always panics. -func (fm *FakeManager) SetMaxRate(int64) []string { - panic(panicMsg) -} - -// GetConfiguration implements the throttler.Manager interface. It always panics. -func (fm *FakeManager) GetConfiguration(throttlerName string) (map[string]*throttlerdatapb.Configuration, error) { - panic(panicMsg) -} - -// UpdateConfiguration implements the throttler.Manager interface. It always panics. -func (fm *FakeManager) UpdateConfiguration(throttlerName string, configuration *throttlerdatapb.Configuration, copyZeroValues bool) ([]string, error) { - panic(panicMsg) -} - -// ResetConfiguration implements the throttler.Manager interface. It always panics. -func (fm *FakeManager) ResetConfiguration(throttlerName string) ([]string, error) { - panic(panicMsg) -} - -// Test methods which test for each RPC that panics are caught. - -func maxRatesPanics(t *testing.T, client throttlerclient.Client) { - _, err := client.MaxRates(context.Background()) - if !errorFromPanicHandler(err) { - t.Fatalf("MaxRates RPC implementation does not catch panics properly: %v", err) - } -} - -func setMaxRatePanics(t *testing.T, client throttlerclient.Client) { - _, err := client.SetMaxRate(context.Background(), 23) - if !errorFromPanicHandler(err) { - t.Fatalf("SetMaxRate RPC implementation does not catch panics properly: %v", err) - } -} - -func getConfigurationPanics(t *testing.T, client throttlerclient.Client) { - _, err := client.GetConfiguration(context.Background(), "") - if !errorFromPanicHandler(err) { - t.Fatalf("GetConfiguration RPC implementation does not catch panics properly: %v", err) - } -} - -func updateConfigurationPanics(t *testing.T, client throttlerclient.Client) { - _, err := client.UpdateConfiguration(context.Background(), "", nil, false) - if !errorFromPanicHandler(err) { - t.Fatalf("UpdateConfiguration RPC implementation does not catch panics properly: %v", err) - } -} - -func resetConfigurationPanics(t *testing.T, client throttlerclient.Client) { - _, err := client.ResetConfiguration(context.Background(), "") - if !errorFromPanicHandler(err) { - t.Fatalf("ResetConfiguration RPC implementation does not catch panics properly: %v", err) - } -} - -func errorFromPanicHandler(err error) bool { - if err == nil || !strings.Contains(err.Error(), panicMsg) { - return false - } - return true -} diff --git a/go/vt/tlstest/tlstest_test.go b/go/vt/tlstest/tlstest_test.go index 5c79e45b906..1a6e0ae70ba 100644 --- a/go/vt/tlstest/tlstest_test.go +++ b/go/vt/tlstest/tlstest_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "golang.org/x/sync/errgroup" "vitess.io/vitess/go/vt/vttls" ) @@ -89,21 +90,20 @@ func testClientServer(t *testing.T, combineCerts bool) { dialer := new(net.Dialer) dialer.Timeout = 10 * time.Second - wg := sync.WaitGroup{} - // // Positive case: accept on server side, connect a client, send data. // - var clientErr error - wg.Add(1) - go func() { - defer wg.Done() - clientConn, clientErr := tls.DialWithDialer(dialer, "tcp", addr, clientConfig) - if clientErr == nil { - _, _ = clientConn.Write([]byte{42}) - clientConn.Close() + var clientEG errgroup.Group + clientEG.Go(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addr, clientConfig) + if err != nil { + return err } - }() + + _, _ = conn.Write([]byte{42}) + _ = conn.Close() + return nil + }) serverConn, err := listener.Accept() if err != nil { @@ -119,10 +119,8 @@ func testClientServer(t *testing.T, combineCerts bool) { } serverConn.Close() - wg.Wait() - - if clientErr != nil { - t.Fatalf("Dial failed: %v", clientErr) + if err := clientEG.Wait(); err != nil { + t.Fatalf("client dial failed: %v", err) } // @@ -142,21 +140,23 @@ func testClientServer(t *testing.T, combineCerts bool) { t.Fatalf("TLSClientConfig failed: %v", err) } - var serverErr error - wg.Add(1) - go func() { + var serverEG errgroup.Group + serverEG.Go(func() error { // We expect the Accept to work, but the first read to fail. - defer wg.Done() - serverConn, serverErr := listener.Accept() + conn, err := listener.Accept() + if err != nil { + return err + } + // This will fail. - if serverErr == nil { - result := make([]byte, 1) - if n, err := serverConn.Read(result); err == nil { - fmt.Printf("Was able to read from server: %v\n", n) - } - serverConn.Close() + result := make([]byte, 1) + if n, err := conn.Read(result); err == nil { + return fmt.Errorf("unexpectedly able to read %d bytes from server", n) } - }() + + _ = conn.Close() + return nil + }) // When using TLS 1.2, the Dial will fail. // With TLS 1.3, the Dial will succeed and the first Read will fail. @@ -167,9 +167,9 @@ func testClientServer(t *testing.T, combineCerts bool) { } return } - wg.Wait() - if serverErr != nil { - t.Fatalf("Connection failed: %v", serverErr) + + if err := serverEG.Wait(); err != nil { + t.Fatalf("server read failed: %v", err) } data := make([]byte, 1) diff --git a/go/vt/topo/consultopo/error.go b/go/vt/topo/consultopo/error.go index 42f474e065b..62167a4d295 100644 --- a/go/vt/topo/consultopo/error.go +++ b/go/vt/topo/consultopo/error.go @@ -40,15 +40,16 @@ var ( // are either application-level errors, or context errors. func convertError(err error, nodePath string) error { // Unwrap errors from the Go HTTP client. - if urlErr, ok := err.(*url.Error); ok { + var urlErr *url.Error + if errors.As(err, &urlErr) { err = urlErr.Err } // Convert specific sentinel values. - switch err { - case context.Canceled: + switch { + case errors.Is(err, context.Canceled): return topo.NewError(topo.Interrupted, nodePath) - case context.DeadlineExceeded: + case errors.Is(err, context.DeadlineExceeded): return topo.NewError(topo.Timeout, nodePath) } diff --git a/go/vt/topo/errors.go b/go/vt/topo/errors.go index a645f1aa178..3be4b60b103 100644 --- a/go/vt/topo/errors.go +++ b/go/vt/topo/errors.go @@ -36,6 +36,7 @@ const ( NoUpdateNeeded NoImplementation NoReadOnlyImplementation + ResourceExhausted ) // Error represents a topo error. @@ -68,6 +69,8 @@ func NewError(code ErrorCode, node string) error { message = fmt.Sprintf("no such topology implementation %s", node) case NoReadOnlyImplementation: message = fmt.Sprintf("no read-only topology implementation %s", node) + case ResourceExhausted: + message = fmt.Sprintf("server resource exhausted: %s", node) default: message = fmt.Sprintf("unknown code: %s", node) } diff --git a/go/vt/topo/etcd2topo/error.go b/go/vt/topo/etcd2topo/error.go index e784fecd9b9..5e13d0bdf8d 100644 --- a/go/vt/topo/etcd2topo/error.go +++ b/go/vt/topo/etcd2topo/error.go @@ -45,7 +45,8 @@ func convertError(err error, nodePath string) error { return nil } - if typeErr, ok := err.(rpctypes.EtcdError); ok { + var typeErr rpctypes.EtcdError + if errors.As(err, &typeErr) { switch typeErr.Code() { case codes.NotFound: return topo.NewError(topo.NoNode, nodePath) @@ -61,6 +62,8 @@ func convertError(err error, nodePath string) error { // etcd primary election is failing, so timeout // also sounds reasonable there. return topo.NewError(topo.Timeout, nodePath) + case codes.ResourceExhausted: + return topo.NewError(topo.ResourceExhausted, nodePath) } return err } @@ -74,15 +77,17 @@ func convertError(err error, nodePath string) error { return topo.NewError(topo.Interrupted, nodePath) case codes.DeadlineExceeded: return topo.NewError(topo.Timeout, nodePath) + case codes.ResourceExhausted: + return topo.NewError(topo.ResourceExhausted, nodePath) default: return err } } - switch err { - case context.Canceled: + switch { + case errors.Is(err, context.Canceled): return topo.NewError(topo.Interrupted, nodePath) - case context.DeadlineExceeded: + case errors.Is(err, context.DeadlineExceeded): return topo.NewError(topo.Timeout, nodePath) default: return err diff --git a/go/vt/topo/events/keyspace_change_syslog.go b/go/vt/topo/events/keyspace_change_syslog.go index d7f456ae6b8..7404c3ca882 100644 --- a/go/vt/topo/events/keyspace_change_syslog.go +++ b/go/vt/topo/events/keyspace_change_syslog.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/events/keyspace_change_syslog_test.go b/go/vt/topo/events/keyspace_change_syslog_test.go index 1367cf27b23..8ba7225a025 100644 --- a/go/vt/topo/events/keyspace_change_syslog_test.go +++ b/go/vt/topo/events/keyspace_change_syslog_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/events/shard_change_syslog.go b/go/vt/topo/events/shard_change_syslog.go index 3f6422a9175..2055e4268ec 100644 --- a/go/vt/topo/events/shard_change_syslog.go +++ b/go/vt/topo/events/shard_change_syslog.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/events/shard_change_syslog_test.go b/go/vt/topo/events/shard_change_syslog_test.go index fc721bae923..bdac457853e 100644 --- a/go/vt/topo/events/shard_change_syslog_test.go +++ b/go/vt/topo/events/shard_change_syslog_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/events/tablet_change_syslog.go b/go/vt/topo/events/tablet_change_syslog.go index e2dae020c8e..55de46674dc 100644 --- a/go/vt/topo/events/tablet_change_syslog.go +++ b/go/vt/topo/events/tablet_change_syslog.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/events/tablet_change_syslog_test.go b/go/vt/topo/events/tablet_change_syslog_test.go index 4a5bb4d7ea9..7ecabf3f7fb 100644 --- a/go/vt/topo/events/tablet_change_syslog_test.go +++ b/go/vt/topo/events/tablet_change_syslog_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topo/helpers/compare_test.go b/go/vt/topo/helpers/compare_test.go index d31eedee2e9..82924e522f5 100644 --- a/go/vt/topo/helpers/compare_test.go +++ b/go/vt/topo/helpers/compare_test.go @@ -17,9 +17,10 @@ limitations under the License. package helpers import ( + "context" "testing" - "context" + "vitess.io/vitess/go/vt/sqlparser" ) func TestBasicCompare(t *testing.T) { @@ -32,7 +33,7 @@ func TestBasicCompare(t *testing.T) { t.Fatalf("Compare keyspaces is not failing when topos are not in sync") } - CopyKeyspaces(ctx, fromTS, toTS) + CopyKeyspaces(ctx, fromTS, toTS, sqlparser.NewTestParser()) err = CompareKeyspaces(ctx, fromTS, toTS) if err != nil { diff --git a/go/vt/topo/helpers/copy.go b/go/vt/topo/helpers/copy.go index 0df706eba31..6dff1c6ac22 100644 --- a/go/vt/topo/helpers/copy.go +++ b/go/vt/topo/helpers/copy.go @@ -25,6 +25,7 @@ import ( "google.golang.org/protobuf/proto" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -33,7 +34,7 @@ import ( ) // CopyKeyspaces will create the keyspaces in the destination topo. -func CopyKeyspaces(ctx context.Context, fromTS, toTS *topo.Server) error { +func CopyKeyspaces(ctx context.Context, fromTS, toTS *topo.Server, parser *sqlparser.Parser) error { keyspaces, err := fromTS.GetKeyspaces(ctx) if err != nil { return fmt.Errorf("GetKeyspaces: %w", err) @@ -57,7 +58,7 @@ func CopyKeyspaces(ctx context.Context, fromTS, toTS *topo.Server) error { vs, err := fromTS.GetVSchema(ctx, keyspace) switch { case err == nil: - _, err = vindexes.BuildKeyspace(vs) + _, err = vindexes.BuildKeyspace(vs, parser) if err != nil { log.Errorf("BuildKeyspace(%v): %v", keyspace, err) break diff --git a/go/vt/topo/helpers/copy_test.go b/go/vt/topo/helpers/copy_test.go index 2086a2e6552..142c6eb49ac 100644 --- a/go/vt/topo/helpers/copy_test.go +++ b/go/vt/topo/helpers/copy_test.go @@ -22,6 +22,8 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" @@ -104,7 +106,7 @@ func TestBasic(t *testing.T) { fromTS, toTS := createSetup(ctx, t) // check keyspace copy - CopyKeyspaces(ctx, fromTS, toTS) + CopyKeyspaces(ctx, fromTS, toTS, sqlparser.NewTestParser()) keyspaces, err := toTS.GetKeyspaces(ctx) if err != nil { t.Fatalf("toTS.GetKeyspaces failed: %v", err) @@ -112,7 +114,7 @@ func TestBasic(t *testing.T) { if len(keyspaces) != 1 || keyspaces[0] != "test_keyspace" { t.Fatalf("unexpected keyspaces: %v", keyspaces) } - CopyKeyspaces(ctx, fromTS, toTS) + CopyKeyspaces(ctx, fromTS, toTS, sqlparser.NewTestParser()) // check shard copy CopyShards(ctx, fromTS, toTS) diff --git a/go/vt/topo/helpers/tee_test.go b/go/vt/topo/helpers/tee_test.go index 4dda901c300..1fbba807937 100644 --- a/go/vt/topo/helpers/tee_test.go +++ b/go/vt/topo/helpers/tee_test.go @@ -17,12 +17,13 @@ limitations under the License. package helpers import ( + "context" "reflect" "testing" "github.com/stretchr/testify/require" - "context" + "vitess.io/vitess/go/vt/sqlparser" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) @@ -32,7 +33,7 @@ func TestTee(t *testing.T) { // create the setup, copy the data fromTS, toTS := createSetup(ctx, t) - CopyKeyspaces(ctx, fromTS, toTS) + CopyKeyspaces(ctx, fromTS, toTS, sqlparser.NewTestParser()) CopyShards(ctx, fromTS, toTS) CopyTablets(ctx, fromTS, toTS) diff --git a/go/vt/topo/keyspace.go b/go/vt/topo/keyspace.go index feb80c374e5..844b4eb4454 100755 --- a/go/vt/topo/keyspace.go +++ b/go/vt/topo/keyspace.go @@ -19,8 +19,15 @@ package topo import ( "context" "path" + "sort" + "sync" + + "github.com/spf13/pflag" + "golang.org/x/sync/errgroup" "vitess.io/vitess/go/constants/sidecar" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/event" @@ -31,7 +38,25 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -// This file contains keyspace utility functions +// This file contains keyspace utility functions. + +// Default concurrency to use in order to avoid overhwelming the topo server. +var DefaultConcurrency = 32 + +// shardKeySuffix is the suffix of a shard key. +// The full key looks like this: +// /vitess/global/keyspaces/customer/shards/80-/Shard +const shardKeySuffix = "Shard" + +func registerFlags(fs *pflag.FlagSet) { + fs.IntVar(&DefaultConcurrency, "topo_read_concurrency", DefaultConcurrency, "Concurrency of topo reads.") +} + +func init() { + servenv.OnParseFor("vtcombo", registerFlags) + servenv.OnParseFor("vtctld", registerFlags) + servenv.OnParseFor("vtgate", registerFlags) +} // KeyspaceInfo is a meta struct that contains metadata to give the // data more context and convenience. This is the main way we interact @@ -58,110 +83,6 @@ func ValidateKeyspaceName(name string) error { return validateObjectName(name) } -// GetServedFrom returns a Keyspace_ServedFrom record if it exists. -func (ki *KeyspaceInfo) GetServedFrom(tabletType topodatapb.TabletType) *topodatapb.Keyspace_ServedFrom { - for _, ksf := range ki.ServedFroms { - if ksf.TabletType == tabletType { - return ksf - } - } - return nil -} - -// CheckServedFromMigration makes sure a requested migration is safe -func (ki *KeyspaceInfo) CheckServedFromMigration(tabletType topodatapb.TabletType, cells []string, keyspace string, remove bool) error { - // primary is a special case with a few extra checks - if tabletType == topodatapb.TabletType_PRIMARY { - // TODO(deepthi): these master references will go away when we delete legacy resharding - if !remove { - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cannot add master back to %v", ki.keyspace) - } - if len(cells) > 0 { - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cannot migrate only some cells for master removal in keyspace %v", ki.keyspace) - } - if len(ki.ServedFroms) > 1 { - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cannot migrate master into %v until everything else is migrated", ki.keyspace) - } - } - - // we can't remove a type we don't have - if ki.GetServedFrom(tabletType) == nil && remove { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "supplied type cannot be migrated") - } - - // check the keyspace is consistent in any case - for _, ksf := range ki.ServedFroms { - if ksf.Keyspace != keyspace { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "inconsistent keyspace specified in migration: %v != %v for type %v", keyspace, ksf.Keyspace, ksf.TabletType) - } - } - - return nil -} - -// UpdateServedFromMap handles ServedFromMap. It can add or remove -// records, cells, ... -func (ki *KeyspaceInfo) UpdateServedFromMap(tabletType topodatapb.TabletType, cells []string, keyspace string, remove bool, allCells []string) error { - // check parameters to be sure - if err := ki.CheckServedFromMigration(tabletType, cells, keyspace, remove); err != nil { - return err - } - - ksf := ki.GetServedFrom(tabletType) - if ksf == nil { - // the record doesn't exist - if remove { - if len(ki.ServedFroms) == 0 { - ki.ServedFroms = nil - } - log.Warningf("Trying to remove KeyspaceServedFrom for missing type %v in keyspace %v", tabletType, ki.keyspace) - } else { - ki.ServedFroms = append(ki.ServedFroms, &topodatapb.Keyspace_ServedFrom{ - TabletType: tabletType, - Cells: cells, - Keyspace: keyspace, - }) - } - return nil - } - - if remove { - result, emptyList := removeCells(ksf.Cells, cells, allCells) - if emptyList { - // we don't have any cell left, we need to clear this record - var newServedFroms []*topodatapb.Keyspace_ServedFrom - for _, k := range ki.ServedFroms { - if k != ksf { - newServedFroms = append(newServedFroms, k) - } - } - ki.ServedFroms = newServedFroms - } else { - ksf.Cells = result - } - } else { - if ksf.Keyspace != keyspace { - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cannot UpdateServedFromMap on existing record for keyspace %v, different keyspace: %v != %v", ki.keyspace, ksf.Keyspace, keyspace) - } - ksf.Cells = addCells(ksf.Cells, cells) - } - return nil -} - -// ComputeCellServedFrom returns the ServedFrom list for a cell -func (ki *KeyspaceInfo) ComputeCellServedFrom(cell string) []*topodatapb.SrvKeyspace_ServedFrom { - var result []*topodatapb.SrvKeyspace_ServedFrom - for _, ksf := range ki.ServedFroms { - if InCellList(cell, ksf.Cells) { - result = append(result, &topodatapb.SrvKeyspace_ServedFrom{ - TabletType: ksf.TabletType, - Keyspace: ksf.Keyspace, - }) - } - } - return result -} - // CreateKeyspace wraps the underlying Conn.Create // and dispatches the event. func (ts *Server) CreateKeyspace(ctx context.Context, keyspace string, value *topodatapb.Keyspace) error { @@ -270,56 +191,163 @@ func (ts *Server) UpdateKeyspace(ctx context.Context, ki *KeyspaceInfo) error { return nil } -// FindAllShardsInKeyspace reads and returns all the existing shards in -// a keyspace. It doesn't take any lock. -func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string) (map[string]*ShardInfo, error) { +// FindAllShardsInKeyspaceOptions controls the behavior of +// Server.FindAllShardsInKeyspace. +type FindAllShardsInKeyspaceOptions struct { + // Concurrency controls the maximum number of concurrent calls to GetShard. + // If <= 0, Concurrency is set to 1. + Concurrency int +} + +// FindAllShardsInKeyspace reads and returns all the existing shards in a +// keyspace. It doesn't take any lock. +// +// If opt is non-nil, it is used to configure the method's behavior. Otherwise, +// the default options are used. +func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, opt *FindAllShardsInKeyspaceOptions) (map[string]*ShardInfo, error) { + // Apply any necessary defaults. + if opt == nil { + opt = &FindAllShardsInKeyspaceOptions{} + } + if opt.Concurrency <= 0 { + opt.Concurrency = DefaultConcurrency + } + + // First try to get all shards using List if we can. + buildResultFromList := func(kvpairs []KVInfo) (map[string]*ShardInfo, error) { + result := make(map[string]*ShardInfo, len(kvpairs)) + for _, entry := range kvpairs { + // The shard key looks like this: /vitess/global/keyspaces/commerce/shards/-80/Shard + shardKey := string(entry.Key) + // We don't want keys that aren't Shards. For example: + // /vitess/global/keyspaces/commerce/shards/0/locks/7587876423742065323 + // This example key can happen with Shards because you can get a shard + // lock in the topo via TopoServer.LockShard(). + if path.Base(shardKey) != shardKeySuffix { + continue + } + shardName := path.Base(path.Dir(shardKey)) // The base part of the dir is "-80" + // Validate the extracted shard name. + if _, _, err := ValidateShardName(shardName); err != nil { + return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): unexpected shard key/path %q contains invalid shard name/range %q", + keyspace, shardKey, shardName) + } + shard := &topodatapb.Shard{} + if err := shard.UnmarshalVT(entry.Value); err != nil { + return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): invalid data found for shard %q in %q", + keyspace, shardName, shardKey) + } + result[shardName] = &ShardInfo{ + keyspace: keyspace, + shardName: shardName, + version: entry.Version, + Shard: shard, + } + } + return result, nil + } + shardsPath := path.Join(KeyspacesPath, keyspace, ShardsPath) + listRes, err := ts.globalCell.List(ctx, shardsPath) + if err == nil { // We have everything we need to build the result + return buildResultFromList(listRes) + } + if IsErrType(err, NoNode) { + // The path doesn't exist, let's see if the keyspace exists. + if _, kerr := ts.GetKeyspace(ctx, keyspace); kerr != nil { + return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): List", keyspace) + } + // We simply have no shards. + return make(map[string]*ShardInfo, 0), nil + } + // Currently the ZooKeeper implementation does not support index prefix + // scans so we fall back to concurrently fetching the shards one by one. + // It is also possible that the response containing all shards is too + // large in which case we also fall back to the one by one fetch. + if !IsErrType(err, NoImplementation) && !IsErrType(err, ResourceExhausted) { + return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): List", keyspace) + } + + // Fall back to the shard by shard method. shards, err := ts.GetShardNames(ctx, keyspace) if err != nil { - return nil, vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace) - } + return nil, vterrors.Wrapf(err, "failed to get list of shard names for keyspace '%s'", keyspace) + } + + // Keyspaces with a large number of shards and geographically distributed + // topo instances may experience significant latency fetching shard records. + // + // A prior version of this logic used unbounded concurrency to fetch shard + // records which resulted in overwhelming topo server instances: + // https://github.com/vitessio/vitess/pull/5436. + // + // However, removing the concurrency altogether can cause large operations + // to fail due to timeout. The caller chooses the appropriate concurrency + // level so that certain paths can be optimized (such as vtctld + // RebuildKeyspace calls, which do not run on every vttablet). + var ( + mu sync.Mutex + result = make(map[string]*ShardInfo, len(shards)) + ) + + eg, ctx := errgroup.WithContext(ctx) + eg.SetLimit(int(opt.Concurrency)) - result := make(map[string]*ShardInfo, len(shards)) for _, shard := range shards { - si, err := ts.GetShard(ctx, keyspace, shard) - if err != nil { - if IsErrType(err, NoNode) { - log.Warningf("GetShard(%v, %v) returned ErrNoNode, consider checking the topology.", keyspace, shard) - } else { - return nil, vterrors.Wrapf(err, "GetShard(%v, %v) failed", keyspace, shard) + shard := shard + + eg.Go(func() error { + si, err := ts.GetShard(ctx, keyspace, shard) + switch { + case IsErrType(err, NoNode): + log.Warningf("GetShard(%s, %s) returned ErrNoNode, consider checking the topology.", keyspace, shard) + return nil + case err == nil: + mu.Lock() + result[shard] = si + mu.Unlock() + + return nil + default: + return vterrors.Wrapf(err, "GetShard(%s, %s) failed", keyspace, shard) } - } - result[shard] = si + }) } + + if err := eg.Wait(); err != nil { + return nil, err + } + return result, nil } // GetServingShards returns all shards where the primary is serving. func (ts *Server) GetServingShards(ctx context.Context, keyspace string) ([]*ShardInfo, error) { - shards, err := ts.GetShardNames(ctx, keyspace) + shards, err := ts.FindAllShardsInKeyspace(ctx, keyspace, nil) if err != nil { return nil, vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace) } result := make([]*ShardInfo, 0, len(shards)) for _, shard := range shards { - si, err := ts.GetShard(ctx, keyspace, shard) - if err != nil { - return nil, vterrors.Wrapf(err, "GetShard(%v, %v) failed", keyspace, shard) - } - if !si.IsPrimaryServing { + if !shard.IsPrimaryServing { continue } - result = append(result, si) + result = append(result, shard) } if len(result) == 0 { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%v has no serving shards", keyspace) } + // Sort the shards by KeyRange for deterministic results. + sort.Slice(result, func(i, j int) bool { + return key.KeyRangeLess(result[i].KeyRange, result[j].KeyRange) + }) + return result, nil } // GetOnlyShard returns the single ShardInfo of an unsharded keyspace. func (ts *Server) GetOnlyShard(ctx context.Context, keyspace string) (*ShardInfo, error) { - allShards, err := ts.FindAllShardsInKeyspace(ctx, keyspace) + allShards, err := ts.FindAllShardsInKeyspace(ctx, keyspace, nil) if err != nil { return nil, err } diff --git a/go/vt/topo/keyspace_external_test.go b/go/vt/topo/keyspace_external_test.go new file mode 100644 index 00000000000..38ff1c8ce7b --- /dev/null +++ b/go/vt/topo/keyspace_external_test.go @@ -0,0 +1,203 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package topo_test + +import ( + "context" + "fmt" + "slices" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +func TestServerFindAllShardsInKeyspace(t *testing.T) { + tests := []struct { + name string + shards int + opt *topo.FindAllShardsInKeyspaceOptions + }{ + { + name: "negative concurrency", + shards: 1, + // Ensure this doesn't panic. + opt: &topo.FindAllShardsInKeyspaceOptions{Concurrency: -1}, + }, + { + name: "unsharded", + shards: 1, + // Make sure the defaults apply as expected. + opt: nil, + }, + { + name: "sharded", + shards: 32, + opt: &topo.FindAllShardsInKeyspaceOptions{Concurrency: 8}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ts := memorytopo.NewServer(ctx) + defer ts.Close() + + // Create an ephemeral keyspace and generate shard records within + // the keyspace to fetch later. + const keyspace = "keyspace" + require.NoError(t, ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{})) + + shards, err := key.GenerateShardRanges(tt.shards) + require.NoError(t, err) + + for _, s := range shards { + require.NoError(t, ts.CreateShard(ctx, keyspace, s)) + } + + // Verify that we return a complete list of shards and that each + // key range is present in the output. + out, err := ts.FindAllShardsInKeyspace(ctx, keyspace, tt.opt) + require.NoError(t, err) + require.Len(t, out, tt.shards) + + for _, s := range shards { + if _, ok := out[s]; !ok { + t.Errorf("shard %q was not found", s) + } + } + }) + } +} + +func TestServerGetServingShards(t *testing.T) { + keyspace := "ks1" + errNoListImpl := topo.NewError(topo.NoImplementation, "don't be doing no listing round here") + + // This is needed because memorytopo doesn't implement locks using + // keys in the topo. So we simulate the behavior of other topo server + // implementations and how they implement TopoServer.LockShard(). + createSimulatedShardLock := func(ctx context.Context, ts *topo.Server, keyspace, shard string) error { + conn, err := ts.ConnForCell(ctx, topo.GlobalCell) + if err != nil { + return err + } + lockKey := fmt.Sprintf("keyspaces/%s/shards/%s/locks/1234", keyspace, shard) + _, err = conn.Create(ctx, lockKey, []byte("lock")) + return err + } + + tests := []struct { + shards int // Number of shards to create + err string // Error message we expect, if any + fallback bool // Should we fallback to the shard by shard method + }{ + { + shards: 0, + err: fmt.Sprintf("%s has no serving shards", keyspace), + }, + { + shards: 2, + }, + { + shards: 128, + }, + { + shards: 512, + fallback: true, + }, + { + shards: 1024, + }, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("%d shards with fallback = %t", tt.shards, tt.fallback), func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ts, factory := memorytopo.NewServerAndFactory(ctx) + defer ts.Close() + stats := factory.GetCallStats() + require.NotNil(t, stats) + + if tt.fallback { + factory.SetListError(errNoListImpl) + } + + err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}) + require.NoError(t, err) + var shardNames []string + if tt.shards > 0 { + shardNames, err = key.GenerateShardRanges(tt.shards) + require.NoError(t, err) + require.Equal(t, tt.shards, len(shardNames)) + for _, shardName := range shardNames { + err = ts.CreateShard(ctx, keyspace, shardName) + require.NoError(t, err) + } + // A shard lock typically becomes a key in the topo like this: + // /vitess/global/keyspaces//shards//locks/XXXX + // We want to confirm that this key is ignored when building + // the results. + err = createSimulatedShardLock(ctx, ts, keyspace, shardNames[0]) + require.NoError(t, err) + } + + // Verify that we return a complete list of shards and that each + // key range is present in the output. + stats.ResetAll() // We only want the stats for GetServingShards + shardInfos, err := ts.GetServingShards(ctx, keyspace) + if tt.err != "" { + require.EqualError(t, err, tt.err) + return + } + require.NoError(t, err) + require.Len(t, shardInfos, tt.shards) + for _, shardName := range shardNames { + f := func(si *topo.ShardInfo) bool { + return key.KeyRangeString(si.Shard.KeyRange) == shardName + } + require.True(t, slices.ContainsFunc(shardInfos, f), "shard %q was not found in the results", + shardName) + } + + // Now we check the stats based on the number of shards and whether or not + // we should have had a List error and fell back to the shard by shard method. + callcounts := stats.Counts() + require.NotNil(t, callcounts) + require.Equal(t, int64(1), callcounts["List"]) // We should always try + switch { + case tt.fallback: // We get the shards one by one from the list + require.Equal(t, int64(1), callcounts["ListDir"]) // GetShardNames + require.Equal(t, int64(tt.shards), callcounts["Get"]) // GetShard + case tt.shards < 1: // We use a Get to check that the keyspace exists + require.Equal(t, int64(0), callcounts["ListDir"]) + require.Equal(t, int64(1), callcounts["Get"]) + default: // We should not make any ListDir or Get calls + require.Equal(t, int64(0), callcounts["ListDir"]) + require.Equal(t, int64(0), callcounts["Get"]) + } + }) + } +} diff --git a/go/vt/topo/keyspace_test.go b/go/vt/topo/keyspace_test.go deleted file mode 100644 index 1abf873b8e0..00000000000 --- a/go/vt/topo/keyspace_test.go +++ /dev/null @@ -1,177 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package topo - -import ( - "reflect" - "testing" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// This file tests the keyspace related object functionalities. - -func TestUpdateServedFromMap(t *testing.T) { - // TODO(deepthi): delete this test once legacy resharding code is deleted - ki := &KeyspaceInfo{ - keyspace: "ks", - version: nil, - Keyspace: &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Cells: nil, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - }, - }, - } - allCells := []string{"first", "second", "third"} - - // migrate one cell - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, []string{"first"}, "source", true, allCells); err != nil || !reflect.DeepEqual(ki.ServedFroms, []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Cells: []string{"second", "third"}, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - }) { - t.Fatalf("one cell add failed: %v", ki) - } - - // re-add that cell, going back - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, []string{"first"}, "source", false, nil); err != nil || !reflect.DeepEqual(ki.ServedFroms, []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Cells: []string{"second", "third", "first"}, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - }) { - t.Fatalf("going back should have remove the record: %#v", ki.Keyspace.ServedFroms) - } - - // now remove the cell again - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, []string{"first"}, "source", true, allCells); err != nil || !reflect.DeepEqual(ki.ServedFroms, []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Cells: []string{"second", "third"}, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - }) { - t.Fatalf("one cell add failed: %v", ki) - } - - // couple error cases - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, []string{"second"}, "othersource", true, allCells); err == nil || (err.Error() != "inconsistent keyspace specified in migration: othersource != source for type MASTER" && err.Error() != "inconsistent keyspace specified in migration: othersource != source for type RDONLY") { - t.Fatalf("different keyspace should fail: %v", err) - } - if err := ki.UpdateServedFromMap(topodatapb.TabletType_PRIMARY, nil, "source", true, allCells); err == nil || err.Error() != "cannot migrate master into ks until everything else is migrated" { - t.Fatalf("migrate the master early should have failed: %v", err) - } - - // now remove all cells - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, []string{"second", "third"}, "source", true, allCells); err != nil || !reflect.DeepEqual(ki.ServedFroms, []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - }) { - t.Fatalf("remove all cells failed: %v", ki) - } - if err := ki.UpdateServedFromMap(topodatapb.TabletType_RDONLY, nil, "source", true, allCells); err == nil || err.Error() != "supplied type cannot be migrated" { - t.Fatalf("migrate rdonly again should have failed: %v", err) - } - - // finally migrate the primary - if err := ki.UpdateServedFromMap(topodatapb.TabletType_PRIMARY, []string{"second"}, "source", true, allCells); err == nil || err.Error() != "cannot migrate only some cells for master removal in keyspace ks" { - t.Fatalf("migrate master with cells should have failed: %v", err) - } - if err := ki.UpdateServedFromMap(topodatapb.TabletType_PRIMARY, nil, "source", true, allCells); err != nil || ki.ServedFroms != nil { - t.Fatalf("migrate the master failed: %v", ki) - } - - // error case again - if err := ki.UpdateServedFromMap(topodatapb.TabletType_PRIMARY, nil, "source", true, allCells); err == nil || err.Error() != "supplied type cannot be migrated" { - t.Fatalf("migrate the master again should have failed: %v", err) - } -} - -func TestComputeCellServedFrom(t *testing.T) { - ki := &KeyspaceInfo{ - keyspace: "ks", - version: nil, - Keyspace: &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_REPLICA, - Cells: []string{"c1", "c2"}, - Keyspace: "source", - }, - }, - }, - } - - m := ki.ComputeCellServedFrom("c3") - if !reflect.DeepEqual(m, []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: "source", - }, - }) { - t.Fatalf("c3 failed: %v", m) - } - - m = ki.ComputeCellServedFrom("c2") - if !reflect.DeepEqual(m, []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: "source", - }, - { - TabletType: topodatapb.TabletType_REPLICA, - Keyspace: "source", - }, - }) { - t.Fatalf("c2 failed: %v", m) - } -} diff --git a/go/vt/topo/locks.go b/go/vt/topo/locks.go index 8d30d85e891..6325124c429 100644 --- a/go/vt/topo/locks.go +++ b/go/vt/topo/locks.go @@ -127,21 +127,6 @@ var locksKey locksKeyType // - a context with a locksInfo structure for future reference. // - an unlock method // - an error if anything failed. -// -// We lock a keyspace for the following operations to be guaranteed -// exclusive operation: -// * changing a keyspace sharding info fields (is this one necessary?) -// * changing a keyspace 'ServedFrom' field (is this one necessary?) -// * resharding operations: -// - horizontal resharding: includes changing the shard's 'ServedType', -// as well as the associated horizontal resharding operations. -// - vertical resharding: includes changing the keyspace 'ServedFrom' -// field, as well as the associated vertical resharding operations. -// - 'vtctl SetShardIsPrimaryServing' emergency operations -// - 'vtctl SetShardTabletControl' emergency operations -// - 'vtctl SourceShardAdd' and 'vtctl SourceShardDelete' emergency operations -// -// * keyspace-wide schema changes func (ts *Server) LockKeyspace(ctx context.Context, keyspace, action string) (context.Context, func(*error), error) { i, ok := ctx.Value(locksKey).(*locksInfo) if !ok { diff --git a/go/vt/topo/memorytopo/directory.go b/go/vt/topo/memorytopo/directory.go index f68c87a2166..8e673f474a6 100644 --- a/go/vt/topo/memorytopo/directory.go +++ b/go/vt/topo/memorytopo/directory.go @@ -27,6 +27,8 @@ import ( // ListDir is part of the topo.Conn interface. func (c *Conn) ListDir(ctx context.Context, dirPath string, full bool) ([]topo.DirEntry, error) { + c.factory.callstats.Add([]string{"ListDir"}, 1) + if err := c.dial(ctx); err != nil { return nil, err } diff --git a/go/vt/topo/memorytopo/election.go b/go/vt/topo/memorytopo/election.go index 868a2c53287..52bbe2e93ce 100644 --- a/go/vt/topo/memorytopo/election.go +++ b/go/vt/topo/memorytopo/election.go @@ -24,8 +24,10 @@ import ( "vitess.io/vitess/go/vt/topo" ) -// NewLeaderParticipation is part of the topo.Server interface +// NewLeaderParticipation is part of the topo.Conn interface. func (c *Conn) NewLeaderParticipation(name, id string) (topo.LeaderParticipation, error) { + c.factory.callstats.Add([]string{"NewLeaderParticipation"}, 1) + if c.closed { return nil, ErrConnectionClosed } diff --git a/go/vt/topo/memorytopo/file.go b/go/vt/topo/memorytopo/file.go index 0007203799f..800e7791afa 100644 --- a/go/vt/topo/memorytopo/file.go +++ b/go/vt/topo/memorytopo/file.go @@ -30,6 +30,8 @@ import ( // Create is part of topo.Conn interface. func (c *Conn) Create(ctx context.Context, filePath string, contents []byte) (topo.Version, error) { + c.factory.callstats.Add([]string{"Create"}, 1) + if err := c.dial(ctx); err != nil { return nil, err } @@ -74,6 +76,8 @@ func (c *Conn) Create(ctx context.Context, filePath string, contents []byte) (to // Update is part of topo.Conn interface. func (c *Conn) Update(ctx context.Context, filePath string, contents []byte, version topo.Version) (topo.Version, error) { + c.factory.callstats.Add([]string{"Update"}, 1) + if err := c.dial(ctx); err != nil { return nil, err } @@ -152,6 +156,8 @@ func (c *Conn) Update(ctx context.Context, filePath string, contents []byte, ver // Get is part of topo.Conn interface. func (c *Conn) Get(ctx context.Context, filePath string) ([]byte, topo.Version, error) { + c.factory.callstats.Add([]string{"Get"}, 1) + if err := c.dial(ctx); err != nil { return nil, nil, err } @@ -177,6 +183,8 @@ func (c *Conn) Get(ctx context.Context, filePath string) ([]byte, topo.Version, // List is part of the topo.Conn interface. func (c *Conn) List(ctx context.Context, filePathPrefix string) ([]topo.KVInfo, error) { + c.factory.callstats.Add([]string{"List"}, 1) + if err := c.dial(ctx); err != nil { return nil, err } @@ -187,6 +195,9 @@ func (c *Conn) List(ctx context.Context, filePathPrefix string) ([]topo.KVInfo, if c.factory.err != nil { return nil, c.factory.err } + if c.factory.listErr != nil { + return nil, c.factory.listErr + } dir, file := path.Split(filePathPrefix) // Get the node to list. @@ -236,6 +247,8 @@ func gatherChildren(n *node, dirPath string) []topo.KVInfo { // Delete is part of topo.Conn interface. func (c *Conn) Delete(ctx context.Context, filePath string, version topo.Version) error { + c.factory.callstats.Add([]string{"Delete"}, 1) + if err := c.dial(ctx); err != nil { return err } diff --git a/go/vt/topo/memorytopo/lock.go b/go/vt/topo/memorytopo/lock.go index c15fb9099bb..0545ba8b182 100644 --- a/go/vt/topo/memorytopo/lock.go +++ b/go/vt/topo/memorytopo/lock.go @@ -42,11 +42,15 @@ type memoryTopoLockDescriptor struct { // TryLock is part of the topo.Conn interface. Its implementation is same as Lock func (c *Conn) TryLock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { + c.factory.callstats.Add([]string{"TryLock"}, 1) + return c.Lock(ctx, dirPath, contents) } // Lock is part of the topo.Conn interface. func (c *Conn) Lock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { + c.factory.callstats.Add([]string{"Lock"}, 1) + return c.lock(ctx, dirPath, contents) } diff --git a/go/vt/topo/memorytopo/memorytopo.go b/go/vt/topo/memorytopo/memorytopo.go index f24b2f6c89e..55057b62468 100644 --- a/go/vt/topo/memorytopo/memorytopo.go +++ b/go/vt/topo/memorytopo/memorytopo.go @@ -26,6 +26,7 @@ import ( "strings" "sync" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo" @@ -70,6 +71,12 @@ type Factory struct { // err is used for testing purposes to force queries / watches // to return the given error err error + // listErr is used for testing purposed to fake errors from + // calls to List. + listErr error + // callstats allows us to keep track of how many topo.Conn calls + // we make (Create, Get, Update, Delete, List, ListDir, etc). + callstats *stats.CountersWithMultiLabels } // HasGlobalReadOnlyCell is part of the topo.Factory interface. @@ -105,6 +112,10 @@ func (f *Factory) SetError(err error) { } } +func (f *Factory) GetCallStats() *stats.CountersWithMultiLabels { + return f.callstats +} + // Lock blocks all requests to the topo and is exposed to allow tests to // simulate an unresponsive topo server func (f *Factory) Lock() { @@ -141,6 +152,7 @@ func (c *Conn) dial(ctx context.Context) error { // Close is part of the topo.Conn interface. func (c *Conn) Close() { + c.factory.callstats.Add([]string{"Close"}, 1) c.closed = true } @@ -237,6 +249,7 @@ func NewServerAndFactory(ctx context.Context, cells ...string) (*topo.Server, *F f := &Factory{ cells: make(map[string]*node), generation: uint64(rand.Int63n(1 << 60)), + callstats: stats.NewCountersWithMultiLabels("", "", []string{"Call"}), } f.cells[topo.GlobalCell] = f.newDirectory(topo.GlobalCell, nil) @@ -348,3 +361,10 @@ func (f *Factory) recursiveDelete(n *node) { f.recursiveDelete(parent) } } + +func (f *Factory) SetListError(err error) { + f.mu.Lock() + defer f.mu.Unlock() + + f.listErr = err +} diff --git a/go/vt/topo/memorytopo/watch.go b/go/vt/topo/memorytopo/watch.go index 73b2d248434..8d9ef5cb54c 100644 --- a/go/vt/topo/memorytopo/watch.go +++ b/go/vt/topo/memorytopo/watch.go @@ -25,6 +25,8 @@ import ( // Watch is part of the topo.Conn interface. func (c *Conn) Watch(ctx context.Context, filePath string) (*topo.WatchData, <-chan *topo.WatchData, error) { + c.factory.callstats.Add([]string{"Watch"}, 1) + if c.closed { return nil, nil, ErrConnectionClosed } @@ -75,6 +77,8 @@ func (c *Conn) Watch(ctx context.Context, filePath string) (*topo.WatchData, <-c // WatchRecursive is part of the topo.Conn interface. func (c *Conn) WatchRecursive(ctx context.Context, dirpath string) ([]*topo.WatchDataRecursive, <-chan *topo.WatchDataRecursive, error) { + c.factory.callstats.Add([]string{"WatchRecursive"}, 1) + if c.closed { return nil, nil, ErrConnectionClosed } diff --git a/go/vt/topo/shard.go b/go/vt/topo/shard.go index 183ed409bbb..7343db2c0ab 100644 --- a/go/vt/topo/shard.go +++ b/go/vt/topo/shard.go @@ -314,7 +314,14 @@ func (ts *Server) CreateShard(ctx context.Context, keyspace, shard string) (err // Set primary as serving only if its keyrange doesn't overlap // with other shards. This applies to unsharded keyspaces also value.IsPrimaryServing = true - sis, err := ts.FindAllShardsInKeyspace(ctx, keyspace) + sis, err := ts.FindAllShardsInKeyspace(ctx, keyspace, &FindAllShardsInKeyspaceOptions{ + // Assume that CreateShard may be called by many vttablets concurrently + // in a large, sharded keyspace. Do not apply concurrency to avoid + // overwhelming the toposerver. + // + // See: https://github.com/vitessio/vitess/pull/5436. + Concurrency: 1, + }) if err != nil && !IsErrType(err, NoNode) { return err } @@ -659,7 +666,7 @@ func (ts *Server) GetTabletMapForShardByCell(ctx context.Context, keyspace, shar // get the tablets for the cells we were able to reach, forward // ErrPartialResult from FindAllTabletAliasesInShard - result, gerr := ts.GetTabletMap(ctx, aliases) + result, gerr := ts.GetTabletMap(ctx, aliases, nil) if gerr == nil && err != nil { gerr = err } diff --git a/go/vt/topo/srv_keyspace.go b/go/vt/topo/srv_keyspace.go index 8054764eeff..11e49f3c569 100644 --- a/go/vt/topo/srv_keyspace.go +++ b/go/vt/topo/srv_keyspace.go @@ -137,7 +137,7 @@ func (ts *Server) GetShardServingCells(ctx context.Context, si *ShardInfo) (serv var mu sync.Mutex for _, cell := range cells { wg.Add(1) - go func(cell, keyspace string) { + go func(cell string) { defer wg.Done() srvKeyspace, err := ts.GetSrvKeyspace(ctx, cell, si.keyspace) switch { @@ -166,7 +166,7 @@ func (ts *Server) GetShardServingCells(ctx context.Context, si *ShardInfo) (serv rec.RecordError(err) return } - }(cell, si.Keyspace()) + }(cell) } wg.Wait() if rec.HasErrors() { @@ -188,7 +188,7 @@ func (ts *Server) GetShardServingTypes(ctx context.Context, si *ShardInfo) (serv var mu sync.Mutex for _, cell := range cells { wg.Add(1) - go func(cell, keyspace string) { + go func(cell string) { defer wg.Done() srvKeyspace, err := ts.GetSrvKeyspace(ctx, cell, si.keyspace) switch { @@ -223,7 +223,7 @@ func (ts *Server) GetShardServingTypes(ctx context.Context, si *ShardInfo) (serv rec.RecordError(err) return } - }(cell, si.Keyspace()) + }(cell) } wg.Wait() if rec.HasErrors() { @@ -613,10 +613,8 @@ func (ts *Server) MigrateServedType(ctx context.Context, keyspace string, shards case IsErrType(err, NoNode): // Assuming this cell is not active, nothing to do. default: - if err != nil { - rec.RecordError(err) - return - } + rec.RecordError(err) + return } }(cell, keyspace) } diff --git a/go/vt/topo/tablet.go b/go/vt/topo/tablet.go index d17235f6948..f412233b43a 100644 --- a/go/vt/topo/tablet.go +++ b/go/vt/topo/tablet.go @@ -24,6 +24,8 @@ import ( "sync" "time" + "golang.org/x/sync/semaphore" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/vt/key" @@ -227,7 +229,7 @@ func NewTabletInfo(tablet *topodatapb.Tablet, version Version) *TabletInfo { func (ts *Server) GetTablet(ctx context.Context, alias *topodatapb.TabletAlias) (*TabletInfo, error) { conn, err := ts.ConnForCell(ctx, alias.Cell) if err != nil { - log.Errorf("Unable to get connection for cell %s", alias.Cell) + log.Errorf("unable to get connection for cell %q: %v", alias.Cell, err) return nil, err } @@ -238,7 +240,7 @@ func (ts *Server) GetTablet(ctx context.Context, alias *topodatapb.TabletAlias) tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(alias), TabletFile) data, version, err := conn.Get(ctx, tabletPath) if err != nil { - log.Errorf("unable to connect to tablet %s: %s", alias, err) + log.Errorf("unable to connect to tablet %q: %v", alias, err) return nil, err } tablet := &topodatapb.Tablet{} @@ -282,10 +284,17 @@ func (ts *Server) GetTabletAliasesByCell(ctx context.Context, cell string) ([]*t return result, nil } +// GetTabletsByCellOptions controls the behavior of +// Server.FindAllShardsInKeyspace. +type GetTabletsByCellOptions struct { + // Concurrency controls the maximum number of concurrent calls to GetTablet. + Concurrency int +} + // GetTabletsByCell returns all the tablets in the cell. // It returns ErrNoNode if the cell doesn't exist. // It returns (nil, nil) if the cell exists, but there are no tablets in it. -func (ts *Server) GetTabletsByCell(ctx context.Context, cellAlias string) ([]*TabletInfo, error) { +func (ts *Server) GetTabletsByCell(ctx context.Context, cellAlias string, opt *GetTabletsByCellOptions) ([]*TabletInfo, error) { // If the cell doesn't exist, this will return ErrNoNode. cellConn, err := ts.ConnForCell(ctx, cellAlias) if err != nil { @@ -293,10 +302,12 @@ func (ts *Server) GetTabletsByCell(ctx context.Context, cellAlias string) ([]*Ta } listResults, err := cellConn.List(ctx, TabletsPath) if err != nil || len(listResults) == 0 { - // Currently the ZooKeeper and Memory topo implementations do not support scans + // Currently the ZooKeeper implementation does not support scans // so we fall back to the more costly method of fetching the tablets one by one. - if IsErrType(err, NoImplementation) { - return ts.GetTabletsIndividuallyByCell(ctx, cellAlias) + // In the etcd case, it is possible that the response is too large. We also fall + // back to fetching the tablets one by one in that case. + if IsErrType(err, NoImplementation) || IsErrType(err, ResourceExhausted) { + return ts.GetTabletsIndividuallyByCell(ctx, cellAlias, opt) } if IsErrType(err, NoNode) { return nil, nil @@ -320,7 +331,7 @@ func (ts *Server) GetTabletsByCell(ctx context.Context, cellAlias string) ([]*Ta // directly support the topoConn.List() functionality. // It returns ErrNoNode if the cell doesn't exist. // It returns (nil, nil) if the cell exists, but there are no tablets in it. -func (ts *Server) GetTabletsIndividuallyByCell(ctx context.Context, cell string) ([]*TabletInfo, error) { +func (ts *Server) GetTabletsIndividuallyByCell(ctx context.Context, cell string, opt *GetTabletsByCellOptions) ([]*TabletInfo, error) { // If the cell doesn't exist, this will return ErrNoNode. aliases, err := ts.GetTabletAliasesByCell(ctx, cell) if err != nil { @@ -328,7 +339,7 @@ func (ts *Server) GetTabletsIndividuallyByCell(ctx context.Context, cell string) } sort.Sort(topoproto.TabletAliasList(aliases)) - tabletMap, err := ts.GetTabletMap(ctx, aliases) + tabletMap, err := ts.GetTabletMap(ctx, aliases, opt) if err != nil { // we got another error than topo.ErrNoNode return nil, err @@ -443,21 +454,20 @@ func (ts *Server) CreateTablet(ctx context.Context, tablet *topodatapb.Tablet) e return err } tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(tablet.Alias), TabletFile) - if _, err = conn.Create(ctx, tabletPath, data); err != nil { + if _, err := conn.Create(ctx, tabletPath, data); err != nil { return err } - if updateErr := UpdateTabletReplicationData(ctx, ts, tablet); updateErr != nil { - return updateErr + if err := UpdateTabletReplicationData(ctx, ts, tablet); err != nil { + return err } - if err == nil { - event.Dispatch(&events.TabletChange{ - Tablet: tablet, - Status: "created", - }) - } - return err + event.Dispatch(&events.TabletChange{ + Tablet: tablet, + Status: "created", + }) + + return nil } // DeleteTablet wraps the underlying conn.Delete @@ -503,41 +513,61 @@ func DeleteTabletReplicationData(ctx context.Context, ts *Server, tablet *topoda } // GetTabletMap tries to read all the tablets in the provided list, -// and returns them all in a map. -// If error is ErrPartialResult, the results in the dictionary are +// and returns them in a map. +// If error is ErrPartialResult, the results in the map are // incomplete, meaning some tablets couldn't be read. // The map is indexed by topoproto.TabletAliasString(tablet alias). -func (ts *Server) GetTabletMap(ctx context.Context, tabletAliases []*topodatapb.TabletAlias) (map[string]*TabletInfo, error) { +func (ts *Server) GetTabletMap(ctx context.Context, tabletAliases []*topodatapb.TabletAlias, opt *GetTabletsByCellOptions) (map[string]*TabletInfo, error) { span, ctx := trace.NewSpan(ctx, "topo.GetTabletMap") span.Annotate("num_tablets", len(tabletAliases)) defer span.Finish() - wg := sync.WaitGroup{} - mutex := sync.Mutex{} + var ( + mu sync.Mutex + wg sync.WaitGroup + tabletMap = make(map[string]*TabletInfo) + returnErr error + ) - tabletMap := make(map[string]*TabletInfo) - var someError error + concurrency := DefaultConcurrency + if opt != nil && opt.Concurrency > 0 { + concurrency = opt.Concurrency + } + var sem = semaphore.NewWeighted(int64(concurrency)) for _, tabletAlias := range tabletAliases { wg.Add(1) go func(tabletAlias *topodatapb.TabletAlias) { defer wg.Done() + if err := sem.Acquire(ctx, 1); err != nil { + // Only happens if context is cancelled. + mu.Lock() + defer mu.Unlock() + log.Warningf("%v: %v", tabletAlias, err) + // We only need to set this on the first error. + if returnErr == nil { + returnErr = NewError(PartialResult, tabletAlias.GetCell()) + } + return + } tabletInfo, err := ts.GetTablet(ctx, tabletAlias) - mutex.Lock() + sem.Release(1) + mu.Lock() + defer mu.Unlock() if err != nil { log.Warningf("%v: %v", tabletAlias, err) // There can be data races removing nodes - ignore them for now. - if !IsErrType(err, NoNode) { - someError = NewError(PartialResult, "") + // We only need to set this on first error. + if returnErr == nil && !IsErrType(err, NoNode) { + returnErr = NewError(PartialResult, tabletAlias.GetCell()) } } else { tabletMap[topoproto.TabletAliasString(tabletAlias)] = tabletInfo } - mutex.Unlock() }(tabletAlias) } wg.Wait() - return tabletMap, someError + return tabletMap, returnErr } // InitTablet creates or updates a tablet. If no parent is specified diff --git a/go/vt/topo/tablet_test.go b/go/vt/topo/tablet_test.go new file mode 100644 index 00000000000..04eea71a8a2 --- /dev/null +++ b/go/vt/topo/tablet_test.go @@ -0,0 +1,118 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package topo_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" +) + +// Test various cases of calls to GetTabletsByCell. +// GetTabletsByCell first tries to get all the tablets using List. +// If the response is too large, we will get an error, and fall back to one tablet at a time. +func TestServerGetTabletsByCell(t *testing.T) { + tests := []struct { + name string + tablets int + opt *topo.GetTabletsByCellOptions + listError error + }{ + { + name: "negative concurrency", + tablets: 1, + // Ensure this doesn't panic. + opt: &topo.GetTabletsByCellOptions{Concurrency: -1}, + }, + { + name: "single", + tablets: 1, + // Make sure the defaults apply as expected. + opt: nil, + }, + { + name: "multiple", + // should work with more than 1 tablet + tablets: 32, + opt: &topo.GetTabletsByCellOptions{Concurrency: 8}, + }, + { + name: "multiple with list error", + // should work with more than 1 tablet when List returns an error + tablets: 32, + opt: &topo.GetTabletsByCellOptions{Concurrency: 8}, + listError: topo.NewError(topo.ResourceExhausted, ""), + }, + } + + const cell = "zone1" + const keyspace = "keyspace" + const shard = "shard" + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ts, factory := memorytopo.NewServerAndFactory(ctx, cell) + defer ts.Close() + if tt.listError != nil { + factory.SetListError(tt.listError) + } + + // Create an ephemeral keyspace and generate shard records within + // the keyspace to fetch later. + require.NoError(t, ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{})) + require.NoError(t, ts.CreateShard(ctx, keyspace, shard)) + + tablets := make([]*topo.TabletInfo, tt.tablets) + + for i := 0; i < tt.tablets; i++ { + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: cell, + Uid: uint32(i), + }, + Hostname: "host1", + PortMap: map[string]int32{ + "vt": int32(i), + }, + Keyspace: keyspace, + Shard: shard, + } + tInfo := &topo.TabletInfo{Tablet: tablet} + tablets[i] = tInfo + require.NoError(t, ts.CreateTablet(ctx, tablet)) + } + + // Verify that we return a complete list of tablets and that each + // tablet matches what we expect. + out, err := ts.GetTabletsByCell(ctx, cell, tt.opt) + require.NoError(t, err) + require.Len(t, out, tt.tablets) + + for i, tab := range tablets { + require.Equal(t, tab.Tablet, tablets[i].Tablet) + } + }) + } +} diff --git a/go/vt/topo/test/keyspace.go b/go/vt/topo/test/keyspace.go index 0458e7fd2d7..9c7b99d016e 100644 --- a/go/vt/topo/test/keyspace.go +++ b/go/vt/topo/test/keyspace.go @@ -61,20 +61,7 @@ func checkKeyspace(t *testing.T, ctx context.Context, ts *topo.Server) { t.Errorf("GetKeyspaces: want %v, got %v", []string{"test_keyspace"}, keyspaces) } - k := &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_REPLICA, - Cells: []string{"c1", "c2"}, - Keyspace: "test_keyspace3", - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Cells: nil, - Keyspace: "test_keyspace3", - }, - }, - } + k := &topodatapb.Keyspace{} if err := ts.CreateKeyspace(ctx, "test_keyspace2", k); err != nil { t.Errorf("CreateKeyspace: %v", err) } diff --git a/go/vt/topo/test/serving.go b/go/vt/topo/test/serving.go index dd00f3da370..62cbca99a99 100644 --- a/go/vt/topo/test/serving.go +++ b/go/vt/topo/test/serving.go @@ -50,12 +50,6 @@ func checkSrvKeyspace(t *testing.T, ctx context.Context, ts *topo.Server) { }, }, }, - ServedFrom: []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_REPLICA, - Keyspace: "other_keyspace", - }, - }, } if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { t.Errorf("UpdateSrvKeyspace(1): %v", err) diff --git a/go/vt/topo/test/shard.go b/go/vt/topo/test/shard.go index b5c92c4a3ec..270b236b98d 100644 --- a/go/vt/topo/test/shard.go +++ b/go/vt/topo/test/shard.go @@ -22,7 +22,6 @@ import ( "time" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" "vitess.io/vitess/go/vt/topo" @@ -82,13 +81,23 @@ func checkShard(t *testing.T, ctx context.Context, ts *topo.Server) { t.Fatalf("shard.PrimaryAlias = %v, want %v", si.Shard.PrimaryAlias, other) } + // Test FindAllShardsInKeyspace. + require.NoError(t, err) + _, err = ts.FindAllShardsInKeyspace(ctx, "test_keyspace", nil) + require.NoError(t, err) + + // Test GetServingShards. + require.NoError(t, err) + _, err = ts.GetServingShards(ctx, "test_keyspace") + require.NoError(t, err) + // test GetShardNames - shards, err := ts.GetShardNames(ctx, "test_keyspace") + shardNames, err := ts.GetShardNames(ctx, "test_keyspace") if err != nil { t.Errorf("GetShardNames: %v", err) } - if len(shards) != 1 || shards[0] != "b0-c0" { - t.Errorf(`GetShardNames: want [ "b0-c0" ], got %v`, shards) + if len(shardNames) != 1 || shardNames[0] != "b0-c0" { + t.Errorf(`GetShardNames: want [ "b0-c0" ], got %v`, shardNames) } if _, err := ts.GetShardNames(ctx, "test_keyspace666"); !topo.IsErrType(err, topo.NoNode) { diff --git a/go/vt/topo/test/trylock.go b/go/vt/topo/test/trylock.go index 4519d1bcaab..c553e74bb61 100644 --- a/go/vt/topo/test/trylock.go +++ b/go/vt/topo/test/trylock.go @@ -138,7 +138,7 @@ func checkTryLockTimeout(ctx context.Context, t *testing.T, conn topo.Conn) { // test we can't unlock again if err := lockDescriptor.Unlock(ctx); err == nil { - require.Fail(t, "Unlock failed", err.Error()) + require.Fail(t, "Unlock succeeded but should not have") } } diff --git a/go/vt/topo/topotests/cell_info_test.go b/go/vt/topo/topotests/cell_info_test.go index becdbd8d14a..5d039ffbe6f 100644 --- a/go/vt/topo/topotests/cell_info_test.go +++ b/go/vt/topo/topotests/cell_info_test.go @@ -217,7 +217,6 @@ func TestExpandCells(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { expanded, err := ts.ExpandCells(ctx, tt.in) if tt.shouldErr { diff --git a/go/vt/topo/topotests/shard_watch_test.go b/go/vt/topo/topotests/shard_watch_test.go index 80b696c106d..f4ac83c627a 100644 --- a/go/vt/topo/topotests/shard_watch_test.go +++ b/go/vt/topo/topotests/shard_watch_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/vt/topo/zk2topo/error.go b/go/vt/topo/zk2topo/error.go index 1ebc3896f40..1149ad60bf3 100644 --- a/go/vt/topo/zk2topo/error.go +++ b/go/vt/topo/zk2topo/error.go @@ -18,6 +18,7 @@ package zk2topo import ( "context" + "errors" "github.com/z-division/go-zookeeper/zk" @@ -26,20 +27,20 @@ import ( // Error codes returned by the zookeeper Go client: func convertError(err error, node string) error { - switch err { - case zk.ErrBadVersion: + switch { + case errors.Is(err, zk.ErrBadVersion): return topo.NewError(topo.BadVersion, node) - case zk.ErrNoNode: + case errors.Is(err, zk.ErrNoNode): return topo.NewError(topo.NoNode, node) - case zk.ErrNodeExists: + case errors.Is(err, zk.ErrNodeExists): return topo.NewError(topo.NodeExists, node) - case zk.ErrNotEmpty: + case errors.Is(err, zk.ErrNotEmpty): return topo.NewError(topo.NodeNotEmpty, node) - case zk.ErrSessionExpired: + case errors.Is(err, zk.ErrSessionExpired): return topo.NewError(topo.Timeout, node) - case context.Canceled: + case errors.Is(err, context.Canceled): return topo.NewError(topo.Interrupted, node) - case context.DeadlineExceeded: + case errors.Is(err, context.DeadlineExceeded): return topo.NewError(topo.Timeout, node) } return err diff --git a/go/vt/topotools/events/reparent_syslog.go b/go/vt/topotools/events/reparent_syslog.go index dae22467d1f..dd995f34d73 100644 --- a/go/vt/topotools/events/reparent_syslog.go +++ b/go/vt/topotools/events/reparent_syslog.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topotools/events/reparent_syslog_test.go b/go/vt/topotools/events/reparent_syslog_test.go index 93a9b860fe2..f4ba39f602b 100644 --- a/go/vt/topotools/events/reparent_syslog_test.go +++ b/go/vt/topotools/events/reparent_syslog_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/topotools/keyspace.go b/go/vt/topotools/keyspace.go index d8a5740f3ae..cab326397ff 100644 --- a/go/vt/topotools/keyspace.go +++ b/go/vt/topotools/keyspace.go @@ -141,22 +141,12 @@ func UpdateShardRecords( return nil } -// KeyspaceEquality returns true iff two KeyspaceInformations are identical for testing purposes +// KeyspaceEquality returns true iff two Keyspace fields are identical for testing purposes. func KeyspaceEquality(left, right *topodatapb.Keyspace) bool { if left.KeyspaceType != right.KeyspaceType { return false } - if len(left.ServedFroms) != len(right.ServedFroms) { - return false - } - for i := range left.ServedFroms { - if left.ServedFroms[i] != right.ServedFroms[i] { - return false - } - } - if left.KeyspaceType != right.KeyspaceType { - return false - } + if left.BaseKeyspace != right.BaseKeyspace { return false } @@ -165,5 +155,9 @@ func KeyspaceEquality(left, right *topodatapb.Keyspace) bool { return false } + if left.SidecarDbName != right.SidecarDbName { + return false + } + return left.DurabilityPolicy == right.DurabilityPolicy } diff --git a/go/vt/topotools/rebuild_keyspace.go b/go/vt/topotools/rebuild_keyspace.go index d58ce0b7160..72e060b79e2 100644 --- a/go/vt/topotools/rebuild_keyspace.go +++ b/go/vt/topotools/rebuild_keyspace.go @@ -64,7 +64,12 @@ func RebuildKeyspaceLocked(ctx context.Context, log logutil.Logger, ts *topo.Ser } } - shards, err := ts.FindAllShardsInKeyspace(ctx, keyspace) + shards, err := ts.FindAllShardsInKeyspace(ctx, keyspace, &topo.FindAllShardsInKeyspaceOptions{ + // Fetch shard records concurrently to speed up the rebuild process. + // This call is invoked by the first tablet in a given keyspace or + // manually via vtctld, so there is little risk of a thundering herd. + Concurrency: 8, + }) if err != nil { return err } @@ -94,9 +99,8 @@ func RebuildKeyspaceLocked(ctx context.Context, log logutil.Logger, ts *topo.Ser return err } srvKeyspaceMap[cell] = &topodatapb.SrvKeyspace{ - ServedFrom: ki.ComputeCellServedFrom(cell), + ThrottlerConfig: ki.ThrottlerConfig, } - srvKeyspaceMap[cell].ThrottlerConfig = ki.ThrottlerConfig } servedTypes := []topodatapb.TabletType{topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY} diff --git a/go/vt/topotools/split.go b/go/vt/topotools/split.go index ace3dda94a7..9da6b99878f 100644 --- a/go/vt/topotools/split.go +++ b/go/vt/topotools/split.go @@ -19,9 +19,6 @@ package topotools import ( "errors" "fmt" - "sort" - - "context" "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -77,185 +74,3 @@ func combineKeyRanges(shards []*topo.ShardInfo) (*topodatapb.KeyRange, error) { } return result, nil } - -// OverlappingShards contains sets of shards that overlap which each-other. -// With this library, there is no guarantee of which set will be left or right. -type OverlappingShards struct { - Left []*topo.ShardInfo - Right []*topo.ShardInfo -} - -// ContainsShard returns true if either Left or Right lists contain -// the provided Shard. -func (os *OverlappingShards) ContainsShard(shardName string) bool { - for _, l := range os.Left { - if l.ShardName() == shardName { - return true - } - } - for _, r := range os.Right { - if r.ShardName() == shardName { - return true - } - } - return false -} - -// OverlappingShardsForShard returns the OverlappingShards object -// from the list that has he provided shard, or nil -func OverlappingShardsForShard(os []*OverlappingShards, shardName string) *OverlappingShards { - for _, o := range os { - if o.ContainsShard(shardName) { - return o - } - } - return nil -} - -// FindOverlappingShards will return an array of OverlappingShards -// for the provided keyspace. -// We do not support more than two overlapping shards (for instance, -// having 40-80, 40-60 and 40-50 in the same keyspace is not supported and -// will return an error). -// If shards don't perfectly overlap, they are not returned. -func FindOverlappingShards(ctx context.Context, ts *topo.Server, keyspace string) ([]*OverlappingShards, error) { - shardMap, err := ts.FindAllShardsInKeyspace(ctx, keyspace) - if err != nil { - return nil, err - } - - return findOverlappingShards(shardMap) -} - -// findOverlappingShards does the work for FindOverlappingShards but -// can be called on test data too. -func findOverlappingShards(shardMap map[string]*topo.ShardInfo) ([]*OverlappingShards, error) { - - var result []*OverlappingShards - - for len(shardMap) > 0 { - var left []*topo.ShardInfo - var right []*topo.ShardInfo - - // get the first value from the map, seed our left array with it - var name string - var si *topo.ShardInfo - for name, si = range shardMap { - break - } - left = append(left, si) - delete(shardMap, name) - - // keep adding entries until we have no more to add - for { - foundOne := false - - // try left to right - si := findIntersectingShard(shardMap, left) - if si != nil { - if intersect(si, right) { - return nil, fmt.Errorf("shard %v intersects with more than one shard, this is not supported", si.ShardName()) - } - foundOne = true - right = append(right, si) - } - - // try right to left - si = findIntersectingShard(shardMap, right) - if si != nil { - if intersect(si, left) { - return nil, fmt.Errorf("shard %v intersects with more than one shard, this is not supported", si.ShardName()) - } - foundOne = true - left = append(left, si) - } - - // we haven't found anything new, we're done - if !foundOne { - break - } - } - - // save what we found if it's good - if len(right) > 0 { - // sort both lists - sort.Sort(shardInfoList(left)) - sort.Sort(shardInfoList(right)) - - // we should not have holes on either side - hasHoles := false - for i := 0; i < len(left)-1; i++ { - if string(left[i].KeyRange.End) != string(left[i+1].KeyRange.Start) { - hasHoles = true - } - } - for i := 0; i < len(right)-1; i++ { - if string(right[i].KeyRange.End) != string(right[i+1].KeyRange.Start) { - hasHoles = true - } - } - if hasHoles { - continue - } - - // the two sides should match - if !key.KeyRangeStartEqual(left[0].KeyRange, right[0].KeyRange) { - continue - } - if !key.KeyRangeEndEqual(left[len(left)-1].KeyRange, right[len(right)-1].KeyRange) { - continue - } - - // all good, we have a valid overlap - result = append(result, &OverlappingShards{ - Left: left, - Right: right, - }) - } - } - return result, nil -} - -// findIntersectingShard will go through the map and take the first -// entry in there that intersect with the source array, remove it from -// the map, and return it -func findIntersectingShard(shardMap map[string]*topo.ShardInfo, sourceArray []*topo.ShardInfo) *topo.ShardInfo { - for name, si := range shardMap { - for _, sourceShardInfo := range sourceArray { - if si.KeyRange == nil || sourceShardInfo.KeyRange == nil || key.KeyRangeIntersect(si.KeyRange, sourceShardInfo.KeyRange) { - delete(shardMap, name) - return si - } - } - } - return nil -} - -// intersect returns true if the provided shard intersect with any shard -// in the destination array -func intersect(si *topo.ShardInfo, allShards []*topo.ShardInfo) bool { - for _, shard := range allShards { - if key.KeyRangeIntersect(si.KeyRange, shard.KeyRange) { - return true - } - } - return false -} - -// shardInfoList is a helper type to sort ShardInfo array by keyrange -type shardInfoList []*topo.ShardInfo - -// Len is part of sort.Interface -func (sil shardInfoList) Len() int { - return len(sil) -} - -// Less is part of sort.Interface -func (sil shardInfoList) Less(i, j int) bool { - return string(sil[i].KeyRange.Start) < string(sil[j].KeyRange.Start) -} - -// Swap is part of sort.Interface -func (sil shardInfoList) Swap(i, j int) { - sil[i], sil[j] = sil[j], sil[i] -} diff --git a/go/vt/topotools/split_test.go b/go/vt/topotools/split_test.go index 003dc767317..6e93ee345d3 100644 --- a/go/vt/topotools/split_test.go +++ b/go/vt/topotools/split_test.go @@ -17,7 +17,6 @@ limitations under the License. package topotools import ( - "encoding/hex" "testing" "github.com/stretchr/testify/assert" @@ -27,75 +26,6 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) -// helper methods for tests to be shorter - -func hki(hexValue string) []byte { - k, err := hex.DecodeString(hexValue) - if err != nil { - panic(err) - } - return k -} - -func si(start, end string) *topo.ShardInfo { - s := hki(start) - e := hki(end) - return topo.NewShardInfo("keyspace", start+"-"+end, &topodatapb.Shard{ - KeyRange: &topodatapb.KeyRange{ - Start: s, - End: e, - }, - }, nil) -} - -type expectedOverlappingShard struct { - left []string - right []string -} - -func overlappingShardMatch(ol []*topo.ShardInfo, or []*topo.ShardInfo, e expectedOverlappingShard) bool { - if len(ol)+1 != len(e.left) { - return false - } - if len(or)+1 != len(e.right) { - return false - } - for i, l := range ol { - if l.ShardName() != e.left[i]+"-"+e.left[i+1] { - return false - } - } - for i, r := range or { - if r.ShardName() != e.right[i]+"-"+e.right[i+1] { - return false - } - } - return true -} - -func compareResultLists(t *testing.T, os []*OverlappingShards, expected []expectedOverlappingShard) { - if len(os) != len(expected) { - t.Errorf("Unexpected result length, got %v, want %v", len(os), len(expected)) - return - } - - for _, o := range os { - found := false - for _, e := range expected { - if overlappingShardMatch(o.Left, o.Right, e) { - found = true - } - if overlappingShardMatch(o.Right, o.Left, e) { - found = true - } - } - if !found { - t.Errorf("OverlappingShard %v not found in expected %v", o, expected) - return - } - } -} - func TestValidateForReshard(t *testing.T) { testcases := []struct { sources []string @@ -169,191 +99,3 @@ func TestValidateForReshard(t *testing.T) { } } } - -func TestFindOverlappingShardsNoOverlap(t *testing.T) { - var shardMap map[string]*topo.ShardInfo - var os []*OverlappingShards - var err error - - // no shards - shardMap = map[string]*topo.ShardInfo{} - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("empty shard map: %v %v", os, err) - } - - // just one shard, full keyrange - shardMap = map[string]*topo.ShardInfo{ - "0": {}, - } - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("just one shard, full keyrange: %v %v", os, err) - } - - // just one shard, partial keyrange - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("just one shard, partial keyrange: %v %v", os, err) - } - - // two non-overlapping shards - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - "80": si("80", ""), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("two non-overlapping shards: %v %v", os, err) - } - - // shards with holes - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - "80": si("80", ""), - "-20": si("", "20"), - // HOLE: "20-40": si("20", "40"), - "40-60": si("40", "60"), - "60-80": si("60", "80"), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("shards with holes: %v %v", os, err) - } - - // shards not overlapping - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - "80": si("80", ""), - // MISSING: "-20": si("", "20"), - "20-40": si("20", "40"), - "40-60": si("40", "60"), - "60-80": si("60", "80"), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 0 || err != nil { - t.Errorf("shards not overlapping: %v %v", os, err) - } -} - -func TestFindOverlappingShardsOverlap(t *testing.T) { - var shardMap map[string]*topo.ShardInfo - var os []*OverlappingShards - var err error - - // split in progress - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - "80": si("80", ""), - "-40": si("", "40"), - "40-80": si("40", "80"), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 1 || err != nil { - t.Errorf("split in progress: %v %v", os, err) - } - compareResultLists(t, os, []expectedOverlappingShard{ - { - left: []string{"", "80"}, - right: []string{"", "40", "80"}, - }, - }) - - // 1 to 4 split - shardMap = map[string]*topo.ShardInfo{ - "-": si("", ""), - "-40": si("", "40"), - "40-80": si("40", "80"), - "80-c0": si("80", "c0"), - "c0-": si("c0", ""), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 1 || err != nil { - t.Errorf("1 to 4 split: %v %v", os, err) - } - compareResultLists(t, os, []expectedOverlappingShard{ - { - left: []string{"", ""}, - right: []string{"", "40", "80", "c0", ""}, - }, - }) - - // 2 to 3 split - shardMap = map[string]*topo.ShardInfo{ - "-40": si("", "40"), - "40-80": si("40", "80"), - "80-": si("80", ""), - "-30": si("", "30"), - "30-60": si("30", "60"), - "60-80": si("60", "80"), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 1 || err != nil { - t.Errorf("2 to 3 split: %v %v", os, err) - } - compareResultLists(t, os, []expectedOverlappingShard{ - { - left: []string{"", "40", "80"}, - right: []string{"", "30", "60", "80"}, - }, - }) - - // multiple concurrent splits - shardMap = map[string]*topo.ShardInfo{ - "-80": si("", "80"), - "80-": si("80", ""), - "-40": si("", "40"), - "40-80": si("40", "80"), - "80-c0": si("80", "c0"), - "c0-": si("c0", ""), - } - os, err = findOverlappingShards(shardMap) - if len(os) != 2 || err != nil { - t.Errorf("2 to 3 split: %v %v", os, err) - } - compareResultLists(t, os, []expectedOverlappingShard{ - { - left: []string{"", "80"}, - right: []string{"", "40", "80"}, - }, - { - left: []string{"80", ""}, - right: []string{"80", "c0", ""}, - }, - }) - - // find a shard in there - if o := OverlappingShardsForShard(os, "-60"); o != nil { - t.Errorf("Found a shard where I shouldn't have!") - } - if o := OverlappingShardsForShard(os, "-40"); o == nil { - t.Errorf("Found no shard where I should have!") - } else { - compareResultLists(t, []*OverlappingShards{o}, - []expectedOverlappingShard{ - { - left: []string{"", "80"}, - right: []string{"", "40", "80"}, - }, - }) - } -} - -func TestFindOverlappingShardsErrors(t *testing.T) { - var shardMap map[string]*topo.ShardInfo - var err error - - // 3 overlapping shards - shardMap = map[string]*topo.ShardInfo{ - "-20": si("", "20"), - "-40": si("", "40"), - "-80": si("", "80"), - } - _, err = findOverlappingShards(shardMap) - if err == nil { - t.Errorf("3 overlapping shards with no error") - } -} diff --git a/go/vt/topotools/tablet.go b/go/vt/topotools/tablet.go index 8bbca4b8c03..76f9e3d6cec 100644 --- a/go/vt/topotools/tablet.go +++ b/go/vt/topotools/tablet.go @@ -127,7 +127,7 @@ func DoCellsHaveRdonlyTablets(ctx context.Context, ts *topo.Server, cells []stri } for _, cell := range cells { - tablets, err := ts.GetTabletsByCell(ctx, cell) + tablets, err := ts.GetTabletsByCell(ctx, cell, nil) if err != nil { return false, err } diff --git a/go/vt/topotools/utils.go b/go/vt/topotools/utils.go index 6b618383a1e..ae70b299bdd 100644 --- a/go/vt/topotools/utils.go +++ b/go/vt/topotools/utils.go @@ -17,10 +17,8 @@ limitations under the License. package topotools import ( - "reflect" - "sync" - "context" + "sync" "vitess.io/vitess/go/vt/topo" @@ -43,7 +41,7 @@ func GetTabletMapForCell(ctx context.Context, ts *topo.Server, cell string) (map if err != nil { return nil, err } - tabletMap, err := ts.GetTabletMap(ctx, aliases) + tabletMap, err := ts.GetTabletMap(ctx, aliases, nil) if err != nil { // we got another error than topo.ErrNoNode return nil, err @@ -65,7 +63,7 @@ func GetAllTabletsAcrossCells(ctx context.Context, ts *topo.Server) ([]*topo.Tab wg.Add(len(cells)) for i, cell := range cells { go func(i int, cell string) { - results[i], errors[i] = ts.GetTabletsByCell(ctx, cell) + results[i], errors[i] = ts.GetTabletsByCell(ctx, cell, nil) wg.Done() }(i, cell) } @@ -101,37 +99,3 @@ func SortedTabletMap(tabletMap map[string]*topo.TabletInfo) (map[string]*topo.Ta } return replicaMap, primaryMap } - -// CopyMapKeys copies keys from map m into a new slice with the -// type specified by typeHint. Reflection can't make a new slice type -// just based on the key type AFAICT. -func CopyMapKeys(m any, typeHint any) any { - mapVal := reflect.ValueOf(m) - keys := reflect.MakeSlice(reflect.TypeOf(typeHint), 0, mapVal.Len()) - for _, k := range mapVal.MapKeys() { - keys = reflect.Append(keys, k) - } - return keys.Interface() -} - -// CopyMapValues copies values from map m into a new slice with the -// type specified by typeHint. Reflection can't make a new slice type -// just based on the key type AFAICT. -func CopyMapValues(m any, typeHint any) any { - mapVal := reflect.ValueOf(m) - vals := reflect.MakeSlice(reflect.TypeOf(typeHint), 0, mapVal.Len()) - for _, k := range mapVal.MapKeys() { - vals = reflect.Append(vals, mapVal.MapIndex(k)) - } - return vals.Interface() -} - -// MapKeys returns an array with th provided map keys. -func MapKeys(m any) []any { - keys := make([]any, 0, 16) - mapVal := reflect.ValueOf(m) - for _, kv := range mapVal.MapKeys() { - keys = append(keys, kv.Interface()) - } - return keys -} diff --git a/go/vt/topotools/vschema_ddl.go b/go/vt/topotools/vschema_ddl.go index ff4d9f4ad04..3c6f5bced3c 100644 --- a/go/vt/topotools/vschema_ddl.go +++ b/go/vt/topotools/vschema_ddl.go @@ -124,7 +124,7 @@ func ApplyVSchemaDDL(ksName string, ks *vschemapb.Keyspace, alterVschema *sqlpar // already exists. spec := alterVschema.VindexSpec name := spec.Name.String() - if !spec.Type.IsEmpty() { + if spec.Type.NotEmpty() { owner, params := spec.ParseParams() if vindex, ok := ks.Vindexes[name]; ok { if vindex.Type != spec.Type.String() { diff --git a/go/vt/vtadmin/api.go b/go/vt/vtadmin/api.go index 92d11ba18ea..dce4ff041c9 100644 --- a/go/vt/vtadmin/api.go +++ b/go/vt/vtadmin/api.go @@ -32,11 +32,14 @@ import ( "github.com/gorilla/mux" "github.com/patrickmn/go-cache" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/sets" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtadmin/cluster" "vitess.io/vitess/go/vt/vtadmin/cluster/dynamic" @@ -77,6 +80,8 @@ type API struct { // vtexplain is now global again due to stat exporters in the tablet layer // we're not super concerned because we will be deleting vtexplain Soon(TM). vtexplainLock sync.Mutex + + env *vtenv.Environment } // Options wraps the configuration options for different components of the @@ -92,7 +97,7 @@ type Options struct { // NewAPI returns a new API, configured to service the given set of clusters, // and configured with the given options. -func NewAPI(clusters []*cluster.Cluster, opts Options) *API { +func NewAPI(env *vtenv.Environment, clusters []*cluster.Cluster, opts Options) *API { clusterMap := make(map[string]*cluster.Cluster, len(clusters)) for _, cluster := range clusters { clusterMap[cluster.ID] = cluster @@ -138,6 +143,7 @@ func NewAPI(clusters []*cluster.Cluster, opts Options) *API { clusters: clusters, clusterMap: clusterMap, authz: authz, + env: env, } if opts.EnableDynamicClusters { @@ -296,6 +302,7 @@ func (api *API) WithCluster(c *cluster.Cluster, id string) dynamic.API { serv: api.serv, authz: api.authz, options: api.options, + env: api.env, } if c != nil { @@ -366,6 +373,13 @@ func (api *API) Handler() http.Handler { router.HandleFunc("/keyspace/{cluster_id}/{name}/validate/schema", httpAPI.Adapt(vtadminhttp.ValidateSchemaKeyspace)).Name("API.ValidateSchemaKeyspace").Methods("PUT", "OPTIONS") router.HandleFunc("/keyspace/{cluster_id}/{name}/validate/version", httpAPI.Adapt(vtadminhttp.ValidateVersionKeyspace)).Name("API.ValidateVersionKeyspace").Methods("PUT", "OPTIONS") router.HandleFunc("/keyspaces", httpAPI.Adapt(vtadminhttp.GetKeyspaces)).Name("API.GetKeyspaces") + router.HandleFunc("/migration/{cluster_id}/{keyspace}", httpAPI.Adapt(vtadminhttp.ApplySchema)).Name("API.ApplySchema").Methods("POST") + router.HandleFunc("/migration/{cluster_id}/{keyspace}/cancel", httpAPI.Adapt(vtadminhttp.CancelSchemaMigration)).Name("API.CancelSchemaMigration").Methods("PUT", "OPTIONS") + router.HandleFunc("/migration/{cluster_id}/{keyspace}/cleanup", httpAPI.Adapt(vtadminhttp.CleanupSchemaMigration)).Name("API.CleanupSchemaMigration").Methods("PUT", "OPTIONS") + router.HandleFunc("/migration/{cluster_id}/{keyspace}/complete", httpAPI.Adapt(vtadminhttp.CompleteSchemaMigration)).Name("API.CompleteSchemaMigration").Methods("PUT", "OPTIONS") + router.HandleFunc("/migration/{cluster_id}/{keyspace}/launch", httpAPI.Adapt(vtadminhttp.LaunchSchemaMigration)).Name("API.LaunchSchemaMigration").Methods("PUT", "OPTIONS") + router.HandleFunc("/migration/{cluster_id}/{keyspace}/retry", httpAPI.Adapt(vtadminhttp.RetrySchemaMigration)).Name("API.RetrySchemaMigration").Methods("PUT", "OPTIONS") + router.HandleFunc("/migrations/", httpAPI.Adapt(vtadminhttp.GetSchemaMigrations)).Name("API.GetSchemaMigrations") router.HandleFunc("/schema/{table}", httpAPI.Adapt(vtadminhttp.FindSchema)).Name("API.FindSchema") router.HandleFunc("/schema/{cluster_id}/{keyspace}/{table}", httpAPI.Adapt(vtadminhttp.GetSchema)).Name("API.GetSchema") router.HandleFunc("/schemas", httpAPI.Adapt(vtadminhttp.GetSchemas)).Name("API.GetSchemas") @@ -432,6 +446,82 @@ func (api *API) EjectDynamicCluster(key string, value any) { api.clusters = append(api.clusters[:clusterIndex], api.clusters[clusterIndex+1:]...) } +// ApplySchema is part of the vtadminpb.VTAdminServer interface. +func (api *API) ApplySchema(ctx context.Context, req *vtadminpb.ApplySchemaRequest) (*vtctldatapb.ApplySchemaResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.ApplySchema") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CreateAction) { + return nil, fmt.Errorf("%w: cannot create schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.ApplySchema(ctx, req.Request) +} + +// CancelSchemaMigration is part of the vtadminpb.VTAdminServer interface. +func (api *API) CancelSchemaMigration(ctx context.Context, req *vtadminpb.CancelSchemaMigrationRequest) (*vtctldatapb.CancelSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.CancelSchemaMigration") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CancelAction) { + return nil, fmt.Errorf("%w: cannot cancel schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.CancelSchemaMigration(ctx, req.Request) +} + +// CleanupSchemaMigration is part of the vtadminpb.VTAdminServer interface. +func (api *API) CleanupSchemaMigration(ctx context.Context, req *vtadminpb.CleanupSchemaMigrationRequest) (*vtctldatapb.CleanupSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.CleanupSchemaMigration") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CleanupSchemaMigrationAction) { + return nil, fmt.Errorf("%w: cannot cleanup schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.CleanupSchemaMigration(ctx, req.Request) +} + +// CompleteSchemaMigration is part of the vtadminpb.VTAdminServer interface. +func (api *API) CompleteSchemaMigration(ctx context.Context, req *vtadminpb.CompleteSchemaMigrationRequest) (*vtctldatapb.CompleteSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.CompleteSchemaMigration") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CompleteSchemaMigrationAction) { + return nil, fmt.Errorf("%w: cannot complete schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.CompleteSchemaMigration(ctx, req.Request) +} + // CreateKeyspace is part of the vtadminpb.VTAdminServer interface. func (api *API) CreateKeyspace(ctx context.Context, req *vtadminpb.CreateKeyspaceRequest) (*vtadminpb.CreateKeyspaceResponse, error) { span, ctx := trace.NewSpan(ctx, "API.CreateKeyspace") @@ -1015,6 +1105,113 @@ func (api *API) GetSchemas(ctx context.Context, req *vtadminpb.GetSchemasRequest }, nil } +// GetSchemaMigrations is part of the vtadminpb.VTAdminServer interface. +func (api *API) GetSchemaMigrations(ctx context.Context, req *vtadminpb.GetSchemaMigrationsRequest) (*vtadminpb.GetSchemaMigrationsResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.GetSchemaMigrations") + defer span.Finish() + + clusterIDs := make([]string, 0, len(req.ClusterRequests)) + requestsByCluster := make(map[string][]*vtctldatapb.GetSchemaMigrationsRequest, len(req.ClusterRequests)) + + for _, r := range req.ClusterRequests { + clusterIDs = append(clusterIDs, r.ClusterId) + requestsByCluster[r.ClusterId] = append(requestsByCluster[r.ClusterId], r.Request) + } + + clusters, _ := api.getClustersForRequest(clusterIDs) + + var ( + m sync.Mutex + wg sync.WaitGroup + rec concurrency.AllErrorRecorder + results = make([]*vtadminpb.SchemaMigration, 0, len(req.ClusterRequests)) + ) + + m.Lock() + for _, c := range clusters { + if len(requestsByCluster[c.ID]) == 0 { + wg.Add(1) + go func(ctx context.Context, c *cluster.Cluster) { + defer wg.Done() + + span, ctx := trace.NewSpan(ctx, "API.getClusterKeyspaces") + defer span.Finish() + + span.Annotate("cluster_id", c.ID) + + if !api.authz.IsAuthorized(ctx, c.ID, rbac.SchemaMigrationResource, rbac.GetAction) { + return + } + + keyspaces, err := c.GetKeyspaces(ctx) + if err != nil { + rec.RecordError(err) + return + } + + m.Lock() + defer m.Unlock() + + for _, ks := range keyspaces { + requestsByCluster[c.ID] = append(requestsByCluster[c.ID], &vtctldatapb.GetSchemaMigrationsRequest{ + Keyspace: ks.Keyspace.Name, + }) + } + }(ctx, c) + } + } + m.Unlock() + + wg.Wait() + if rec.HasErrors() { + return nil, rec.Error() + } + + for _, c := range clusters { + if requestsByCluster[c.ID] == nil { + continue + } + + for _, r := range requestsByCluster[c.ID] { + wg.Add(1) + + go func(ctx context.Context, c *cluster.Cluster, r *vtctldatapb.GetSchemaMigrationsRequest) { + defer wg.Done() + + span, ctx := trace.NewSpan(ctx, "API.getClusterSchemaMigrations") + defer span.Finish() + + span.Annotate("cluster_id", c.ID) + + if !api.authz.IsAuthorized(ctx, c.ID, rbac.SchemaMigrationResource, rbac.GetAction) { + return + } + + migrations, err := c.GetSchemaMigrations(ctx, r) + if err != nil { + rec.RecordError(err) + return + } + + m.Lock() + defer m.Unlock() + + results = append(results, migrations...) + }(ctx, c, r) + } + } + + wg.Wait() + + if rec.HasErrors() { + return nil, rec.Error() + } + + return &vtadminpb.GetSchemaMigrationsResponse{ + SchemaMigrations: results, + }, nil +} + // GetShardReplicationPositions is part of the vtadminpb.VTAdminServer interface. func (api *API) GetShardReplicationPositions(ctx context.Context, req *vtadminpb.GetShardReplicationPositionsRequest) (*vtadminpb.GetShardReplicationPositionsResponse, error) { span, ctx := trace.NewSpan(ctx, "API.GetShardReplicationPositions") @@ -1515,6 +1712,25 @@ func (api *API) GetWorkflows(ctx context.Context, req *vtadminpb.GetWorkflowsReq }, nil } +// LaunchSchemaMigration is part of the vtadminpb.VTAdminServer interface. +func (api *API) LaunchSchemaMigration(ctx context.Context, req *vtadminpb.LaunchSchemaMigrationRequest) (*vtctldatapb.LaunchSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.LaunchSchemaMigration") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.LaunchSchemaMigrationAction) { + return nil, fmt.Errorf("%w: cannot launch schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.LaunchSchemaMigration(ctx, req.Request) +} + // PingTablet is part of the vtadminpb.VTAdminServer interface. func (api *API) PingTablet(ctx context.Context, req *vtadminpb.PingTabletRequest) (*vtadminpb.PingTabletResponse, error) { span, ctx := trace.NewSpan(ctx, "API.PingTablet") @@ -1722,6 +1938,25 @@ func (api *API) ReloadSchemaShard(ctx context.Context, req *vtadminpb.ReloadSche }, nil } +// RetrySchemaMigration is part of the vtadminpb.VTAdminServer interface. +func (api *API) RetrySchemaMigration(ctx context.Context, req *vtadminpb.RetrySchemaMigrationRequest) (*vtctldatapb.RetrySchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "API.RetrySchemaMigration") + defer span.Finish() + + span.Annotate("cluster_id", req.ClusterId) + + if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.RetryAction) { + return nil, fmt.Errorf("%w: cannot retry schema migration in %s", errors.ErrUnauthorized, req.ClusterId) + } + + c, err := api.getClusterForRequest(req.ClusterId) + if err != nil { + return nil, err + } + + return c.RetrySchemaMigration(ctx, req.Request) +} + // RunHealthCheck is part of the vtadminpb.VTAdminServer interface. func (api *API) RunHealthCheck(ctx context.Context, req *vtadminpb.RunHealthCheckRequest) (*vtadminpb.RunHealthCheckResponse, error) { span, ctx := trace.NewSpan(ctx, "API.RunHealthCheck") @@ -2148,7 +2383,8 @@ func (api *API) VTExplain(ctx context.Context, req *vtadminpb.VTExplainRequest) return nil, er.Error() } - vte, err := vtexplain.Init(ctx, srvVSchema, schema, shardMap, &vtexplain.Options{ReplicationMode: "ROW"}) + ts := memorytopo.NewServer(ctx, vtexplain.Cell) + vte, err := vtexplain.Init(ctx, api.env, ts, srvVSchema, schema, shardMap, &vtexplain.Options{ReplicationMode: "ROW"}) if err != nil { return nil, fmt.Errorf("error initilaizing vtexplain: %w", err) } diff --git a/go/vt/vtadmin/api_authz_test.go b/go/vt/vtadmin/api_authz_test.go index eb67757a1c1..94a8befd473 100644 --- a/go/vt/vtadmin/api_authz_test.go +++ b/go/vt/vtadmin/api_authz_test.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/vt/vtadmin/rbac" "vitess.io/vitess/go/vt/vtadmin/testutil" "vitess.io/vitess/go/vt/vtadmin/vtctldclient/fakevtctldclient" + "vitess.io/vitess/go/vt/vtenv" logutilpb "vitess.io/vitess/go/vt/proto/logutil" mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" @@ -43,6 +44,266 @@ import ( vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) +func TestApplySchema(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"create"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.ApplySchema(ctx, &vtadminpb.ApplySchemaRequest{ + ClusterId: "test", + Request: &vtctldatapb.ApplySchemaRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to ApplySchema", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to ApplySchema", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.ApplySchema(ctx, &vtadminpb.ApplySchemaRequest{ + ClusterId: "test", + Request: &vtctldatapb.ApplySchemaRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to ApplySchema", actor) + }) +} + +func TestCancelSchemaMigration(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"cancel"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CancelSchemaMigration(ctx, &vtadminpb.CancelSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CancelSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to CancelSchemaMigration", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to CancelSchemaMigration", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CancelSchemaMigration(ctx, &vtadminpb.CancelSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CancelSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to CancelSchemaMigration", actor) + }) +} + +func TestCleanupSchemaMigration(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"cleanup_schema_migration"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CleanupSchemaMigration(ctx, &vtadminpb.CleanupSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CleanupSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to CleanupSchemaMigration", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to CleanupSchemaMigration", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CleanupSchemaMigration(ctx, &vtadminpb.CleanupSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CleanupSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to CleanupSchemaMigration", actor) + }) +} + +func TestCompleteSchemaMigration(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"complete_schema_migration"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CompleteSchemaMigration(ctx, &vtadminpb.CompleteSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CompleteSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to CompleteSchemaMigration", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to CompleteSchemaMigration", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.CompleteSchemaMigration(ctx, &vtadminpb.CompleteSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.CompleteSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to CompleteSchemaMigration", actor) + }) +} + func TestCreateKeyspace(t *testing.T) { t.Parallel() @@ -66,7 +327,7 @@ func TestCreateKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -78,9 +339,7 @@ func TestCreateKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.CreateKeyspace(ctx, &vtadminpb.CreateKeyspaceRequest{ ClusterId: "test", @@ -97,9 +356,7 @@ func TestCreateKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.CreateKeyspace(ctx, &vtadminpb.CreateKeyspaceRequest{ ClusterId: "test", @@ -135,7 +392,7 @@ func TestCreateShard(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -147,9 +404,7 @@ func TestCreateShard(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.CreateShard(ctx, &vtadminpb.CreateShardRequest{ ClusterId: "test", @@ -167,9 +422,7 @@ func TestCreateShard(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.CreateShard(ctx, &vtadminpb.CreateShardRequest{ ClusterId: "test", @@ -206,7 +459,7 @@ func TestDeleteKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -218,9 +471,7 @@ func TestDeleteKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteKeyspace(ctx, &vtadminpb.DeleteKeyspaceRequest{ ClusterId: "test", @@ -237,9 +488,7 @@ func TestDeleteKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteKeyspace(ctx, &vtadminpb.DeleteKeyspaceRequest{ ClusterId: "test", @@ -275,7 +524,7 @@ func TestDeleteShards(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -287,9 +536,7 @@ func TestDeleteShards(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteShards(ctx, &vtadminpb.DeleteShardsRequest{ ClusterId: "test", @@ -311,9 +558,7 @@ func TestDeleteShards(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteShards(ctx, &vtadminpb.DeleteShardsRequest{ ClusterId: "test", @@ -354,7 +599,7 @@ func TestDeleteTablet(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -366,9 +611,7 @@ func TestDeleteTablet(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteTablet(ctx, &vtadminpb.DeleteTabletRequest{ ClusterIds: []string{"test"}, @@ -386,9 +629,7 @@ func TestDeleteTablet(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.DeleteTablet(ctx, &vtadminpb.DeleteTabletRequest{ ClusterIds: []string{"test"}, @@ -425,7 +666,7 @@ func TestEmergencyFailoverShard(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -437,9 +678,7 @@ func TestEmergencyFailoverShard(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.EmergencyFailoverShard(ctx, &vtadminpb.EmergencyFailoverShardRequest{ ClusterId: "test", @@ -457,9 +696,7 @@ func TestEmergencyFailoverShard(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.EmergencyFailoverShard(ctx, &vtadminpb.EmergencyFailoverShardRequest{ ClusterId: "test", @@ -505,7 +742,7 @@ func TestFindSchema(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -514,9 +751,7 @@ func TestFindSchema(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ Table: "t1", @@ -528,7 +763,7 @@ func TestFindSchema(t *testing.T) { t.Run("partial access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -537,9 +772,7 @@ func TestFindSchema(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ Table: "t1", @@ -550,7 +783,7 @@ func TestFindSchema(t *testing.T) { t.Run("full access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -559,9 +792,7 @@ func TestFindSchema(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ Table: "t1", @@ -601,7 +832,7 @@ func TestGetBackups(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -613,9 +844,7 @@ func TestGetBackups(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) assert.NoError(t, err) @@ -627,9 +856,7 @@ func TestGetBackups(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) assert.NotEmpty(t, resp.Backups, "actor %+v should be permitted to GetBackups", actor) @@ -641,9 +868,7 @@ func TestGetBackups(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) assert.NotEmpty(t, resp.Backups, "actor %+v should be permitted to GetBackups", actor) @@ -680,7 +905,7 @@ func TestGetCellInfos(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -692,9 +917,7 @@ func TestGetCellInfos(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ NamesOnly: true, @@ -708,9 +931,7 @@ func TestGetCellInfos(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ NamesOnly: true, @@ -724,9 +945,7 @@ func TestGetCellInfos(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ NamesOnly: true, @@ -765,7 +984,7 @@ func TestGetCellsAliases(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -777,9 +996,7 @@ func TestGetCellsAliases(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) assert.NoError(t, err) @@ -791,9 +1008,7 @@ func TestGetCellsAliases(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) assert.NotEmpty(t, resp.Aliases, "actor %+v should be permitted to GetCellsAliases", actor) @@ -805,9 +1020,7 @@ func TestGetCellsAliases(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) assert.NotEmpty(t, resp.Aliases, "actor %+v should be permitted to GetCellsAliases", actor) @@ -838,7 +1051,7 @@ func TestGetClusters(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -850,9 +1063,7 @@ func TestGetClusters(t *testing.T) { var actor *rbac.Actor ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) assert.Empty(t, resp.Clusters, "actor %+v should not be permitted to GetClusters", actor) @@ -863,9 +1074,7 @@ func TestGetClusters(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) assert.Empty(t, resp.Clusters, "actor %+v should not be permitted to GetClusters", actor) @@ -876,9 +1085,7 @@ func TestGetClusters(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) require.NoError(t, err) @@ -918,7 +1125,7 @@ func TestGetGates(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -927,9 +1134,7 @@ func TestGetGates(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) assert.NoError(t, err) @@ -939,7 +1144,7 @@ func TestGetGates(t *testing.T) { t.Run("partial access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -948,9 +1153,7 @@ func TestGetGates(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) assert.NotEmpty(t, resp.Gates, "actor %+v should be permitted to GetGates", actor) @@ -961,7 +1164,7 @@ func TestGetGates(t *testing.T) { t.Run("full access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -970,9 +1173,7 @@ func TestGetGates(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) assert.NotEmpty(t, resp.Gates, "actor %+v should be permitted to GetGates", actor) @@ -1004,7 +1205,7 @@ func TestGetKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1016,9 +1217,7 @@ func TestGetKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetKeyspace(ctx, &vtadminpb.GetKeyspaceRequest{ ClusterId: "test", @@ -1033,20 +1232,105 @@ func TestGetKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.GetKeyspace(ctx, &vtadminpb.GetKeyspaceRequest{ + ClusterId: "test", + Keyspace: "test", + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to GetKeyspace", actor) + }) +} + +func TestGetKeyspaces(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "Keyspace", + Actions: []string{"get"}, + Subjects: []string{"user:allowed-all"}, + Clusters: []string{"*"}, + }, + { + Resource: "Keyspace", + Actions: []string{"get"}, + Subjects: []string{"user:allowed-other"}, + Clusters: []string{"other"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "unauthorized"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) + assert.NoError(t, err) + assert.Empty(t, resp.Keyspaces, "actor %+v should not be permitted to GetKeyspaces", actor) + }) + + t.Run("partial access", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed-other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) + assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) + ksMap := map[string][]string{} + for _, ks := range resp.Keyspaces { + if _, ok := ksMap[ks.Cluster.Id]; !ok { + ksMap[ks.Cluster.Id] = []string{} + } + ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) + } + assert.Equal(t, ksMap, map[string][]string{"other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) + }) + + t.Run("full access", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed-all"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) - resp, err := api.GetKeyspace(ctx, &vtadminpb.GetKeyspaceRequest{ - ClusterId: "test", - Keyspace: "test", - }) - require.NoError(t, err) - assert.NotNil(t, resp, "actor %+v should be permitted to GetKeyspace", actor) + resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) + assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) + ksMap := map[string][]string{} + for _, ks := range resp.Keyspaces { + if _, ok := ksMap[ks.Cluster.Id]; !ok { + ksMap[ks.Cluster.Id] = []string{} + } + ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) + } + assert.Equal(t, ksMap, map[string][]string{"test": {"test"}, "other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) }) } -func TestGetKeyspaces(t *testing.T) { +func TestGetSchemaMigrations(t *testing.T) { t.Parallel() opts := vtadmin.Options{ @@ -1058,13 +1342,13 @@ func TestGetKeyspaces(t *testing.T) { Clusters []string }{ { - Resource: "Keyspace", + Resource: "SchemaMigration", Actions: []string{"get"}, Subjects: []string{"user:allowed-all"}, Clusters: []string{"*"}, }, { - Resource: "Keyspace", + Resource: "SchemaMigration", Actions: []string{"get"}, Subjects: []string{"user:allowed-other"}, Clusters: []string{"other"}, @@ -1075,7 +1359,7 @@ func TestGetKeyspaces(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1087,13 +1371,11 @@ func TestGetKeyspaces(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) - resp, err := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) + resp, err := api.GetSchemaMigrations(ctx, &vtadminpb.GetSchemaMigrationsRequest{}) assert.NoError(t, err) - assert.Empty(t, resp.Keyspaces, "actor %+v should not be permitted to GetKeyspaces", actor) + assert.Empty(t, resp.SchemaMigrations, "actor %+v should not be permitted to GetSchemaMigrations", actor) }) t.Run("partial access", func(t *testing.T) { @@ -1101,20 +1383,11 @@ func TestGetKeyspaces(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) - resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) - assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) - ksMap := map[string][]string{} - for _, ks := range resp.Keyspaces { - if _, ok := ksMap[ks.Cluster.Id]; !ok { - ksMap[ks.Cluster.Id] = []string{} - } - ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) - } - assert.Equal(t, ksMap, map[string][]string{"other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) + resp, _ := api.GetSchemaMigrations(ctx, &vtadminpb.GetSchemaMigrationsRequest{}) + assert.NotEmpty(t, resp.SchemaMigrations, "actor %+v should be permitted to GetSchemaMigrations", actor) + assert.Len(t, resp.SchemaMigrations, 3, "'other' actor should be able to see the 3 migrations in cluster 'other'") }) t.Run("full access", func(t *testing.T) { @@ -1122,20 +1395,11 @@ func TestGetKeyspaces(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) - resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) - assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) - ksMap := map[string][]string{} - for _, ks := range resp.Keyspaces { - if _, ok := ksMap[ks.Cluster.Id]; !ok { - ksMap[ks.Cluster.Id] = []string{} - } - ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) - } - assert.Equal(t, ksMap, map[string][]string{"test": {"test"}, "other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) + resp, _ := api.GetSchemaMigrations(ctx, &vtadminpb.GetSchemaMigrationsRequest{}) + assert.NotEmpty(t, resp.SchemaMigrations, "actor %+v should be permitted to GetSchemaMigrations", actor) + assert.Len(t, resp.SchemaMigrations, 4, "'all' actor should be able to see migrations in all clusters") }) } @@ -1165,7 +1429,7 @@ func TestGetSchema(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1174,9 +1438,7 @@ func TestGetSchema(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{ ClusterId: "test", @@ -1190,7 +1452,7 @@ func TestGetSchema(t *testing.T) { t.Run("authorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1199,9 +1461,7 @@ func TestGetSchema(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{ ClusterId: "test", @@ -1245,7 +1505,7 @@ func TestGetSchemas(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1254,9 +1514,7 @@ func TestGetSchemas(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) assert.NoError(t, err) @@ -1266,7 +1524,7 @@ func TestGetSchemas(t *testing.T) { t.Run("partial access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1275,9 +1533,7 @@ func TestGetSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) assert.NotEmpty(t, resp.Schemas, "actor %+v should be permitted to GetSchemas", actor) @@ -1294,7 +1550,7 @@ func TestGetSchemas(t *testing.T) { t.Run("full access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1303,9 +1559,7 @@ func TestGetSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) assert.NotEmpty(t, resp.Schemas, "actor %+v should be permitted to GetSchemas", actor) @@ -1349,7 +1603,7 @@ func TestGetShardReplicationPositions(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1361,9 +1615,7 @@ func TestGetShardReplicationPositions(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) assert.NoError(t, err) @@ -1375,9 +1627,7 @@ func TestGetShardReplicationPositions(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) assert.NotEmpty(t, resp.ReplicationPositions, "actor %+v should be permitted to GetShardReplicationPositions", actor) @@ -1396,9 +1646,7 @@ func TestGetShardReplicationPositions(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) assert.NotEmpty(t, resp.ReplicationPositions, "actor %+v should be permitted to GetShardReplicationPositions", actor) @@ -1436,7 +1684,7 @@ func TestGetSrvVSchema(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1448,9 +1696,7 @@ func TestGetSrvVSchema(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSrvVSchema(ctx, &vtadminpb.GetSrvVSchemaRequest{ ClusterId: "test", @@ -1465,9 +1711,7 @@ func TestGetSrvVSchema(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSrvVSchema(ctx, &vtadminpb.GetSrvVSchemaRequest{ ClusterId: "test", @@ -1507,7 +1751,7 @@ func TestGetSrvVSchemas(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1519,9 +1763,7 @@ func TestGetSrvVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) require.NoError(t, err) @@ -1533,9 +1775,7 @@ func TestGetSrvVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) assert.NotEmpty(t, resp.SrvVSchemas, "actor %+v should be permitted to GetSrvVSchemas", actor) @@ -1554,9 +1794,7 @@ func TestGetSrvVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) assert.NotEmpty(t, resp.SrvVSchemas, "actor %+v should be permitted to GetSrvVSchemas", actor) @@ -1594,7 +1832,7 @@ func TestGetTablet(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1606,9 +1844,7 @@ func TestGetTablet(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetTablet(ctx, &vtadminpb.GetTabletRequest{ Alias: &topodatapb.TabletAlias{ @@ -1625,9 +1861,7 @@ func TestGetTablet(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetTablet(ctx, &vtadminpb.GetTabletRequest{ Alias: &topodatapb.TabletAlias{ @@ -1669,7 +1903,7 @@ func TestGetTablets(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1681,9 +1915,7 @@ func TestGetTablets(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) require.NoError(t, err) @@ -1695,9 +1927,7 @@ func TestGetTablets(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) assert.NotEmpty(t, resp.Tablets, "actor %+v should be permitted to GetTablets", actor) @@ -1716,9 +1946,7 @@ func TestGetTablets(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) assert.NotEmpty(t, resp.Tablets, "actor %+v should be permitted to GetTablets", actor) @@ -1756,7 +1984,7 @@ func TestGetVSchema(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1768,9 +1996,7 @@ func TestGetVSchema(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetVSchema(ctx, &vtadminpb.GetVSchemaRequest{ ClusterId: "test", @@ -1785,9 +2011,7 @@ func TestGetVSchema(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetVSchema(ctx, &vtadminpb.GetVSchemaRequest{ ClusterId: "test", @@ -1827,7 +2051,7 @@ func TestGetVSchemas(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1839,9 +2063,7 @@ func TestGetVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) require.NoError(t, err) @@ -1853,9 +2075,7 @@ func TestGetVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) assert.NotEmpty(t, resp.VSchemas, "actor %+v should be permitted to GetVSchemas", actor) @@ -1874,9 +2094,7 @@ func TestGetVSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) assert.NotEmpty(t, resp.VSchemas, "actor %+v should be permitted to GetVSchemas", actor) @@ -1923,7 +2141,7 @@ func TestGetVtctlds(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1932,9 +2150,7 @@ func TestGetVtctlds(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) assert.NoError(t, err) @@ -1944,7 +2160,7 @@ func TestGetVtctlds(t *testing.T) { t.Run("partial access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1953,9 +2169,7 @@ func TestGetVtctlds(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) assert.NotEmpty(t, resp.Vtctlds, "actor %+v should be permitted to GetVtctlds", actor) @@ -1966,7 +2180,7 @@ func TestGetVtctlds(t *testing.T) { t.Run("full access", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -1975,9 +2189,7 @@ func TestGetVtctlds(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) assert.NotEmpty(t, resp.Vtctlds, "actor %+v should be permitted to GetVtctlds", actor) @@ -2009,7 +2221,7 @@ func TestGetWorkflow(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2021,9 +2233,7 @@ func TestGetWorkflow(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetWorkflow(ctx, &vtadminpb.GetWorkflowRequest{ ClusterId: "test", @@ -2039,9 +2249,7 @@ func TestGetWorkflow(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetWorkflow(ctx, &vtadminpb.GetWorkflowRequest{ ClusterId: "test", @@ -2082,7 +2290,7 @@ func TestGetWorkflows(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2094,9 +2302,7 @@ func TestGetWorkflows(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) require.NoError(t, err) @@ -2108,9 +2314,7 @@ func TestGetWorkflows(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) assert.NotEmpty(t, resp.WorkflowsByCluster, "actor %+v should be permitted to GetWorkflows", actor) @@ -2122,9 +2326,7 @@ func TestGetWorkflows(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) assert.NotEmpty(t, resp.WorkflowsByCluster, "actor %+v should be permitted to GetWorkflows", actor) @@ -2132,6 +2334,71 @@ func TestGetWorkflows(t *testing.T) { }) } +func TestLaunchSchemaMigration(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"launch_schema_migration"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.LaunchSchemaMigration(ctx, &vtadminpb.LaunchSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.LaunchSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to LaunchSchemaMigration", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to LaunchSchemaMigration", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.LaunchSchemaMigration(ctx, &vtadminpb.LaunchSchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.LaunchSchemaMigrationRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to LaunchSchemaMigration", actor) + }) +} + func TestPingTablet(t *testing.T) { t.Parallel() @@ -2155,7 +2422,7 @@ func TestPingTablet(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2167,9 +2434,7 @@ func TestPingTablet(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.PingTablet(ctx, &vtadminpb.PingTabletRequest{ Alias: &topodatapb.TabletAlias{ @@ -2186,9 +2451,7 @@ func TestPingTablet(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.PingTablet(ctx, &vtadminpb.PingTabletRequest{ Alias: &topodatapb.TabletAlias{ @@ -2224,7 +2487,7 @@ func TestPlannedFailoverShard(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2236,9 +2499,7 @@ func TestPlannedFailoverShard(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.PlannedFailoverShard(ctx, &vtadminpb.PlannedFailoverShardRequest{ ClusterId: "test", @@ -2256,9 +2517,7 @@ func TestPlannedFailoverShard(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.PlannedFailoverShard(ctx, &vtadminpb.PlannedFailoverShardRequest{ ClusterId: "test", @@ -2295,7 +2554,7 @@ func TestRefreshState(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2307,9 +2566,7 @@ func TestRefreshState(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RefreshState(ctx, &vtadminpb.RefreshStateRequest{ Alias: &topodatapb.TabletAlias{ @@ -2326,9 +2583,7 @@ func TestRefreshState(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RefreshState(ctx, &vtadminpb.RefreshStateRequest{ Alias: &topodatapb.TabletAlias{ @@ -2364,7 +2619,7 @@ func TestRefreshTabletReplicationSource(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2376,9 +2631,7 @@ func TestRefreshTabletReplicationSource(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RefreshTabletReplicationSource(ctx, &vtadminpb.RefreshTabletReplicationSourceRequest{ Alias: &topodatapb.TabletAlias{ @@ -2395,9 +2648,7 @@ func TestRefreshTabletReplicationSource(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RefreshTabletReplicationSource(ctx, &vtadminpb.RefreshTabletReplicationSourceRequest{ Alias: &topodatapb.TabletAlias{ @@ -2439,7 +2690,7 @@ func TestReloadSchemas(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2451,9 +2702,7 @@ func TestReloadSchemas(t *testing.T) { actor := &rbac.Actor{Name: "unauthorized"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ Keyspaces: []string{ @@ -2470,9 +2719,7 @@ func TestReloadSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ Keyspaces: []string{ @@ -2488,9 +2735,7 @@ func TestReloadSchemas(t *testing.T) { actor := &rbac.Actor{Name: "allowed-all"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, _ := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ Keyspaces: []string{ @@ -2502,6 +2747,71 @@ func TestReloadSchemas(t *testing.T) { }) } +func TestRetrySchemaMigration(t *testing.T) { + t.Parallel() + + opts := vtadmin.Options{ + RBAC: &rbac.Config{ + Rules: []*struct { + Resource string + Actions []string + Subjects []string + Clusters []string + }{ + { + Resource: "SchemaMigration", + Actions: []string{"retry"}, + Subjects: []string{"user:allowed"}, + Clusters: []string{"*"}, + }, + }, + }, + } + err := opts.RBAC.Reify() + require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) + + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) + t.Cleanup(func() { + if err := api.Close(); err != nil { + t.Logf("api did not close cleanly: %s", err.Error()) + } + }) + + t.Run("unauthorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "other"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.RetrySchemaMigration(ctx, &vtadminpb.RetrySchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.RetrySchemaMigrationRequest{ + Keyspace: "test", + }, + }) + assert.Error(t, err, "actor %+v should not be permitted to RetrySchemaMigration", actor) + assert.Nil(t, resp, "actor %+v should not be permitted to RetrySchemaMigration", actor) + }) + + t.Run("authorized actor", func(t *testing.T) { + t.Parallel() + + actor := &rbac.Actor{Name: "allowed"} + ctx := context.Background() + ctx = rbac.NewContext(ctx, actor) + + resp, err := api.RetrySchemaMigration(ctx, &vtadminpb.RetrySchemaMigrationRequest{ + ClusterId: "test", + Request: &vtctldatapb.RetrySchemaMigrationRequest{ + Keyspace: "test", + }, + }) + require.NoError(t, err) + assert.NotNil(t, resp, "actor %+v should be permitted to RetrySchemaMigration", actor) + }) +} + func TestRunHealthCheck(t *testing.T) { t.Parallel() @@ -2525,7 +2835,7 @@ func TestRunHealthCheck(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2537,9 +2847,7 @@ func TestRunHealthCheck(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RunHealthCheck(ctx, &vtadminpb.RunHealthCheckRequest{ Alias: &topodatapb.TabletAlias{ @@ -2556,9 +2864,7 @@ func TestRunHealthCheck(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.RunHealthCheck(ctx, &vtadminpb.RunHealthCheckRequest{ Alias: &topodatapb.TabletAlias{ @@ -2594,7 +2900,7 @@ func TestSetReadOnly(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2606,9 +2912,7 @@ func TestSetReadOnly(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.SetReadOnly(ctx, &vtadminpb.SetReadOnlyRequest{ Alias: &topodatapb.TabletAlias{ @@ -2625,9 +2929,7 @@ func TestSetReadOnly(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.SetReadOnly(ctx, &vtadminpb.SetReadOnlyRequest{ Alias: &topodatapb.TabletAlias{ @@ -2663,7 +2965,7 @@ func TestSetReadWrite(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2675,9 +2977,7 @@ func TestSetReadWrite(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.SetReadWrite(ctx, &vtadminpb.SetReadWriteRequest{ Alias: &topodatapb.TabletAlias{ @@ -2694,9 +2994,7 @@ func TestSetReadWrite(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.SetReadWrite(ctx, &vtadminpb.SetReadWriteRequest{ Alias: &topodatapb.TabletAlias{ @@ -2732,7 +3030,7 @@ func TestStartReplication(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2744,9 +3042,7 @@ func TestStartReplication(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.StartReplication(ctx, &vtadminpb.StartReplicationRequest{ Alias: &topodatapb.TabletAlias{ @@ -2763,9 +3059,7 @@ func TestStartReplication(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.StartReplication(ctx, &vtadminpb.StartReplicationRequest{ Alias: &topodatapb.TabletAlias{ @@ -2801,7 +3095,7 @@ func TestStopReplication(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2813,9 +3107,7 @@ func TestStopReplication(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.StopReplication(ctx, &vtadminpb.StopReplicationRequest{ Alias: &topodatapb.TabletAlias{ @@ -2832,9 +3124,7 @@ func TestStopReplication(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.StopReplication(ctx, &vtadminpb.StopReplicationRequest{ Alias: &topodatapb.TabletAlias{ @@ -2870,7 +3160,7 @@ func TestTabletExternallyPromoted(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2882,9 +3172,7 @@ func TestTabletExternallyPromoted(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.TabletExternallyPromoted(ctx, &vtadminpb.TabletExternallyPromotedRequest{ Alias: &topodatapb.TabletAlias{ @@ -2901,9 +3189,7 @@ func TestTabletExternallyPromoted(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.TabletExternallyPromoted(ctx, &vtadminpb.TabletExternallyPromotedRequest{ Alias: &topodatapb.TabletAlias{ @@ -2942,7 +3228,7 @@ func TestVTExplain(t *testing.T) { t.Run("unauthorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2951,9 +3237,7 @@ func TestVTExplain(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.VTExplain(ctx, &vtadminpb.VTExplainRequest{ Cluster: "test", @@ -2966,7 +3250,7 @@ func TestVTExplain(t *testing.T) { t.Run("authorized actor", func(t *testing.T) { t.Parallel() - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -2975,9 +3259,7 @@ func TestVTExplain(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.VTExplain(ctx, &vtadminpb.VTExplainRequest{ Cluster: "test", @@ -3011,7 +3293,7 @@ func TestValidateKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -3023,9 +3305,7 @@ func TestValidateKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateKeyspace(ctx, &vtadminpb.ValidateKeyspaceRequest{ ClusterId: "test", @@ -3040,9 +3320,7 @@ func TestValidateKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateKeyspace(ctx, &vtadminpb.ValidateKeyspaceRequest{ ClusterId: "test", @@ -3076,7 +3354,7 @@ func TestValidateSchemaKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -3088,9 +3366,7 @@ func TestValidateSchemaKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateSchemaKeyspace(ctx, &vtadminpb.ValidateSchemaKeyspaceRequest{ ClusterId: "test", @@ -3105,9 +3381,7 @@ func TestValidateSchemaKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateSchemaKeyspace(ctx, &vtadminpb.ValidateSchemaKeyspaceRequest{ ClusterId: "test", @@ -3141,7 +3415,7 @@ func TestValidateVersionKeyspace(t *testing.T) { err := opts.RBAC.Reify() require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -3153,9 +3427,7 @@ func TestValidateVersionKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "other"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateVersionKeyspace(ctx, &vtadminpb.ValidateVersionKeyspaceRequest{ ClusterId: "test", @@ -3170,9 +3442,7 @@ func TestValidateVersionKeyspace(t *testing.T) { actor := &rbac.Actor{Name: "allowed"} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) resp, err := api.ValidateVersionKeyspace(ctx, &vtadminpb.ValidateVersionKeyspaceRequest{ ClusterId: "test", @@ -3191,6 +3461,38 @@ func testClusters(t testing.TB) []*cluster.Cluster { Name: "test", }, VtctldClient: &fakevtctldclient.VtctldClient{ + ApplySchemaResults: map[string]struct { + Response *vtctldatapb.ApplySchemaResponse + Error error + }{ + "test": { + Response: &vtctldatapb.ApplySchemaResponse{}, + }, + }, + CancelSchemaMigrationResults: map[string]struct { + Response *vtctldatapb.CancelSchemaMigrationResponse + Error error + }{ + "test": { + Response: &vtctldatapb.CancelSchemaMigrationResponse{}, + }, + }, + CleanupSchemaMigrationResults: map[string]struct { + Response *vtctldatapb.CleanupSchemaMigrationResponse + Error error + }{ + "test": { + Response: &vtctldatapb.CleanupSchemaMigrationResponse{}, + }, + }, + CompleteSchemaMigrationResults: map[string]struct { + Response *vtctldatapb.CompleteSchemaMigrationResponse + Error error + }{ + "test": { + Response: &vtctldatapb.CompleteSchemaMigrationResponse{}, + }, + }, DeleteShardsResults: map[string]error{ "test/-": nil, }, @@ -3279,6 +3581,18 @@ func testClusters(t testing.TB) []*cluster.Cluster { }, }, }, + GetSchemaMigrationsResults: map[string]struct { + Response *vtctldatapb.GetSchemaMigrationsResponse + Error error + }{ + "test": { + Response: &vtctldatapb.GetSchemaMigrationsResponse{ + Migrations: []*vtctldatapb.SchemaMigration{ + {}, + }, + }, + }, + }, GetSchemaResults: map[string]struct { Response *vtctldatapb.GetSchemaResponse Error error @@ -3348,6 +3662,14 @@ func testClusters(t testing.TB) []*cluster.Cluster { }, }}, }, + LaunchSchemaMigrationResults: map[string]struct { + Response *vtctldatapb.LaunchSchemaMigrationResponse + Error error + }{ + "test": { + Response: &vtctldatapb.LaunchSchemaMigrationResponse{}, + }, + }, PingTabletResults: map[string]error{ "zone1-0000000100": nil, }, @@ -3379,6 +3701,14 @@ func testClusters(t testing.TB) []*cluster.Cluster { Response: &vtctldatapb.ReparentTabletResponse{}, }, }, + RetrySchemaMigrationResults: map[string]struct { + Response *vtctldatapb.RetrySchemaMigrationResponse + Error error + }{ + "test": { + Response: &vtctldatapb.RetrySchemaMigrationResponse{}, + }, + }, RunHealthCheckResults: map[string]error{ "zone1-0000000100": nil, }, @@ -3449,7 +3779,7 @@ func testClusters(t testing.TB) []*cluster.Cluster { Config: &cluster.Config{ TopoReadPoolConfig: &cluster.RPCPoolConfig{ Size: 100, - WaitTimeout: time.Millisecond * 50, + WaitTimeout: time.Millisecond * 500, }, }, }, { @@ -3516,6 +3846,18 @@ func testClusters(t testing.TB) []*cluster.Cluster { }, }, }, + GetSchemaMigrationsResults: map[string]struct { + Response *vtctldatapb.GetSchemaMigrationsResponse + Error error + }{ + "otherks": { + Response: &vtctldatapb.GetSchemaMigrationsResponse{ + Migrations: []*vtctldatapb.SchemaMigration{ + {}, {}, {}, + }, + }, + }, + }, GetSchemaResults: map[string]struct { Response *vtctldatapb.GetSchemaResponse Error error @@ -3598,7 +3940,7 @@ func testClusters(t testing.TB) []*cluster.Cluster { Config: &cluster.Config{ TopoReadPoolConfig: &cluster.RPCPoolConfig{ Size: 100, - WaitTimeout: time.Millisecond * 50, + WaitTimeout: time.Millisecond * 500, }, }, }, diff --git a/go/vt/vtadmin/api_test.go b/go/vt/vtadmin/api_test.go index 4a68abd6b73..bb9cd62d788 100644 --- a/go/vt/vtadmin/api_test.go +++ b/go/vt/vtadmin/api_test.go @@ -32,6 +32,8 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/vt/vtenv" + _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/topo" @@ -545,8 +547,6 @@ func TestFindSchema(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -555,7 +555,7 @@ func TestFindSchema(t *testing.T) { clusters[i] = vtadmintestutil.BuildCluster(t, cfg) } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) defer api.Close() resp, err := api.FindSchema(ctx, tt.req) @@ -765,7 +765,7 @@ func TestFindSchema(t *testing.T) { }, ) - api := NewAPI([]*cluster.Cluster{c1, c2}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{c1, c2}, Options{}) defer api.Close() schema, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ @@ -860,12 +860,10 @@ func TestGetClusters(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() - api := NewAPI(tt.clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), tt.clusters, Options{}) resp, err := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) assert.NoError(t, err) @@ -943,7 +941,7 @@ func TestGetGates(t *testing.T) { }, } - api := NewAPI([]*cluster.Cluster{cluster1, cluster2}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{cluster1, cluster2}, Options{}) ctx := context.Background() resp, err := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) @@ -1050,8 +1048,6 @@ func TestGetKeyspace(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1065,7 +1061,7 @@ func TestGetKeyspace(t *testing.T) { testutil.AddShards(ctx, t, ts, shards...) topos[i] = ts vtctlds[i] = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) } @@ -1081,7 +1077,7 @@ func TestGetKeyspace(t *testing.T) { }) } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) ks, err := api.GetKeyspace(ctx, tt.req) if tt.shouldErr { assert.Error(t, err) @@ -1282,8 +1278,6 @@ func TestGetKeyspaces(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1309,10 +1303,10 @@ func TestGetKeyspaces(t *testing.T) { servers := []vtctlservicepb.VtctldServer{ testutil.NewVtctldServerWithTabletManagerClient(t, topos[0], nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }), testutil.NewVtctldServerWithTabletManagerClient(t, topos[1], nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }), } @@ -1334,7 +1328,7 @@ func TestGetKeyspaces(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetKeyspaces(ctx, tt.req) require.NoError(t, err) @@ -1536,15 +1530,13 @@ func TestGetSchema(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddTablets(ctx, t, tt.ts, nil, vtadmintestutil.TopodataTabletsFromVTAdminTablets(tt.tablets)...) @@ -1558,7 +1550,7 @@ func TestGetSchema(t *testing.T) { VtctldClient: client, Tablets: tt.tablets, }) - api := NewAPI([]*cluster.Cluster{c}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{c}, Options{}) defer api.Close() resp, err := api.GetSchema(ctx, tt.req) @@ -1688,7 +1680,7 @@ func TestGetSchema(t *testing.T) { }, ) - api := NewAPI([]*cluster.Cluster{c1, c2}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{c1, c2}, Options{}) defer api.Close() schema, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{ @@ -2177,8 +2169,6 @@ func TestGetSchemas(t *testing.T) { // Note that these test cases were written prior to the existence of // WithTestServers, so they are all written with the assumption that // there are exactly 2 clusters. - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -2198,10 +2188,10 @@ func TestGetSchemas(t *testing.T) { vtctlds := []vtctlservicepb.VtctldServer{ testutil.NewVtctldServerWithTabletManagerClient(t, topos[0], &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }), testutil.NewVtctldServerWithTabletManagerClient(t, topos[1], &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }), } @@ -2242,7 +2232,7 @@ func TestGetSchemas(t *testing.T) { }) } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) defer api.Close() resp, err := api.GetSchemas(ctx, tt.req) @@ -2463,7 +2453,7 @@ func TestGetSchemas(t *testing.T) { }, ) - api := NewAPI([]*cluster.Cluster{c1, c2}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{c1, c2}, Options{}) defer api.Close() resp, err := api.GetSchemas(context.Background(), &vtadminpb.GetSchemasRequest{ @@ -2624,8 +2614,6 @@ func TestGetSrvKeyspace(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2637,7 +2625,7 @@ func TestGetSrvKeyspace(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -2656,7 +2644,7 @@ func TestGetSrvKeyspace(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetSrvKeyspace(ctx, tt.req) if tt.shouldErr { @@ -2785,8 +2773,6 @@ func TestGetSrvKeyspaces(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -2801,7 +2787,7 @@ func TestGetSrvKeyspaces(t *testing.T) { } vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -2822,7 +2808,7 @@ func TestGetSrvKeyspaces(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetSrvKeyspaces(ctx, tt.req) if tt.shouldErr { @@ -2954,8 +2940,6 @@ func TestGetSrvVSchema(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -2966,7 +2950,7 @@ func TestGetSrvVSchema(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -2985,7 +2969,7 @@ func TestGetSrvVSchema(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetSrvVSchema(ctx, tt.req) if tt.shouldErr { @@ -3248,8 +3232,6 @@ func TestGetSrvVSchemas(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -3260,7 +3242,7 @@ func TestGetSrvVSchemas(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -3279,7 +3261,7 @@ func TestGetSrvVSchemas(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetSrvVSchemas(ctx, tt.req) if tt.shouldErr { @@ -3529,8 +3511,6 @@ func TestGetTablet(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3550,7 +3530,7 @@ func TestGetTablet(t *testing.T) { }) } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetTablet(ctx, tt.req) if tt.shouldErr { assert.Error(t, err) @@ -3724,8 +3704,6 @@ func TestGetTablets(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3745,7 +3723,7 @@ func TestGetTablets(t *testing.T) { }) } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetTablets(ctx, tt.req) if tt.shouldErr { assert.Error(t, err) @@ -3870,13 +3848,11 @@ func TestGetVSchema(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() clusters := []*cluster.Cluster{vtadmintestutil.BuildCluster(t, tt.clusterCfg)} - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetVSchema(ctx, tt.req) if tt.shouldErr { @@ -4196,8 +4172,6 @@ func TestGetVSchemas(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -4206,7 +4180,7 @@ func TestGetVSchemas(t *testing.T) { } clusters := vtadmintestutil.BuildClusters(t, tt.clusterCfgs...) - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.GetVSchemas(ctx, tt.req) if tt.shouldErr { @@ -4290,7 +4264,7 @@ func TestGetVtctlds(t *testing.T) { }, } - api := NewAPI([]*cluster.Cluster{cluster1, cluster2}, Options{}) + api := NewAPI(vtenv.NewTestEnv(), []*cluster.Cluster{cluster1, cluster2}, Options{}) ctx := context.Background() resp, err := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) @@ -4417,12 +4391,10 @@ func TestGetWorkflow(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() - api := NewAPI(vtadmintestutil.BuildClusters(t, tt.cfgs...), Options{}) + api := NewAPI(vtenv.NewTestEnv(), vtadmintestutil.BuildClusters(t, tt.cfgs...), Options{}) resp, err := api.GetWorkflow(ctx, tt.req) if tt.shouldErr { @@ -4856,12 +4828,10 @@ func TestGetWorkflows(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() - api := NewAPI(vtadmintestutil.BuildClusters(t, tt.cfgs...), Options{}) + api := NewAPI(vtenv.NewTestEnv(), vtadmintestutil.BuildClusters(t, tt.cfgs...), Options{}) resp, err := api.GetWorkflows(ctx, tt.req) if tt.shouldErr { @@ -5097,8 +5067,6 @@ func TestVTExplain(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -5112,7 +5080,7 @@ func TestVTExplain(t *testing.T) { } vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -5151,7 +5119,7 @@ func TestVTExplain(t *testing.T) { }), } - api := NewAPI(clusters, Options{}) + api := NewAPI(vtenv.NewTestEnv(), clusters, Options{}) resp, err := api.VTExplain(ctx, tt.req) if tt.expectedError != nil { @@ -5348,12 +5316,10 @@ func TestServeHTTP(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() - api := NewAPI(tt.clusters, Options{EnableDynamicClusters: tt.enableDynamicClusters}) + api := NewAPI(vtenv.NewTestEnv(), tt.clusters, Options{EnableDynamicClusters: tt.enableDynamicClusters}) // Copy the Cookie over to a new Request req := httptest.NewRequest(http.MethodGet, "/api/clusters", nil) diff --git a/go/vt/vtadmin/cache/cache_test.go b/go/vt/vtadmin/cache/cache_test.go index 93a6898db5d..277ac9524dc 100644 --- a/go/vt/vtadmin/cache/cache_test.go +++ b/go/vt/vtadmin/cache/cache_test.go @@ -67,7 +67,6 @@ func TestBackfillDuplicates(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -131,7 +130,6 @@ func TestBackfillTTL(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cache/refresh_test.go b/go/vt/vtadmin/cache/refresh_test.go index c12bb63ad6a..b16a10f34d5 100644 --- a/go/vt/vtadmin/cache/refresh_test.go +++ b/go/vt/vtadmin/cache/refresh_test.go @@ -65,7 +65,6 @@ func TestShouldRefreshFromIncomingContext(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -114,7 +113,6 @@ func TestShouldRefreshFromRequest(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/cluster.go b/go/vt/vtadmin/cluster/cluster.go index 6f8e355c326..966f43bc67c 100644 --- a/go/vt/vtadmin/cluster/cluster.go +++ b/go/vt/vtadmin/cluster/cluster.go @@ -35,6 +35,7 @@ import ( "vitess.io/vitess/go/sets" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/trace" + "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo/topoproto" @@ -46,6 +47,7 @@ import ( "vitess.io/vitess/go/vt/vtadmin/vtadminproto" "vitess.io/vitess/go/vt/vtadmin/vtctldclient" "vitess.io/vitess/go/vt/vtadmin/vtsql" + "vitess.io/vitess/go/vt/vtctl/schematools" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" @@ -351,6 +353,70 @@ func (c *Cluster) parseTablet(rows *sql.Rows) (*vtadminpb.Tablet, error) { return tablet, nil } +// ApplySchema applies a schema to the given keyspace in this cluster. +func (c *Cluster) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySchemaRequest) (*vtctldatapb.ApplySchemaResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.ApplySchema") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("sql", strings.Join(req.Sql, "; ")) + span.Annotate("ddl_strategy", req.DdlStrategy) + span.Annotate("uuid_list", strings.Join(req.UuidList, ", ")) + span.Annotate("migration_context", req.MigrationContext) + + if d, ok, err := protoutil.DurationFromProto(req.WaitReplicasTimeout); ok && err != nil { + span.Annotate("wait_replicas_timeout", d.String()) + } + + span.Annotate("caller_id", strings.Join( + []string{callerid.GetPrincipal(req.CallerId), callerid.GetComponent(req.CallerId), callerid.GetSubcomponent(req.CallerId)}, + ":", + )) + span.Annotate("batch_size", req.BatchSize) + + return c.Vtctld.ApplySchema(ctx, req) +} + +// CancelSchemaMigration cancels one or all migrations in a keyspace in this +// cluster, terminating any running ones as needed. +func (c *Cluster) CancelSchemaMigration(ctx context.Context, req *vtctldatapb.CancelSchemaMigrationRequest) (*vtctldatapb.CancelSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.CancelSchemaMigration") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + return c.Vtctld.CancelSchemaMigration(ctx, req) +} + +// CleanupSchemaMigration marks a schema migration in this cluster as ready for +// artifact cleanup. +func (c *Cluster) CleanupSchemaMigration(ctx context.Context, req *vtctldatapb.CleanupSchemaMigrationRequest) (*vtctldatapb.CleanupSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.CleanupSchemaMigration") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + return c.Vtctld.CleanupSchemaMigration(ctx, req) +} + +// CompleteSchemaMigration completes one or all migrations in a keyspace +// executed with --postpone-completion in this cluster. +func (c *Cluster) CompleteSchemaMigration(ctx context.Context, req *vtctldatapb.CompleteSchemaMigrationRequest) (*vtctldatapb.CompleteSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.CompleteSchemaMigration") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + return c.Vtctld.CompleteSchemaMigration(ctx, req) +} + // CreateKeyspace creates a keyspace in the given cluster, proxying a // CreateKeyspaceRequest to a vtctld in that cluster. func (c *Cluster) CreateKeyspace(ctx context.Context, req *vtctldatapb.CreateKeyspaceRequest) (*vtadminpb.Keyspace, error) { @@ -1543,6 +1609,51 @@ func (c *Cluster) GetSchemas(ctx context.Context, opts GetSchemaOptions) ([]*vta return schemas, nil } +// GetSchemaMigrations returns one or more schema migrations for a keyspace in +// this cluster. +func (c *Cluster) GetSchemaMigrations(ctx context.Context, req *vtctldatapb.GetSchemaMigrationsRequest) ([]*vtadminpb.SchemaMigration, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.GetSchemaMigrations") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + span.Annotate("migration_context", req.MigrationContext) + + if req.Status != vtctldatapb.SchemaMigration_UNKNOWN { + span.Annotate("status", schematools.SchemaMigrationStatusName(req.Status)) + } + + if d, ok, err := protoutil.DurationFromProto(req.Recent); ok && err == nil { + span.Annotate("recent", d.String()) + } + + switch req.Order { + case vtctldatapb.QueryOrdering_ASCENDING: + span.Annotate("order", "asc") + default: + span.Annotate("order", "desc") + } + + span.Annotate("skip", req.Skip) + span.Annotate("limit", req.Limit) + + resp, err := c.Vtctld.GetSchemaMigrations(ctx, req) + if err != nil { + return nil, err + } + + migrations := make([]*vtadminpb.SchemaMigration, len(resp.Migrations)) + for i, m := range resp.Migrations { + migrations[i] = &vtadminpb.SchemaMigration{ + Cluster: c.ToProto(), + SchemaMigration: m, + } + } + + return migrations, nil +} + // Note that for this function we use the tablets parameter, ignoring the // opts.Tablets value completely. func (c *Cluster) getSchemaFromTablets(ctx context.Context, keyspace string, tablets []*vtadminpb.Tablet, opts GetSchemaOptions) (*vtadminpb.Schema, error) { @@ -1956,6 +2067,19 @@ func (c *Cluster) GetWorkflows(ctx context.Context, keyspaces []string, opts Get }) } +// LaunchSchemaMigration starts a schema migration in the given keyspace in +// this cluster that was started with --postpone-launch. +func (c *Cluster) LaunchSchemaMigration(ctx context.Context, req *vtctldatapb.LaunchSchemaMigrationRequest) (*vtctldatapb.LaunchSchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.LaunchSchemaMigration") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + return c.Vtctld.LaunchSchemaMigration(ctx, req) +} + // PlannedFailoverShard fails over the shard either to a new primary or away // from an old primary. Both the current and candidate primaries must be // reachable and running. @@ -2297,6 +2421,19 @@ func (c *Cluster) reloadTabletSchemas(ctx context.Context, req *vtadminpb.Reload return results, nil } +// RetrySchemaMigration retries a schema migration in the given keyspace in +// this cluster. +func (c *Cluster) RetrySchemaMigration(ctx context.Context, req *vtctldatapb.RetrySchemaMigrationRequest) (*vtctldatapb.RetrySchemaMigrationResponse, error) { + span, ctx := trace.NewSpan(ctx, "Cluster.RetrySchemaMigration") + defer span.Finish() + + AnnotateSpan(c, span) + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + return c.Vtctld.RetrySchemaMigration(ctx, req) +} + // SetWritable toggles the writability of a tablet, setting it to either // read-write or read-only. func (c *Cluster) SetWritable(ctx context.Context, req *vtctldatapb.SetWritableRequest) error { diff --git a/go/vt/vtadmin/cluster/cluster_internal_test.go b/go/vt/vtadmin/cluster/cluster_internal_test.go index 696d7783d15..d39b81329ae 100644 --- a/go/vt/vtadmin/cluster/cluster_internal_test.go +++ b/go/vt/vtadmin/cluster/cluster_internal_test.go @@ -142,8 +142,6 @@ func TestDeleteTablets(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -331,8 +329,6 @@ func TestEmergencyFailoverShard(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -493,8 +489,6 @@ func Test_getShardSets(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -662,8 +656,6 @@ func TestPlannedFailoverShard(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -775,8 +767,6 @@ func TestRefreshState(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -899,8 +889,6 @@ func TestRefreshTabletReplicationSource(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1189,8 +1177,6 @@ func Test_reloadKeyspaceSchemas(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1516,8 +1502,6 @@ func Test_reloadShardSchemas(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1700,8 +1684,6 @@ func Test_reloadTabletSchemas(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1903,8 +1885,6 @@ func TestTabletExternallyPromoted(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/cluster_test.go b/go/vt/vtadmin/cluster/cluster_test.go index 53c3b4f71cd..4deba4ff05b 100644 --- a/go/vt/vtadmin/cluster/cluster_test.go +++ b/go/vt/vtadmin/cluster/cluster_test.go @@ -255,7 +255,6 @@ func TestCreateShard(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { defer tt.tc.Cluster.Close() _, err := tt.tc.Cluster.CreateShard(ctx, tt.req) @@ -595,8 +594,6 @@ func TestFindTablet(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -807,8 +804,6 @@ func TestFindTablets(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1195,8 +1190,6 @@ func TestFindWorkflows(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1474,7 +1467,6 @@ func TestGetCellInfos(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1557,7 +1549,6 @@ func TestGetCellsAliases(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1704,8 +1695,6 @@ func TestGetSchema(t *testing.T) { for i, tt := range tests { i := i - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2683,8 +2672,6 @@ func TestGetSchema(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2942,8 +2929,6 @@ func TestGetShardReplicationPositions(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3032,8 +3017,6 @@ func TestGetVSchema(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3191,8 +3174,6 @@ func TestGetWorkflow(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3358,8 +3339,6 @@ func TestGetWorkflows(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3436,8 +3415,6 @@ func TestSetWritable(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3583,8 +3560,6 @@ func TestToggleTabletReplication(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/config_test.go b/go/vt/vtadmin/cluster/config_test.go index a6246429585..252fa217bdc 100644 --- a/go/vt/vtadmin/cluster/config_test.go +++ b/go/vt/vtadmin/cluster/config_test.go @@ -131,8 +131,6 @@ func TestMergeConfig(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/discovery/discovery_consul_test.go b/go/vt/vtadmin/cluster/discovery/discovery_consul_test.go index a298e5906c9..701251a7910 100644 --- a/go/vt/vtadmin/cluster/discovery/discovery_consul_test.go +++ b/go/vt/vtadmin/cluster/discovery/discovery_consul_test.go @@ -233,8 +233,6 @@ func TestConsulDiscoverVTGates(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -344,8 +342,6 @@ func TestConsulDiscoverVTGate(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -442,8 +438,6 @@ func TestConsulDiscoverVTGateAddr(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/discovery/discovery_dynamic_test.go b/go/vt/vtadmin/cluster/discovery/discovery_dynamic_test.go index f2c2874fc6f..8e9c8c9a043 100644 --- a/go/vt/vtadmin/cluster/discovery/discovery_dynamic_test.go +++ b/go/vt/vtadmin/cluster/discovery/discovery_dynamic_test.go @@ -93,8 +93,6 @@ func TestDynamicDiscoverVTGate(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() disco := &DynamicDiscovery{} @@ -237,8 +235,6 @@ func TestDynamicDiscoverVTGates(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -330,8 +326,6 @@ func TestDynamicDiscoverVtctld(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -475,8 +469,6 @@ func TestDynamicDiscoverVtctlds(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/discovery/discovery_static_file_test.go b/go/vt/vtadmin/cluster/discovery/discovery_static_file_test.go index 344ee32863d..46ae2aa2577 100644 --- a/go/vt/vtadmin/cluster/discovery/discovery_static_file_test.go +++ b/go/vt/vtadmin/cluster/discovery/discovery_static_file_test.go @@ -93,8 +93,6 @@ func TestDiscoverVTGate(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -238,8 +236,6 @@ func TestDiscoverVTGates(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -331,8 +327,6 @@ func TestDiscoverVtctld(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -476,8 +470,6 @@ func TestDiscoverVtctlds(t *testing.T) { ctx := context.Background() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/discovery/discovery_test.go b/go/vt/vtadmin/cluster/discovery/discovery_test.go index 3ee67cbc9df..76616d6514c 100644 --- a/go/vt/vtadmin/cluster/discovery/discovery_test.go +++ b/go/vt/vtadmin/cluster/discovery/discovery_test.go @@ -50,8 +50,6 @@ func TestNew(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/dynamic/cluster_test.go b/go/vt/vtadmin/cluster/dynamic/cluster_test.go index 54b2b46d44b..4de01a48dda 100644 --- a/go/vt/vtadmin/cluster/dynamic/cluster_test.go +++ b/go/vt/vtadmin/cluster/dynamic/cluster_test.go @@ -53,7 +53,6 @@ func TestClusterFromString(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/file_config_test.go b/go/vt/vtadmin/cluster/file_config_test.go index e7fd2393cfb..2536a7aa5b9 100644 --- a/go/vt/vtadmin/cluster/file_config_test.go +++ b/go/vt/vtadmin/cluster/file_config_test.go @@ -215,7 +215,6 @@ name="devcluster"`, } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -380,8 +379,6 @@ func TestCombine(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/flags_test.go b/go/vt/vtadmin/cluster/flags_test.go index 676c0243029..33a3cdf416b 100644 --- a/go/vt/vtadmin/cluster/flags_test.go +++ b/go/vt/vtadmin/cluster/flags_test.go @@ -98,8 +98,6 @@ func TestMergeFlagsByImpl(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/internal/caches/schemacache/cache_test.go b/go/vt/vtadmin/cluster/internal/caches/schemacache/cache_test.go index 1c7d76edeb0..2b22396d827 100644 --- a/go/vt/vtadmin/cluster/internal/caches/schemacache/cache_test.go +++ b/go/vt/vtadmin/cluster/internal/caches/schemacache/cache_test.go @@ -131,7 +131,6 @@ func TestLoadOptions(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/cluster/resolver/resolver_test.go b/go/vt/vtadmin/cluster/resolver/resolver_test.go index fd1dbab5f13..f1f13f8200c 100644 --- a/go/vt/vtadmin/cluster/resolver/resolver_test.go +++ b/go/vt/vtadmin/cluster/resolver/resolver_test.go @@ -291,7 +291,6 @@ func TestBuild(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/credentials/credentials_test.go b/go/vt/vtadmin/credentials/credentials_test.go index bc85c4884a2..e8cd2c0d197 100644 --- a/go/vt/vtadmin/credentials/credentials_test.go +++ b/go/vt/vtadmin/credentials/credentials_test.go @@ -56,8 +56,6 @@ func Test_loadCredentials(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/http/request.go b/go/vt/vtadmin/http/request.go index a9cc7a16965..9d38b88ed91 100644 --- a/go/vt/vtadmin/http/request.go +++ b/go/vt/vtadmin/http/request.go @@ -77,6 +77,25 @@ func (r Request) ParseQueryParamAsUint32(name string, defaultVal uint32) (uint32 return defaultVal, nil } +// ParseQueryParamAsInt32 attempts to parse the query parameter of the given +// name into a uint32 value. If the parameter is not set, the provided default +// value is returned. +func (r Request) ParseQueryParamAsInt32(name string, defaultVal int32) (int32, error) { + if param := r.URL.Query().Get(name); param != "" { + val, err := strconv.ParseInt(param, 10, 32) + if err != nil { + return defaultVal, &errors.BadRequest{ + Err: err, + ErrDetails: fmt.Sprintf("could not parse query parameter %s (= %v) into int32 value", name, param), + } + } + + return int32(val), nil + } + + return defaultVal, nil +} + // Vars is a mapping of the route variable values in a given request. // // See (gorilla/mux).Vars for details. We define a type here to add some diff --git a/go/vt/vtadmin/http/request_test.go b/go/vt/vtadmin/http/request_test.go index ba235a4877f..0a7e602c163 100644 --- a/go/vt/vtadmin/http/request_test.go +++ b/go/vt/vtadmin/http/request_test.go @@ -71,8 +71,6 @@ func TestParseQueryParamAsBool(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/http/schema_migrations.go b/go/vt/vtadmin/http/schema_migrations.go new file mode 100644 index 00000000000..e0207989648 --- /dev/null +++ b/go/vt/vtadmin/http/schema_migrations.go @@ -0,0 +1,144 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package http + +import ( + "context" + "encoding/json" + "io" + + "github.com/gorilla/mux" + + "vitess.io/vitess/go/vt/vtadmin/errors" + + vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// ApplySchema implements the http wrapper for POST /migration/{cluster_id}/{keyspace}/. +func ApplySchema(ctx context.Context, r Request, api *API) *JSONResponse { + decoder := json.NewDecoder(r.Body) + defer r.Body.Close() + + var req vtctldatapb.ApplySchemaRequest + if err := decoder.Decode(&req); err != nil { + return NewJSONResponse(nil, &errors.BadRequest{ + Err: err, + }) + } + + vars := mux.Vars(r.Request) + req.Keyspace = vars["keyspace"] + + resp, err := api.server.ApplySchema(ctx, &vtadminpb.ApplySchemaRequest{ + ClusterId: vars["cluster_id"], + Request: &req, + }) + + return NewJSONResponse(resp, err) +} + +// CancelSchemaMigration implements the http wrapper for /migration/{cluster_id}/{keyspace}/cancel[?uuid]. +func CancelSchemaMigration(ctx context.Context, r Request, api *API) *JSONResponse { + vars := mux.Vars(r.Request) + + resp, err := api.server.CancelSchemaMigration(ctx, &vtadminpb.CancelSchemaMigrationRequest{ + ClusterId: vars["cluster_id"], + Request: &vtctldatapb.CancelSchemaMigrationRequest{ + Keyspace: vars["keyspace"], + Uuid: r.URL.Query().Get("uuid"), + }, + }) + + return NewJSONResponse(resp, err) +} + +// CleanupSchemaMigration implements the http wrapper for /migration/{cluster_id}/{keyspace}/cleanup[?uuid]. +func CleanupSchemaMigration(ctx context.Context, r Request, api *API) *JSONResponse { + vars := mux.Vars(r.Request) + + resp, err := api.server.CleanupSchemaMigration(ctx, &vtadminpb.CleanupSchemaMigrationRequest{ + ClusterId: vars["cluster_id"], + Request: &vtctldatapb.CleanupSchemaMigrationRequest{ + Keyspace: vars["keyspace"], + Uuid: r.URL.Query().Get("uuid"), + }, + }) + + return NewJSONResponse(resp, err) +} + +// CompleteSchemaMigration implements the http wrapper for /migration/{cluster_id}/{keyspace}/complete[?uuid]. +func CompleteSchemaMigration(ctx context.Context, r Request, api *API) *JSONResponse { + vars := mux.Vars(r.Request) + + resp, err := api.server.CompleteSchemaMigration(ctx, &vtadminpb.CompleteSchemaMigrationRequest{ + ClusterId: vars["cluster_id"], + Request: &vtctldatapb.CompleteSchemaMigrationRequest{ + Keyspace: vars["keyspace"], + Uuid: r.URL.Query().Get("uuid"), + }, + }) + + return NewJSONResponse(resp, err) +} + +// GetSchemaMigrations implements the http wrapper for /migrations/. +func GetSchemaMigrations(ctx context.Context, r Request, api *API) *JSONResponse { + decoder := json.NewDecoder(r.Body) + defer r.Body.Close() + + var req vtadminpb.GetSchemaMigrationsRequest + if err := decoder.Decode(&req); err != nil && err != io.EOF { + return NewJSONResponse(nil, &errors.BadRequest{ + Err: err, + }) + } + + resp, err := api.server.GetSchemaMigrations(ctx, &req) + return NewJSONResponse(resp, err) +} + +// LaunchSchemaMigration implements the http wrapper for /migration/{cluster_id}/{keyspace}/launch[?uuid]. +func LaunchSchemaMigration(ctx context.Context, r Request, api *API) *JSONResponse { + vars := mux.Vars(r.Request) + + resp, err := api.server.LaunchSchemaMigration(ctx, &vtadminpb.LaunchSchemaMigrationRequest{ + ClusterId: vars["cluster_id"], + Request: &vtctldatapb.LaunchSchemaMigrationRequest{ + Keyspace: vars["keyspace"], + Uuid: r.URL.Query().Get("uuid"), + }, + }) + + return NewJSONResponse(resp, err) +} + +// RetrySchemaMigration implements the http wrapper for /migration/{cluster_id}/{keyspace}/retry[?uuid]. +func RetrySchemaMigration(ctx context.Context, r Request, api *API) *JSONResponse { + vars := mux.Vars(r.Request) + + resp, err := api.server.RetrySchemaMigration(ctx, &vtadminpb.RetrySchemaMigrationRequest{ + ClusterId: vars["cluster_id"], + Request: &vtctldatapb.RetrySchemaMigrationRequest{ + Keyspace: vars["keyspace"], + Uuid: r.URL.Query().Get("uuid"), + }, + }) + + return NewJSONResponse(resp, err) +} diff --git a/go/vt/vtadmin/http/schemas.go b/go/vt/vtadmin/http/schemas.go index 4b157720cb7..c97ff45ac5c 100644 --- a/go/vt/vtadmin/http/schemas.go +++ b/go/vt/vtadmin/http/schemas.go @@ -101,7 +101,7 @@ func getTableSizeOpts(r Request) (*vtadminpb.GetSchemaTableSizeOptions, error) { // ReloadSchemas implements the http wrapper for /schemas/reload func ReloadSchemas(ctx context.Context, r Request, api *API) *JSONResponse { - concurrency, err := r.ParseQueryParamAsUint32("concurrency", 0) + concurrency, err := r.ParseQueryParamAsInt32("concurrency", 0) if err != nil { return NewJSONResponse(nil, err) } diff --git a/go/vt/vtadmin/http/shards.go b/go/vt/vtadmin/http/shards.go index 56d22742be6..776944d0a16 100644 --- a/go/vt/vtadmin/http/shards.go +++ b/go/vt/vtadmin/http/shards.go @@ -178,7 +178,7 @@ func ReloadSchemaShard(ctx context.Context, r Request, api *API) *JSONResponse { var params struct { WaitPosition string `json:"wait_position"` IncludePrimary bool `json:"include_primary"` - Concurrency uint32 `json:"concurrency"` + Concurrency int32 `json:"concurrency"` } if err := decoder.Decode(¶ms); err != nil { diff --git a/go/vt/vtadmin/internal/backoff/backoff_test.go b/go/vt/vtadmin/internal/backoff/backoff_test.go index 687ab936cd7..daa2f8078ec 100644 --- a/go/vt/vtadmin/internal/backoff/backoff_test.go +++ b/go/vt/vtadmin/internal/backoff/backoff_test.go @@ -51,7 +51,6 @@ func TestExponentialBackoff(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/rbac/authentication.go b/go/vt/vtadmin/rbac/authentication.go index 374f95b636d..9e5e3aebb96 100644 --- a/go/vt/vtadmin/rbac/authentication.go +++ b/go/vt/vtadmin/rbac/authentication.go @@ -99,7 +99,13 @@ type actorkey struct{} // NewContext returns a context with the given actor stored in it. This is used // to pass actor information from the authentication middleware and interceptors // to the actual vtadmin api methods. +// +// If actor is nil, the context is returned with no actor attached. func NewContext(ctx context.Context, actor *Actor) context.Context { + if actor == nil { + return ctx + } + return context.WithValue(ctx, actorkey{}, actor) } diff --git a/go/vt/vtadmin/rbac/authorizer_test.go b/go/vt/vtadmin/rbac/authorizer_test.go index c61f4c7fc59..62abd76cc99 100644 --- a/go/vt/vtadmin/rbac/authorizer_test.go +++ b/go/vt/vtadmin/rbac/authorizer_test.go @@ -104,8 +104,6 @@ func TestIsAuthorized(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtadmin/rbac/rbac.go b/go/vt/vtadmin/rbac/rbac.go index 12d23b3ac20..3f15a0d22d2 100644 --- a/go/vt/vtadmin/rbac/rbac.go +++ b/go/vt/vtadmin/rbac/rbac.go @@ -65,12 +65,22 @@ type Action string const ( /* generic actions */ + CancelAction Action = "cancel" CreateAction Action = "create" DeleteAction Action = "delete" GetAction Action = "get" PingAction Action = "ping" PutAction Action = "put" ReloadAction Action = "reload" + RetryAction Action = "retry" + + // cancel, complete, cleanup, launch, retry + + /* schema-migration-specific actions */ + + CleanupSchemaMigrationAction Action = "cleanup_schema_migration" + CompleteSchemaMigrationAction Action = "complete_schema_migration" + LaunchSchemaMigrationAction Action = "launch_schema_migration" /* shard-specific actions */ @@ -112,10 +122,14 @@ const ( SrvKeyspaceResource Resource = "SrvKeyspace" SrvVSchemaResource Resource = "SrvVSchema" + /* schema resources */ + + SchemaResource Resource = "Schema" + SchemaMigrationResource Resource = "SchemaMigration" + /* misc resources */ BackupResource Resource = "Backup" - SchemaResource Resource = "Schema" ShardReplicationPositionResource Resource = "ShardReplicationPosition" WorkflowResource Resource = "Workflow" diff --git a/go/vt/vtadmin/testutil/authztestgen/config.json b/go/vt/vtadmin/testutil/authztestgen/config.json index 01b0da66465..eb229625d3b 100644 --- a/go/vt/vtadmin/testutil/authztestgen/config.json +++ b/go/vt/vtadmin/testutil/authztestgen/config.json @@ -5,6 +5,26 @@ "id": "test", "name": "test", "vtctldclient_mock_data": [ + { + "field": "ApplySchemaResults", + "type": "map[string]struct{\nResponse *vtctldatapb.ApplySchemaResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.ApplySchemaResponse{},\n}," + }, + { + "field": "CancelSchemaMigrationResults", + "type": "map[string]struct{\nResponse *vtctldatapb.CancelSchemaMigrationResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.CancelSchemaMigrationResponse{},\n}," + }, + { + "field": "CleanupSchemaMigrationResults", + "type": "map[string]struct{\nResponse *vtctldatapb.CleanupSchemaMigrationResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.CleanupSchemaMigrationResponse{},\n}," + }, + { + "field": "CompleteSchemaMigrationResults", + "type": "map[string]struct{\nResponse *vtctldatapb.CompleteSchemaMigrationResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.CompleteSchemaMigrationResponse{},\n}," + }, { "field": "DeleteShardsResults", "type": "map[string]error", @@ -50,6 +70,11 @@ "type": "&struct{\nKeyspaces []*vtctldatapb.Keyspace\nError error}", "value": "Keyspaces: []*vtctldatapb.Keyspace{\n{\nName: \"test\",\nKeyspace: &topodatapb.Keyspace{},\n},\n}," }, + { + "field": "GetSchemaMigrationsResults", + "type": "map[string]struct{\nResponse *vtctldatapb.GetSchemaMigrationsResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.GetSchemaMigrationsResponse{\nMigrations: []*vtctldatapb.SchemaMigration{\n{},\n},\n},\n}," + }, { "field": "GetSchemaResults", "type": "map[string]struct{\nResponse *vtctldatapb.GetSchemaResponse\nError error}", @@ -71,6 +96,11 @@ "type": "map[string]struct{\nResponse *vtctldatapb.GetWorkflowsResponse\nError error}", "value": "\"test\": {\nResponse: &vtctldatapb.GetWorkflowsResponse{\nWorkflows: []*vtctldatapb.Workflow{\n{\nName: \"testworkflow\",\n},\n},\n}}," }, + { + "field": "LaunchSchemaMigrationResults", + "type": "map[string]struct{\nResponse *vtctldatapb.LaunchSchemaMigrationResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.LaunchSchemaMigrationResponse{},\n}," + }, { "field": "PingTabletResults", "type": "map[string]error", @@ -96,6 +126,11 @@ "type": "map[string]struct{\nResponse *vtctldatapb.ReparentTabletResponse\nError error\n}", "value": "\"zone1-0000000100\": {\nResponse: &vtctldatapb.ReparentTabletResponse{},\n}," }, + { + "field": "RetrySchemaMigrationResults", + "type": "map[string]struct{\nResponse *vtctldatapb.RetrySchemaMigrationResponse\nError error}", + "value": "\"test\": {\nResponse: &vtctldatapb.RetrySchemaMigrationResponse{},\n}," + }, { "field": "RunHealthCheckResults", "type": "map[string]error", @@ -182,6 +217,11 @@ "type": "&struct{\nKeyspaces []*vtctldatapb.Keyspace\nError error}", "value": "Keyspaces: []*vtctldatapb.Keyspace{\n{\nName: \"otherks\",\nKeyspace: &topodatapb.Keyspace{},\n},\n}," }, + { + "field": "GetSchemaMigrationsResults", + "type": "map[string]struct{\nResponse *vtctldatapb.GetSchemaMigrationsResponse\nError error}", + "value": "\"otherks\": {\nResponse: &vtctldatapb.GetSchemaMigrationsResponse{\nMigrations: []*vtctldatapb.SchemaMigration{\n{}, {}, {},\n},\n},\n}," + }, { "field": "GetSchemaResults", "type": "map[string]struct{\nResponse *vtctldatapb.GetSchemaResponse\nError error}", @@ -225,6 +265,138 @@ } ], "tests": [ + { + "method": "ApplySchema", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["create"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.ApplySchemaRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.ApplySchemaRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, + { + "method": "CancelSchemaMigration", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["cancel"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.CancelSchemaMigrationRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.CancelSchemaMigrationRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, + { + "method": "CleanupSchemaMigration", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["cleanup_schema_migration"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.CleanupSchemaMigrationRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.CleanupSchemaMigrationRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, + { + "method": "CompleteSchemaMigration", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["complete_schema_migration"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.CompleteSchemaMigrationRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.CompleteSchemaMigrationRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, { "method": "CreateKeyspace", "rules": [ @@ -798,6 +970,54 @@ } ] }, + { + "method": "GetSchemaMigrations", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["get"], + "subjects": ["user:allowed-all"], + "clusters": ["*"] + }, + { + "resource": "SchemaMigration", + "actions": ["get"], + "subjects": ["user:allowed-other"], + "clusters": ["other"] + } + ], + "request": "&vtadminpb.GetSchemaMigrationsRequest{}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "unauthorized"}, + "is_permitted": false, + "include_error_var": true, + "assertions": [ + "assert.NoError(t, err)", + "assert.Empty(t, resp.SchemaMigrations, $$)" + ] + }, + { + "name": "partial access", + "actor": {"name": "allowed-other"}, + "is_permitted": true, + "assertions": [ + "assert.NotEmpty(t, resp.SchemaMigrations, $$)", + "assert.Len(t, resp.SchemaMigrations, 3, \"'other' actor should be able to see the 3 migrations in cluster 'other'\")" + ] + }, + { + "name": "full access", + "actor": {"name": "allowed-all"}, + "is_permitted": true, + "assertions": [ + "assert.NotEmpty(t, resp.SchemaMigrations, $$)", + "assert.Len(t, resp.SchemaMigrations, 4, \"'all' actor should be able to see migrations in all clusters\")" + ] + } + ] + }, { "method": "GetSchema", "rules": [ @@ -1350,6 +1570,39 @@ } ] }, + { + "method": "LaunchSchemaMigration", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["launch_schema_migration"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.LaunchSchemaMigrationRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.LaunchSchemaMigrationRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, { "method": "PingTablet", "rules": [ @@ -1527,6 +1780,39 @@ } ] }, + { + "method": "RetrySchemaMigration", + "rules": [ + { + "resource": "SchemaMigration", + "actions": ["retry"], + "subjects": ["user:allowed"], + "clusters": ["*"] + } + ], + "request": "&vtadminpb.RetrySchemaMigrationRequest{\nClusterId: \"test\",\nRequest: &vtctldatapb.RetrySchemaMigrationRequest{\nKeyspace: \"test\",\n},\n}", + "cases": [ + { + "name": "unauthorized actor", + "actor": {"name": "other"}, + "include_error_var": true, + "assertions": [ + "assert.Error(t, err, $$)", + "assert.Nil(t, resp, $$)" + ] + }, + { + "name": "authorized actor", + "actor": {"name": "allowed"}, + "include_error_var": true, + "is_permitted": true, + "assertions": [ + "require.NoError(t, err)", + "assert.NotNil(t, resp, $$)" + ] + } + ] + }, { "method": "RunHealthCheck", "rules": [ diff --git a/go/vt/vtadmin/testutil/authztestgen/template.go b/go/vt/vtadmin/testutil/authztestgen/template.go index 518d710fb3f..1c96d7aeede 100644 --- a/go/vt/vtadmin/testutil/authztestgen/template.go +++ b/go/vt/vtadmin/testutil/authztestgen/template.go @@ -51,6 +51,7 @@ import ( "vitess.io/vitess/go/vt/vtadmin/rbac" "vitess.io/vitess/go/vt/vtadmin/testutil" "vitess.io/vitess/go/vt/vtadmin/vtctldclient/fakevtctldclient" + "vitess.io/vitess/go/vt/vtenv" logutilpb "vitess.io/vitess/go/vt/proto/logutil" mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" @@ -88,7 +89,7 @@ func Test{{ .Method }}(t *testing.T) { require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) {{ if not .SerializeCases }} - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -101,7 +102,7 @@ func Test{{ .Method }}(t *testing.T) { t.Run("{{ .Name }}", func(t *testing.T) { t.Parallel() {{ if $test.SerializeCases }} - api := vtadmin.NewAPI(testClusters(t), opts) + api := vtadmin.NewAPI(vtenv.NewTestEnv(), testClusters(t), opts) t.Cleanup(func() { if err := api.Close(); err != nil { t.Logf("api did not close cleanly: %s", err.Error()) @@ -111,9 +112,8 @@ func Test{{ .Method }}(t *testing.T) { {{ getActor .Actor }} ctx := context.Background() - if actor != nil { - ctx = rbac.NewContext(ctx, actor) - } + ctx = rbac.NewContext(ctx, actor) + {{ if .IncludeErrorVar }} resp, err := api.{{ $test.Method }}(ctx, {{ $test.Request }}) {{ else }} @@ -165,7 +165,7 @@ func testClusters(t testing.TB) []*cluster.Cluster { Config: &cluster.Config{ TopoReadPoolConfig: &cluster.RPCPoolConfig{ Size: 100, - WaitTimeout: time.Millisecond * 50, + WaitTimeout: time.Millisecond * 500, }, }, }, diff --git a/go/vt/vtadmin/testutil/cluster.go b/go/vt/vtadmin/testutil/cluster.go index 9141d6b0c22..793ef3b2142 100644 --- a/go/vt/vtadmin/testutil/cluster.go +++ b/go/vt/vtadmin/testutil/cluster.go @@ -41,6 +41,7 @@ import ( grpcvtctldtestutil "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" "vitess.io/vitess/go/vt/vtctl/localvtctldclient" "vitess.io/vitess/go/vt/vtctl/vtctldclient" + "vitess.io/vitess/go/vt/vtenv" vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" @@ -169,7 +170,7 @@ func BuildIntegrationTestCluster(t testing.TB, ctx context.Context, c *vtadminpb ts, factory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := grpcvtctldtestutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) localclient := localvtctldclient.New(vtctld) diff --git a/go/vt/vtadmin/vtctldclient/fakevtctldclient/vtctldclient.go b/go/vt/vtadmin/vtctldclient/fakevtctldclient/vtctldclient.go index e2701a1f594..428d38fc8e0 100644 --- a/go/vt/vtadmin/vtctldclient/fakevtctldclient/vtctldclient.go +++ b/go/vt/vtadmin/vtctldclient/fakevtctldclient/vtctldclient.go @@ -38,6 +38,22 @@ import ( type VtctldClient struct { vtctldclient.VtctldClient + ApplySchemaResults map[string]struct { + Response *vtctldatapb.ApplySchemaResponse + Error error + } + CancelSchemaMigrationResults map[string]struct { + Response *vtctldatapb.CancelSchemaMigrationResponse + Error error + } + CleanupSchemaMigrationResults map[string]struct { + Response *vtctldatapb.CleanupSchemaMigrationResponse + Error error + } + CompleteSchemaMigrationResults map[string]struct { + Response *vtctldatapb.CompleteSchemaMigrationResponse + Error error + } CreateKeyspaceShouldErr bool CreateShardShouldErr bool DeleteKeyspaceShouldErr bool @@ -77,6 +93,10 @@ type VtctldClient struct { Keyspaces []*vtctldatapb.Keyspace Error error } + GetSchemaMigrationsResults map[string]struct { + Response *vtctldatapb.GetSchemaMigrationsResponse + Error error + } GetSchemaResults map[string]struct { Response *vtctldatapb.GetSchemaResponse Error error @@ -93,6 +113,10 @@ type VtctldClient struct { Response *vtctldatapb.GetWorkflowsResponse Error error } + LaunchSchemaMigrationResults map[string]struct { + Response *vtctldatapb.LaunchSchemaMigrationResponse + Error error + } PingTabletResults map[string]error PlannedReparentShardResults map[string]struct { Response *vtctldatapb.PlannedReparentShardResponse @@ -115,6 +139,10 @@ type VtctldClient struct { Response *vtctldatapb.ReparentTabletResponse Error error } + RetrySchemaMigrationResults map[string]struct { + Response *vtctldatapb.RetrySchemaMigrationResponse + Error error + } RunHealthCheckResults map[string]error SetWritableResults map[string]error ShardReplicationPositionsResults map[string]struct { @@ -152,6 +180,66 @@ var _ vtctldclient.VtctldClient = (*VtctldClient)(nil) // Close is part of the vtctldclient.VtctldClient interface. func (fake *VtctldClient) Close() error { return nil } +// ApplySchema is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySchemaRequest, opts ...grpc.CallOption) (*vtctldatapb.ApplySchemaResponse, error) { + if fake.ApplySchemaResults == nil { + return nil, fmt.Errorf("%w: ApplySchemaResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.ApplySchemaResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + +// CancelSchemaMigration is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) CancelSchemaMigration(ctx context.Context, req *vtctldatapb.CancelSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.CancelSchemaMigrationResponse, error) { + if fake.CancelSchemaMigrationResults == nil { + return nil, fmt.Errorf("%w: CancelSchemaMigrationResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.CancelSchemaMigrationResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + +// CleanupSchemaMigration is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) CleanupSchemaMigration(ctx context.Context, req *vtctldatapb.CleanupSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.CleanupSchemaMigrationResponse, error) { + if fake.CleanupSchemaMigrationResults == nil { + return nil, fmt.Errorf("%w: CleanupSchemaMigrationResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.CleanupSchemaMigrationResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + +// CompleteSchemaMigration is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) CompleteSchemaMigration(ctx context.Context, req *vtctldatapb.CompleteSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.CompleteSchemaMigrationResponse, error) { + if fake.CompleteSchemaMigrationResults == nil { + return nil, fmt.Errorf("%w: CompleteSchemaMigrationResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.CompleteSchemaMigrationResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + // CreateKeyspace is part of the vtctldclient.VtctldClient interface. func (fake *VtctldClient) CreateKeyspace(ctx context.Context, req *vtctldatapb.CreateKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.CreateKeyspaceResponse, error) { if fake.CreateKeyspaceShouldErr { @@ -159,7 +247,6 @@ func (fake *VtctldClient) CreateKeyspace(ctx context.Context, req *vtctldatapb.C } ks := &topodatapb.Keyspace{ - ServedFroms: req.ServedFroms, KeyspaceType: req.Type, BaseKeyspace: req.BaseKeyspace, SnapshotTime: req.SnapshotTime, @@ -346,6 +433,20 @@ func (fake *VtctldClient) GetKeyspaces(ctx context.Context, req *vtctldatapb.Get }, nil } +// GetSchemaMigrations is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) GetSchemaMigrations(ctx context.Context, req *vtctldatapb.GetSchemaMigrationsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetSchemaMigrationsResponse, error) { + if fake.GetSchemaMigrationsResults == nil { + return nil, fmt.Errorf("%w: GetSchemaMigrationsResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + if result, ok := fake.GetSchemaMigrationsResults[key]; ok { + return result.Response, result.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + // GetSchema is part of the vtctldclient.VtctldClient interface. func (fake *VtctldClient) GetSchema(ctx context.Context, req *vtctldatapb.GetSchemaRequest, opts ...grpc.CallOption) (*vtctldatapb.GetSchemaResponse, error) { if fake.GetSchemaResults == nil { @@ -429,6 +530,21 @@ func (fake *VtctldClient) GetWorkflows(ctx context.Context, req *vtctldatapb.Get return nil, fmt.Errorf("%w: no result set for keyspace %s", assert.AnError, req.Keyspace) } +// LaunchSchemaMigration is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) LaunchSchemaMigration(ctx context.Context, req *vtctldatapb.LaunchSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.LaunchSchemaMigrationResponse, error) { + if fake.LaunchSchemaMigrationResults == nil { + return nil, fmt.Errorf("%w: LaunchSchemaMigrationResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.LaunchSchemaMigrationResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + // PingTablet is part of the vtctldclient.VtctldClient interface. func (fake *VtctldClient) PingTablet(ctx context.Context, req *vtctldatapb.PingTabletRequest, opts ...grpc.CallOption) (*vtctldatapb.PingTabletResponse, error) { if fake.PingTabletResults == nil { @@ -534,6 +650,21 @@ func (fake *VtctldClient) ReparentTablet(ctx context.Context, req *vtctldatapb.R return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) } +// RetrySchemaMigration is part of the vtctldclient.VtctldClient interface. +func (fake *VtctldClient) RetrySchemaMigration(ctx context.Context, req *vtctldatapb.RetrySchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.RetrySchemaMigrationResponse, error) { + if fake.RetrySchemaMigrationResults == nil { + return nil, fmt.Errorf("%w: RetrySchemaMigrationResults not set on fake vtctldclient", assert.AnError) + } + + key := req.Keyspace + + if resp, ok := fake.RetrySchemaMigrationResults[key]; ok { + return resp.Response, resp.Error + } + + return nil, fmt.Errorf("%w: no result set for %s", assert.AnError, key) +} + // RunHealthCheck is part of the vtctldclient.VtctldClient interface. func (fake *VtctldClient) RunHealthCheck(ctx context.Context, req *vtctldatapb.RunHealthCheckRequest, opts ...grpc.CallOption) (*vtctldatapb.RunHealthCheckResponse, error) { if fake.RunHealthCheckResults == nil { diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go index 77b7f267a30..369a8138b5a 100644 --- a/go/vt/vtcombo/tablet_map.go +++ b/go/vt/vtcombo/tablet_map.go @@ -34,6 +34,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/queryservice" @@ -75,6 +76,7 @@ var tabletMap map[uint32]*comboTablet // it to the map. If it's a primary tablet, it also issues a TER. func CreateTablet( ctx context.Context, + env *vtenv.Environment, ts *topo.Server, cell string, uid uint32, @@ -89,7 +91,7 @@ func CreateTablet( } log.Infof("Creating %v tablet %v for %v/%v", tabletType, topoproto.TabletAliasString(alias), keyspace, shard) - controller := tabletserver.NewServer(ctx, topoproto.TabletAliasString(alias), ts, alias) + controller := tabletserver.NewServer(ctx, env, topoproto.TabletAliasString(alias), ts, alias) initTabletType := tabletType if tabletType == topodatapb.TabletType_PRIMARY { initTabletType = topodatapb.TabletType_REPLICA @@ -100,6 +102,7 @@ func CreateTablet( } tm := &tabletmanager.TabletManager{ BatchCtx: context.Background(), + Env: env, TopoServer: ts, MysqlDaemon: mysqld, DBConfigs: dbcfgs, @@ -117,7 +120,7 @@ func CreateTablet( Type: initTabletType, DbNameOverride: dbname, } - if err := tm.Start(tablet, 0); err != nil { + if err := tm.Start(tablet, nil); err != nil { return err } @@ -163,6 +166,7 @@ func InitRoutingRules( // InitTabletMap creates the action tms and associated data structures // for all tablets, based on the vttest proto parameter. func InitTabletMap( + env *vtenv.Environment, ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlctl.MysqlDaemon, @@ -184,11 +188,11 @@ func InitTabletMap( }) // iterate through the keyspaces - wr := wrangler.New(logutil.NewConsoleLogger(), ts, nil) + wr := wrangler.New(env, logutil.NewConsoleLogger(), ts, nil) var uid uint32 = 1 for _, kpb := range tpb.Keyspaces { var err error - uid, err = CreateKs(ctx, ts, tpb, mysqld, dbcfgs, schemaDir, kpb, ensureDatabase, uid, wr) + uid, err = CreateKs(ctx, env, ts, tpb, mysqld, dbcfgs, schemaDir, kpb, ensureDatabase, uid, wr) if err != nil { return 0, err } @@ -279,6 +283,7 @@ func DeleteKs( // CreateKs creates keyspace, shards and tablets with mysql database func CreateKs( ctx context.Context, + env *vtenv.Environment, ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlctl.MysqlDaemon, @@ -291,95 +296,72 @@ func CreateKs( ) (uint32, error) { keyspace := kpb.Name - if kpb.ServedFrom != "" { - // if we have a redirect, create a completely redirected - // keyspace and no tablet - if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{ - ServedFroms: []*topodatapb.Keyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: kpb.ServedFrom, - }, - { - TabletType: topodatapb.TabletType_REPLICA, - Keyspace: kpb.ServedFrom, - }, - { - TabletType: topodatapb.TabletType_RDONLY, - Keyspace: kpb.ServedFrom, - }, - }, - }); err != nil { - return 0, fmt.Errorf("CreateKeyspace(%v) failed: %v", keyspace, err) - } - } else { - // create a regular keyspace - if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { - return 0, fmt.Errorf("CreateKeyspace(%v) failed: %v", keyspace, err) + // create a regular keyspace + if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { + return 0, fmt.Errorf("CreateKeyspace(%v) failed: %v", keyspace, err) + } + + // iterate through the shards + for _, spb := range kpb.Shards { + shard := spb.Name + if err := ts.CreateShard(ctx, keyspace, shard); err != nil { + return 0, fmt.Errorf("CreateShard(%v:%v) failed: %v", keyspace, shard, err) } - // iterate through the shards - for _, spb := range kpb.Shards { - shard := spb.Name - if err := ts.CreateShard(ctx, keyspace, shard); err != nil { - return 0, fmt.Errorf("CreateShard(%v:%v) failed: %v", keyspace, shard, err) + for _, cell := range tpb.Cells { + dbname := spb.DbNameOverride + if dbname == "" { + dbname = fmt.Sprintf("vt_%v_%v", keyspace, shard) } - for _, cell := range tpb.Cells { - dbname := spb.DbNameOverride - if dbname == "" { - dbname = fmt.Sprintf("vt_%v_%v", keyspace, shard) - } + replicas := int(kpb.ReplicaCount) + if replicas == 0 { + // 2 replicas in order to ensure the primary cell has a primary and a replica + replicas = 2 + } + rdonlys := int(kpb.RdonlyCount) + if rdonlys == 0 { + rdonlys = 1 + } - replicas := int(kpb.ReplicaCount) - if replicas == 0 { - // 2 replicas in order to ensure the primary cell has a primary and a replica - replicas = 2 - } - rdonlys := int(kpb.RdonlyCount) - if rdonlys == 0 { - rdonlys = 1 + if ensureDatabase { + // Create Database if not exist + conn, err := mysqld.GetDbaConnection(context.TODO()) + if err != nil { + return 0, fmt.Errorf("GetConnection failed: %v", err) } + defer conn.Close() - if ensureDatabase { - // Create Database if not exist - conn, err := mysqld.GetDbaConnection(context.TODO()) - if err != nil { - return 0, fmt.Errorf("GetConnection failed: %v", err) - } - defer conn.Close() + _, err = conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS `"+dbname+"`", 1, false) + if err != nil { + return 0, fmt.Errorf("error ensuring database exists: %v", err) + } - _, err = conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS `"+dbname+"`", 1, false) - if err != nil { - return 0, fmt.Errorf("error ensuring database exists: %v", err) - } + } + if cell == tpb.Cells[0] { + replicas-- + // create the primary + if err := CreateTablet(ctx, env, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_PRIMARY, mysqld, dbcfgs.Clone()); err != nil { + return 0, err } - if cell == tpb.Cells[0] { - replicas-- - - // create the primary - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_PRIMARY, mysqld, dbcfgs.Clone()); err != nil { - return 0, err - } - uid++ - } + uid++ + } - for i := 0; i < replicas; i++ { - // create a replica tablet - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_REPLICA, mysqld, dbcfgs.Clone()); err != nil { - return 0, err - } - uid++ + for i := 0; i < replicas; i++ { + // create a replica tablet + if err := CreateTablet(ctx, env, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_REPLICA, mysqld, dbcfgs.Clone()); err != nil { + return 0, err } + uid++ + } - for i := 0; i < rdonlys; i++ { - // create a rdonly tablet - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_RDONLY, mysqld, dbcfgs.Clone()); err != nil { - return 0, err - } - uid++ + for i := 0; i < rdonlys; i++ { + // create a rdonly tablet + if err := CreateTablet(ctx, env, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_RDONLY, mysqld, dbcfgs.Clone()); err != nil { + return 0, err } + uid++ } } } @@ -394,7 +376,7 @@ func CreateKs( return 0, fmt.Errorf("cannot load vschema file %v for keyspace %v: %v", f, keyspace, err) } - _, err = vindexes.BuildKeyspace(formal) + _, err = vindexes.BuildKeyspace(formal, wr.SQLParser()) if err != nil { return 0, fmt.Errorf("BuildKeyspace(%v) failed: %v", keyspace, err) } diff --git a/go/vt/vtctl/backup.go b/go/vt/vtctl/backup.go index c2f90ec4b14..e832b1f79d1 100644 --- a/go/vt/vtctl/backup.go +++ b/go/vt/vtctl/backup.go @@ -70,9 +70,9 @@ func init() { } func commandBackup(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - concurrency := subFlags.Int("concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously") + concurrency := subFlags.Int32("concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously") allowPrimary := subFlags.Bool("allow_primary", false, "Allows backups to be taken on primary. Warning!! If you are using the builtin backup engine, this will shutdown your primary mysql for as long as it takes to create a backup.") - incrementalFromPos := subFlags.String("incremental_from_pos", "", "Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position") + incrementalFromPos := subFlags.String("incremental_from_pos", "", "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.") upgradeSafe := subFlags.Bool("upgrade-safe", false, "Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades.") if err := subFlags.Parse(args); err != nil { @@ -89,7 +89,7 @@ func commandBackup(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.F return wr.VtctldServer().Backup(&vtctldatapb.BackupRequest{ TabletAlias: tabletAlias, - Concurrency: uint64(*concurrency), + Concurrency: *concurrency, AllowPrimary: *allowPrimary, IncrementalFromPos: *incrementalFromPos, UpgradeSafe: *upgradeSafe, @@ -112,9 +112,9 @@ func (b *backupEventStreamLogger) Send(resp *vtctldatapb.BackupResponse) error { } func commandBackupShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - concurrency := subFlags.Int("concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously") + concurrency := subFlags.Int32("concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously") allowPrimary := subFlags.Bool("allow_primary", false, "Whether to use primary tablet for backup. Warning!! If you are using the builtin backup engine, this will shutdown your primary mysql for as long as it takes to create a backup.") - incrementalFromPos := subFlags.String("incremental_from_pos", "", "Position of previous backup. Default: empty. If given, then this backup becomes an incremental backup from given position. If value is 'auto', backup taken from last successful backup position") + incrementalFromPos := subFlags.String("incremental_from_pos", "", "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.") upgradeSafe := subFlags.Bool("upgrade-safe", false, "Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades.") if err := subFlags.Parse(args); err != nil { @@ -132,7 +132,7 @@ func commandBackupShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pf return wr.VtctldServer().BackupShard(&vtctldatapb.BackupShardRequest{ Keyspace: keyspace, Shard: shard, - Concurrency: uint64(*concurrency), + Concurrency: *concurrency, AllowPrimary: *allowPrimary, IncrementalFromPos: *incrementalFromPos, UpgradeSafe: *upgradeSafe, diff --git a/go/vt/vtctl/endtoend/get_schema_test.go b/go/vt/vtctl/endtoend/get_schema_test.go index 2373fb6e3a5..a9829a193f3 100644 --- a/go/vt/vtctl/endtoend/get_schema_test.go +++ b/go/vt/vtctl/endtoend/get_schema_test.go @@ -5,6 +5,7 @@ import ( "testing" "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/vtenv" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -161,7 +162,7 @@ func TestGetSchema(t *testing.T) { logger := logutil.NewMemoryLogger() - err := vtctl.RunCommand(ctx, wrangler.New(logger, topo, &tmc), []string{ + err := vtctl.RunCommand(ctx, wrangler.New(vtenv.NewTestEnv(), logger, topo, &tmc), []string{ "GetSchema", topoproto.TabletAliasString(tablet.Alias), }) @@ -201,7 +202,7 @@ func TestGetSchema(t *testing.T) { }, } - err = vtctl.RunCommand(ctx, wrangler.New(logger, topo, &tmc), []string{ + err = vtctl.RunCommand(ctx, wrangler.New(vtenv.NewTestEnv(), logger, topo, &tmc), []string{ "GetSchema", "--table_sizes_only", topoproto.TabletAliasString(tablet.Alias), diff --git a/go/vt/vtctl/endtoend/onlineddl_show_test.go b/go/vt/vtctl/endtoend/onlineddl_show_test.go index fe795af752d..ed848c14be8 100644 --- a/go/vt/vtctl/endtoend/onlineddl_show_test.go +++ b/go/vt/vtctl/endtoend/onlineddl_show_test.go @@ -13,6 +13,7 @@ import ( "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vtctl" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/vttablet/tmclienttest" "vitess.io/vitess/go/vt/wrangler" @@ -119,14 +120,14 @@ func onlineDDLTest(t *testing.T, args []string, expectedQuery string) { tmclienttest.SetProtocol("go.vt.vtctl.endtoend", t.Name()) logger := logutil.NewMemoryLogger() - wr := wrangler.New(logger, fakeTopo, &tmc) + wr := wrangler.New(vtenv.NewTestEnv(), logger, fakeTopo, &tmc) err := vtctl.RunCommand(ctx, wr, args) assert.Error(t, err) assert.NotEmpty(t, err.Error()) containsExpectedError := false expectedErrors := []string{ - "unable to get shard names for keyspace", + "unable to get shards for keyspace", "no ExecuteFetchAsDba results on fake TabletManagerClient", } for _, expect := range expectedErrors { diff --git a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go b/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go deleted file mode 100644 index 14147316508..00000000000 --- a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fakevtctlclient - -import ( - "fmt" - "io" - "strings" - "sync" - "time" - - "vitess.io/vitess/go/protoutil" - "vitess.io/vitess/go/vt/logutil" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" -) - -// FakeLoggerEventStreamingClient is the base for the fakes for vtctlclient. -// It allows to register a (multi-)line string for a given command and return the result as channel which streams it back. -type FakeLoggerEventStreamingClient struct { - results map[string]*result - // mu guards all fields of the structs. - mu sync.Mutex -} - -// NewFakeLoggerEventStreamingClient creates a new fake. -func NewFakeLoggerEventStreamingClient() *FakeLoggerEventStreamingClient { - return &FakeLoggerEventStreamingClient{results: make(map[string]*result)} -} - -// generateKey returns a map key for a []string. -// ([]string is not supported as map key.) -func generateKey(args []string) string { - return strings.Join(args, " ") -} - -// result contains the result the fake should respond for a given command. -type result struct { - output string - err error - // count is the number of times this result is registered for the same - // command. With each stream of this result, count will be decreased by one. - count int - // addr optionally specifies which server address is expected from the client. - addr string -} - -func (r1 result) Equals(r2 result) bool { - return r1.output == r2.output && - ((r1.err == nil && r2.err == nil) || - (r1.err != nil && r2.err != nil && r1.err.Error() == r2.err.Error())) -} - -// RegisterResult registers for a given command (args) the result which the fake should return. -// Once the result was returned, it will be automatically deregistered. -func (f *FakeLoggerEventStreamingClient) RegisterResult(args []string, output string, err error) error { - return f.RegisterResultForAddr("" /* addr */, args, output, err) -} - -// RegisterResultForAddr is identical to RegisterResult but also expects that -// the client did dial "addr" as server address. -func (f *FakeLoggerEventStreamingClient) RegisterResultForAddr(addr string, args []string, output string, err error) error { - f.mu.Lock() - defer f.mu.Unlock() - - k := generateKey(args) - v := result{output, err, 1, addr} - if result, ok := f.results[k]; ok { - if result.Equals(v) { - result.count++ - return nil - } - return fmt.Errorf("a different result (%v) is already registered for command: %v", result, args) - } - f.results[k] = &v - return nil -} - -// RegisteredCommands returns a list of commands which are currently registered. -// This is useful to check that all registered results have been consumed. -func (f *FakeLoggerEventStreamingClient) RegisteredCommands() []string { - f.mu.Lock() - defer f.mu.Unlock() - - var commands []string - for k := range f.results { - commands = append(commands, k) - } - return commands -} - -type streamResultAdapter struct { - lines []string - index int - err error -} - -func (s *streamResultAdapter) Recv() (*logutilpb.Event, error) { - if s.index < len(s.lines) { - result := &logutilpb.Event{ - Time: protoutil.TimeToProto(time.Now()), - Level: logutilpb.Level_CONSOLE, - File: "fakevtctlclient", - Line: -1, - Value: s.lines[s.index], - } - s.index++ - return result, nil - } - if s.err == nil { - return nil, io.EOF - } - return nil, s.err -} - -// StreamResult returns an EventStream which streams back a registered result as logging events. -// "addr" is the server address which the client dialed and may be empty. -func (f *FakeLoggerEventStreamingClient) StreamResult(addr string, args []string) (logutil.EventStream, error) { - f.mu.Lock() - defer f.mu.Unlock() - - k := generateKey(args) - result, ok := f.results[k] - if !ok { - return nil, fmt.Errorf("no response was registered for args: %v", args) - } - if result.addr != "" && addr != result.addr { - return nil, fmt.Errorf("client sent request to wrong server address. got: %v want: %v", addr, result.addr) - } - result.count-- - if result.count == 0 { - delete(f.results, k) - } - - return &streamResultAdapter{ - lines: strings.Split(result.output, "\n"), - index: 0, - err: result.err, - }, nil -} diff --git a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient_test.go b/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient_test.go deleted file mode 100644 index 04a0ad5e03d..00000000000 --- a/go/vt/vtctl/fakevtctlclient/fake_loggerevent_streamingclient_test.go +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fakevtctlclient - -import ( - "errors" - "io" - "reflect" - "strings" - "testing" - - logutilpb "vitess.io/vitess/go/vt/proto/logutil" -) - -func TestStreamOutputAndError(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - args := []string{"CopySchemaShard", "test_keyspace/0", "test_keyspace/2"} - output := []string{"event1", "event2"} - wantErr := errors.New("something went wrong") - - err := fake.RegisterResult(args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - - verifyStreamOutputAndError(t, fake, "" /* addr */, args, output, wantErr) -} - -func TestStreamOutput(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - args := []string{"CopySchemaShard", "test_keyspace/0", "test_keyspace/2"} - output := []string{"event1", "event2"} - var wantErr error - - err := fake.RegisterResult(args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - - verifyStreamOutputAndError(t, fake, "" /* addr */, args, output, wantErr) -} - -// TestStreamOutputForAddr is similar to TestStreamOutput but also tests that -// the correct server address was used by the client. -func TestStreamOutputForAddr(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - addr := "localhost:12345" - args := []string{"CopySchemaShard", "test_keyspace/0", "test_keyspace/2"} - output := []string{"event1", "event2"} - var wantErr error - - // Used address matches. - err := fake.RegisterResultForAddr(addr, args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - verifyStreamOutputAndError(t, fake, addr, args, output, wantErr) - - // Used address does not match. - err = fake.RegisterResultForAddr(addr, args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - _, err = fake.StreamResult("different-addr", args) - if err == nil || !strings.Contains(err.Error(), "client sent request to wrong server address") { - t.Fatalf("fake should have failed because the client used the wrong address: %v", err) - } -} - -func verifyStreamOutputAndError(t *testing.T, fake *FakeLoggerEventStreamingClient, addr string, args, output []string, wantErr error) { - stream, err := fake.StreamResult(addr, args) - if err != nil { - t.Fatalf("Failed to stream result: %v", err) - } - - // Verify output and error. - i := 0 - for { - var event *logutilpb.Event - event, err = stream.Recv() - if err != nil { - break - } - if i > len(output) { - t.Fatalf("Received more events than expected. got: %v want: %v", i, len(output)) - } - if event.Value != output[i] { - t.Errorf("Received event is not identical to the received one. got: %v want: %v", event.Value, output[i]) - } - t.Logf("Received event: %v", event) - i++ - } - if i != len(output) { - t.Errorf("Number of received events mismatches. got: %v want: %v", i, len(output)) - } - if err == io.EOF { - err = nil - } - if err != wantErr { - t.Errorf("Wrong error received. got: %v want: %v", err, wantErr) - } -} - -func TestNoResultRegistered(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - stream, err := fake.StreamResult("" /* addr */, []string{"ListShardTablets", "test_keyspace/0"}) - if stream != nil { - t.Fatalf("No stream should have been returned because no matching result is registered.") - } - wantErr := "no response was registered for args: [ListShardTablets test_keyspace/0]" - if err.Error() != wantErr { - t.Errorf("Wrong error for missing result was returned. got: '%v' want: '%v'", err, wantErr) - } -} - -func TestResultAlreadyRegistered(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - errFirst := fake.RegisterResult([]string{"ListShardTablets", "test_keyspace/0"}, "output1", nil) - if errFirst != nil { - t.Fatalf("Registering the result should have been successful. Error: %v", errFirst) - } - - errSecond := fake.RegisterResult([]string{"ListShardTablets", "test_keyspace/0"}, "output2", nil) - if errSecond == nil { - t.Fatal("Registering a duplicate, different result should not have been successful.") - } - want := ") is already registered for command: " - if !strings.Contains(errSecond.Error(), want) { - t.Fatalf("Wrong error message: got: '%v' want: '%v'", errSecond, want) - } -} - -func TestRegisterMultipleResultsForSameCommand(t *testing.T) { - fake := NewFakeLoggerEventStreamingClient() - args := []string{"CopySchemaShard", "test_keyspace/0", "test_keyspace/2"} - output := []string{"event1", "event2"} - var wantErr error - - // Register first result. - err := fake.RegisterResult(args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - registeredCommands := []string{strings.Join(args, " ")} - verifyListOfRegisteredCommands(t, fake, registeredCommands) - - // Register second result. - err = fake.RegisterResult(args, strings.Join(output, "\n"), wantErr) - if err != nil { - t.Fatalf("Failed to register fake result for: %v err: %v", args, err) - } - verifyListOfRegisteredCommands(t, fake, registeredCommands) - - // Consume first result. - verifyStreamOutputAndError(t, fake, "" /* addr */, args, output, wantErr) - verifyListOfRegisteredCommands(t, fake, registeredCommands) - - // Consume second result. - verifyStreamOutputAndError(t, fake, "" /* addr */, args, output, wantErr) - verifyListOfRegisteredCommands(t, fake, []string{}) -} - -func verifyListOfRegisteredCommands(t *testing.T, fake *FakeLoggerEventStreamingClient, want []string) { - got := fake.RegisteredCommands() - if len(got) == 0 && len(want) == 0 { - return - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("fake.RegisteredCommands() = %v, want: %v", got, want) - } -} diff --git a/go/vt/vtctl/fakevtctlclient/fakevtctlclient.go b/go/vt/vtctl/fakevtctlclient/fakevtctlclient.go deleted file mode 100644 index 11224b745e9..00000000000 --- a/go/vt/vtctl/fakevtctlclient/fakevtctlclient.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package fakevtctlclient contains a fake for the vtctlclient interface. -package fakevtctlclient - -import ( - "time" - - "context" - - "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/vtctl/vtctlclient" -) - -// FakeVtctlClient is a fake which implements the vtctlclient interface. -// The fake can be used to return a specific result for a given command. -// If the command is not registered, an error will be thrown. -type FakeVtctlClient struct { - *FakeLoggerEventStreamingClient -} - -// NewFakeVtctlClient creates a FakeVtctlClient struct. -func NewFakeVtctlClient() *FakeVtctlClient { - return &FakeVtctlClient{NewFakeLoggerEventStreamingClient()} -} - -// FakeVtctlClientFactory always returns the current instance. -func (f *FakeVtctlClient) FakeVtctlClientFactory(addr string) (vtctlclient.VtctlClient, error) { - return f, nil -} - -// ExecuteVtctlCommand is part of the vtctlclient interface. -func (f *FakeVtctlClient) ExecuteVtctlCommand(ctx context.Context, args []string, actionTimeout time.Duration) (logutil.EventStream, error) { - return f.FakeLoggerEventStreamingClient.StreamResult("" /* addr */, args) -} - -// Close is part of the vtctlclient interface. -func (f *FakeVtctlClient) Close() {} diff --git a/go/vt/vtctl/grpcvtctlclient/client_test.go b/go/vt/vtctl/grpcvtctlclient/client_test.go index 50e1968533e..d065a706c65 100644 --- a/go/vt/vtctl/grpcvtctlclient/client_test.go +++ b/go/vt/vtctl/grpcvtctlclient/client_test.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtctl/grpcvtctlserver" "vitess.io/vitess/go/vt/vtctl/vtctlclienttest" + "vitess.io/vitess/go/vt/vtenv" vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" ) @@ -52,7 +53,7 @@ func TestVtctlServer(t *testing.T) { // Create a gRPC server and listen on the port server := grpc.NewServer() - vtctlservicepb.RegisterVtctlServer(server, grpcvtctlserver.NewVtctlServer(ts)) + vtctlservicepb.RegisterVtctlServer(server, grpcvtctlserver.NewVtctlServer(vtenv.NewTestEnv(), ts)) go server.Serve(listener) // Create a VtctlClient gRPC client to talk to the fake server @@ -86,7 +87,7 @@ func TestVtctlAuthClient(t *testing.T) { opts = append(opts, grpc.UnaryInterceptor(servenv.FakeAuthUnaryInterceptor)) server := grpc.NewServer(opts...) - vtctlservicepb.RegisterVtctlServer(server, grpcvtctlserver.NewVtctlServer(ts)) + vtctlservicepb.RegisterVtctlServer(server, grpcvtctlserver.NewVtctlServer(vtenv.NewTestEnv(), ts)) go server.Serve(listener) authJSON := `{ diff --git a/go/vt/vtctl/grpcvtctldclient/client_gen.go b/go/vt/vtctl/grpcvtctldclient/client_gen.go index 087b566fe5d..c1d251487fd 100644 --- a/go/vt/vtctl/grpcvtctldclient/client_gen.go +++ b/go/vt/vtctl/grpcvtctldclient/client_gen.go @@ -254,6 +254,15 @@ func (client *gRPCVtctldClient) FindAllShardsInKeyspace(ctx context.Context, in return client.c.FindAllShardsInKeyspace(ctx, in, opts...) } +// ForceCutOverSchemaMigration is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) ForceCutOverSchemaMigration(ctx context.Context, in *vtctldatapb.ForceCutOverSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.ForceCutOverSchemaMigrationResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.ForceCutOverSchemaMigration(ctx, in, opts...) +} + // GetBackups is part of the vtctlservicepb.VtctldClient interface. func (client *gRPCVtctldClient) GetBackups(ctx context.Context, in *vtctldatapb.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetBackupsResponse, error) { if client.c == nil { diff --git a/go/vt/vtctl/grpcvtctldclient/client_test.go b/go/vt/vtctl/grpcvtctldclient/client_test.go index 93c95ffa607..1de9b6c895c 100644 --- a/go/vt/vtctl/grpcvtctldclient/client_test.go +++ b/go/vt/vtctl/grpcvtctldclient/client_test.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" "vitess.io/vitess/go/vt/vtctl/vtctldclient" + "vitess.io/vitess/go/vt/vtenv" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" @@ -41,7 +42,7 @@ func TestFindAllShardsInKeyspace(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { @@ -88,7 +89,7 @@ func TestGetKeyspace(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { @@ -117,7 +118,7 @@ func TestGetKeyspaces(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts) + return grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { diff --git a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go index f5f7847b499..f83861d7fc8 100644 --- a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go +++ b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go @@ -23,6 +23,7 @@ import ( "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -46,7 +47,7 @@ func TestInitShardPrimary(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") tmc := tmclient.NewTabletManagerClient() defer tmc.Close() - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmc) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmc) primaryDb := fakesqldb.New(t) defer primaryDb.Close() @@ -93,7 +94,7 @@ func TestInitShardPrimary(t *testing.T) { tablet.TM.QueryServiceControl.(*tabletservermock.Controller).SetQueryServiceEnabledForTests(true) } - vtctld := grpcvtctldserver.NewVtctldServer(ts) + vtctld := grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) resp, err := vtctld.InitShardPrimary(context.Background(), &vtctldatapb.InitShardPrimaryRequest{ Keyspace: tablet1.Tablet.Keyspace, Shard: tablet1.Tablet.Shard, @@ -109,7 +110,7 @@ func TestInitShardPrimaryNoFormerPrimary(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") tmc := tmclient.NewTabletManagerClient() - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmc) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmc) primaryDb := fakesqldb.New(t) defer primaryDb.Close() @@ -148,7 +149,7 @@ func TestInitShardPrimaryNoFormerPrimary(t *testing.T) { tablet.TM.QueryServiceControl.(*tabletservermock.Controller).SetQueryServiceEnabledForTests(true) } - vtctld := grpcvtctldserver.NewVtctldServer(ts) + vtctld := grpcvtctldserver.NewVtctldServer(vtenv.NewTestEnv(), ts) _, err := vtctld.InitShardPrimary(context.Background(), &vtctldatapb.InitShardPrimaryRequest{ Keyspace: tablet1.Tablet.Keyspace, Shard: tablet1.Tablet.Shard, diff --git a/go/vt/vtctl/grpcvtctldserver/query_test.go b/go/vt/vtctl/grpcvtctldserver/query_test.go index 6073d3bc395..b9299592c15 100644 --- a/go/vt/vtctl/grpcvtctldserver/query_test.go +++ b/go/vt/vtctl/grpcvtctldserver/query_test.go @@ -25,12 +25,11 @@ import ( "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/vtctl/schematools" - "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/vtctl/schematools" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" - "vitess.io/vitess/go/vt/proto/vttime" + vttimepb "vitess.io/vitess/go/vt/proto/vttime" ) var now = time.Now() @@ -86,7 +85,6 @@ func TestRowToSchemaMigration(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { out, err := rowToSchemaMigration(test.row) if test.shouldErr { @@ -110,7 +108,7 @@ func TestValueToVTTime(t *testing.T) { tests := []struct { name string value string - expected *vttime.Time + expected *vttimepb.Time shouldErr bool }{ { @@ -130,7 +128,6 @@ func TestValueToVTTime(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() @@ -153,7 +150,7 @@ func TestValueToVTDuration(t *testing.T) { name string value string defaultUnit string - expected *vttime.Duration + expected *vttimepb.Duration shouldErr bool }{ { @@ -182,7 +179,6 @@ func TestValueToVTDuration(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { out, err := valueToVTDuration(test.value, test.defaultUnit) if test.shouldErr { @@ -197,6 +193,8 @@ func TestValueToVTDuration(t *testing.T) { } func TestAlterSchemaMigrationQuery(t *testing.T) { + t.Parallel() + uuid := "4e5dcf80_354b_11eb_82cd_f875a4d24e90" tcases := []struct { @@ -228,6 +226,8 @@ func TestAlterSchemaMigrationQuery(t *testing.T) { for _, tcase := range tcases { testName := fmt.Sprintf("%s %s", tcase.command, tcase.uuid) t.Run(testName, func(t *testing.T) { + t.Parallel() + query, err := alterSchemaMigrationQuery(tcase.command, tcase.uuid) assert.NoError(t, err) assert.Equal(t, tcase.expect, query) diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index 25b711e1019..0d131e36f96 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -60,6 +60,7 @@ import ( "vitess.io/vitess/go/vt/vtctl/reparentutil" "vitess.io/vitess/go/vt/vtctl/schematools" "vitess.io/vitess/go/vt/vtctl/workflow" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tmclient" @@ -92,13 +93,13 @@ type VtctldServer struct { } // NewVtctldServer returns a new VtctldServer for the given topo server. -func NewVtctldServer(ts *topo.Server) *VtctldServer { +func NewVtctldServer(env *vtenv.Environment, ts *topo.Server) *VtctldServer { tmc := tmclient.NewTabletManagerClient() return &VtctldServer{ ts: ts, tmc: tmc, - ws: workflow.NewServer(ts, tmc), + ws: workflow.NewServer(env, ts, tmc), } } @@ -108,7 +109,7 @@ func NewTestVtctldServer(ts *topo.Server, tmc tmclient.TabletManagerClient) *Vtc return &VtctldServer{ ts: ts, tmc: tmc, - ws: workflow.NewServer(ts, tmc), + ws: workflow.NewServer(vtenv.NewTestEnv(), ts, tmc), } } @@ -221,6 +222,8 @@ func (s *VtctldServer) ApplyShardRoutingRules(ctx context.Context, req *vtctldat // ApplySchema is part of the vtctlservicepb.VtctldServer interface. func (s *VtctldServer) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySchemaRequest) (resp *vtctldatapb.ApplySchemaResponse, err error) { + log.Infof("VtctldServer.ApplySchema: keyspace=%s, migrationContext=%v, ddlStrategy=%v, batchSize=%v", req.Keyspace, req.MigrationContext, req.DdlStrategy, req.BatchSize) + span, ctx := trace.NewSpan(ctx, "VtctldServer.ApplySchema") defer span.Finish() @@ -268,7 +271,7 @@ func (s *VtctldServer) ApplySchema(ctx context.Context, req *vtctldatapb.ApplySc logstream = append(logstream, e) }) - executor := schemamanager.NewTabletExecutor(migrationContext, s.ts, s.tmc, logger, waitReplicasTimeout, req.BatchSize) + executor := schemamanager.NewTabletExecutor(migrationContext, s.ts, s.tmc, logger, waitReplicasTimeout, req.BatchSize, s.ws.SQLParser()) if err = executor.SetDDLStrategy(req.DdlStrategy); err != nil { err = vterrors.Wrapf(err, "invalid DdlStrategy: %s", req.DdlStrategy) @@ -337,7 +340,7 @@ func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyV span.Annotate("sql_mode", true) var stmt sqlparser.Statement - stmt, err = sqlparser.Parse(req.Sql) + stmt, err = s.ws.SQLParser().Parse(req.Sql) if err != nil { err = vterrors.Wrapf(err, "Parse(%s)", req.Sql) return nil, err @@ -364,15 +367,43 @@ func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyV vs = req.VSchema } - if req.DryRun { // we return what was passed in and parsed, rather than current - return &vtctldatapb.ApplyVSchemaResponse{VSchema: vs}, nil - } - - _, err = vindexes.BuildKeyspace(vs) + ksVs, err := vindexes.BuildKeyspace(vs, s.ws.SQLParser()) if err != nil { err = vterrors.Wrapf(err, "BuildKeyspace(%s)", req.Keyspace) return nil, err } + response := &vtctldatapb.ApplyVSchemaResponse{ + VSchema: vs, + UnknownVindexParams: make(map[string]*vtctldatapb.ApplyVSchemaResponse_ParamList), + } + + // Attach unknown Vindex params to the response. + var vdxNames []string + var unknownVindexParams []string + for name := range ksVs.Vindexes { + vdxNames = append(vdxNames, name) + } + sort.Strings(vdxNames) + for _, name := range vdxNames { + vdx := ksVs.Vindexes[name] + if val, ok := vdx.(vindexes.ParamValidating); ok { + ups := val.UnknownParams() + if len(ups) == 0 { + continue + } + response.UnknownVindexParams[name] = &vtctldatapb.ApplyVSchemaResponse_ParamList{Params: ups} + unknownVindexParams = append(unknownVindexParams, fmt.Sprintf("%s (%s)", name, strings.Join(ups, ", "))) + } + } + + if req.Strict && len(unknownVindexParams) > 0 { // return early if unknown params found in strict mode + err = vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongArguments, "unknown vindex params: %s", strings.Join(unknownVindexParams, "; ")) + return response, err + } + + if req.DryRun { // return early if dry run + return response, err + } if err = s.ts.SaveVSchema(ctx, req.Keyspace, vs); err != nil { err = vterrors.Wrapf(err, "SaveVSchema(%s, %v)", req.Keyspace, req.VSchema) @@ -390,7 +421,8 @@ func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyV err = vterrors.Wrapf(err, "GetVSchema(%s)", req.Keyspace) return nil, err } - return &vtctldatapb.ApplyVSchemaResponse{VSchema: updatedVS}, nil + response.VSchema = updatedVS + return response, nil } // Backup is part of the vtctlservicepb.VtctldServer interface. @@ -431,8 +463,14 @@ func (s *VtctldServer) BackupShard(req *vtctldatapb.BackupShardRequest, stream v span.Annotate("incremental_from_pos", req.IncrementalFromPos) tablets, stats, err := reparentutil.ShardReplicationStatuses(ctx, s.ts, s.tmc, req.Keyspace, req.Shard) + + // Instead of return on err directly, only return err when no tablets for backup at all if err != nil { - return err + tablets = reparentutil.GetBackupCandidates(tablets, stats) + // Only return err when no usable tablet + if len(tablets) == 0 { + return err + } } var ( @@ -480,7 +518,7 @@ func (s *VtctldServer) backupTablet(ctx context.Context, tablet *topodatapb.Tabl Send(resp *vtctldatapb.BackupResponse) error }) error { r := &tabletmanagerdatapb.BackupRequest{ - Concurrency: int64(req.Concurrency), + Concurrency: req.Concurrency, AllowPrimary: req.AllowPrimary, IncrementalFromPos: req.IncrementalFromPos, UpgradeSafe: req.UpgradeSafe, @@ -673,6 +711,37 @@ func (s *VtctldServer) CleanupSchemaMigration(ctx context.Context, req *vtctldat return resp, nil } +// ForceCutOverSchemaMigration is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) ForceCutOverSchemaMigration(ctx context.Context, req *vtctldatapb.ForceCutOverSchemaMigrationRequest) (resp *vtctldatapb.ForceCutOverSchemaMigrationResponse, err error) { + span, ctx := trace.NewSpan(ctx, "VtctldServer.ForceCutOverSchemaMigration") + defer span.Finish() + + defer panicHandler(&err) + + span.Annotate("keyspace", req.Keyspace) + span.Annotate("uuid", req.Uuid) + + query, err := alterSchemaMigrationQuery("force_cutover", req.Uuid) + if err != nil { + return nil, err + } + + log.Infof("Calling ApplySchema to force cut-over migration %s", req.Uuid) + qr, err := s.ApplySchema(ctx, &vtctldatapb.ApplySchemaRequest{ + Keyspace: req.Keyspace, + Sql: []string{query}, + WaitReplicasTimeout: protoutil.DurationToProto(DefaultWaitReplicasTimeout), + }) + if err != nil { + return nil, err + } + + resp = &vtctldatapb.ForceCutOverSchemaMigrationResponse{ + RowsAffectedByShard: qr.RowsAffectedByShard, + } + return resp, nil +} + // CompleteSchemaMigration is part of the vtctlservicepb.VtctldServer interface. func (s *VtctldServer) CompleteSchemaMigration(ctx context.Context, req *vtctldatapb.CompleteSchemaMigrationRequest) (resp *vtctldatapb.CompleteSchemaMigrationResponse, err error) { span, ctx := trace.NewSpan(ctx, "VtctldServer.CompleteSchemaMigration") @@ -738,7 +807,6 @@ func (s *VtctldServer) CreateKeyspace(ctx context.Context, req *vtctldatapb.Crea ki := &topodatapb.Keyspace{ KeyspaceType: req.Type, - ServedFroms: req.ServedFroms, BaseKeyspace: req.BaseKeyspace, SnapshotTime: req.SnapshotTime, DurabilityPolicy: req.DurabilityPolicy, @@ -1237,7 +1305,7 @@ func (s *VtctldServer) FindAllShardsInKeyspace(ctx context.Context, req *vtctlda span.Annotate("keyspace", req.Keyspace) - result, err := s.ts.FindAllShardsInKeyspace(ctx, req.Keyspace) + result, err := s.ts.FindAllShardsInKeyspace(ctx, req.Keyspace, nil) if err != nil { return nil, err } @@ -1976,7 +2044,7 @@ func (s *VtctldServer) GetTablets(ctx context.Context, req *vtctldatapb.GetTable case len(req.TabletAliases) > 0: span.Annotate("tablet_aliases", strings.Join(topoproto.TabletAliasList(req.TabletAliases).ToStringSlice(), ",")) - tabletMap, err = s.ts.GetTabletMap(ctx, req.TabletAliases) + tabletMap, err = s.ts.GetTabletMap(ctx, req.TabletAliases, nil) if err != nil { err = fmt.Errorf("GetTabletMap(%v) failed: %w", req.TabletAliases, err) } @@ -2018,7 +2086,7 @@ func (s *VtctldServer) GetTablets(ctx context.Context, req *vtctldatapb.GetTable tablets := make([]*topodatapb.Tablet, 0, len(tabletMap)) for _, ti := range tabletMap { - if req.TabletType != 0 && ti.Type != req.TabletType { + if req.TabletType != topodatapb.TabletType_UNKNOWN && ti.Type != req.TabletType { continue } adjustTypeForStalePrimary(ti, truePrimaryTimestamp) @@ -2052,7 +2120,7 @@ func (s *VtctldServer) GetTablets(ctx context.Context, req *vtctldatapb.GetTable go func(cell string) { defer wg.Done() - tablets, err := s.ts.GetTabletsByCell(ctx, cell) + tablets, err := s.ts.GetTabletsByCell(ctx, cell, nil) if err != nil { if req.Strict { log.Infof("GetTablets got an error from cell %s: %s. Running in strict mode, so canceling other cell RPCs", cell, err) @@ -2086,7 +2154,7 @@ func (s *VtctldServer) GetTablets(ctx context.Context, req *vtctldatapb.GetTable if req.Keyspace != "" && tablet.Keyspace != req.Keyspace { continue } - if req.TabletType != 0 && tablet.Type != req.TabletType { + if req.TabletType != topodatapb.TabletType_UNKNOWN && tablet.Type != req.TabletType { continue } @@ -2705,6 +2773,10 @@ func (s *VtctldServer) PlannedReparentShard(ctx context.Context, req *vtctldatap } else if !ok { waitReplicasTimeout = time.Second * 30 } + tolerableReplLag, _, err := protoutil.DurationFromProto(req.TolerableReplicationLag) + if err != nil { + return nil, err + } span.Annotate("keyspace", req.Keyspace) span.Annotate("shard", req.Shard) @@ -2734,6 +2806,7 @@ func (s *VtctldServer) PlannedReparentShard(ctx context.Context, req *vtctldatap AvoidPrimaryAlias: req.AvoidPrimary, NewPrimaryAlias: req.NewPrimary, WaitReplicasTimeout: waitReplicasTimeout, + TolerableReplLag: tolerableReplLag, }, ) @@ -2746,7 +2819,7 @@ func (s *VtctldServer) PlannedReparentShard(ctx context.Context, req *vtctldatap resp.Keyspace = ev.ShardInfo.Keyspace() resp.Shard = ev.ShardInfo.ShardName() - if !topoproto.TabletAliasIsZero(ev.NewPrimary.Alias) { + if ev.NewPrimary != nil && !topoproto.TabletAliasIsZero(ev.NewPrimary.Alias) { resp.PromotedPrimary = ev.NewPrimary.Alias } } @@ -3318,47 +3391,6 @@ func (s *VtctldServer) SetKeyspaceDurabilityPolicy(ctx context.Context, req *vtc }, nil } -// SetKeyspaceServedFrom is part of the vtctlservicepb.VtctldServer interface. -func (s *VtctldServer) SetKeyspaceServedFrom(ctx context.Context, req *vtctldatapb.SetKeyspaceServedFromRequest) (resp *vtctldatapb.SetKeyspaceServedFromResponse, err error) { - span, ctx := trace.NewSpan(ctx, "VtctldServer.SetKeyspaceServedFrom") - defer span.Finish() - - defer panicHandler(&err) - - span.Annotate("keyspace", req.Keyspace) - span.Annotate("tablet_type", topoproto.TabletTypeLString(req.TabletType)) - span.Annotate("cells", strings.Join(req.Cells, ",")) - span.Annotate("remove", req.Remove) - span.Annotate("source_keyspace", req.SourceKeyspace) - - ctx, unlock, lockErr := s.ts.LockKeyspace(ctx, req.Keyspace, "SetKeyspaceServedFrom") - if lockErr != nil { - err = lockErr - return nil, err - } - - defer unlock(&err) - - ki, err := s.ts.GetKeyspace(ctx, req.Keyspace) - if err != nil { - return nil, err - } - - err = ki.UpdateServedFromMap(req.TabletType, req.Cells, req.SourceKeyspace, req.Remove, nil) - if err != nil { - return nil, err - } - - err = s.ts.UpdateKeyspace(ctx, ki) - if err != nil { - return nil, err - } - - return &vtctldatapb.SetKeyspaceServedFromResponse{ - Keyspace: ki.Keyspace, - }, nil -} - // SetShardIsPrimaryServing is part of the vtctlservicepb.VtctldServer interface. func (s *VtctldServer) SetShardIsPrimaryServing(ctx context.Context, req *vtctldatapb.SetShardIsPrimaryServingRequest) (resp *vtctldatapb.SetShardIsPrimaryServingResponse, err error) { span, ctx := trace.NewSpan(ctx, "VtctldServer.SetShardIsPrimaryServing") @@ -4421,7 +4453,7 @@ func (s *VtctldServer) ValidateShard(ctx context.Context, req *vtctldatapb.Valid getTabletMapCtx, getTabletMapCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer getTabletMapCancel() - tabletMap, _ := s.ts.GetTabletMap(getTabletMapCtx, aliases) + tabletMap, _ := s.ts.GetTabletMap(getTabletMapCtx, aliases, nil) var primaryAlias *topodatapb.TabletAlias for _, alias := range aliases { @@ -4830,6 +4862,7 @@ func (s *VtctldServer) VDiffCreate(ctx context.Context, req *vtctldatapb.VDiffCr span.Annotate("tablet_types", req.TabletTypes) span.Annotate("tables", req.Tables) span.Annotate("auto_retry", req.AutoRetry) + span.Annotate("max_diff_duration", req.MaxDiffDuration) resp, err = s.ws.VDiffCreate(ctx, req) return resp, err @@ -4959,8 +4992,8 @@ func (s *VtctldServer) WorkflowUpdate(ctx context.Context, req *vtctldatapb.Work } // StartServer registers a VtctldServer for RPCs on the given gRPC server. -func StartServer(s *grpc.Server, ts *topo.Server) { - vtctlservicepb.RegisterVtctldServer(s, NewVtctldServer(ts)) +func StartServer(s *grpc.Server, env *vtenv.Environment, ts *topo.Server) { + vtctlservicepb.RegisterVtctldServer(s, NewVtctldServer(env, ts)) } // getTopologyCell is a helper method that returns a topology cell given its path. @@ -4981,9 +5014,7 @@ func (s *VtctldServer) getTopologyCell(ctx context.Context, cellPath string) (*v return nil, err } - data, _, dataErr := conn.Get(ctx, relativePath) - - if dataErr == nil { + if data, _, err := conn.Get(ctx, relativePath); err == nil { result, err := topo.DecodeContent(relativePath, data, false) if err != nil { err := vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "error decoding file content for cell %s: %v", cellPath, err) @@ -4995,15 +5026,13 @@ func (s *VtctldServer) getTopologyCell(ctx context.Context, cellPath string) (*v return &topoCell, nil } - children, childrenErr := conn.ListDir(ctx, relativePath, false /*full*/) - - if childrenErr != nil && dataErr != nil { + children, err := conn.ListDir(ctx, relativePath, false /*full*/) + if err != nil { err := vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cell %s with path %s has no file contents and no children: %v", cell, cellPath, err) return nil, err } topoCell.Children = make([]string, len(children)) - for i, c := range children { topoCell.Children[i] = c.Name } diff --git a/go/vt/vtctl/grpcvtctldserver/server_slow_test.go b/go/vt/vtctl/grpcvtctldserver/server_slow_test.go index 3100855e370..8170e7d8cd3 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_slow_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_slow_test.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" @@ -290,8 +291,6 @@ func TestEmergencyReparentShardSlow(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -310,7 +309,7 @@ func TestEmergencyReparentShardSlow(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.EmergencyReparentShard(ctx, tt.req) @@ -592,8 +591,6 @@ func TestPlannedReparentShardSlow(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -608,7 +605,7 @@ func TestPlannedReparentShardSlow(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.PlannedReparentShard(ctx, tt.req) @@ -738,7 +735,7 @@ func TestSleepTablet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) start := time.Now() diff --git a/go/vt/vtctl/grpcvtctldserver/server_test.go b/go/vt/vtctl/grpcvtctldserver/server_test.go index 38029f0e799..ed6ee6aca1f 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_test.go @@ -46,6 +46,7 @@ import ( "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" "vitess.io/vitess/go/vt/vtctl/localvtctldclient" "vitess.io/vitess/go/vt/vtctl/schematools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/vttablet/tmclienttest" @@ -85,7 +86,7 @@ func TestPanicHandler(t *testing.T) { }() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, nil, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.AddCellInfo(context.Background(), nil) @@ -141,7 +142,7 @@ func TestAddCellInfo(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.AddCellInfo(ctx, tt.req) if tt.shouldErr { @@ -214,7 +215,7 @@ func TestAddCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.AddCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -326,7 +327,7 @@ func TestApplyRoutingRules(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.ApplyRoutingRules(ctx, tt.req) if tt.shouldErr { @@ -351,6 +352,7 @@ func TestApplyVSchema(t *testing.T) { req *vtctldatapb.ApplyVSchemaRequest exp *vtctldatapb.ApplyVSchemaResponse shouldErr bool + err string }{ { name: "normal", @@ -397,6 +399,84 @@ func TestApplyVSchema(t *testing.T) { Keyspace: "testkeyspace", }, shouldErr: true, + }, { + name: "unknown params", + req: &vtctldatapb.ApplyVSchemaRequest{ + Keyspace: "testkeyspacesharded", + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + SkipRebuild: true, + }, + exp: &vtctldatapb.ApplyVSchemaResponse{ + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + UnknownVindexParams: map[string]*vtctldatapb.ApplyVSchemaResponse_ParamList{ + "lookup1": { + Params: []string{"goodbye", "hello"}, + }, + }, + }, + shouldErr: false, + }, { + name: "strict unknown params", + req: &vtctldatapb.ApplyVSchemaRequest{ + Keyspace: "testkeyspacesharded", + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + SkipRebuild: true, + Strict: true, + }, + exp: &vtctldatapb.ApplyVSchemaResponse{ + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + UnknownVindexParams: map[string]*vtctldatapb.ApplyVSchemaResponse_ParamList{ + "lookup1": { + Params: []string{"goodbye", "hello"}, + }, + }, + }, + shouldErr: true, + err: "unknown vindex params: lookup1 (goodbye, hello)", }, { name: "dry run", req: &vtctldatapb.ApplyVSchemaRequest{ @@ -412,6 +492,100 @@ func TestApplyVSchema(t *testing.T) { }, }, shouldErr: false, + }, { + name: "dry run with invalid params", + req: &vtctldatapb.ApplyVSchemaRequest{ + Keyspace: "testkeyspacesharded", + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup_invalid", + }, + }, + }, + DryRun: true, + }, + exp: &vtctldatapb.ApplyVSchemaResponse{}, + shouldErr: true, + }, { + name: "dry run with unknown params", + req: &vtctldatapb.ApplyVSchemaRequest{ + Keyspace: "testkeyspacesharded", + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + DryRun: true, + }, + exp: &vtctldatapb.ApplyVSchemaResponse{ + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + UnknownVindexParams: map[string]*vtctldatapb.ApplyVSchemaResponse_ParamList{ + "lookup1": { + Params: []string{"goodbye", "hello"}, + }, + }, + }, + shouldErr: false, + }, { + name: "strict dry run with unknown params", + req: &vtctldatapb.ApplyVSchemaRequest{ + Keyspace: "testkeyspacesharded", + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + DryRun: true, + Strict: true, + }, + exp: &vtctldatapb.ApplyVSchemaResponse{ + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "lookup1": { + Type: "lookup", + Params: map[string]string{ + "hello": "world", + "goodbye": "world", + }, + }, + }, + }, + UnknownVindexParams: map[string]*vtctldatapb.ApplyVSchemaResponse_ParamList{ + "lookup1": { + Params: []string{"goodbye", "hello"}, + }, + }, + }, + shouldErr: true, + err: "unknown vindex params: lookup1 (goodbye, hello)", }, } @@ -421,7 +595,7 @@ func TestApplyVSchema(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ @@ -463,6 +637,9 @@ func TestApplyVSchema(t *testing.T) { res, err := vtctld.ApplyVSchema(ctx, tt.req) if tt.shouldErr { assert.Error(t, err) + if tt.err != "" { + assert.ErrorContains(t, err, tt.err) + } return } @@ -701,7 +878,7 @@ func TestBackup(t *testing.T) { testutil.AddTablet(ctx, t, tt.ts, tt.tablet, nil) } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) client := localvtctldclient.New(vtctld) stream, err := client.Backup(ctx, tt.req) @@ -1041,7 +1218,7 @@ func TestBackupShard(t *testing.T) { }, tt.tablets..., ) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) client := localvtctldclient.New(vtctld) stream, err := client.BackupShard(ctx, tt.req) @@ -1249,8 +1426,8 @@ func TestCancelSchemaMigration(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1261,7 +1438,7 @@ func TestCancelSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.CancelSchemaMigration(ctx, test.req) @@ -1483,8 +1660,6 @@ func TestChangeTabletType(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1493,7 +1668,9 @@ func TestChangeTabletType(t *testing.T) { ts := memorytopo.NewServer(ctx, tt.cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ TopoServer: ts, - }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) + }, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return NewVtctldServer(vtenv.NewTestEnv(), ts) + }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ AlsoSetShardPrimary: true, @@ -1539,7 +1716,9 @@ func TestChangeTabletType(t *testing.T) { ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ TopoServer: nil, - }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) + }, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return NewVtctldServer(vtenv.NewTestEnv(), ts) + }) testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ @@ -1751,6 +1930,8 @@ func TestCleanupSchemaMigration(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "zone1") @@ -1760,7 +1941,7 @@ func TestCleanupSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.CleanupSchemaMigration(ctx, test.req) @@ -1775,6 +1956,210 @@ func TestCleanupSchemaMigration(t *testing.T) { } } +func TestForceCutOverSchemaMigration(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + tablets []*topodatapb.Tablet + tmc *testutil.TabletManagerClient + req *vtctldatapb.ForceCutOverSchemaMigrationRequest + expected *vtctldatapb.ForceCutOverSchemaMigrationResponse + shouldErr bool + }{ + { + tablets: []*topodatapb.Tablet{ + { + Keyspace: "ks", + Shard: "-80", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + { + Keyspace: "ks", + Shard: "80-", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + tmc: &testutil.TabletManagerClient{ + ExecuteQueryResults: map[string]struct { + Response *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + Response: &querypb.QueryResult{ + RowsAffected: 1, + }, + }, + "zone1-0000000200": { + Response: &querypb.QueryResult{}, + }, + }, + PrimaryPositionResults: map[string]struct { + Position string + Error error + }{ + "zone1-0000000100": {}, + "zone1-0000000200": {}, + }, + ReloadSchemaResults: map[string]error{ + "zone1-0000000100": nil, + "zone1-0000000200": nil, + }, + }, + req: &vtctldatapb.ForceCutOverSchemaMigrationRequest{ + Keyspace: "ks", + Uuid: "abc", + }, + expected: &vtctldatapb.ForceCutOverSchemaMigrationResponse{ + RowsAffectedByShard: map[string]uint64{ + "-80": 1, + "80-": 0, + }, + }, + }, + { + name: "no shard primary", + tablets: []*topodatapb.Tablet{ + { + Keyspace: "ks", + Shard: "-80", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + { + Keyspace: "ks", + Shard: "80-", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + tmc: &testutil.TabletManagerClient{ + ExecuteQueryResults: map[string]struct { + Response *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + Response: &querypb.QueryResult{}, + }, + "zone1-0000000200": { + Response: &querypb.QueryResult{}, + }, + }, + PrimaryPositionResults: map[string]struct { + Position string + Error error + }{ + "zone1-0000000100": {}, + "zone1-0000000200": {}, + }, + ReloadSchemaResults: map[string]error{ + "zone1-0000000100": nil, + "zone1-0000000200": nil, + }, + }, + req: &vtctldatapb.ForceCutOverSchemaMigrationRequest{ + Keyspace: "ks", + Uuid: "abc", + }, + shouldErr: true, + }, + { + name: "executeQuery failure", + tablets: []*topodatapb.Tablet{ + { + Keyspace: "ks", + Shard: "-80", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + { + Keyspace: "ks", + Shard: "80-", + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + tmc: &testutil.TabletManagerClient{ + ExecuteQueryResults: map[string]struct { + Response *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + Error: assert.AnError, + }, + "zone1-0000000200": { + Response: &querypb.QueryResult{}, + }, + }, + PrimaryPositionResults: map[string]struct { + Position string + Error error + }{ + "zone1-0000000100": {}, + "zone1-0000000200": {}, + }, + ReloadSchemaResults: map[string]error{ + "zone1-0000000100": nil, + "zone1-0000000200": nil, + }, + }, + req: &vtctldatapb.ForceCutOverSchemaMigrationRequest{ + Keyspace: "ks", + Uuid: "abc", + }, + shouldErr: true, + }, + // execute query failure + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ts := memorytopo.NewServer(ctx, "zone1") + + testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ + AlsoSetShardPrimary: true, + }, test.tablets...) + + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return NewVtctldServer(vtenv.NewTestEnv(), ts) + }) + + resp, err := vtctld.ForceCutOverSchemaMigration(ctx, test.req) + if test.shouldErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + utils.MustMatch(t, test.expected, resp) + }) + } +} + func TestCompleteSchemaMigration(t *testing.T) { t.Parallel() @@ -1952,11 +2337,11 @@ func TestCompleteSchemaMigration(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() - ctx, Complete := context.WithCancel(context.Background()) - defer Complete() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() ts := memorytopo.NewServer(ctx, "zone1") testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -1964,7 +2349,7 @@ func TestCompleteSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.CompleteSchemaMigration(ctx, test.req) @@ -2207,8 +2592,6 @@ func TestCreateKeyspace(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2220,7 +2603,7 @@ func TestCreateKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) for name, ks := range tt.topo { @@ -2486,8 +2869,6 @@ func TestCreateShard(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() if tt.req == nil { @@ -2498,7 +2879,7 @@ func TestCreateShard(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) for _, ks := range tt.keyspaces { @@ -2553,7 +2934,7 @@ func TestDeleteCellInfo(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.DeleteCellInfo(ctx, tt.req) if tt.shouldErr { @@ -2614,7 +2995,7 @@ func TestDeleteCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.DeleteCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -2846,7 +3227,7 @@ func TestDeleteKeyspace(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) @@ -3348,19 +3729,17 @@ func TestDeleteShards(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() cells := []string{"zone1", "zone2", "zone3"} - ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) + ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -3477,8 +3856,6 @@ func TestDeleteSrvKeyspace(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3503,7 +3880,7 @@ func TestDeleteSrvKeyspace(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.DeleteSrvVSchema(ctx, tt.req) if tt.shouldErr { @@ -3951,8 +4328,6 @@ func TestDeleteTablets(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3964,7 +4339,7 @@ func TestDeleteTablets(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) // Setup tablets and shards @@ -3986,7 +4361,7 @@ func TestDeleteTablets(t *testing.T) { // value anymore defer unlock(&lerr) - // we do, however, care that the lock context gets propogated + // we do, however, care that the lock context gets propagated // both to additional calls to lock, and to the actual RPC call. ctx = lctx } @@ -4193,7 +4568,7 @@ func TestEmergencyReparentShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.EmergencyReparentShard(ctx, tt.req) @@ -4325,7 +4700,6 @@ func TestExecuteFetchAsApp(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -4335,7 +4709,7 @@ func TestExecuteFetchAsApp(t *testing.T) { testutil.AddTablet(ctx, t, ts, tt.tablet, nil) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.ExecuteFetchAsApp(ctx, tt.req) if tt.shouldErr { @@ -4452,7 +4826,6 @@ func TestExecuteFetchAsDBA(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -4462,7 +4835,7 @@ func TestExecuteFetchAsDBA(t *testing.T) { testutil.AddTablet(ctx, t, ts, tt.tablet, nil) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.ExecuteFetchAsDBA(ctx, tt.req) if tt.shouldErr { @@ -4647,7 +5020,7 @@ func TestExecuteHook(t *testing.T) { t.Run(tt.name, func(t *testing.T) { testutil.AddTablets(ctx, t, tt.ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.ExecuteHook(ctx, tt.req) @@ -4668,7 +5041,7 @@ func TestFindAllShardsInKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) ks := &vtctldatapb.Keyspace{ @@ -4710,7 +5083,7 @@ func TestGetBackups(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.BackupStorage.Backups = map[string][]string{ @@ -4818,7 +5191,7 @@ func TestGetKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) expected := &vtctldatapb.GetKeyspaceResponse{ @@ -4844,7 +5217,7 @@ func TestGetCellInfoNames(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2", "cell3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) @@ -4853,7 +5226,7 @@ func TestGetCellInfoNames(t *testing.T) { ts = memorytopo.NewServer(ctx) vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err = vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) @@ -4862,7 +5235,7 @@ func TestGetCellInfoNames(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx, "cell1") vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) topofactory.SetError(assert.AnError) @@ -4877,7 +5250,7 @@ func TestGetCellInfo(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) expected := &topodatapb.CellInfo{ @@ -4905,7 +5278,7 @@ func TestGetCellsAliases(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "c11", "c12", "c13", "c21", "c22") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) alias1 := &topodatapb.CellsAlias{ @@ -4932,7 +5305,7 @@ func TestGetCellsAliases(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx) vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) topofactory.SetError(assert.AnError) @@ -4999,8 +5372,6 @@ func TestGetFullStatus(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -5012,7 +5383,9 @@ func TestGetFullStatus(t *testing.T) { FullStatusResult: &replicationdatapb.FullStatus{ ServerUuid: tt.serverUUID, }, - }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) + }, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return NewVtctldServer(vtenv.NewTestEnv(), ts) + }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ AlsoSetShardPrimary: true, @@ -5037,7 +5410,7 @@ func TestGetKeyspaces(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) @@ -5194,7 +5567,6 @@ func TestGetPermissions(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -5205,7 +5577,7 @@ func TestGetPermissions(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.GetPermissions(ctx, tt.req) if tt.shouldErr { @@ -5263,7 +5635,6 @@ func TestGetRoutingRules(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -5281,7 +5652,7 @@ func TestGetRoutingRules(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.GetRoutingRules(ctx, &vtctldatapb.GetRoutingRulesRequest{}) if tt.shouldErr { @@ -5306,7 +5677,7 @@ func TestGetSchema(t *testing.T) { }{}, } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) validAlias := &topodatapb.TabletAlias{ @@ -5631,7 +6002,6 @@ func TestGetSchemaMigrations(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() @@ -5671,7 +6041,7 @@ func TestGetSchemaMigrations(t *testing.T) { ts, factory := memorytopo.NewServerAndFactory(ctx, cells...) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{AlsoSetShardPrimary: true}, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) if test.failTopo { @@ -5751,8 +6121,6 @@ func TestGetShard(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -5762,7 +6130,7 @@ func TestGetShard(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddShards(ctx, t, ts, tt.topo...) @@ -5873,8 +6241,6 @@ func TestGetSrvKeyspaceNames(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -5899,7 +6265,7 @@ func TestGetSrvKeyspaceNames(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.GetSrvKeyspaceNames(ctx, tt.req) if tt.shouldErr { @@ -6040,8 +6406,6 @@ func TestGetSrvKeyspaces(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -6056,7 +6420,7 @@ func TestGetSrvKeyspaces(t *testing.T) { testutil.AddSrvKeyspaces(t, ts, tt.srvKeyspaces...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) if tt.topoErr != nil { @@ -6083,7 +6447,7 @@ func TestGetSrvVSchema(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1", "zone2") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) zone1SrvVSchema := &vschemapb.SrvVSchema{ @@ -6285,8 +6649,6 @@ func TestGetSrvVSchemas(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -6294,7 +6656,7 @@ func TestGetSrvVSchemas(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1", "zone2", "zone3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) zone1SrvVSchema := &vschemapb.SrvVSchema{ @@ -6354,7 +6716,7 @@ func TestGetTablet(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) tablet := &topodatapb.Tablet{ @@ -6968,8 +7330,6 @@ func TestGetTablets(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -6977,7 +7337,7 @@ func TestGetTablets(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, tt.cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) @@ -7001,7 +7361,7 @@ func TestGetTopologyPath(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2", "cell3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) err := ts.CreateKeyspace(ctx, "keyspace1", &topodatapb.Keyspace{}) @@ -7062,8 +7422,6 @@ func TestGetTopologyPath(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -7090,7 +7448,7 @@ func TestGetVSchema(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) t.Run("found", func(t *testing.T) { @@ -7309,11 +7667,11 @@ func TestLaunchSchemaMigration(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() - ctx, Launch := context.WithCancel(context.Background()) - defer Launch() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() ts := memorytopo.NewServer(ctx, "zone1") testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -7321,7 +7679,7 @@ func TestLaunchSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.LaunchSchemaMigration(ctx, test.req) @@ -7408,7 +7766,7 @@ func TestPingTablet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.PingTablet(ctx, tt.req) @@ -7439,7 +7797,7 @@ func TestPlannedReparentShard(t *testing.T) { req *vtctldatapb.PlannedReparentShardRequest expected *vtctldatapb.PlannedReparentShardResponse expectEventsToOccur bool - shouldErr bool + expectedErr string }{ { name: "successful reparent", @@ -7554,7 +7912,6 @@ func TestPlannedReparentShard(t *testing.T) { }, }, expectEventsToOccur: true, - shouldErr: false, }, { // Note: this is testing the error-handling done in @@ -7570,7 +7927,7 @@ func TestPlannedReparentShard(t *testing.T) { Shard: "-", }, expectEventsToOccur: false, - shouldErr: true, + expectedErr: "node doesn't exist: keyspaces/testkeyspace/shards/-", }, { name: "invalid WaitReplicasTimeout", @@ -7580,7 +7937,71 @@ func TestPlannedReparentShard(t *testing.T) { Nanos: 1, }, }, - shouldErr: true, + expectedErr: "duration: seconds:-1 nanos:1 is out of range for time.Duration", + }, + { + name: "tablet unreachable", + ts: memorytopo.NewServer(ctx, "zone1"), + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + PrimaryTermStartTime: &vttime.Time{ + Seconds: 100, + }, + Keyspace: "testkeyspace", + Shard: "-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_RDONLY, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + tmc: &testutil.TabletManagerClient{ + // This is only needed to verify reachability, so empty results are fine. + PrimaryStatusResults: map[string]struct { + Status *replicationdatapb.PrimaryStatus + Error error + }{ + "zone1-0000000200": { + Error: fmt.Errorf("primary status failed"), + }, + "zone1-0000000101": { + Status: &replicationdatapb.PrimaryStatus{}, + }, + "zone1-0000000100": { + Status: &replicationdatapb.PrimaryStatus{}, + }, + }, + }, + req: &vtctldatapb.PlannedReparentShardRequest{ + Keyspace: "testkeyspace", + Shard: "-", + NewPrimary: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + WaitReplicasTimeout: protoutil.DurationToProto(time.Millisecond * 10), + }, + expectEventsToOccur: true, + expectedErr: "primary status failed", }, } @@ -7593,7 +8014,7 @@ func TestPlannedReparentShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.PlannedReparentShard(ctx, tt.req) @@ -7610,8 +8031,8 @@ func TestPlannedReparentShard(t *testing.T) { testutil.AssertLogutilEventsOccurred(t, resp, "expected events to occur during ERS") }() - if tt.shouldErr { - assert.Error(t, err) + if tt.expectedErr != "" { + assert.EqualError(t, err, tt.expectedErr) return } @@ -7636,7 +8057,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.RebuildKeyspaceGraph(ctx, &vtctldatapb.RebuildKeyspaceGraphRequest{ @@ -7653,7 +8074,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.RebuildKeyspaceGraph(context.Background(), &vtctldatapb.RebuildKeyspaceGraphRequest{ @@ -7673,7 +8094,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) factory.SetError(assert.AnError) @@ -7694,7 +8115,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) lctx, unlock, lerr := ts.LockKeyspace(context.Background(), "testkeyspace", "test lock") @@ -7730,7 +8151,6 @@ func TestRebuildVSchemaGraph(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -7743,7 +8163,7 @@ func TestRebuildVSchemaGraph(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.RebuildVSchemaGraph(ctx, req) if tt.shouldErr { @@ -7842,7 +8262,7 @@ func TestRefreshState(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.RefreshState(ctx, tt.req) if tt.shouldErr { @@ -8027,7 +8447,7 @@ func TestRefreshStateByShard(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.RefreshStateByShard(ctx, tt.req) if tt.shouldErr { @@ -8122,7 +8542,6 @@ func TestReloadSchema(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -8131,7 +8550,7 @@ func TestReloadSchema(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.ReloadSchema(ctx, tt.req) if tt.shouldErr { @@ -8216,7 +8635,6 @@ func TestReloadSchemaKeyspace(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -8229,7 +8647,7 @@ func TestReloadSchemaKeyspace(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.ReloadSchemaKeyspace(ctx, tt.req) if tt.shouldErr { @@ -8374,7 +8792,6 @@ func TestReloadSchemaShard(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -8387,7 +8804,7 @@ func TestReloadSchemaShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.ReloadSchemaShard(ctx, tt.req) if tt.shouldErr { @@ -8406,7 +8823,7 @@ func TestRemoveBackup(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) setup := func() { @@ -8586,8 +9003,6 @@ func TestRemoveKeyspaceCell(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -8597,7 +9012,7 @@ func TestRemoveKeyspaceCell(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) // Setup topo @@ -8875,8 +9290,6 @@ func TestRemoveShardCell(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -8886,7 +9299,7 @@ func TestRemoveShardCell(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) // Setup shard topos and replication graphs. @@ -9481,8 +9894,6 @@ func TestReparentTablet(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -9496,7 +9907,7 @@ func TestReparentTablet(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -9629,7 +10040,7 @@ func TestRestoreFromBackup(t *testing.T) { }, tt.tablets..., ) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) client := localvtctldclient.New(vtctld) stream, err := client.RestoreFromBackup(ctx, tt.req) @@ -9847,7 +10258,7 @@ func TestRetrySchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.RetrySchemaMigration(ctx, test.req) @@ -9943,7 +10354,6 @@ func TestRunHealthCheck(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -9954,7 +10364,7 @@ func TestRunHealthCheck(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.RunHealthCheck(ctx, tt.req) if tt.shouldErr { @@ -10023,7 +10433,6 @@ func TestSetKeyspaceDurabilityPolicy(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -10034,7 +10443,7 @@ func TestSetKeyspaceDurabilityPolicy(t *testing.T) { testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.SetKeyspaceDurabilityPolicy(ctx, tt.req) if tt.expectedErr != "" { @@ -10093,7 +10502,7 @@ func TestSetShardIsPrimaryServing(t *testing.T) { name: "lock error", setup: func(t *testing.T, tt *testcase) context.Context { var cancel func() - tt.ctx, cancel = context.WithTimeout(ctx, time.Millisecond*50) + tt.ctx, cancel = context.WithCancel(ctx) tt.ts = memorytopo.NewServer(ctx, "zone1") testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ Keyspace: "testkeyspace", @@ -10131,7 +10540,7 @@ func TestSetShardIsPrimaryServing(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.SetShardIsPrimaryServing(tt.ctx, tt.req) if tt.shouldErr { @@ -10345,7 +10754,7 @@ func TestSetShardTabletControl(t *testing.T) { name: "keyspace lock error", setup: func(t *testing.T, tt *testcase) { var cancel func() - tt.ctx, cancel = context.WithTimeout(ctx, time.Millisecond*50) + tt.ctx, cancel = context.WithCancel(ctx) tt.ts = memorytopo.NewServer(ctx, "zone1") testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ Keyspace: "testkeyspace", @@ -10381,7 +10790,7 @@ func TestSetShardTabletControl(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.SetShardTabletControl(tt.ctx, tt.req) if tt.shouldErr { @@ -10573,7 +10982,6 @@ func TestSetWritable(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -10585,7 +10993,7 @@ func TestSetWritable(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.SetWritable(ctx, tt.req) @@ -10606,7 +11014,7 @@ func TestShardReplicationAdd(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) tablets := []*topodatapb.Tablet{ @@ -10774,7 +11182,7 @@ func TestShardReplicationPositions(t *testing.T) { }, tmc: &testutil.TabletManagerClient{ PrimaryPositionDelays: map[string]time.Duration{ - "zone1-0000000100": time.Millisecond * 100, + "zone1-0000000100": time.Second * 2, }, PrimaryPositionResults: map[string]struct { Position string @@ -10785,7 +11193,7 @@ func TestShardReplicationPositions(t *testing.T) { }, }, ReplicationStatusDelays: map[string]time.Duration{ - "zone1-0000000101": time.Millisecond * 100, + "zone1-0000000101": time.Second * 2, }, ReplicationStatusResults: map[string]struct { Position *replicationdatapb.Status @@ -10798,7 +11206,7 @@ func TestShardReplicationPositions(t *testing.T) { }, }, }, - ctxTimeout: time.Millisecond * 10, + ctxTimeout: time.Second, req: &vtctldatapb.ShardReplicationPositionsRequest{ Keyspace: "testkeyspace", Shard: "-", @@ -10901,7 +11309,7 @@ func TestShardReplicationPositions(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) requestCtx := ctx @@ -10932,7 +11340,7 @@ func TestShardReplicationRemove(t *testing.T) { ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) tablets := []*topodatapb.Tablet{ @@ -11084,7 +11492,6 @@ func TestSourceShardAdd(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -11092,7 +11499,7 @@ func TestSourceShardAdd(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -11219,7 +11626,6 @@ func TestSourceShardDelete(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -11227,7 +11633,7 @@ func TestSourceShardDelete(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -11405,7 +11811,6 @@ func TestStartReplication(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -11419,7 +11824,7 @@ func TestStartReplication(t *testing.T) { AlsoSetShardPrimary: true, }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.StartReplication(ctx, tt.req) @@ -11544,7 +11949,6 @@ func TestStopReplication(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -11556,7 +11960,7 @@ func TestStopReplication(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) _, err := vtctld.StopReplication(ctx, tt.req) @@ -11929,8 +12333,6 @@ func TestTabletExternallyReparented(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -11943,7 +12345,7 @@ func TestTabletExternallyReparented(t *testing.T) { TopoServer: ts, } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) if tt.tmcHasNoTopo { @@ -12110,7 +12512,6 @@ func TestUpdateCellInfo(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -12128,7 +12529,7 @@ func TestUpdateCellInfo(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.UpdateCellInfo(ctx, tt.req) if tt.shouldErr { @@ -12251,7 +12652,6 @@ func TestUpdateCellsAlias(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -12278,7 +12678,7 @@ func TestUpdateCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.UpdateCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -12386,7 +12786,7 @@ func TestValidate(t *testing.T) { SkipShardCreation: false, }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.Validate(ctx, &vtctldatapb.ValidateRequest{ @@ -12503,7 +12903,7 @@ func TestValidateSchemaKeyspace(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) schema1 := &tabletmanagerdatapb.SchemaDefinition{ @@ -12689,7 +13089,7 @@ func TestValidateVersionKeyspace(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) tests := []*struct { @@ -12804,7 +13204,7 @@ func TestValidateVersionShard(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) tests := []*struct { @@ -13396,7 +13796,7 @@ func TestValidateShard(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts) + return NewVtctldServer(vtenv.NewTestEnv(), ts) }) resp, err := vtctld.ValidateShard(ctx, tt.req) if tt.shouldErr { diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go b/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go index dc273dcb962..1097d2994cc 100644 --- a/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go +++ b/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go @@ -104,7 +104,7 @@ func (a handlesByName) Less(i, j int) bool { return a[i].Name() < a[j].Name() } // *backupstorage.BackupStorageImplementation to this value before use. const BackupStorageImplementation = "grpcvtctldserver.testutil" -// BackupStorage is the singleton test backupstorage.BackupStorage intastnce. It +// BackupStorage is the singleton test backupstorage.BackupStorage instance. It // is public and singleton to allow tests to both mutate and assert against its // state. var BackupStorage = &backupStorage{ diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go index ba7c8477d22..736bda4a1f4 100644 --- a/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go +++ b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go @@ -1348,7 +1348,7 @@ func (fake *TabletManagerClient) UndoDemotePrimary(ctx context.Context, tablet * return assert.AnError } -// VReplicationExec is part of the tmclient.TabletManagerCLient interface. +// VReplicationExec is part of the tmclient.TabletManagerClient interface. func (fake *TabletManagerClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { if fake.VReplicationExecResults == nil { return nil, assert.AnError @@ -1374,7 +1374,7 @@ func (fake *TabletManagerClient) VReplicationExec(ctx context.Context, tablet *t if resultsForTablet, ok := fake.VReplicationExecResults[key]; ok { // Round trip the expected query both to ensure it's valid and to // standardize on capitalization and formatting. - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) if err != nil { return nil, err } @@ -1393,7 +1393,7 @@ func (fake *TabletManagerClient) VReplicationExec(ctx context.Context, tablet *t return nil, assert.AnError } -// CheckThrottler is part of the tmclient.TabletManagerCLient interface. +// CheckThrottler is part of the tmclient.TabletManagerClient interface. func (fake *TabletManagerClient) CheckThrottler(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.CheckThrottlerRequest) (*tabletmanagerdatapb.CheckThrottlerResponse, error) { if fake.CheckThrottlerResults == nil { return nil, assert.AnError diff --git a/go/vt/vtctl/grpcvtctldserver/topo.go b/go/vt/vtctl/grpcvtctldserver/topo.go index 70fae6613aa..5ec369ca17f 100644 --- a/go/vt/vtctl/grpcvtctldserver/topo.go +++ b/go/vt/vtctl/grpcvtctldserver/topo.go @@ -161,7 +161,7 @@ func deleteShardCell(ctx context.Context, ts *topo.Server, keyspace string, shar // Get all the tablet records for the aliases we've collected. Note that // GetTabletMap ignores ErrNoNode, which is convenient for our purpose; it // means a tablet was deleted but is still referenced. - tabletMap, err := ts.GetTabletMap(ctx, aliases) + tabletMap, err := ts.GetTabletMap(ctx, aliases, nil) if err != nil { return fmt.Errorf("GetTabletMap() failed: %w", err) } diff --git a/go/vt/vtctl/grpcvtctlserver/server.go b/go/vt/vtctl/grpcvtctlserver/server.go index afd7b9df1c9..d89f91b2d29 100644 --- a/go/vt/vtctl/grpcvtctlserver/server.go +++ b/go/vt/vtctl/grpcvtctlserver/server.go @@ -25,6 +25,8 @@ import ( "google.golang.org/grpc" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/topo" @@ -40,12 +42,13 @@ import ( // VtctlServer is our RPC server type VtctlServer struct { vtctlservicepb.UnimplementedVtctlServer - ts *topo.Server + ts *topo.Server + env *vtenv.Environment } // NewVtctlServer returns a new Vtctl Server for the topo server. -func NewVtctlServer(ts *topo.Server) *VtctlServer { - return &VtctlServer{ts: ts} +func NewVtctlServer(env *vtenv.Environment, ts *topo.Server) *VtctlServer { + return &VtctlServer{env: env, ts: ts} } // ExecuteVtctlCommand is part of the vtctldatapb.VtctlServer interface @@ -72,13 +75,13 @@ func (s *VtctlServer) ExecuteVtctlCommand(args *vtctldatapb.ExecuteVtctlCommandR // create the wrangler tmc := tmclient.NewTabletManagerClient() defer tmc.Close() - wr := wrangler.New(logger, s.ts, tmc) + wr := wrangler.New(s.env, logger, s.ts, tmc) // execute the command return vtctl.RunCommand(stream.Context(), wr, args.Args) } // StartServer registers the VtctlServer for RPCs -func StartServer(s *grpc.Server, ts *topo.Server) { - vtctlservicepb.RegisterVtctlServer(s, NewVtctlServer(ts)) +func StartServer(s *grpc.Server, env *vtenv.Environment, ts *topo.Server) { + vtctlservicepb.RegisterVtctlServer(s, NewVtctlServer(env, ts)) } diff --git a/go/vt/vtctl/internal/grpcshim/bidi_stream.go b/go/vt/vtctl/internal/grpcshim/bidi_stream.go index a620cb929aa..92e7c24068b 100644 --- a/go/vt/vtctl/internal/grpcshim/bidi_stream.go +++ b/go/vt/vtctl/internal/grpcshim/bidi_stream.go @@ -101,7 +101,7 @@ type BidiStream struct { // NewBidiStream returns a BidiStream ready for embedded use. The provided ctx // will be used for the stream context, and types embedding BidiStream should -// check context cancellation/expiriation in their respective Recv and Send +// check context cancellation/expiration in their respective Recv and Send // methods. // // See the documentation on BidiStream for example usage. @@ -123,7 +123,7 @@ func (bs *BidiStream) Closed() <-chan struct{} { // IsClosed returns true if the stream has been closed for sending. // -// It is a conveince function for attempting to select on the channel returned +// It is a convenience function for attempting to select on the channel returned // by bs.Closed(). func (bs *BidiStream) IsClosed() bool { select { diff --git a/go/vt/vtctl/localvtctldclient/client_gen.go b/go/vt/vtctl/localvtctldclient/client_gen.go index 198fc12908f..cbde68f1b27 100644 --- a/go/vt/vtctl/localvtctldclient/client_gen.go +++ b/go/vt/vtctl/localvtctldclient/client_gen.go @@ -246,6 +246,11 @@ func (client *localVtctldClient) FindAllShardsInKeyspace(ctx context.Context, in return client.s.FindAllShardsInKeyspace(ctx, in) } +// ForceCutOverSchemaMigration is part of the vtctlservicepb.VtctldClient interface. +func (client *localVtctldClient) ForceCutOverSchemaMigration(ctx context.Context, in *vtctldatapb.ForceCutOverSchemaMigrationRequest, opts ...grpc.CallOption) (*vtctldatapb.ForceCutOverSchemaMigrationResponse, error) { + return client.s.ForceCutOverSchemaMigration(ctx, in) +} + // GetBackups is part of the vtctlservicepb.VtctldClient interface. func (client *localVtctldClient) GetBackups(ctx context.Context, in *vtctldatapb.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetBackupsResponse, error) { return client.s.GetBackups(ctx, in) diff --git a/go/vt/vtctl/reparent.go b/go/vt/vtctl/reparent.go index 7ed0f6582b9..4498228d9c7 100644 --- a/go/vt/vtctl/reparent.go +++ b/go/vt/vtctl/reparent.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl/reparentutil" "vitess.io/vitess/go/vt/wrangler" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -114,6 +115,7 @@ func commandPlannedReparentShard(ctx context.Context, wr *wrangler.Wrangler, sub } waitReplicasTimeout := subFlags.Duration("wait_replicas_timeout", topo.RemoteOperationTimeout, "time to wait for replicas to catch up on replication before and after reparenting") + tolerableReplicationLag := subFlags.Duration("tolerable-replication-lag", 0, "amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary") keyspaceShard := subFlags.String("keyspace_shard", "", "keyspace/shard of the shard that needs to be reparented") newPrimary := subFlags.String("new_primary", "", "alias of a tablet that should be the new primary") avoidTablet := subFlags.String("avoid_tablet", "", "alias of a tablet that should not be the primary, i.e. reparent to any other tablet if this one is the primary") @@ -149,7 +151,13 @@ func commandPlannedReparentShard(ctx context.Context, wr *wrangler.Wrangler, sub return err } } - return wr.PlannedReparentShard(ctx, keyspace, shard, newPrimaryAlias, avoidTabletAlias, *waitReplicasTimeout) + + return wr.PlannedReparentShard(ctx, keyspace, shard, reparentutil.PlannedReparentOptions{ + NewPrimaryAlias: newPrimaryAlias, + AvoidPrimaryAlias: avoidTabletAlias, + WaitReplicasTimeout: *waitReplicasTimeout, + TolerableReplLag: *tolerableReplicationLag, + }) } func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { @@ -189,8 +197,14 @@ func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, s return err } } - unreachableReplicas := topoproto.ParseTabletSet(*ignoreReplicasList) - return wr.EmergencyReparentShard(ctx, keyspace, shard, tabletAlias, *waitReplicasTimeout, unreachableReplicas, *preventCrossCellPromotion, *waitForAllTablets) + + return wr.EmergencyReparentShard(ctx, keyspace, shard, reparentutil.EmergencyReparentOptions{ + NewPrimaryAlias: tabletAlias, + WaitAllTablets: *waitForAllTablets, + WaitReplicasTimeout: *waitReplicasTimeout, + IgnoreReplicas: topoproto.ParseTabletSet(*ignoreReplicasList), + PreventCrossCellPromotion: *preventCrossCellPromotion, + }) } func commandTabletExternallyReparented(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { diff --git a/go/vt/vtctl/reparentutil/emergency_reparenter.go b/go/vt/vtctl/reparentutil/emergency_reparenter.go index 7f190a4d994..607fec700b5 100644 --- a/go/vt/vtctl/reparentutil/emergency_reparenter.go +++ b/go/vt/vtctl/reparentutil/emergency_reparenter.go @@ -697,7 +697,7 @@ func (erp *EmergencyReparenter) identifyPrimaryCandidate( } } // Unreachable code. - // We should have found atleast 1 tablet in the valid list. + // We should have found at least 1 tablet in the valid list. // If the list is empty, then we should have errored out much sooner. return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unreachable - did not find a valid primary candidate even though the valid candidate list was non-empty") } diff --git a/go/vt/vtctl/reparentutil/emergency_reparenter_test.go b/go/vt/vtctl/reparentutil/emergency_reparenter_test.go index d7f8bb6a1db..8e793bcf6d5 100644 --- a/go/vt/vtctl/reparentutil/emergency_reparenter_test.go +++ b/go/vt/vtctl/reparentutil/emergency_reparenter_test.go @@ -60,8 +60,6 @@ func TestNewEmergencyReparenter(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -100,8 +98,6 @@ func TestEmergencyReparenter_getLockAction(t *testing.T) { erp := &EmergencyReparenter{} for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1900,8 +1896,6 @@ func TestEmergencyReparenter_reparentShardLocked(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -2428,8 +2422,6 @@ func TestEmergencyReparenter_promoteNewPrimary(t *testing.T) { durability, _ := GetDurabilityPolicy("none") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2716,8 +2708,6 @@ func TestEmergencyReparenter_waitForAllRelayLogsToApply(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3542,8 +3532,6 @@ func TestEmergencyReparenter_reparentReplicas(t *testing.T) { durability, _ := GetDurabilityPolicy("none") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3990,8 +3978,6 @@ func TestEmergencyReparenter_promoteIntermediateSource(t *testing.T) { durability, _ := GetDurabilityPolicy("none") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/reparentutil/planned_reparenter.go b/go/vt/vtctl/reparentutil/planned_reparenter.go index 9fc933a8e35..4eee914ce0d 100644 --- a/go/vt/vtctl/reparentutil/planned_reparenter.go +++ b/go/vt/vtctl/reparentutil/planned_reparenter.go @@ -60,6 +60,7 @@ type PlannedReparentOptions struct { NewPrimaryAlias *topodatapb.TabletAlias AvoidPrimaryAlias *topodatapb.TabletAlias WaitReplicasTimeout time.Duration + TolerableReplLag time.Duration // Private options managed internally. We use value-passing semantics to // set these options inside a PlannedReparent without leaking these details @@ -151,7 +152,7 @@ func (pr *PlannedReparenter) getLockAction(opts PlannedReparentOptions) string { // primary), as well as an error. // // It will also set the NewPrimaryAlias option if the caller did not specify -// one, provided it can choose a new primary candidate. See ChooseNewPrimary() +// one, provided it can choose a new primary candidate. See ElectNewPrimary() // for details on primary candidate selection. func (pr *PlannedReparenter) preflightChecks( ctx context.Context, @@ -176,22 +177,17 @@ func (pr *PlannedReparenter) preflightChecks( event.DispatchUpdate(ev, "current primary is different than tablet to avoid, nothing to do") return true, nil } + } - event.DispatchUpdate(ev, "searching for primary candidate") - - opts.NewPrimaryAlias, err = ChooseNewPrimary(ctx, pr.tmc, &ev.ShardInfo, tabletMap, opts.AvoidPrimaryAlias, opts.WaitReplicasTimeout, opts.durability, pr.logger) - if err != nil { - return true, err - } - - if opts.NewPrimaryAlias == nil { - return true, vterrors.Errorf(vtrpc.Code_INTERNAL, "cannot find a tablet to reparent to in the same cell as the current primary") - } - - pr.logger.Infof("elected new primary candidate %v", topoproto.TabletAliasString(opts.NewPrimaryAlias)) - event.DispatchUpdate(ev, "elected new primary candidate") + event.DispatchUpdate(ev, "electing a primary candidate") + opts.NewPrimaryAlias, err = ElectNewPrimary(ctx, pr.tmc, &ev.ShardInfo, tabletMap, opts.NewPrimaryAlias, opts.AvoidPrimaryAlias, opts.WaitReplicasTimeout, opts.TolerableReplLag, opts.durability, pr.logger) + if err != nil { + return true, err } + pr.logger.Infof("elected new primary candidate %v", topoproto.TabletAliasString(opts.NewPrimaryAlias)) + event.DispatchUpdate(ev, "elected new primary candidate") + primaryElectAliasStr := topoproto.TabletAliasString(opts.NewPrimaryAlias) newPrimaryTabletInfo, ok := tabletMap[primaryElectAliasStr] diff --git a/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go b/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go index c564a95167e..25e4c86f7c5 100644 --- a/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go +++ b/go/vt/vtctl/reparentutil/planned_reparenter_flaky_test.go @@ -62,8 +62,6 @@ func TestNewPlannedReparenter(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -444,8 +442,6 @@ func TestPlannedReparenter_ReparentShard(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -542,8 +538,6 @@ func TestPlannedReparenter_getLockAction(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -616,6 +610,114 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) { }, shouldErr: false, }, + { + name: "new primary provided - replication lag is tolerable", + ev: &events.Reparent{ + ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 500, + }, + }, nil), + }, + tmc: &testutil.TabletManagerClient{ + ReplicationStatusResults: map[string]struct { + Position *replicationdatapb.Status + Error error + }{ + "zone1-0000000100": { + Position: &replicationdatapb.Status{ + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-2", + ReplicationLagSeconds: 2, + }, + }, + }, + }, + tabletMap: map[string]*topo.TabletInfo{ + "zone1-0000000100": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + }, + opts: &PlannedReparentOptions{ + NewPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + TolerableReplLag: 10 * time.Second, + }, + expectedIsNoop: false, + expectedEvent: &events.Reparent{ + ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 500, + }, + }, nil), + NewPrimary: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + shouldErr: false, + }, + { + name: "new primary provided - replication lag is not tolerable", + ev: &events.Reparent{ + ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 500, + }, + }, nil), + }, + tmc: &testutil.TabletManagerClient{ + ReplicationStatusResults: map[string]struct { + Position *replicationdatapb.Status + Error error + }{ + "zone1-0000000100": { + Position: &replicationdatapb.Status{ + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-2", + ReplicationLagSeconds: 25, + }, + }, + }, + }, + tabletMap: map[string]*topo.TabletInfo{ + "zone1-0000000100": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + }, + opts: &PlannedReparentOptions{ + NewPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + TolerableReplLag: 10 * time.Second, + }, + expectedIsNoop: true, + expectedEvent: &events.Reparent{ + ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 500, + }, + }, nil), + }, + shouldErr: true, + }, { name: "invariants hold with primary selection", tmc: &testutil.TabletManagerClient{ @@ -745,10 +847,10 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) { shouldErr: false, }, { - // this doesn't cause an actual error from ChooseNewPrimary, because + // this doesn't cause an actual error from ElectNewPrimary, because // there is no way to do that other than something going horribly wrong // in go runtime, however we do check that we - // get a non-nil result from ChooseNewPrimary in preflightChecks and + // get a non-nil result from ElectNewPrimary in preflightChecks and // bail out if we don't, so we're forcing that case here. name: "cannot choose new primary-elect", ev: &events.Reparent{ @@ -779,9 +881,12 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) { shouldErr: true, }, { - name: "primary-elect is not in tablet map", - ev: &events.Reparent{}, - tabletMap: map[string]*topo.TabletInfo{}, + name: "primary-elect is not in tablet map", + ev: &events.Reparent{ + ShardInfo: *topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: nil, + }, nil), + }, tabletMap: map[string]*topo.TabletInfo{}, opts: &PlannedReparentOptions{ NewPrimaryAlias: &topodatapb.TabletAlias{ Cell: "zone1", @@ -952,8 +1057,6 @@ func TestPlannedReparenter_preflightChecks(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1565,8 +1668,6 @@ func TestPlannedReparenter_performGracefulPromotion(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1727,8 +1828,6 @@ func TestPlannedReparenter_performInitialPromotion(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1898,8 +1997,6 @@ func TestPlannedReparenter_performPartialPromotionRecovery(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -2264,8 +2361,6 @@ func TestPlannedReparenter_performPotentialPromotion(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3085,8 +3180,6 @@ func TestPlannedReparenter_reparentShardLocked(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -3702,8 +3795,6 @@ func TestPlannedReparenter_reparentTablets(t *testing.T) { logger := logutil.NewMemoryLogger() for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/reparentutil/replication_test.go b/go/vt/vtctl/reparentutil/replication_test.go index ed7bd152e9c..922dc6bc2da 100644 --- a/go/vt/vtctl/reparentutil/replication_test.go +++ b/go/vt/vtctl/reparentutil/replication_test.go @@ -54,7 +54,7 @@ func TestFindValidEmergencyReparentCandidates(t *testing.T) { statusMap map[string]*replicationdatapb.StopReplicationStatus primaryStatusMap map[string]*replicationdatapb.PrimaryStatus // Note: for these tests, it's simpler to compare keys than actual - // mysql.Postion structs, which are just thin wrappers around the + // mysql.Position structs, which are just thin wrappers around the // mysql.GTIDSet interface. If a tablet alias makes it into the map, we // know it was chosen by the method, and that either // mysql.DecodePosition was successful (in the primary case) or @@ -205,8 +205,6 @@ func TestFindValidEmergencyReparentCandidates(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1280,8 +1278,6 @@ func Test_stopReplicationAndBuildStatusMaps(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { durability, err := GetDurabilityPolicy(tt.durability) require.NoError(t, err) @@ -1377,8 +1373,6 @@ func TestReplicaWasRunning(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1465,8 +1459,6 @@ func TestSQLThreadWasRunning(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1563,8 +1555,6 @@ func TestWaitForRelayLogsToApply(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/reparentutil/util.go b/go/vt/vtctl/reparentutil/util.go index cfde8f34508..cee588044bb 100644 --- a/go/vt/vtctl/reparentutil/util.go +++ b/go/vt/vtctl/reparentutil/util.go @@ -22,6 +22,7 @@ import ( "sync" "time" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "vitess.io/vitess/go/mysql/replication" @@ -32,7 +33,6 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtctl/reparentutil/promotionrule" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tmclient" @@ -48,7 +48,7 @@ var ( successResult = "success" ) -// ChooseNewPrimary finds a tablet that should become a primary after reparent. +// ElectNewPrimary finds a tablet that should become a primary after reparent. // The criteria for the new primary-elect are (preferably) to be in the same // cell as the current primary, and to be different from avoidPrimaryAlias. The // tablet with the most advanced replication position is chosen to minimize the @@ -58,13 +58,15 @@ var ( // with transactions being executed on the current primary, so when all tablets // are at roughly the same position, then the choice of new primary-elect will // be somewhat unpredictable. -func ChooseNewPrimary( +func ElectNewPrimary( ctx context.Context, tmc tmclient.TabletManagerClient, shardInfo *topo.ShardInfo, tabletMap map[string]*topo.TabletInfo, + newPrimaryAlias *topodatapb.TabletAlias, avoidPrimaryAlias *topodatapb.TabletAlias, waitReplicasTimeout time.Duration, + tolerableReplLag time.Duration, durability Durabler, // (TODO:@ajm188) it's a little gross we need to pass this, maybe embed in the context? logger logutil.Logger, @@ -84,8 +86,15 @@ func ChooseNewPrimary( errorGroup, groupCtx = errgroup.WithContext(ctx) ) + // candidates are the list of tablets that can be potentially promoted after filtering out based on preliminary checks. + candidates := []*topodatapb.Tablet{} for _, tablet := range tabletMap { switch { + case newPrimaryAlias != nil: + // If newPrimaryAlias is provided, then that is the only valid tablet, even if it is not of type replica or in a different cell. + if !topoproto.TabletAliasEqual(tablet.Alias, newPrimaryAlias) { + continue + } case primaryCell != "" && tablet.Alias.Cell != primaryCell: continue case avoidPrimaryAlias != nil && topoproto.TabletAliasEqual(tablet.Alias, avoidPrimaryAlias): @@ -94,13 +103,25 @@ func ChooseNewPrimary( continue } - tb := tablet.Tablet + candidates = append(candidates, tablet.Tablet) + } + + // There is only one tablet and tolerable replication lag is unspecified, + // then we don't need to find the position of the said tablet for sorting. + // We can just return the tablet quickly. + // This check isn't required, but it saves us an RPC call that is otherwise unnecessary. + if len(candidates) == 1 && tolerableReplLag == 0 { + return candidates[0].Alias, nil + } + + for _, tablet := range candidates { + tb := tablet errorGroup.Go(func() error { // find and store the positions for the tablet - pos, err := findPositionForTablet(groupCtx, tb, logger, tmc, waitReplicasTimeout) + pos, replLag, err := findPositionAndLagForTablet(groupCtx, tb, logger, tmc, waitReplicasTimeout) mu.Lock() defer mu.Unlock() - if err == nil { + if err == nil && (tolerableReplLag == 0 || tolerableReplLag >= replLag) { validTablets = append(validTablets, tb) tabletPositions = append(tabletPositions, pos) } @@ -113,9 +134,9 @@ func ChooseNewPrimary( return nil, err } - // return nothing if there are no valid tablets available + // return an error if there are no valid tablets available if len(validTablets) == 0 { - return nil, nil + return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "cannot find a tablet to reparent to in the same cell as the current primary") } // sort the tablets for finding the best primary @@ -127,9 +148,9 @@ func ChooseNewPrimary( return validTablets[0].Alias, nil } -// findPositionForTablet processes the replication position for a single tablet and +// findPositionAndLagForTablet processes the replication position and lag for a single tablet and // returns it. It is safe to call from multiple goroutines. -func findPositionForTablet(ctx context.Context, tablet *topodatapb.Tablet, logger logutil.Logger, tmc tmclient.TabletManagerClient, waitTimeout time.Duration) (replication.Position, error) { +func findPositionAndLagForTablet(ctx context.Context, tablet *topodatapb.Tablet, logger logutil.Logger, tmc tmclient.TabletManagerClient, waitTimeout time.Duration) (replication.Position, time.Duration, error) { logger.Infof("getting replication position from %v", topoproto.TabletAliasString(tablet.Alias)) ctx, cancel := context.WithTimeout(ctx, waitTimeout) @@ -140,10 +161,10 @@ func findPositionForTablet(ctx context.Context, tablet *topodatapb.Tablet, logge sqlErr, isSQLErr := sqlerror.NewSQLErrorFromError(err).(*sqlerror.SQLError) if isSQLErr && sqlErr != nil && sqlErr.Number() == sqlerror.ERNotReplica { logger.Warningf("no replication statue from %v, using empty gtid set", topoproto.TabletAliasString(tablet.Alias)) - return replication.Position{}, nil + return replication.Position{}, 0, nil } logger.Warningf("failed to get replication status from %v, ignoring tablet: %v", topoproto.TabletAliasString(tablet.Alias), err) - return replication.Position{}, err + return replication.Position{}, 0, err } // Use the relay log position if available, otherwise use the executed GTID set (binary log position). @@ -154,10 +175,10 @@ func findPositionForTablet(ctx context.Context, tablet *topodatapb.Tablet, logge pos, err := replication.DecodePosition(positionString) if err != nil { logger.Warningf("cannot decode replica position %v for tablet %v, ignoring tablet: %v", positionString, topoproto.TabletAliasString(tablet.Alias), err) - return replication.Position{}, err + return replication.Position{}, 0, err } - return pos, nil + return pos, time.Second * time.Duration(status.ReplicationLagSeconds), nil } // FindCurrentPrimary returns the current primary tablet of a shard, if any. The @@ -218,7 +239,7 @@ func ShardReplicationStatuses(ctx context.Context, ts *topo.Server, tmc tmclient if err != nil { return nil, nil, err } - tablets := topotools.CopyMapValues(tabletMap, []*topo.TabletInfo{}).([]*topo.TabletInfo) + tablets := maps.Values(tabletMap) log.Infof("Gathering tablet replication status for: %v", tablets) wg := sync.WaitGroup{} @@ -343,3 +364,15 @@ func waitForCatchUp( } return nil } + +// GetBackupCandidates is used to get a list of healthy tablets for backup +func GetBackupCandidates(tablets []*topo.TabletInfo, stats []*replicationdatapb.Status) (res []*topo.TabletInfo) { + for i, stat := range stats { + // shardTablets[i] and stats[i] is 1:1 mapping + // Always include TabletType_PRIMARY. Healthy shardTablets[i] will be added to tablets + if tablets[i].Type == topodatapb.TabletType_PRIMARY || stat != nil { + res = append(res, tablets[i]) + } + } + return res +} diff --git a/go/vt/vtctl/reparentutil/util_test.go b/go/vt/vtctl/reparentutil/util_test.go index a9e6274d490..b1d03e0d023 100644 --- a/go/vt/vtctl/reparentutil/util_test.go +++ b/go/vt/vtctl/reparentutil/util_test.go @@ -61,7 +61,7 @@ func (fake *chooseNewPrimaryTestTMClient) ReplicationStatus(ctx context.Context, return nil, assert.AnError } -func TestChooseNewPrimary(t *testing.T) { +func TestElectNewPrimary(t *testing.T) { t.Parallel() ctx := context.Background() @@ -71,23 +71,237 @@ func TestChooseNewPrimary(t *testing.T) { tmc *chooseNewPrimaryTestTMClient shardInfo *topo.ShardInfo tabletMap map[string]*topo.TabletInfo + newPrimaryAlias *topodatapb.TabletAlias avoidPrimaryAlias *topodatapb.TabletAlias + tolerableReplLag time.Duration expected *topodatapb.TabletAlias shouldErr bool }{ { name: "found a replica", tmc: &chooseNewPrimaryTestTMClient{ - // zone1-101 is behind zone1-102 + // zone1-101 is behind zone1-102 and zone1-102 has a tolerable replication lag replicationStatuses: map[string]*replicationdatapb.Status{ "zone1-0000000101": { Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1", }, "zone1-0000000102": { - Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + ReplicationLagSeconds: 20, + }, + }, + }, + tolerableReplLag: 50 * time.Second, + shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, nil), + tabletMap: map[string]*topo.TabletInfo{ + "primary": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + "replica1": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + "replica2": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + }, + avoidPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 0, + }, + expected: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + shouldErr: false, + }, + { + name: "new primary alias provided - no tolerable replication lag", + tolerableReplLag: 0, + shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, nil), + tabletMap: map[string]*topo.TabletInfo{ + "primary": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + "replica1": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + }, + newPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + expected: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + shouldErr: false, + }, + { + name: "new primary alias provided - with tolerable replication lag", + tmc: &chooseNewPrimaryTestTMClient{ + // zone1-102 has a tolerable replication lag + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + ReplicationLagSeconds: 20, + }, + }, + }, + tolerableReplLag: 50 * time.Second, + shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, nil), + tabletMap: map[string]*topo.TabletInfo{ + "primary": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + "replica1": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + "replica2": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + }, + newPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + expected: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + shouldErr: false, + }, + { + name: "new primary alias provided - with intolerable replication lag", + tmc: &chooseNewPrimaryTestTMClient{ + // zone1-102 has an intolerable replication lag + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + ReplicationLagSeconds: 100, + }, + }, + }, + tolerableReplLag: 50 * time.Second, + shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, nil), + tabletMap: map[string]*topo.TabletInfo{ + "primary": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + "replica1": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + "replica2": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + Type: topodatapb.TabletType_REPLICA, }, }, }, + newPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + expected: nil, + shouldErr: true, + }, + { + name: "found a replica ignoring replica lag", + tmc: &chooseNewPrimaryTestTMClient{ + // zone1-101 is behind zone1-102 and we don't care about the replication lag + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000101": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1", + }, + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + ReplicationLagSeconds: 230, + }, + }, + }, + tolerableReplLag: 0, shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ PrimaryAlias: &topodatapb.TabletAlias{ Cell: "zone1", @@ -133,6 +347,66 @@ func TestChooseNewPrimary(t *testing.T) { }, shouldErr: false, }, + { + name: "found a replica - ignore one with replication lag", + tmc: &chooseNewPrimaryTestTMClient{ + // zone1-101 is behind zone1-102 + replicationStatuses: map[string]*replicationdatapb.Status{ + "zone1-0000000101": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1", + }, + "zone1-0000000102": { + Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", + ReplicationLagSeconds: 232, + }, + }, + }, + tolerableReplLag: 50 * time.Second, + shardInfo: topo.NewShardInfo("testkeyspace", "-", &topodatapb.Shard{ + PrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, nil), + tabletMap: map[string]*topo.TabletInfo{ + "primary": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + }, + "replica1": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + "replica2": { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + }, + avoidPrimaryAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 0, + }, + expected: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + shouldErr: false, + }, { name: "found a replica - more advanced relay log position", tmc: &chooseNewPrimaryTestTMClient{ @@ -367,7 +641,7 @@ func TestChooseNewPrimary(t *testing.T) { Uid: 0, }, expected: nil, - shouldErr: false, + shouldErr: true, }, { name: "only available tablet is AvoidPrimary", @@ -404,7 +678,7 @@ func TestChooseNewPrimary(t *testing.T) { Uid: 101, }, expected: nil, - shouldErr: false, + shouldErr: true, }, { name: "no replicas in shard", @@ -431,19 +705,17 @@ func TestChooseNewPrimary(t *testing.T) { Uid: 0, }, expected: nil, - shouldErr: false, + shouldErr: true, }, } durability, err := GetDurabilityPolicy("none") require.NoError(t, err) for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() - actual, err := ChooseNewPrimary(ctx, tt.tmc, tt.shardInfo, tt.tabletMap, tt.avoidPrimaryAlias, time.Millisecond*50, durability, logger) + actual, err := ElectNewPrimary(ctx, tt.tmc, tt.shardInfo, tt.tabletMap, tt.newPrimaryAlias, tt.avoidPrimaryAlias, time.Millisecond*50, tt.tolerableReplLag, durability, logger) if tt.shouldErr { assert.Error(t, err) return @@ -465,6 +737,7 @@ func TestFindPositionForTablet(t *testing.T) { tmc *testutil.TabletManagerClient tablet *topodatapb.Tablet expectedPosition string + expectedLag time.Duration expectedErr string }{ { @@ -476,7 +749,8 @@ func TestFindPositionForTablet(t *testing.T) { }{ "zone1-0000000100": { Position: &replicationdatapb.Status{ - Position: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + Position: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + ReplicationLagSeconds: 201, }, }, }, @@ -487,6 +761,7 @@ func TestFindPositionForTablet(t *testing.T) { Uid: 100, }, }, + expectedLag: 201 * time.Second, expectedPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", }, { name: "no replication status", @@ -506,6 +781,7 @@ func TestFindPositionForTablet(t *testing.T) { Uid: 100, }, }, + expectedLag: 0, expectedPosition: "", }, { name: "relay log", @@ -516,8 +792,9 @@ func TestFindPositionForTablet(t *testing.T) { }{ "zone1-0000000100": { Position: &replicationdatapb.Status{ - Position: "unused", - RelayLogPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + Position: "unused", + RelayLogPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + ReplicationLagSeconds: 291, }, }, }, @@ -528,6 +805,7 @@ func TestFindPositionForTablet(t *testing.T) { Uid: 100, }, }, + expectedLag: 291 * time.Second, expectedPosition: "MySQL56/3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", }, { name: "error in parsing position", @@ -555,7 +833,7 @@ func TestFindPositionForTablet(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - pos, err := findPositionForTablet(ctx, test.tablet, logger, test.tmc, 10*time.Second) + pos, lag, err := findPositionAndLagForTablet(ctx, test.tablet, logger, test.tmc, 10*time.Second) if test.expectedErr != "" { require.EqualError(t, err, test.expectedErr) return @@ -563,6 +841,7 @@ func TestFindPositionForTablet(t *testing.T) { require.NoError(t, err) posString := replication.EncodePosition(pos) require.Equal(t, test.expectedPosition, posString) + require.Equal(t, test.expectedLag, lag) }) } } @@ -726,8 +1005,6 @@ func TestFindCurrentPrimary(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -1218,3 +1495,105 @@ func Test_getTabletsWithPromotionRules(t *testing.T) { }) } } + +func TestGetBackupCandidates(t *testing.T) { + var ( + primaryTablet = &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 1, + }, + Type: topodatapb.TabletType_PRIMARY, + }, + } + replicaTablet = &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 2, + }, + Type: topodatapb.TabletType_REPLICA, + }, + } + rdonlyTablet = &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 3, + }, + Type: topodatapb.TabletType_RDONLY, + }, + } + spareTablet = &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 4, + }, + Type: topodatapb.TabletType_SPARE, + }, + } + ) + tests := []struct { + name string + in []*topo.TabletInfo + expected []*topo.TabletInfo + status []*replicationdatapb.Status + }{ + { + name: "one primary tablet with status", + in: []*topo.TabletInfo{primaryTablet}, + expected: []*topo.TabletInfo{primaryTablet}, + status: []*replicationdatapb.Status{{}}, + }, + { + name: "one primary tablet with no status", + in: []*topo.TabletInfo{primaryTablet}, + expected: []*topo.TabletInfo{primaryTablet}, + status: []*replicationdatapb.Status{nil}, + }, + { + name: "4 tablets with no status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet}, + status: []*replicationdatapb.Status{nil, nil, nil, nil}, + }, + { + name: "4 tablets with full status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + status: []*replicationdatapb.Status{{}, {}, {}, {}}, + }, + { + name: "4 tablets with no primaryTablet status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + status: []*replicationdatapb.Status{nil, {}, {}, {}}, + }, + { + name: "4 tablets with no replicaTablet status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet, rdonlyTablet, spareTablet}, + status: []*replicationdatapb.Status{{}, nil, {}, {}}, + }, + { + name: "4 tablets with no rdonlyTablet status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet, replicaTablet, spareTablet}, + status: []*replicationdatapb.Status{{}, {}, nil, {}}, + }, + { + name: "4 tablets with no spareTablet status", + in: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet, spareTablet}, + expected: []*topo.TabletInfo{primaryTablet, replicaTablet, rdonlyTablet}, + status: []*replicationdatapb.Status{{}, {}, {}, nil}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := GetBackupCandidates(tt.in, tt.status) + require.EqualValues(t, tt.expected, res) + }) + } +} diff --git a/go/vt/vtctl/schematools/reload_test.go b/go/vt/vtctl/schematools/reload_test.go index 4f00e300d13..58a579b3821 100644 --- a/go/vt/vtctl/schematools/reload_test.go +++ b/go/vt/vtctl/schematools/reload_test.go @@ -325,7 +325,6 @@ func TestReloadShard(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/vdiff2.go b/go/vt/vtctl/vdiff2.go index 7cd1c7e00ca..9f53bb3590d 100644 --- a/go/vt/vtctl/vdiff2.go +++ b/go/vt/vtctl/vdiff2.go @@ -121,9 +121,10 @@ func commandVDiff2(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.F UpdateTableStats: *updateTableStats, }, ReportOptions: &tabletmanagerdatapb.VDiffReportOptions{ - OnlyPks: *onlyPks, - DebugQuery: *debugQuery, - Format: format, + OnlyPks: *onlyPks, + DebugQuery: *debugQuery, + Format: format, + MaxSampleRows: 10, }, } diff --git a/go/vt/vtctl/vdiff2_test.go b/go/vt/vtctl/vdiff2_test.go index 1348cd06448..0e5b2f41c60 100644 --- a/go/vt/vtctl/vdiff2_test.go +++ b/go/vt/vtctl/vdiff2_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/google/uuid" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gotest.tools/assert" "vitess.io/vitess/go/sqltypes" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" diff --git a/go/vt/vtctl/vdiff_env_test.go b/go/vt/vtctl/vdiff_env_test.go index 955d2673d20..b5324e7bd6e 100644 --- a/go/vt/vtctl/vdiff_env_test.go +++ b/go/vt/vtctl/vdiff_env_test.go @@ -43,7 +43,7 @@ import ( const ( // vdiffStopPosition is the default stop position for the target vreplication. - // It can be overridden with the positons argument to newTestVDiffEnv. + // It can be overridden with the positions argument to newTestVDiffEnv. vdiffStopPosition = "MySQL56/d834e6b8-7cbf-11ed-a1eb-0242ac120002:1-892" // vdiffSourceGtid should be the position reported by the source side VStreamResults. // It's expected to be higher the vdiffStopPosition. diff --git a/go/vt/vtctl/vtctl.go b/go/vt/vtctl/vtctl.go index 11d3cf85e68..919783e2f76 100644 --- a/go/vt/vtctl/vtctl.go +++ b/go/vt/vtctl/vtctl.go @@ -1814,8 +1814,6 @@ func commandCreateKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags force := subFlags.Bool("force", false, "Proceeds even if the keyspace already exists") allowEmptyVSchema := subFlags.Bool("allow_empty_vschema", false, "If set this will allow a new keyspace to have no vschema") - var servedFrom flagutil.StringMapValue - subFlags.Var(&servedFrom, "served_from", "Specifies a comma-separated list of tablet_type:keyspace pairs used to serve traffic") keyspaceType := subFlags.String("keyspace_type", "", "Specifies the type of the keyspace") baseKeyspace := subFlags.String("base_keyspace", "", "Specifies the base keyspace for a snapshot keyspace") timestampStr := subFlags.String("snapshot_time", "", "Specifies the snapshot time for this keyspace") @@ -1870,18 +1868,6 @@ func commandCreateKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags DurabilityPolicy: *durabilityPolicy, SidecarDbName: *sidecarDBName, } - if len(servedFrom) > 0 { - for name, value := range servedFrom { - tt, err := topo.ParseServingTabletType(name) - if err != nil { - return err - } - ki.ServedFroms = append(ki.ServedFroms, &topodatapb.Keyspace_ServedFrom{ - TabletType: tt, - Keyspace: value, - }) - } - } err := wr.TopoServer().CreateKeyspace(ctx, keyspace, ki) if *force && topo.IsErrType(err, topo.NodeExists) { wr.Logger().Infof("keyspace %v already exists (ignoring error with --force)", keyspace) @@ -2089,7 +2075,7 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub const defaultMaxReplicationLagAllowed = defaultWaitTime cells := subFlags.String("cells", "", "Cell(s) or CellAlias(es) (comma-separated) to replicate from.") - tabletTypesStr := subFlags.String("tablet_types", "in_order:REPLICA,PRIMARY", "Source tablet types to replicate from (e.g. PRIMARY, REPLICA, RDONLY). Defaults to --vreplication_tablet_type parameter value for the tablet, which has the default value of in_order:REPLICA,PRIMARY. Note: SwitchTraffic overrides this default and uses in_order:RDONLY,REPLICA,PRIMARY to switch all traffic by default.") + tabletTypesStr := subFlags.String("tablet_types", "in_order:REPLICA,PRIMARY", "Source tablet types to replicate from (e.g. PRIMARY, REPLICA, RDONLY). Note: SwitchTraffic overrides this default and uses in_order:RDONLY,REPLICA,PRIMARY to switch all traffic by default.") dryRun := subFlags.Bool("dry_run", false, "Does a dry run of SwitchTraffic and only reports the actions to be taken. --dry_run is only supported for SwitchTraffic, ReverseTraffic and Complete.") timeout := subFlags.Duration("timeout", defaultWaitTime, "Specifies the maximum time to wait, in seconds, for vreplication to catch up on primary migrations. The migration will be cancelled on a timeout. --timeout is only supported for SwitchTraffic and ReverseTraffic.") reverseReplication := subFlags.Bool("reverse_replication", true, "Also reverse the replication (default true). --reverse_replication is only supported for SwitchTraffic.") @@ -2100,6 +2086,7 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub dropForeignKeys := subFlags.Bool("drop_foreign_keys", false, "If true, tables in the target keyspace will be created without foreign keys.") maxReplicationLagAllowed := subFlags.Duration("max_replication_lag_allowed", defaultMaxReplicationLagAllowed, "Allow traffic to be switched only if vreplication lag is below this (in seconds)") atomicCopy := subFlags.Bool("atomic-copy", false, "(EXPERIMENTAL) Use this if your source keyspace has tables which use foreign key constraints. All tables from the source will be moved.") + shards := subFlags.StringSlice("shards", nil, "(Optional) Specifies a comma-separated list of shards to operate on.") onDDL := "IGNORE" subFlags.StringVar(&onDDL, "on-ddl", onDDL, "What to do when DDL is encountered in the VReplication stream. Possible values are IGNORE, STOP, EXEC, and EXEC_IGNORE.") @@ -2162,10 +2149,11 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub StopAfterCopy: *stopAfterCopy, AtomicCopy: *atomicCopy, } + var shardsWithStreams []string printDetails := func() error { s := "" - res, err := wr.ShowWorkflow(ctx, workflowName, target) + res, err := wr.ShowWorkflow(ctx, workflowName, target, shardsWithStreams) if err != nil { return err } @@ -2329,6 +2317,7 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub vrwp.KeepRoutingRules = *keepRoutingRules } vrwp.WorkflowType = workflowType + vrwp.ShardSubset = *shards wf, err := wr.NewVReplicationWorkflow(ctx, workflowType, vrwp) if err != nil { log.Warningf("NewVReplicationWorkflow returned error %+v", wf) @@ -2338,6 +2327,15 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub return fmt.Errorf("workflow %s does not exist", ksWorkflow) } + if len(vrwp.ShardSubset) > 0 { + if workflowType == wrangler.MoveTablesWorkflow && action != vReplicationWorkflowActionCreate && wf.IsPartialMigration() { + log.Infof("Subset of shards: %s have been specified for keyspace %s, workflow %s, for action %s", + vrwp.ShardSubset, target, workflowName, action) + } else { + return fmt.Errorf("The --shards option can only be specified for existing Partial MoveTables workflows") + } + } + printCopyProgress := func() error { copyProgress, err := wf.GetCopyProgress() if err != nil { @@ -2352,7 +2350,7 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub sort.Strings(tables) s := "" var progress wrangler.TableCopyProgress - for table := range *copyProgress { + for _, table := range tables { var rowCountPct, tableSizePct int64 progress = *(*copyProgress)[table] if progress.SourceRowCount > 0 { @@ -2378,6 +2376,18 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub } } + wr.WorkflowParams = vrwp + + switch vrwp.WorkflowType { + case wrangler.MoveTablesWorkflow: + // If this is not a partial MoveTables, SourceShards is nil and all shards will be polled. + shardsWithStreams = vrwp.SourceShards + case wrangler.ReshardWorkflow: + shardsWithStreams = vrwp.TargetShards + default: + } + + wr.WorkflowParams = vrwp var dryRunResults *[]string startState := wf.CachedState() switch action { @@ -2413,7 +2423,7 @@ func commandVReplicationWorkflow(ctx context.Context, wr *wrangler.Wrangler, sub case <-ctx.Done(): return case <-ticker.C: - totalStreams, startedStreams, workflowErrors, err := wf.GetStreamCount() + totalStreams, startedStreams, workflowErrors, err := wf.GetStreamCount(shardsWithStreams) if err != nil { errCh <- err close(errCh) @@ -2783,7 +2793,7 @@ func commandReloadSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *p } func commandReloadSchemaShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - concurrency := subFlags.Int("concurrency", 10, "How many tablets to reload in parallel") + concurrency := subFlags.Int32("concurrency", 10, "How many tablets to reload in parallel") includePrimary := subFlags.Bool("include_primary", true, "Include the primary tablet") if err := subFlags.Parse(args); err != nil { @@ -2801,7 +2811,7 @@ func commandReloadSchemaShard(ctx context.Context, wr *wrangler.Wrangler, subFla Shard: shard, WaitPosition: "", IncludePrimary: *includePrimary, - Concurrency: uint32(*concurrency), + Concurrency: *concurrency, }) if resp != nil { for _, e := range resp.Events { @@ -2812,7 +2822,7 @@ func commandReloadSchemaShard(ctx context.Context, wr *wrangler.Wrangler, subFla } func commandReloadSchemaKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - concurrency := subFlags.Int("concurrency", 10, "How many tablets to reload in parallel") + concurrency := subFlags.Int32("concurrency", 10, "How many tablets to reload in parallel") includePrimary := subFlags.Bool("include_primary", true, "Include the primary tablet(s)") if err := subFlags.Parse(args); err != nil { @@ -2825,7 +2835,7 @@ func commandReloadSchemaKeyspace(ctx context.Context, wr *wrangler.Wrangler, sub Keyspace: subFlags.Arg(0), WaitPosition: "", IncludePrimary: *includePrimary, - Concurrency: uint32(*concurrency), + Concurrency: *concurrency, }) if resp != nil { for _, e := range resp.Events { @@ -2900,7 +2910,6 @@ func commandValidateSchemaKeyspace(ctx context.Context, wr *wrangler.Wrangler, s } func commandApplySchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - subFlags.MarkDeprecated("allow_long_unavailability", "") sql := subFlags.String("sql", "", "A list of semicolon-delimited SQL commands") sqlFile := subFlags.String("sql-file", "", "Identifies the file that contains the SQL commands") ddlStrategy := subFlags.String("ddl_strategy", string(schema.DDLStrategyDirect), "Online DDL strategy, compatible with @@ddl_strategy session variable (examples: 'gh-ost', 'pt-osc', 'gh-ost --max-load=Threads_running=100'") @@ -2936,7 +2945,7 @@ func commandApplySchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *pf *migrationContext = *requestContext } - parts, err := sqlparser.SplitStatementToPieces(change) + parts, err := wr.SQLParser().SplitStatementToPieces(change) if err != nil { return err } @@ -3356,7 +3365,7 @@ func commandApplyVSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *p *sql = string(sqlBytes) } - stmt, err := sqlparser.Parse(*sql) + stmt, err := wr.SQLParser().Parse(*sql) if err != nil { return fmt.Errorf("error parsing vschema statement `%s`: %v", *sql, err) } @@ -3407,7 +3416,7 @@ func commandApplyVSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *p } // Validate the VSchema. - ksVs, err := vindexes.BuildKeyspace(vs) + ksVs, err := vindexes.BuildKeyspace(vs, wr.SQLParser()) if err != nil { return err } @@ -3422,7 +3431,7 @@ func commandApplyVSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *p vdx := ksVs.Vindexes[name] if val, ok := vdx.(vindexes.ParamValidating); ok { for _, param := range val.UnknownParams() { - wr.Logger().Warningf("Unknown param in vindex %s: %s", name, param) + wr.Logger().Warningf("Unknown parameter in vindex %s: %s", name, param) } } } @@ -3439,7 +3448,7 @@ func commandApplyVSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *p return err } - if _, err := vindexes.BuildKeyspace(vs); err != nil { + if _, err := vindexes.BuildKeyspace(vs, wr.SQLParser()); err != nil { return err } @@ -3721,8 +3730,9 @@ func commandHelp(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.Fla } func commandWorkflow(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error { - usage := "usage: Workflow [--dry-run] [--cells] [--tablet-types] [.] start/stop/update/delete/show/listall/tags []" + usage := "usage: Workflow [--shards ] [--dry-run] [--cells] [--tablet-types] [.] start/stop/update/delete/show/listall/tags []" dryRun := subFlags.Bool("dry-run", false, "Does a dry run of the Workflow action and reports the query and list of tablets on which the operation will be applied") + shards := subFlags.StringSlice("shards", nil, "(Optional) Specifies a comma-separated list of shards to operate on.") cells := subFlags.StringSlice("cells", []string{}, "New Cell(s) or CellAlias(es) (comma-separated) to replicate from. (Update only)") tabletTypesStrs := subFlags.StringSlice("tablet-types", []string{}, "New source tablet types to replicate from (e.g. PRIMARY, REPLICA, RDONLY). (Update only)") onDDL := subFlags.String("on-ddl", "", "New instruction on what to do when DDL is encountered in the VReplication stream. Possible values are IGNORE, STOP, EXEC, and EXEC_IGNORE. (Update only)") @@ -3732,6 +3742,9 @@ func commandWorkflow(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag if subFlags.NArg() < 2 { return fmt.Errorf(usage) } + if len(*shards) > 0 { + log.Infof("Subset of shards specified: %d, %v", len(*shards), strings.Join(*shards, ",")) + } keyspace := subFlags.Arg(0) action := strings.ToLower(subFlags.Arg(1)) var workflow string @@ -3818,7 +3831,7 @@ func commandWorkflow(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag OnDdl: binlogdatapb.OnDDLAction(onddl), } } - results, err = wr.WorkflowAction(ctx, workflow, keyspace, action, *dryRun, rpcReq) // Only update currently uses the new RPC path + results, err = wr.WorkflowAction(ctx, workflow, keyspace, action, *dryRun, rpcReq, *shards) // Only update currently uses the new RPC path if err != nil { return err } diff --git a/go/vt/vtctl/vtctl_test.go b/go/vt/vtctl/vtctl_test.go index eb6a5f5941f..92c1cb6549e 100644 --- a/go/vt/vtctl/vtctl_test.go +++ b/go/vt/vtctl/vtctl_test.go @@ -89,9 +89,9 @@ func TestApplyVSchema(t *testing.T) { } If this is not what you expected, check the input data \(as JSON parsing will skip unexpected fields\)\. -.*W.* .* vtctl.go:.* Unknown param in vindex binary_vdx: hello -W.* .* vtctl.go:.* Unknown param in vindex hash_vdx: foo -W.* .* vtctl.go:.* Unknown param in vindex hash_vdx: hello`, +.*W.* .* vtctl.go:.* Unknown parameter in vindex binary_vdx: hello +W.* .* vtctl.go:.* Unknown parameter in vindex hash_vdx: foo +W.* .* vtctl.go:.* Unknown parameter in vindex hash_vdx: hello`, }, { name: "UnknownParamsLoggedWithDryRun", @@ -117,9 +117,9 @@ W.* .* vtctl.go:.* Unknown param in vindex hash_vdx: hello`, } If this is not what you expected, check the input data \(as JSON parsing will skip unexpected fields\)\. -.*W.* .* vtctl.go:.* Unknown param in vindex binary_vdx: hello -W.* .* vtctl.go:.* Unknown param in vindex hash_vdx: foo -W.* .* vtctl.go:.* Unknown param in vindex hash_vdx: hello +.*W.* .* vtctl.go:.* Unknown parameter in vindex binary_vdx: hello +W.* .* vtctl.go:.* Unknown parameter in vindex hash_vdx: foo +W.* .* vtctl.go:.* Unknown parameter in vindex hash_vdx: hello Dry run: Skipping update of VSchema`, }, } @@ -149,7 +149,8 @@ func TestMoveTables(t *testing.T) { shard := "0" sourceKs := "sourceks" targetKs := "targetks" - table := "customer" + table1 := "customer" + table2 := "customer_order" wf := "testwf" ksWf := fmt.Sprintf("%s.%s", targetKs, wf) minTableSize := 16384 // a single 16KiB InnoDB page @@ -159,16 +160,22 @@ func TestMoveTables(t *testing.T) { defer env.close() source := env.addTablet(100, sourceKs, shard, &topodatapb.KeyRange{}, topodatapb.TabletType_PRIMARY) target := env.addTablet(200, targetKs, shard, &topodatapb.KeyRange{}, topodatapb.TabletType_PRIMARY) - sourceCol := fmt.Sprintf(`keyspace:"%s" shard:"%s" filter:{rules:{match:"%s" filter:"select * from %s"}}`, - sourceKs, shard, table, table) + sourceCol := fmt.Sprintf(`keyspace:"%s" shard:"%s" filter:{rules:{match:"%s" filter:"select * from %s"} rules:{match:"%s" filter:"select * from %s"}}`, + sourceKs, shard, table1, table1, table2, table2) bls := &binlogdatapb.BinlogSource{ Keyspace: sourceKs, Shard: shard, Filter: &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: table, - Filter: fmt.Sprintf("select * from %s", table), - }}, + Rules: []*binlogdatapb.Rule{ + { + Match: table1, + Filter: fmt.Sprintf("select * from %s", table1), + }, + { + Match: table2, + Filter: fmt.Sprintf("select * from %s", table2), + }, + }, }, } now := time.Now().UTC().Unix() @@ -200,12 +207,13 @@ func TestMoveTables(t *testing.T) { expectResults: func() { env.tmc.setVRResults( target.tablet, - fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)", + fmt.Sprintf("select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (%d) and id in (select max(id) from _vt.copy_state where vrepl_id in (%d) group by vrepl_id, table_name)", vrID, vrID), sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "table_name|lastpk", - "varchar|varbinary"), - fmt.Sprintf("%s|", table), + "vrepl_id|table_name|lastpk", + "int64|varchar|varbinary"), + fmt.Sprintf("%d|%s|", vrID, table1), + fmt.Sprintf("%d|%s|", vrID, table2), ), ) env.tmc.setDBAResults( @@ -215,7 +223,8 @@ func TestMoveTables(t *testing.T) { sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name", "varchar"), - table, + table1, + table2, ), ) env.tmc.setVRResults( @@ -231,26 +240,28 @@ func TestMoveTables(t *testing.T) { ) env.tmc.setDBAResults( target.tablet, - fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s')", - targetKs, table), + fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s','%s')", + targetKs, table1, table2), sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name|table_rows|data_length", "varchar|int64|int64"), - fmt.Sprintf("%s|0|%d", table, minTableSize), + fmt.Sprintf("%s|0|%d", table1, minTableSize), + fmt.Sprintf("%s|0|%d", table2, minTableSize), ), ) env.tmc.setDBAResults( source.tablet, - fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s')", - sourceKs, table), + fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s','%s')", + sourceKs, table1, table2), sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name|table_rows|data_length", "varchar|int64|int64"), - fmt.Sprintf("%s|10|%d", table, minTableSize), + fmt.Sprintf("%s|10|%d", table1, minTableSize), + fmt.Sprintf("%s|10|%d", table2, minTableSize), ), ) }, - want: fmt.Sprintf("\nCopy Progress (approx):\n\n\ncustomer: rows copied 0/10 (0%%), size copied 16384/16384 (100%%)\n\n\n\nThe following vreplication streams exist for workflow %s:\n\nid=%d on %s/%s-0000000%d: Status: Copying. VStream has not started.\n\n\n", + want: fmt.Sprintf("\nCopy Progress (approx):\n\n\ncustomer: rows copied 0/10 (0%%), size copied 16384/16384 (100%%)\ncustomer_order: rows copied 0/10 (0%%), size copied 16384/16384 (100%%)\n\n\n\nThe following vreplication streams exist for workflow %s:\n\nid=%d on %s/%s-0000000%d: Status: Copying. VStream has not started.\n\n\n", ksWf, vrID, shard, env.cell, target.tablet.Alias.Uid), }, { @@ -260,12 +271,13 @@ func TestMoveTables(t *testing.T) { expectResults: func() { env.tmc.setVRResults( target.tablet, - fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)", + fmt.Sprintf("select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (%d) and id in (select max(id) from _vt.copy_state where vrepl_id in (%d) group by vrepl_id, table_name)", vrID, vrID), sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "table_name|lastpk", - "varchar|varbinary"), - fmt.Sprintf("%s|", table), + "vrepl_id|table_name|lastpk", + "int64|varchar|varbinary"), + fmt.Sprintf("%d|%s|", vrID, table1), + fmt.Sprintf("%d|%s|", vrID, table2), ), ) env.tmc.setDBAResults( @@ -275,7 +287,8 @@ func TestMoveTables(t *testing.T) { sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name", "varchar"), - table, + table1, + table2, ), ) env.tmc.setVRResults( @@ -291,26 +304,28 @@ func TestMoveTables(t *testing.T) { ) env.tmc.setDBAResults( target.tablet, - fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s')", - targetKs, table), + fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s','%s')", + targetKs, table1, table2), sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name|table_rows|data_length", "varchar|int64|int64"), - fmt.Sprintf("%s|5|%d", table, minTableSize), + fmt.Sprintf("%s|5|%d", table1, minTableSize), + fmt.Sprintf("%s|5|%d", table2, minTableSize), ), ) env.tmc.setDBAResults( source.tablet, - fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s')", - sourceKs, table), + fmt.Sprintf("select table_name, table_rows, data_length from information_schema.tables where table_schema = 'vt_%s' and table_name in ('%s','%s')", + sourceKs, table1, table2), sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name|table_rows|data_length", "varchar|int64|int64"), - fmt.Sprintf("%s|10|%d", table, minTableSize), + fmt.Sprintf("%s|10|%d", table1, minTableSize), + fmt.Sprintf("%s|10|%d", table2, minTableSize), ), ) }, - want: fmt.Sprintf("\nCopy Progress (approx):\n\n\ncustomer: rows copied 5/10 (50%%), size copied 16384/16384 (100%%)\n\n\n\nThe following vreplication streams exist for workflow %s:\n\nid=%d on %s/%s-0000000%d: Status: Error: Duplicate entry '6' for key 'customer.PRIMARY' (errno 1062) (sqlstate 23000) during query: insert into customer(customer_id,email) values (6,'mlord@planetscale.com').\n\n\n", + want: fmt.Sprintf("\nCopy Progress (approx):\n\n\ncustomer: rows copied 5/10 (50%%), size copied 16384/16384 (100%%)\ncustomer_order: rows copied 5/10 (50%%), size copied 16384/16384 (100%%)\n\n\n\nThe following vreplication streams exist for workflow %s:\n\nid=%d on %s/%s-0000000%d: Status: Error: Duplicate entry '6' for key 'customer.PRIMARY' (errno 1062) (sqlstate 23000) during query: insert into customer(customer_id,email) values (6,'mlord@planetscale.com').\n\n\n", ksWf, vrID, shard, env.cell, target.tablet.Alias.Uid), }, { @@ -320,7 +335,7 @@ func TestMoveTables(t *testing.T) { expectResults: func() { env.tmc.setVRResults( target.tablet, - fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)", + fmt.Sprintf("select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (%d) and id in (select max(id) from _vt.copy_state where vrepl_id in (%d) group by vrepl_id, table_name)", vrID, vrID), &sqltypes.Result{}, ) diff --git a/go/vt/vtctl/vtctlclient/interface.go b/go/vt/vtctl/vtctlclient/interface.go index e9bf0cdc257..b750cdf8db6 100644 --- a/go/vt/vtctl/vtctlclient/interface.go +++ b/go/vt/vtctl/vtctlclient/interface.go @@ -68,17 +68,6 @@ func RegisterFactory(name string, factory Factory) { factories[name] = factory } -// UnregisterFactoryForTest allows to unregister a client implementation from the static map. -// This function is used by unit tests to cleanly unregister any fake implementations. -// This way, a test package can use the same name for different fakes and no dangling fakes are -// left behind in the static factories map after the test. -func UnregisterFactoryForTest(name string) { - if _, ok := factories[name]; !ok { - log.Fatalf("UnregisterFactoryForTest: %s is not registered", name) - } - delete(factories, name) -} - // New allows a user of the client library to get its implementation. func New(addr string) (VtctlClient, error) { factory, ok := factories[vtctlClientProtocol] diff --git a/go/vt/vtctl/vtctldclient/client.go b/go/vt/vtctl/vtctldclient/client.go index 5b90a08ecdd..6e0c97bb8a5 100644 --- a/go/vt/vtctl/vtctldclient/client.go +++ b/go/vt/vtctl/vtctldclient/client.go @@ -22,7 +22,7 @@ type Factory func(addr string) (VtctldClient, error) var registry = map[string]Factory{} // Register adds a VtctldClient factory for the given name (protocol). -// Attempting to register mulitple factories for the same protocol is a fatal +// Attempting to register multiple factories for the same protocol is a fatal // error. func Register(name string, factory Factory) { if _, ok := registry[name]; ok { diff --git a/go/vt/vtctl/workflow/common/utils.go b/go/vt/vtctl/workflow/common/utils.go new file mode 100644 index 00000000000..86fa33bc191 --- /dev/null +++ b/go/vt/vtctl/workflow/common/utils.go @@ -0,0 +1,55 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "context" + "fmt" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" +) + +// GetShards returns a subset of shards in a keyspace. If no subset is provided, all shards are returned. +func GetShards(ctx context.Context, ts *topo.Server, keyspace string, shardSubset []string) ([]string, error) { + var err error + allShards, err := ts.GetShardNames(ctx, keyspace) + if err != nil { + return nil, err + } + if len(allShards) == 0 { + return nil, fmt.Errorf("no shards found in keyspace %s", keyspace) + } + + if len(shardSubset) == 0 { + return allShards, nil + } + existingShards := make(map[string]bool, len(allShards)) + for _, shard := range allShards { + existingShards[shard] = true + } + // Validate that the provided shards are part of the keyspace. + for _, shard := range shardSubset { + _, found := existingShards[shard] + if !found { + return nil, fmt.Errorf("shard %s not found in keyspace %s", shard, keyspace) + } + } + log.Infof("Selecting subset of shards in keyspace %s: %d from %d :: %+v", + keyspace, len(shardSubset), len(allShards), shardSubset) + return shardSubset, nil +} diff --git a/go/vt/vtctl/workflow/log_recorder_test.go b/go/vt/vtctl/workflow/log_recorder_test.go index b58d1d42a79..0234e811771 100644 --- a/go/vt/vtctl/workflow/log_recorder_test.go +++ b/go/vt/vtctl/workflow/log_recorder_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/assert" ) func TestLogRecorder(t *testing.T) { diff --git a/go/vt/vtctl/workflow/materializer.go b/go/vt/vtctl/workflow/materializer.go index 6be5ac7f445..a965d801cee 100644 --- a/go/vt/vtctl/workflow/materializer.go +++ b/go/vt/vtctl/workflow/materializer.go @@ -29,9 +29,11 @@ import ( "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/tmutils" + "vitess.io/vitess/go/vt/schemadiff" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtctl/schematools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" @@ -62,6 +64,8 @@ type materializer struct { isPartial bool primaryVindexesDiffer bool workflowType binlogdatapb.VReplicationWorkflowType + + env *vtenv.Environment } func (mz *materializer) getWorkflowSubType() (binlogdatapb.VReplicationWorkflowSubType, error) { @@ -196,7 +200,7 @@ func (mz *materializer) generateInserts(ctx context.Context, sourceShards []*top } // Validate non-empty query. - stmt, err := sqlparser.Parse(ts.SourceExpression) + stmt, err := mz.env.Parser().Parse(ts.SourceExpression) if err != nil { return "", err } @@ -295,7 +299,7 @@ func (mz *materializer) generateBinlogSources(ctx context.Context, targetShard * } // Validate non-empty query. - stmt, err := sqlparser.Parse(ts.SourceExpression) + stmt, err := mz.env.Parser().Parse(ts.SourceExpression) if err != nil { return nil, err } @@ -405,7 +409,7 @@ func (mz *materializer) deploySchema() error { if createDDL == createDDLAsCopy || createDDL == createDDLAsCopyDropConstraint || createDDL == createDDLAsCopyDropForeignKeys { if ts.SourceExpression != "" { // Check for table if non-empty SourceExpression. - sourceTableName, err := sqlparser.TableFromStatement(ts.SourceExpression) + sourceTableName, err := mz.env.Parser().TableFromStatement(ts.SourceExpression) if err != nil { return err } @@ -421,7 +425,7 @@ func (mz *materializer) deploySchema() error { } if createDDL == createDDLAsCopyDropConstraint { - strippedDDL, err := stripTableConstraints(ddl) + strippedDDL, err := stripTableConstraints(ddl, mz.env.Parser()) if err != nil { return err } @@ -430,7 +434,7 @@ func (mz *materializer) deploySchema() error { } if createDDL == createDDLAsCopyDropForeignKeys { - strippedDDL, err := stripTableForeignKeys(ddl) + strippedDDL, err := stripTableForeignKeys(ddl, mz.env.Parser()) if err != nil { return err } @@ -444,6 +448,22 @@ func (mz *materializer) deploySchema() error { } if len(applyDDLs) > 0 { + if mz.ms.AtomicCopy { + // AtomicCopy suggests we may be interested in Foreign Key support. As such, we want to + // normalize the source schema: ensure the order of table definitions is compatible with + // the constraints graph. We want to first create the parents, then the children. + // We use schemadiff to normalize the schema. + // For now, and because this is could have wider implications, we ignore any errors in + // reading the source schema. + env := schemadiff.NewEnv(mz.env, mz.env.CollationEnv().DefaultConnectionCharset()) + schema, err := schemadiff.NewSchemaFromQueries(env, applyDDLs) + if err != nil { + log.Error(vterrors.Wrapf(err, "AtomicCopy: failed to normalize schema via schemadiff")) + } else { + applyDDLs = schema.ToQueries() + log.Infof("AtomicCopy used, and schema was normalized via schemadiff. %v queries normalized", len(applyDDLs)) + } + } sql := strings.Join(applyDDLs, ";\n") _, err = mz.tmc.ApplySchema(mz.ctx, targetTablet.Tablet, &tmutils.SchemaChange{ @@ -468,7 +488,7 @@ func (mz *materializer) buildMaterializer() error { if err != nil { return err } - targetVSchema, err := vindexes.BuildKeyspaceSchema(vschema, ms.TargetKeyspace) + targetVSchema, err := vindexes.BuildKeyspaceSchema(vschema, ms.TargetKeyspace, mz.env.Parser()) if err != nil { return err } @@ -582,22 +602,6 @@ func (mz *materializer) startStreams(ctx context.Context) error { }) } -func Materialize(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, ms *vtctldatapb.MaterializeSettings) error { - mz := &materializer{ - ctx: ctx, - ts: ts, - sourceTs: ts, - tmc: tmc, - ms: ms, - } - - err := mz.createMaterializerStreams() - if err != nil { - return err - } - return mz.startStreams(ctx) -} - func (mz *materializer) forAllTargets(f func(*topo.ShardInfo) error) error { var wg sync.WaitGroup allErrors := &concurrency.AllErrorRecorder{} diff --git a/go/vt/vtctl/workflow/materializer_env_test.go b/go/vt/vtctl/workflow/materializer_env_test.go index 1026628405e..452c5755a10 100644 --- a/go/vt/vtctl/workflow/materializer_env_test.go +++ b/go/vt/vtctl/workflow/materializer_env_test.go @@ -30,9 +30,9 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/mysqlctl/tmutils" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tmclient" @@ -82,7 +82,8 @@ func newTestMaterializerEnv(t *testing.T, ctx context.Context, ms *vtctldatapb.M cell: "cell", tmc: newTestMaterializerTMClient(), } - env.ws = NewServer(env.topoServ, env.tmc) + venv := vtenv.NewTestEnv() + env.ws = NewServer(venv, env.topoServ, env.tmc) tabletID := 100 for _, shard := range sources { _ = env.addTablet(tabletID, env.ms.SourceKeyspace, shard, topodatapb.TabletType_PRIMARY) @@ -98,7 +99,7 @@ func newTestMaterializerEnv(t *testing.T, ctx context.Context, ms *vtctldatapb.M for _, ts := range ms.TableSettings { tableName := ts.TargetTable - table, err := sqlparser.TableFromStatement(ts.SourceExpression) + table, err := venv.Parser().TableFromStatement(ts.SourceExpression) if err == nil { tableName = table.Name.String() } diff --git a/go/vt/vtctl/workflow/materializer_test.go b/go/vt/vtctl/workflow/materializer_test.go index fc39bb4d30b..82cc07fdf7f 100644 --- a/go/vt/vtctl/workflow/materializer_test.go +++ b/go/vt/vtctl/workflow/materializer_test.go @@ -28,6 +28,9 @@ import ( "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/topo/memorytopo" @@ -46,9 +49,9 @@ const getWorkflowQuery = "select id from _vt.vreplication where db_name='vt_targ const mzUpdateQuery = "update _vt.vreplication set state='Running' where db_name='vt_targetks' and workflow='workflow'" const mzSelectFrozenQuery = "select 1 from _vt.vreplication where db_name='vt_targetks' and message='FROZEN' and workflow_sub_type != 1" const mzCheckJournal = "/select val from _vt.resharding_journal where id=" -const mzGetWorkflowStatusQuery = "select id, workflow, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, message, tags, workflow_type, workflow_sub_type, time_heartbeat, defer_secondary_keys, component_throttled, time_throttled, rows_copied from _vt.vreplication where workflow = 'workflow' and db_name = 'vt_targetks'" +const mzGetWorkflowStatusQuery = "select id, workflow, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, message, tags, workflow_type, workflow_sub_type, time_heartbeat, defer_secondary_keys, component_throttled, time_throttled, rows_copied, tablet_types, cell from _vt.vreplication where workflow = 'workflow' and db_name = 'vt_targetks'" const mzGetCopyState = "select distinct table_name from _vt.copy_state cs, _vt.vreplication vr where vr.id = cs.vrepl_id and vr.id = 1" -const mzGetLatestCopyState = "select table_name, lastpk from _vt.copy_state where vrepl_id = 1 and id in (select max(id) from _vt.copy_state where vrepl_id = 1 group by vrepl_id, table_name)" +const mzGetLatestCopyState = "select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (1) and id in (select max(id) from _vt.copy_state where vrepl_id in (1) group by vrepl_id, table_name)" const insertPrefix = `/insert into _vt.vreplication\(workflow, source, pos, max_tps, max_replication_lag, cell, tablet_types, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type, defer_secondary_keys\) values ` const eol = "$" @@ -134,7 +137,7 @@ func TestStripForeignKeys(t *testing.T) { } for _, tc := range tcs { - newDDL, err := stripTableForeignKeys(tc.ddl) + newDDL, err := stripTableForeignKeys(tc.ddl, sqlparser.NewTestParser()) if tc.hasErr != (err != nil) { t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) } @@ -208,7 +211,7 @@ func TestStripConstraints(t *testing.T) { } for _, tc := range tcs { - newDDL, err := stripTableConstraints(tc.ddl) + newDDL, err := stripTableConstraints(tc.ddl, sqlparser.NewTestParser()) if tc.hasErr != (err != nil) { t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) } @@ -3013,7 +3016,7 @@ func TestMaterializerNoSourcePrimary(t *testing.T) { cell: "cell", tmc: newTestMaterializerTMClient(), } - env.ws = NewServer(env.topoServ, env.tmc) + env.ws = NewServer(vtenv.NewTestEnv(), env.topoServ, env.tmc) defer env.close() tabletID := 100 @@ -3563,6 +3566,7 @@ func TestKeyRangesEqualOptimization(t *testing.T) { tmc: env.tmc, ms: ms, workflowType: workflowType, + env: vtenv.NewTestEnv(), } err = mz.createMoveTablesStreams(tc.moveTablesReq) require.NoError(t, err, "createMoveTablesStreams failed: %v", err) diff --git a/go/vt/vtctl/workflow/resharder.go b/go/vt/vtctl/workflow/resharder.go index 161b1c4567d..18f10f25319 100644 --- a/go/vt/vtctl/workflow/resharder.go +++ b/go/vt/vtctl/workflow/resharder.go @@ -21,6 +21,7 @@ import ( "context" "errors" "fmt" + "slices" "sync" "time" @@ -126,6 +127,9 @@ func (s *Server) buildResharder(ctx context.Context, keyspace, workflow string, return rs, nil } +// validateTargets ensures that the target shards have no existing +// VReplication workflow streams as that is an invalid starting +// state for the non-serving shards involved in a Reshard. func (rs *resharder) validateTargets(ctx context.Context) error { err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error { targetPrimary := rs.targetPrimaries[target.ShardName()] @@ -260,6 +264,8 @@ func (rs *resharder) copySchema(ctx context.Context) error { return err } +// createStreams creates all of the VReplication streams that +// need to now exist on the new shards. func (rs *resharder) createStreams(ctx context.Context) error { var excludeRules []*binlogdatapb.Rule for tableName, table := range rs.vschema.Tables { @@ -276,8 +282,8 @@ func (rs *resharder) createStreams(ctx context.Context) error { ig := vreplication.NewInsertGenerator(binlogdatapb.VReplicationWorkflowState_Stopped, targetPrimary.DbName()) - // copy excludeRules to prevent data race. - copyExcludeRules := append([]*binlogdatapb.Rule(nil), excludeRules...) + // Clone excludeRules to prevent data races. + copyExcludeRules := slices.Clone(excludeRules) for _, source := range rs.sourceShards { if !key.KeyRangeIntersect(target.KeyRange, source.KeyRange) { continue @@ -321,7 +327,14 @@ func (rs *resharder) createStreams(ctx context.Context) error { func (rs *resharder) startStreams(ctx context.Context) error { err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error { targetPrimary := rs.targetPrimaries[target.ShardName()] - query := fmt.Sprintf("update _vt.vreplication set state='Running' where db_name=%s", encodeString(targetPrimary.DbName())) + // This is the rare case where we truly want to update every stream/record + // because we've already confirmed that there were no existing workflows + // on the shards when we started, and we want to start all of the ones + // that we've created on the new shards as we're migrating them. + // We use the comment directive to indicate that this is intentional + // and OK. + query := fmt.Sprintf("update /*vt+ %s */ _vt.vreplication set state='Running' where db_name=%s", + vreplication.AllowUnsafeWriteCommentDirective, encodeString(targetPrimary.DbName())) if _, err := rs.s.tmc.VReplicationExec(ctx, targetPrimary.Tablet, query); err != nil { return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetPrimary.Tablet, query) } diff --git a/go/vt/vtctl/workflow/server.go b/go/vt/vtctl/workflow/server.go index 6927b56b89d..5e9f3fc9300 100644 --- a/go/vt/vtctl/workflow/server.go +++ b/go/vt/vtctl/workflow/server.go @@ -17,7 +17,6 @@ limitations under the License. package workflow import ( - "bytes" "context" "errors" "fmt" @@ -31,6 +30,7 @@ import ( "time" "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" @@ -55,6 +55,7 @@ import ( "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtctl/schematools" "vitess.io/vitess/go/vt/vtctl/workflow/vexec" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vdiff" @@ -142,19 +143,25 @@ var ( type Server struct { ts *topo.Server tmc tmclient.TabletManagerClient - // Limt the number of concurrent background goroutines if needed. + // Limit the number of concurrent background goroutines if needed. sem *semaphore.Weighted + env *vtenv.Environment } // NewServer returns a new server instance with the given topo.Server and // TabletManagerClient. -func NewServer(ts *topo.Server, tmc tmclient.TabletManagerClient) *Server { +func NewServer(env *vtenv.Environment, ts *topo.Server, tmc tmclient.TabletManagerClient) *Server { return &Server{ ts: ts, tmc: tmc, + env: env, } } +func (s *Server) SQLParser() *sqlparser.Parser { + return s.env.Parser() +} + // CheckReshardingJournalExistsOnTablet returns the journal (or an empty // journal) and a boolean to indicate if the resharding_journal table exists on // the given tablet. @@ -338,11 +345,12 @@ func (s *Server) GetCellsWithTableReadsSwitched( return cellsSwitched, cellsNotSwitched, nil } -func (s *Server) GetWorkflow(ctx context.Context, keyspace, workflow string, includeLogs bool) (*vtctldatapb.Workflow, error) { +func (s *Server) GetWorkflow(ctx context.Context, keyspace, workflow string, includeLogs bool, shards []string) (*vtctldatapb.Workflow, error) { res, err := s.GetWorkflows(ctx, &vtctldatapb.GetWorkflowsRequest{ Keyspace: keyspace, Workflow: workflow, IncludeLogs: includeLogs, + Shards: shards, }) if err != nil { return nil, err @@ -368,6 +376,7 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows span.Annotate("keyspace", req.Keyspace) span.Annotate("active_only", req.ActiveOnly) span.Annotate("include_logs", req.IncludeLogs) + span.Annotate("shards", req.Shards) where := "" predicates := []string{} @@ -401,20 +410,81 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows defer_secondary_keys, component_throttled, time_throttled, - rows_copied + rows_copied, + tablet_types, + cell FROM _vt.vreplication %s`, where, ) - vx := vexec.NewVExec(req.Keyspace, "", s.ts, s.tmc) + vx := vexec.NewVExec(req.Keyspace, "", s.ts, s.tmc, s.env.Parser()) + vx.SetShardSubset(req.Shards) results, err := vx.QueryContext(ctx, query) if err != nil { return nil, err } - m := sync.Mutex{} // guards access to the following maps during concurrent calls to scanWorkflow + m := sync.Mutex{} // guards access to the following maps during concurrent calls to fetchCopyStates and scanWorkflow + + copyStatesByShardStreamId := make(map[string][]*vtctldatapb.Workflow_Stream_CopyState, len(results)) + + fetchCopyStates := func(ctx context.Context, tablet *topo.TabletInfo, streamIds []int64) error { + span, ctx := trace.NewSpan(ctx, "workflow.Server.fetchCopyStates") + defer span.Finish() + + span.Annotate("keyspace", req.Keyspace) + span.Annotate("shard", tablet.Shard) + span.Annotate("tablet_alias", tablet.AliasString()) + + copyStates, err := s.getWorkflowCopyStates(ctx, tablet, streamIds) + if err != nil { + return err + } + + m.Lock() + defer m.Unlock() + + for _, copyState := range copyStates { + shardStreamId := fmt.Sprintf("%s/%d", tablet.Shard, copyState.StreamId) + copyStatesByShardStreamId[shardStreamId] = append( + copyStatesByShardStreamId[shardStreamId], + copyState, + ) + } + + return nil + } + + fetchCopyStatesEg, fetchCopyStatesCtx := errgroup.WithContext(ctx) + + for tablet, result := range results { + qr := sqltypes.Proto3ToResult(result) + tablet := tablet // loop closure + + streamIds := make([]int64, 0, len(qr.Rows)) + for _, row := range qr.Named().Rows { + streamId, err := row.ToInt64("id") + if err != nil { + return nil, err + } + streamIds = append(streamIds, streamId) + } + + if len(streamIds) == 0 { + continue + } + + fetchCopyStatesEg.Go(func() error { + return fetchCopyStates(fetchCopyStatesCtx, tablet, streamIds) + }) + } + + if err := fetchCopyStatesEg.Wait(); err != nil { + return nil, err + } + workflowsMap := make(map[string]*vtctldatapb.Workflow, len(results)) sourceKeyspaceByWorkflow := make(map[string]string, len(results)) sourceShardsByWorkflow := make(map[string]sets.Set[string], len(results)) @@ -453,7 +523,6 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows if err := prototext.Unmarshal(rowBytes, &bls); err != nil { return err } - // The value in the pos column can be compressed and thus not // have a valid GTID consisting of valid UTF-8 characters so we // have to decode it so that it's properly decompressed first @@ -516,15 +585,31 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows return err } + tabletTypes, inOrder, err := discovery.ParseTabletTypesAndOrder(row["tablet_types"].ToString()) + if err != nil { + return err + } + tsp := tabletmanagerdatapb.TabletSelectionPreference_ANY + if inOrder { + tsp = tabletmanagerdatapb.TabletSelectionPreference_INORDER + } + cells := strings.Split(row["cell"].ToString(), ",") + for i, cell := range cells { + cells[i] = strings.TrimSpace(cell) + } + stream := &vtctldatapb.Workflow_Stream{ - Id: id, - Shard: tablet.Shard, - Tablet: tablet.Alias, - BinlogSource: &bls, - Position: pos, - StopPosition: stopPos, - State: state, - DbName: dbName, + Id: id, + Shard: tablet.Shard, + Tablet: tablet.Alias, + BinlogSource: &bls, + Position: pos, + StopPosition: stopPos, + State: state, + DbName: dbName, + TabletTypes: tabletTypes, + TabletSelectionPreference: tsp, + Cells: cells, TransactionTimestamp: &vttimepb.Time{ Seconds: transactionTimeSeconds, }, @@ -542,19 +627,15 @@ func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflows }, } - stream.CopyStates, err = s.getWorkflowCopyStates(ctx, tablet, id) - if err != nil { - return err + // Merge in copy states, which we've already fetched. + shardStreamId := fmt.Sprintf("%s/%d", tablet.Shard, id) + if copyState, ok := copyStatesByShardStreamId[shardStreamId]; ok { + stream.CopyStates = copyState } - span.Annotate("num_copy_states", len(stream.CopyStates)) - // At this point, we're going to start modifying the maps defined // outside this function, as well as fields on the passed-in Workflow // pointer. Since we're running concurrently, take the lock. - // - // We've already made the remote call to getCopyStates, so synchronizing - // here shouldn't hurt too badly, performance-wise. m.Lock() defer m.Unlock() @@ -929,7 +1010,6 @@ ORDER BY func (s *Server) getWorkflowState(ctx context.Context, targetKeyspace, workflowName string) (*trafficSwitcher, *State, error) { ts, err := s.buildTrafficSwitcher(ctx, targetKeyspace, workflowName) - if err != nil { log.Errorf("buildTrafficSwitcher failed: %v", err) return nil, nil, err @@ -973,7 +1053,6 @@ func (s *Server) getWorkflowState(ctx context.Context, targetKeyspace, workflowN // We assume a consistent state, so only choose routing rule for one table. if len(ts.Tables()) == 0 { return nil, nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "no tables in workflow %s.%s", targetKeyspace, workflowName) - } table := ts.Tables()[0] @@ -1050,16 +1129,24 @@ func (s *Server) getWorkflowState(ctx context.Context, targetKeyspace, workflowN return ts, state, nil } -func (s *Server) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletInfo, id int64) ([]*vtctldatapb.Workflow_Stream_CopyState, error) { +func (s *Server) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletInfo, streamIds []int64) ([]*vtctldatapb.Workflow_Stream_CopyState, error) { span, ctx := trace.NewSpan(ctx, "workflow.Server.getWorkflowCopyStates") defer span.Finish() span.Annotate("keyspace", tablet.Keyspace) span.Annotate("shard", tablet.Shard) span.Annotate("tablet_alias", tablet.AliasString()) - span.Annotate("vrepl_id", id) + span.Annotate("stream_ids", fmt.Sprintf("%#v", streamIds)) - query := fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)", id, id) + idsBV, err := sqltypes.BuildBindVariable(streamIds) + if err != nil { + return nil, err + } + query, err := sqlparser.ParseAndBind("select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in %a and id in (select max(id) from _vt.copy_state where vrepl_id in %a group by vrepl_id, table_name)", + idsBV, idsBV) + if err != nil { + return nil, err + } qr, err := s.tmc.VReplicationExec(ctx, tablet.Tablet, query) if err != nil { return nil, err @@ -1072,10 +1159,15 @@ func (s *Server) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletI copyStates := make([]*vtctldatapb.Workflow_Stream_CopyState, len(result.Rows)) for i, row := range result.Rows { - // These fields are technically varbinary, but this is close enough. + streamId, err := row[0].ToInt64() + if err != nil { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to cast vrepl_id to int64: %v", err) + } + // These string fields are technically varbinary, but this is close enough. copyStates[i] = &vtctldatapb.Workflow_Stream_CopyState{ - Table: row[0].ToString(), - LastPk: row[1].ToString(), + StreamId: streamId, + Table: row[1].ToString(), + LastPk: row[2].ToString(), } } @@ -1246,6 +1338,7 @@ func (s *Server) Materialize(ctx context.Context, ms *vtctldatapb.MaterializeSet sourceTs: s.ts, tmc: s.tmc, ms: ms, + env: s.env, } err := mz.createMaterializerStreams() @@ -1263,8 +1356,8 @@ func (s *Server) MoveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl } func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTablesCreateRequest, - workflowType binlogdatapb.VReplicationWorkflowType) (res *vtctldatapb.WorkflowStatusResponse, err error) { - + workflowType binlogdatapb.VReplicationWorkflowType, +) (res *vtctldatapb.WorkflowStatusResponse, err error) { span, ctx := trace.NewSpan(ctx, "workflow.Server.MoveTablesCreate") defer span.Finish() @@ -1276,7 +1369,7 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl sourceKeyspace := req.SourceKeyspace targetKeyspace := req.TargetKeyspace - //FIXME validate tableSpecs, allTables, excludeTables + // FIXME validate tableSpecs, allTables, excludeTables var ( tables = req.IncludeTables externalTopo *topo.Server @@ -1344,7 +1437,6 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl return nil, err } } - ms := &vtctldatapb.MaterializeSettings{ Workflow: req.Workflow, MaterializationIntent: vtctldatapb.MaterializationIntent_MOVETABLES, @@ -1385,6 +1477,7 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl tmc: s.tmc, ms: ms, workflowType: workflowType, + env: s.env, } err = mz.createMoveTablesStreams(req) if err != nil { @@ -1445,13 +1538,11 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl return nil, err } } - if vschema != nil { - // We added to the vschema. - if err := s.ts.SaveVSchema(ctx, targetKeyspace, vschema); err != nil { - return nil, err - } - } + // We added to the vschema. + if err := s.ts.SaveVSchema(ctx, targetKeyspace, vschema); err != nil { + return nil, err + } } if err := s.ts.RebuildSrvVSchema(ctx, nil); err != nil { return nil, err @@ -1622,12 +1713,28 @@ func (s *Server) VDiffCreate(ctx context.Context, req *vtctldatapb.VDiffCreateRe span.Annotate("tablet_types", req.TabletTypes) span.Annotate("tables", req.Tables) span.Annotate("auto_retry", req.AutoRetry) + span.Annotate("max_diff_duration", req.MaxDiffDuration) tabletTypesStr := topoproto.MakeStringTypeCSV(req.TabletTypes) if req.TabletSelectionPreference == tabletmanagerdatapb.TabletSelectionPreference_INORDER { tabletTypesStr = discovery.InOrderHint + tabletTypesStr } + // This is a pointer so there's no ZeroValue in the message + // and an older v18 client will not provide it. + if req.MaxDiffDuration == nil { + req.MaxDiffDuration = &vttimepb.Duration{} + } + // The other vttime.Duration vars should not be nil as the + // client should always provide them, but we check anyway to + // be safe. + if req.FilteredReplicationWaitTime == nil { + req.FilteredReplicationWaitTime = &vttimepb.Duration{} + } + if req.WaitUpdateInterval == nil { + req.WaitUpdateInterval = &vttimepb.Duration{} + } + options := &tabletmanagerdatapb.VDiffOptions{ PickerOptions: &tabletmanagerdatapb.VDiffPickerOptions{ TabletTypes: tabletTypesStr, @@ -1637,14 +1744,16 @@ func (s *Server) VDiffCreate(ctx context.Context, req *vtctldatapb.VDiffCreateRe CoreOptions: &tabletmanagerdatapb.VDiffCoreOptions{ Tables: strings.Join(req.Tables, ","), AutoRetry: req.AutoRetry, - MaxRows: req.MaxExtraRowsToCompare, + MaxRows: req.Limit, TimeoutSeconds: req.FilteredReplicationWaitTime.Seconds, MaxExtraRowsToCompare: req.MaxExtraRowsToCompare, UpdateTableStats: req.UpdateTableStats, + MaxDiffSeconds: req.MaxDiffDuration.Seconds, }, ReportOptions: &tabletmanagerdatapb.VDiffReportOptions{ - OnlyPks: req.OnlyPKs, - DebugQuery: req.DebugQuery, + OnlyPks: req.OnlyPKs, + DebugQuery: req.DebugQuery, + MaxSampleRows: req.MaxReportSampleRows, }, } @@ -1828,6 +1937,9 @@ func (s *Server) WorkflowDelete(ctx context.Context, req *vtctldatapb.WorkflowDe span.Annotate("keyspace", req.Keyspace) span.Annotate("workflow", req.Workflow) + span.Annotate("keep_data", req.KeepData) + span.Annotate("keep_routing_rules", req.KeepRoutingRules) + span.Annotate("shards", req.Shards) // Cleanup related data and artifacts. if _, err := s.DropTargets(ctx, req.Keyspace, req.Workflow, req.KeepData, req.KeepRoutingRules, false); err != nil { @@ -1840,7 +1952,8 @@ func (s *Server) WorkflowDelete(ctx context.Context, req *vtctldatapb.WorkflowDe deleteReq := &tabletmanagerdatapb.DeleteVReplicationWorkflowRequest{ Workflow: req.Workflow, } - vx := vexec.NewVExec(req.Keyspace, req.Workflow, s.ts, s.tmc) + vx := vexec.NewVExec(req.Keyspace, req.Workflow, s.ts, s.tmc, s.env.Parser()) + vx.SetShardSubset(req.Shards) callback := func(ctx context.Context, tablet *topo.TabletInfo) (*querypb.QueryResult, error) { res, err := s.tmc.DeleteVReplicationWorkflow(ctx, tablet.Tablet, deleteReq) if err != nil { @@ -1914,7 +2027,7 @@ func (s *Server) WorkflowStatus(ctx context.Context, req *vtctldatapb.WorkflowSt } } - workflow, err := s.GetWorkflow(ctx, req.Keyspace, req.Workflow, false) + workflow, err := s.GetWorkflow(ctx, req.Keyspace, req.Workflow, false, req.Shards) if err != nil { return nil, err } @@ -2028,7 +2141,7 @@ func (s *Server) GetCopyProgress(ctx context.Context, ts *trafficSwitcher, state sourceTableSizes[table] = 0 } - var getTableMetrics = func(tablet *topodatapb.Tablet, query string, rowCounts *map[string]int64, tableSizes *map[string]int64) error { + getTableMetrics := func(tablet *topodatapb.Tablet, query string, rowCounts *map[string]int64, tableSizes *map[string]int64) error { p3qr, err := s.tmc.ExecuteFetchAsDba(ctx, tablet, true, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ Query: []byte(query), MaxRows: uint64(len(tables)), @@ -2115,8 +2228,10 @@ func (s *Server) WorkflowUpdate(ctx context.Context, req *vtctldatapb.WorkflowUp span.Annotate("tablet_types", req.TabletRequest.TabletTypes) span.Annotate("on_ddl", req.TabletRequest.OnDdl) span.Annotate("state", req.TabletRequest.State) + span.Annotate("shards", req.TabletRequest.Shards) - vx := vexec.NewVExec(req.Keyspace, req.TabletRequest.Workflow, s.ts, s.tmc) + vx := vexec.NewVExec(req.Keyspace, req.TabletRequest.Workflow, s.ts, s.tmc, s.env.Parser()) + vx.SetShardSubset(req.TabletRequest.Shards) callback := func(ctx context.Context, tablet *topo.TabletInfo) (*querypb.QueryResult, error) { res, err := s.tmc.UpdateVReplicationWorkflow(ctx, tablet.Tablet, req.TabletRequest) if err != nil { @@ -2529,7 +2644,7 @@ func (s *Server) buildTrafficSwitcher(ctx context.Context, targetKeyspace, workf if err != nil { return nil, err } - ts.sourceKSSchema, err = vindexes.BuildKeyspaceSchema(vs, ts.sourceKeyspace) + ts.sourceKSSchema, err = vindexes.BuildKeyspaceSchema(vs, ts.sourceKeyspace, s.env.Parser()) if err != nil { return nil, err } @@ -2709,7 +2824,7 @@ func (s *Server) DeleteShard(ctx context.Context, keyspace, shard string, recurs // GetTabletMap ignores ErrNoNode, and it's good for // our purpose, it means a tablet was deleted but is // still referenced. - tabletMap, err := s.ts.GetTabletMap(ctx, aliases) + tabletMap, err := s.ts.GetTabletMap(ctx, aliases, nil) if err != nil { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "GetTabletMap() failed: %v", err) } @@ -2762,7 +2877,8 @@ func (s *Server) DeleteShard(ctx context.Context, keyspace, shard string, recurs // updateShardRecords updates the shard records based on 'from' or 'to' direction. func (s *Server) updateShardRecords(ctx context.Context, keyspace string, shards []*topo.ShardInfo, cells []string, - servedType topodatapb.TabletType, isFrom bool, clearSourceShards bool, logger logutil.Logger) (err error) { + servedType topodatapb.TabletType, isFrom bool, clearSourceShards bool, logger logutil.Logger, +) (err error) { return topotools.UpdateShardRecords(ctx, s.ts, s.tmc, keyspace, shards, cells, servedType, isFrom, clearSourceShards, logger) } @@ -2872,7 +2988,7 @@ func (s *Server) WorkflowSwitchTraffic(ctx context.Context, req *vtctldatapb.Wor return nil, err } } - reason, err := s.canSwitch(ctx, ts, startState, direction, int64(maxReplicationLagAllowed.Seconds())) + reason, err := s.canSwitch(ctx, ts, startState, direction, int64(maxReplicationLagAllowed.Seconds()), req.Shards) if err != nil { return nil, err } @@ -2977,11 +3093,14 @@ func (s *Server) switchReads(ctx context.Context, req *vtctldatapb.WorkflowSwitc return handleError("invalid request", vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "requesting reversal of SwitchReads for RDONLYs but RDONLY reads have not been switched")) } } - var cells = req.Cells + cells := req.Cells // If no cells were provided in the command then use the value from the workflow. if len(cells) == 0 && ts.optCells != "" { cells = strings.Split(strings.TrimSpace(ts.optCells), ",") } + for i, cell := range cells { + cells[i] = strings.TrimSpace(cell) + } // If there are no rdonly tablets in the cells ask to switch rdonly tablets as well so that routing rules // are updated for rdonly as well. Otherwise vitess will not know that the workflow has completed and will @@ -3048,8 +3167,8 @@ func (s *Server) switchReads(ctx context.Context, req *vtctldatapb.WorkflowSwitc // switchWrites is a generic way of migrating write traffic for a workflow. func (s *Server) switchWrites(ctx context.Context, req *vtctldatapb.WorkflowSwitchTrafficRequest, ts *trafficSwitcher, timeout time.Duration, - cancel bool) (journalID int64, dryRunResults *[]string, err error) { - + cancel bool, +) (journalID int64, dryRunResults *[]string, err error) { var sw iswitcher if req.DryRun { sw = &switcherDryRun{ts: ts, drLog: NewLogRecorder()} @@ -3117,7 +3236,7 @@ func (s *Server) switchWrites(ctx context.Context, req *vtctldatapb.WorkflowSwit } if !journalsExist { ts.Logger().Infof("No previous journals were found. Proceeding normally.") - sm, err := BuildStreamMigrator(ctx, ts, cancel) + sm, err := BuildStreamMigrator(ctx, ts, cancel, s.env.Parser()) if err != nil { return handleError("failed to migrate the workflow streams", err) } @@ -3182,6 +3301,20 @@ func (s *Server) switchWrites(ctx context.Context, req *vtctldatapb.WorkflowSwit sw.cancelMigration(ctx, sm) return handleError("failed to create the reverse vreplication streams", err) } + + // Initialize any target sequences, if there are any, before allowing new writes. + if req.InitializeTargetSequences && len(sequenceMetadata) > 0 { + ts.Logger().Infof("Initializing target sequences") + // Writes are blocked so we can safely initialize the sequence tables but + // we also want to use a shorter timeout than the parent context. + // We use at most half of the overall timeout. + initSeqCtx, cancel := context.WithTimeout(ctx, timeout/2) + defer cancel() + if err := sw.initializeTargetSequences(initSeqCtx, sequenceMetadata); err != nil { + sw.cancelMigration(ctx, sm) + return handleError(fmt.Sprintf("failed to initialize the sequences used in the %s keyspace", ts.TargetKeyspaceName()), err) + } + } } else { if cancel { return handleError("invalid cancel", vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "traffic switching has reached the point of no return, cannot cancel")) @@ -3198,17 +3331,6 @@ func (s *Server) switchWrites(ctx context.Context, req *vtctldatapb.WorkflowSwit if err := sw.createJournals(ctx, sourceWorkflows); err != nil { return handleError("failed to create the journal", err) } - // Initialize any target sequences, if there are any, before allowing new writes. - if req.InitializeTargetSequences && len(sequenceMetadata) > 0 { - // Writes are blocked so we can safely initialize the sequence tables but - // we also want to use a shorter timeout than the parent context. - // We use up at most half of the overall timeout. - initSeqCtx, cancel := context.WithTimeout(ctx, timeout/2) - defer cancel() - if err := sw.initializeTargetSequences(initSeqCtx, sequenceMetadata); err != nil { - return handleError(fmt.Sprintf("failed to initialize the sequences used in the %s keyspace", ts.TargetKeyspaceName()), err) - } - } if err := sw.allowTargetWrites(ctx); err != nil { return handleError(fmt.Sprintf("failed to allow writes in the %s keyspace", ts.TargetKeyspaceName()), err) } @@ -3231,13 +3353,14 @@ func (s *Server) switchWrites(ctx context.Context, req *vtctldatapb.WorkflowSwit return ts.id, sw.logs(), nil } -func (s *Server) canSwitch(ctx context.Context, ts *trafficSwitcher, state *State, direction TrafficSwitchDirection, maxAllowedReplLagSecs int64) (reason string, err error) { +func (s *Server) canSwitch(ctx context.Context, ts *trafficSwitcher, state *State, direction TrafficSwitchDirection, + maxAllowedReplLagSecs int64, shards []string) (reason string, err error) { if direction == DirectionForward && state.WritesSwitched || direction == DirectionBackward && !state.WritesSwitched { log.Infof("writes already switched no need to check lag") return "", nil } - wf, err := s.GetWorkflow(ctx, state.TargetKeyspace, state.Workflow, false) + wf, err := s.GetWorkflow(ctx, state.TargetKeyspace, state.Workflow, false, shards) if err != nil { return "", err } @@ -3402,8 +3525,8 @@ func (s *Server) applySQLShard(ctx context.Context, tabletInfo *topo.TabletInfo, // fillStringTemplate returns the string template filled. func fillStringTemplate(tmpl string, vars any) (string, error) { myTemplate := template.Must(template.New("").Parse(tmpl)) - data := new(bytes.Buffer) - if err := myTemplate.Execute(data, vars); err != nil { + var data strings.Builder + if err := myTemplate.Execute(&data, vars); err != nil { return "", err } return data.String(), nil @@ -3447,11 +3570,14 @@ func (s *Server) prepareCreateLookup(ctx context.Context, workflow, keyspace str if !strings.Contains(vindex.Type, "lookup") { return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "vindex %s is not a lookup type", vindex.Type) } - targetKeyspace, targetTableName, err = sqlparser.ParseTable(vindex.Params["table"]) + targetKeyspace, targetTableName, err = s.env.Parser().ParseTable(vindex.Params["table"]) if err != nil || targetKeyspace == "" { return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "vindex table name (%s) must be in the form .", vindex.Params["table"]) } vindexFromCols = strings.Split(vindex.Params["from"], ",") + for i, col := range vindexFromCols { + vindexFromCols[i] = strings.TrimSpace(col) + } if strings.Contains(vindex.Type, "unique") { if len(vindexFromCols) != 1 { return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unique vindex 'from' should have only one column") diff --git a/go/vt/vtctl/workflow/server_test.go b/go/vt/vtctl/workflow/server_test.go index 85c60336351..6bb3993fe1c 100644 --- a/go/vt/vtctl/workflow/server_test.go +++ b/go/vt/vtctl/workflow/server_test.go @@ -27,12 +27,15 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" ) type fakeTMC struct { @@ -130,7 +133,6 @@ func TestCheckReshardingJournalExistsOnTablet(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -142,7 +144,7 @@ func TestCheckReshardingJournalExistsOnTablet(t *testing.T) { }, } - ws := NewServer(nil, tmc) + ws := NewServer(vtenv.NewTestEnv(), nil, tmc) journal, exists, err := ws.CheckReshardingJournalExistsOnTablet(ctx, tt.tablet, 1) if tt.shouldErr { assert.Error(t, err) @@ -163,3 +165,36 @@ func TestCheckReshardingJournalExistsOnTablet(t *testing.T) { }) } } + +// TestVDiffCreate performs some basic tests of the VDiffCreate function +// to ensure that it behaves as expected given a specific request. +func TestVDiffCreate(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer(ctx, "cell") + tmc := &fakeTMC{} + s := NewServer(vtenv.NewTestEnv(), ts, tmc) + + tests := []struct { + name string + req *vtctldatapb.VDiffCreateRequest + wantErr string + }{ + { + name: "no values", + req: &vtctldatapb.VDiffCreateRequest{}, + wantErr: "FindAllShardsInKeyspace(): List: node doesn't exist: keyspaces/shards", // We did not provide any keyspace or shard + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := s.VDiffCreate(ctx, tt.req) + if tt.wantErr != "" { + require.EqualError(t, err, tt.wantErr) + return + } + require.NoError(t, err) + require.NotNil(t, got) + require.NotEmpty(t, got.UUID) + }) + } +} diff --git a/go/vt/vtctl/workflow/stream_migrator.go b/go/vt/vtctl/workflow/stream_migrator.go index 75d509614b7..7d225f6dd9f 100644 --- a/go/vt/vtctl/workflow/stream_migrator.go +++ b/go/vt/vtctl/workflow/stream_migrator.go @@ -26,7 +26,6 @@ import ( "google.golang.org/protobuf/encoding/prototext" "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/key" @@ -41,6 +40,13 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) +/* + This file contains code that is specific to VReplication Reshard + workflows -- which require migrating the *other* VReplication + workflows (aside from the Reshard workflow itself) that exist in + the keyspace from one set of shards to another when switching traffic. +*/ + // StreamType is an enum representing the kind of stream. // // (TODO:@ajm188) This should be made package-private once the last references @@ -54,21 +60,25 @@ const ( StreamTypeReference ) -// StreamMigrator contains information needed to migrate a stream +// StreamMigrator contains information needed to migrate VReplication +// streams during Reshard workflows when the keyspace's VReplication +// workflows need to be migrated from one set of shards to another. type StreamMigrator struct { streams map[string][]*VReplicationStream workflows []string templates []*VReplicationStream ts ITrafficSwitcher logger logutil.Logger + parser *sqlparser.Parser } // BuildStreamMigrator creates a new StreamMigrator based on the given // TrafficSwitcher. -func BuildStreamMigrator(ctx context.Context, ts ITrafficSwitcher, cancelMigrate bool) (*StreamMigrator, error) { +func BuildStreamMigrator(ctx context.Context, ts ITrafficSwitcher, cancelMigrate bool, parser *sqlparser.Parser) (*StreamMigrator, error) { sm := &StreamMigrator{ ts: ts, logger: ts.Logger(), + parser: parser, } if sm.ts.MigrationType() == binlogdatapb.MigrationType_TABLES { @@ -147,21 +157,26 @@ func (sm *StreamMigrator) Templates() []*VReplicationStream { return VReplicationStreams(sm.templates).Copy().ToSlice() } -// CancelMigration cancels a migration -func (sm *StreamMigrator) CancelMigration(ctx context.Context) { +// CancelStreamMigrations cancels the stream migrations. +func (sm *StreamMigrator) CancelStreamMigrations(ctx context.Context) { if sm.streams == nil { return } _ = sm.deleteTargetStreams(ctx) + // Restart the source streams, but leave the Reshard workflow's reverse + // variant stopped. err := sm.ts.ForAllSources(func(source *MigrationSource) error { - query := fmt.Sprintf("update _vt.vreplication set state='Running', stop_pos=null, message='' where db_name=%s and workflow != %s", encodeString(source.GetPrimary().DbName()), encodeString(sm.ts.ReverseWorkflowName())) + // We intend to update all but our workflow's reverse streams, so we + // indicate that it's safe in this case using the comment diretive. + query := fmt.Sprintf("update /*vt+ %s */ _vt.vreplication set state='Running', stop_pos=null, message='' where db_name=%s and workflow != %s", + vreplication.AllowUnsafeWriteCommentDirective, encodeString(source.GetPrimary().DbName()), encodeString(sm.ts.ReverseWorkflowName())) _, err := sm.ts.VReplicationExec(ctx, source.GetPrimary().Alias, query) return err }) if err != nil { - sm.logger.Errorf("Cancel migration failed: could not restart source streams: %v", err) + sm.logger.Errorf("Cancel stream migrations failed: could not restart source streams: %v", err) } } @@ -198,6 +213,8 @@ func (sm *StreamMigrator) StopStreams(ctx context.Context) ([]string, error) { /* tablet streams */ +// readTabletStreams reads all of the VReplication workflow streams *except* +// the Reshard workflow's reverse variant. func (sm *StreamMigrator) readTabletStreams(ctx context.Context, ti *topo.TabletInfo, constraint string) ([]*VReplicationStream, error) { query := fmt.Sprintf("select id, workflow, source, pos, workflow_type, workflow_sub_type, defer_secondary_keys from _vt.vreplication where db_name=%s and workflow != %s", encodeString(ti.DbName()), encodeString(sm.ts.ReverseWorkflowName())) @@ -674,7 +691,7 @@ func (sm *StreamMigrator) templatizeRule(ctx context.Context, rule *binlogdatapb } func (sm *StreamMigrator) templatizeKeyRange(ctx context.Context, rule *binlogdatapb.Rule) error { - statement, err := sqlparser.Parse(rule.Filter) + statement, err := sm.parser.Parse(rule.Filter) if err != nil { return err } diff --git a/go/vt/vtctl/workflow/stream_migrator_test.go b/go/vt/vtctl/workflow/stream_migrator_test.go index 04f787eb4d4..38ae10280f7 100644 --- a/go/vt/vtctl/workflow/stream_migrator_test.go +++ b/go/vt/vtctl/workflow/stream_migrator_test.go @@ -24,6 +24,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" @@ -304,7 +306,7 @@ func TestTemplatize(t *testing.T) { }, }, } - ksschema, err := vindexes.BuildKeyspaceSchema(vs, "ks") + ksschema, err := vindexes.BuildKeyspaceSchema(vs, "ks", sqlparser.NewTestParser()) require.NoError(t, err, "could not create test keyspace %+v", vs) ts := &testTrafficSwitcher{ diff --git a/go/vt/vtctl/workflow/switcher_dry_run.go b/go/vt/vtctl/workflow/switcher_dry_run.go index 1c8a05e00c2..21b975a0d6b 100644 --- a/go/vt/vtctl/workflow/switcher_dry_run.go +++ b/go/vt/vtctl/workflow/switcher_dry_run.go @@ -24,7 +24,8 @@ import ( "strings" "time" - "vitess.io/vitess/go/maps2" + "golang.org/x/exp/maps" + "vitess.io/vitess/go/mysql/replication" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -214,7 +215,7 @@ func (dr *switcherDryRun) stopStreams(ctx context.Context, sm *StreamMigrator) ( } func (dr *switcherDryRun) cancelMigration(ctx context.Context, sm *StreamMigrator) { - dr.drLog.Log("Cancel stream migrations as requested") + dr.drLog.Log("Cancel migration as requested") } func (dr *switcherDryRun) lockKeyspace(ctx context.Context, keyspace, _ string) (context.Context, func(*error), error) { @@ -380,7 +381,7 @@ func (dr *switcherDryRun) resetSequences(ctx context.Context) error { } func (dr *switcherDryRun) initializeTargetSequences(ctx context.Context, sequencesByBackingTable map[string]*sequenceMetadata) error { - sortedBackingTableNames := maps2.Keys(sequencesByBackingTable) + sortedBackingTableNames := maps.Keys(sequencesByBackingTable) slices.Sort(sortedBackingTableNames) dr.drLog.Log(fmt.Sprintf("The following sequence backing tables used by tables being moved will be initialized: %s", strings.Join(sortedBackingTableNames, ","))) diff --git a/go/vt/vtctl/workflow/traffic_switcher.go b/go/vt/vtctl/workflow/traffic_switcher.go index 35f1d1b966b..c8551c5ff73 100644 --- a/go/vt/vtctl/workflow/traffic_switcher.go +++ b/go/vt/vtctl/workflow/traffic_switcher.go @@ -25,10 +25,10 @@ import ( "sync" "time" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "vitess.io/vitess/go/json2" - "vitess.io/vitess/go/maps2" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -38,6 +38,7 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -380,9 +381,6 @@ func (ts *trafficSwitcher) addParticipatingTablesToKeyspace(ctx context.Context, if err := json2.Unmarshal([]byte(wrap), ks); err != nil { return err } - if err != nil { - return err - } for table, vtab := range ks.Tables { vschema.Tables[table] = vtab } @@ -490,26 +488,33 @@ func (ts *trafficSwitcher) dropParticipatingTablesFromKeyspace(ctx context.Conte func (ts *trafficSwitcher) removeSourceTables(ctx context.Context, removalType TableRemovalType) error { err := ts.ForAllSources(func(source *MigrationSource) error { for _, tableName := range ts.Tables() { - query := fmt.Sprintf("drop table %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName))) + primaryDbName, err := sqlescape.EnsureEscaped(source.GetPrimary().DbName()) + if err != nil { + return err + } + tableNameEscaped, err := sqlescape.EnsureEscaped(tableName) + if err != nil { + return err + } + + query := fmt.Sprintf("drop table %s.%s", primaryDbName, tableNameEscaped) if removalType == DropTable { ts.Logger().Infof("%s: Dropping table %s.%s\n", source.GetPrimary().String(), source.GetPrimary().DbName(), tableName) } else { - renameName := getRenameFileName(tableName) + renameName, err := sqlescape.EnsureEscaped(getRenameFileName(tableName)) + if err != nil { + return err + } ts.Logger().Infof("%s: Renaming table %s.%s to %s.%s\n", source.GetPrimary().String(), source.GetPrimary().DbName(), tableName, source.GetPrimary().DbName(), renameName) - query = fmt.Sprintf("rename table %s.%s TO %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName)), - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(renameName))) + query = fmt.Sprintf("rename table %s.%s TO %s.%s", primaryDbName, tableNameEscaped, primaryDbName, renameName) } - _, err := ts.ws.tmc.ExecuteFetchAsDba(ctx, source.GetPrimary().Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(query), - MaxRows: 1, - ReloadSchema: true, + _, err = ts.ws.tmc.ExecuteFetchAsDba(ctx, source.GetPrimary().Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ + Query: []byte(query), + MaxRows: 1, + ReloadSchema: true, + DisableForeignKeyChecks: true, }) if err != nil { ts.Logger().Errorf("%s: Error removing table %s: %v", source.GetPrimary().String(), tableName, err) @@ -612,7 +617,8 @@ func (ts *trafficSwitcher) switchTableReads(ctx context.Context, cells []string, func (ts *trafficSwitcher) startReverseVReplication(ctx context.Context) error { return ts.ForAllSources(func(source *MigrationSource) error { - query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s", encodeString(source.GetPrimary().DbName())) + query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s and workflow=%s", + encodeString(source.GetPrimary().DbName()), encodeString(ts.ReverseWorkflowName())) _, err := ts.VReplicationExec(ctx, source.GetPrimary().Alias, query) return err }) @@ -1002,7 +1008,7 @@ func (ts *trafficSwitcher) cancelMigration(ctx context.Context, sm *StreamMigrat ts.Logger().Errorf("Cancel migration failed:", err) } - sm.CancelMigration(ctx) + sm.CancelStreamMigrations(ctx) err = ts.ForAllTargets(func(target *MigrationTarget) error { query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s and workflow=%s", @@ -1063,22 +1069,27 @@ func (ts *trafficSwitcher) dropSourceReverseVReplicationStreams(ctx context.Cont } func (ts *trafficSwitcher) removeTargetTables(ctx context.Context) error { - log.Flush() err := ts.ForAllTargets(func(target *MigrationTarget) error { log.Infof("ForAllTargets: %+v", target) for _, tableName := range ts.Tables() { - query := fmt.Sprintf("drop table %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(target.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName))) + primaryDbName, err := sqlescape.EnsureEscaped(target.GetPrimary().DbName()) + if err != nil { + return err + } + tableName, err := sqlescape.EnsureEscaped(tableName) + if err != nil { + return err + } + query := fmt.Sprintf("drop table %s.%s", primaryDbName, tableName) ts.Logger().Infof("%s: Dropping table %s.%s\n", target.GetPrimary().String(), target.GetPrimary().DbName(), tableName) res, err := ts.ws.tmc.ExecuteFetchAsDba(ctx, target.GetPrimary().Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(query), - MaxRows: 1, - ReloadSchema: true, + Query: []byte(query), + MaxRows: 1, + ReloadSchema: true, + DisableForeignKeyChecks: true, }) log.Infof("Removed target table with result: %+v", res) - log.Flush() if err != nil { ts.Logger().Errorf("%s: Error removing table %s: %v", target.GetPrimary().String(), tableName, err) @@ -1349,7 +1360,7 @@ func (ts *trafficSwitcher) getTargetSequenceMetadata(ctx context.Context) (map[s // error if any is seen. func (ts *trafficSwitcher) findSequenceUsageInKeyspace(vschema *vschemapb.Keyspace) (map[string]*sequenceMetadata, bool, error) { allFullyQualified := true - targets := maps2.Values(ts.Targets()) + targets := maps.Values(ts.Targets()) if len(targets) == 0 || targets[0].GetPrimary() == nil { // This should never happen return nil, false, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "no primary tablet found for target keyspace %s", ts.targetKeyspace) } @@ -1422,13 +1433,17 @@ func (ts *trafficSwitcher) initializeTargetSequences(ctx context.Context, sequen MaxRows: 1, }) if terr != nil || len(qr.Rows) != 1 { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s in order to initialize the backing sequence table: %v", - ts.targetKeyspace, sequenceMetadata.usingTableName, terr) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s on tablet %s in order to initialize the backing sequence table: %v", + ts.targetKeyspace, sequenceMetadata.usingTableName, topoproto.TabletAliasString(primary.Alias), terr) } - maxID, terr := sqltypes.Proto3ToResult(qr).Rows[0][0].ToInt64() - if terr != nil { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s in order to initialize the backing sequence table: %v", - ts.targetKeyspace, sequenceMetadata.usingTableName, terr) + rawVal := sqltypes.Proto3ToResult(qr).Rows[0][0] + maxID := int64(0) + if !rawVal.IsNull() { // If it's NULL then there are no rows and 0 remains the max + maxID, terr = rawVal.ToInt64() + if terr != nil { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s on tablet %s in order to initialize the backing sequence table: %v", + ts.targetKeyspace, sequenceMetadata.usingTableName, topoproto.TabletAliasString(primary.Alias), terr) + } } srMu.Lock() defer srMu.Unlock() diff --git a/go/vt/vtctl/workflow/utils.go b/go/vt/vtctl/workflow/utils.go index 1a723c6192c..80b981026d8 100644 --- a/go/vt/vtctl/workflow/utils.go +++ b/go/vt/vtctl/workflow/utils.go @@ -86,7 +86,7 @@ func getTablesInKeyspace(ctx context.Context, ts *topo.Server, tmc tmclient.Tabl // validateNewWorkflow ensures that the specified workflow doesn't already exist // in the keyspace. func validateNewWorkflow(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, keyspace, workflow string) error { - allshards, err := ts.FindAllShardsInKeyspace(ctx, keyspace) + allshards, err := ts.FindAllShardsInKeyspace(ctx, keyspace, nil) if err != nil { return err } @@ -167,8 +167,8 @@ func createDefaultShardRoutingRules(ctx context.Context, ms *vtctldatapb.Materia return nil } -func stripTableConstraints(ddl string) (string, error) { - ast, err := sqlparser.ParseStrictDDL(ddl) +func stripTableConstraints(ddl string, parser *sqlparser.Parser) (string, error) { + ast, err := parser.ParseStrictDDL(ddl) if err != nil { return "", err } @@ -189,8 +189,8 @@ func stripTableConstraints(ddl string) (string, error) { return newDDL, nil } -func stripTableForeignKeys(ddl string) (string, error) { - ast, err := sqlparser.ParseStrictDDL(ddl) +func stripTableForeignKeys(ddl string, parser *sqlparser.Parser) (string, error) { + ast, err := parser.ParseStrictDDL(ddl) if err != nil { return "", err } @@ -326,7 +326,7 @@ func getMigrationID(targetKeyspace string, shardTablets []string) (int64, error) // // It returns ErrNoStreams if there are no targets found for the workflow. func BuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, targetKeyspace string, workflow string) (*TargetInfo, error) { - targetShards, err := ts.GetShardNames(ctx, targetKeyspace) + targetShards, err := ts.FindAllShardsInKeyspace(ctx, targetKeyspace, nil) if err != nil { return nil, err } @@ -344,18 +344,13 @@ func BuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManag // stream. For example, if we're splitting -80 to [-40,40-80], only those // two target shards will have vreplication streams, and the other shards in // the target keyspace will not. - for _, targetShard := range targetShards { - si, err := ts.GetShard(ctx, targetKeyspace, targetShard) - if err != nil { - return nil, err - } - - if si.PrimaryAlias == nil { + for targetShardName, targetShard := range targetShards { + if targetShard.PrimaryAlias == nil { // This can happen if bad inputs are given. return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "shard %v/%v doesn't have a primary set", targetKeyspace, targetShard) } - primary, err := ts.GetTablet(ctx, si.PrimaryAlias) + primary, err := ts.GetTablet(ctx, targetShard.PrimaryAlias) if err != nil { return nil, err } @@ -372,7 +367,7 @@ func BuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManag } target := &MigrationTarget{ - si: si, + si: targetShard, primary: primary, Sources: make(map[int32]*binlogdatapb.BinlogSource), } @@ -389,7 +384,7 @@ func BuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManag target.Sources[stream.Id] = stream.Bls } - targets[targetShard] = target + targets[targetShardName] = target } if len(targets) == 0 { @@ -657,11 +652,8 @@ func areTabletsAvailableToStreamFrom(ctx context.Context, req *vtctldatapb.Workf // New callers should instead use the new BuildTargets function. // // It returns ErrNoStreams if there are no targets found for the workflow. -func LegacyBuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, targetKeyspace string, workflow string) (*TargetInfo, error) { - targetShards, err := ts.GetShardNames(ctx, targetKeyspace) - if err != nil { - return nil, err - } +func LegacyBuildTargets(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, targetKeyspace string, workflow string, + targetShards []string) (*TargetInfo, error) { var ( frozen bool diff --git a/go/vt/vtctl/workflow/vexec/query_plan.go b/go/vt/vtctl/workflow/vexec/query_plan.go index e6a9cb3a54d..52e7ee00b61 100644 --- a/go/vt/vtctl/workflow/vexec/query_plan.go +++ b/go/vt/vtctl/workflow/vexec/query_plan.go @@ -31,7 +31,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" ) -// QueryPlan defines the interface to executing a preprared vexec query on one +// QueryPlan defines the interface to executing a prepared vexec query on one // or more tablets. Implementations should ensure that it is safe to call the // various Execute* methods repeatedly and in multiple goroutines. type QueryPlan interface { diff --git a/go/vt/vtctl/workflow/vexec/query_plan_test.go b/go/vt/vtctl/workflow/vexec/query_plan_test.go index 2899b9a3107..382e5213b5b 100644 --- a/go/vt/vtctl/workflow/vexec/query_plan_test.go +++ b/go/vt/vtctl/workflow/vexec/query_plan_test.go @@ -153,8 +153,6 @@ func TestQueryPlanExecute(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -303,8 +301,6 @@ func TestQueryPlanExecuteScatter(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vtctl/workflow/vexec/query_planner_test.go b/go/vt/vtctl/workflow/vexec/query_planner_test.go index 9199c8a0947..ec162ebc4c7 100644 --- a/go/vt/vtctl/workflow/vexec/query_planner_test.go +++ b/go/vt/vtctl/workflow/vexec/query_planner_test.go @@ -65,8 +65,6 @@ func TestVReplicationQueryPlanner_PlanQuery(t *testing.T) { planner := NewVReplicationQueryPlanner(nil, "", "") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -115,8 +113,6 @@ func TestVReplicationQueryPlanner_planSelect(t *testing.T) { planner := NewVReplicationQueryPlanner(nil, "testworkflow", "vt_testkeyspace") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -169,8 +165,6 @@ func TestVReplicationQueryPlanner_planUpdate(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -230,8 +224,6 @@ func TestVReplicationQueryPlanner_planDelete(t *testing.T) { planner := NewVReplicationQueryPlanner(nil, "", "vt_testkeyspace") for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -351,13 +343,11 @@ func TestVReplicationLogQueryPlanner(t *testing.T) { } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { t.Parallel() planner := NewVReplicationLogQueryPlanner(nil, tt.targetStreamIDs) - stmt, err := sqlparser.Parse(tt.query) + stmt, err := sqlparser.NewTestParser().Parse(tt.query) require.NoError(t, err, "could not parse query %q", tt.query) qp, err := planner.planSelect(stmt.(*sqlparser.Select)) if tt.shouldErr { diff --git a/go/vt/vtctl/workflow/vexec/testutil/query.go b/go/vt/vtctl/workflow/vexec/testutil/query.go index 3988f7a112f..1add74e5b02 100644 --- a/go/vt/vtctl/workflow/vexec/testutil/query.go +++ b/go/vt/vtctl/workflow/vexec/testutil/query.go @@ -41,7 +41,7 @@ func ParsedQueryFromString(t *testing.T, query string) *sqlparser.ParsedQuery { func StatementFromString(t *testing.T, query string) sqlparser.Statement { t.Helper() - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err, "could not parse query %v", query) return stmt diff --git a/go/vt/vtctl/workflow/vexec/vexec.go b/go/vt/vtctl/workflow/vexec/vexec.go index 477b81a1a03..9e6fbe96940 100644 --- a/go/vt/vtctl/workflow/vexec/vexec.go +++ b/go/vt/vtctl/workflow/vexec/vexec.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl/workflow/common" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tmclient" @@ -95,6 +96,9 @@ type VExec struct { // to support running in modes like: // - Execute serially rather than concurrently. // - Only return error if greater than some percentage of the targets fail. + + parser *sqlparser.Parser + shardSubset []string } // NewVExec returns a new instance suitable for making vexec queries to a given @@ -102,15 +106,24 @@ type VExec struct { // string). The provided topo server is used to look up target tablets for // queries. A given instance will discover targets exactly once for its // lifetime, so to force a refresh, create another instance. -func NewVExec(keyspace string, workflow string, ts *topo.Server, tmc tmclient.TabletManagerClient) *VExec { +func NewVExec(keyspace string, workflow string, ts *topo.Server, tmc tmclient.TabletManagerClient, parser *sqlparser.Parser) *VExec { return &VExec{ ts: ts, tmc: tmc, keyspace: keyspace, workflow: workflow, + parser: parser, } } +func (vx *VExec) SetShardSubset(shardSubset []string) { + vx.shardSubset = shardSubset +} + +func (vx *VExec) GetShardSubset() []string { + return vx.shardSubset +} + // QueryContext executes the given vexec query, returning a mapping of tablet // to querypb.QueryResult. // @@ -127,7 +140,7 @@ func (vx *VExec) QueryContext(ctx context.Context, query string) (map[*topo.Tabl } } - stmt, err := sqlparser.Parse(query) + stmt, err := vx.parser.Parse(query) if err != nil { return nil, err } @@ -205,15 +218,11 @@ func (vx *VExec) initialize(ctx context.Context) error { getShardsCtx, getShardsCancel := context.WithTimeout(ctx, topo.RemoteOperationTimeout) defer getShardsCancel() - shards, err := vx.ts.GetShardNames(getShardsCtx, vx.keyspace) + shards, err := common.GetShards(getShardsCtx, vx.ts, vx.keyspace, vx.shardSubset) if err != nil { return err } - if len(shards) == 0 { - return fmt.Errorf("%w %s", ErrNoShardsForKeyspace, vx.keyspace) - } - primaries := make([]*topo.TabletInfo, 0, len(shards)) for _, shard := range shards { @@ -299,6 +308,7 @@ func (vx *VExec) WithWorkflow(workflow string) *VExec { ts: vx.ts, tmc: vx.tmc, primaries: vx.primaries, + parser: vx.parser, workflow: workflow, } } @@ -306,9 +316,9 @@ func (vx *VExec) WithWorkflow(workflow string) *VExec { func extractTableName(stmt sqlparser.Statement) (string, error) { switch stmt := stmt.(type) { case *sqlparser.Update: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Delete: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Insert: return sqlparser.String(stmt.Table), nil case *sqlparser.Select: diff --git a/go/vt/vtctld/action_repository.go b/go/vt/vtctld/action_repository.go index 0076ee65ba6..e0f6c45535a 100644 --- a/go/vt/vtctld/action_repository.go +++ b/go/vt/vtctld/action_repository.go @@ -23,6 +23,8 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" @@ -79,6 +81,7 @@ type actionTabletRecord struct { // ActionRepository is a repository of actions that can be performed // on a {Keyspace,Shard,Tablet}. type ActionRepository struct { + env *vtenv.Environment keyspaceActions map[string]actionKeyspaceMethod shardActions map[string]actionShardMethod tabletActions map[string]actionTabletRecord @@ -87,8 +90,9 @@ type ActionRepository struct { // NewActionRepository creates and returns a new ActionRepository, // with no actions. -func NewActionRepository(ts *topo.Server) *ActionRepository { +func NewActionRepository(env *vtenv.Environment, ts *topo.Server) *ActionRepository { return &ActionRepository{ + env: env, keyspaceActions: make(map[string]actionKeyspaceMethod), shardActions: make(map[string]actionShardMethod), tabletActions: make(map[string]actionTabletRecord), @@ -125,7 +129,7 @@ func (ar *ActionRepository) ApplyKeyspaceAction(ctx context.Context, actionName, } ctx, cancel := context.WithTimeout(ctx, actionTimeout) - wr := wrangler.New(logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(ar.env, logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) output, err := action(ctx, wr, keyspace) cancel() if err != nil { @@ -152,7 +156,7 @@ func (ar *ActionRepository) ApplyShardAction(ctx context.Context, actionName, ke } ctx, cancel := context.WithTimeout(ctx, actionTimeout) - wr := wrangler.New(logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(ar.env, logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) output, err := action(ctx, wr, keyspace, shard) cancel() if err != nil { @@ -186,7 +190,7 @@ func (ar *ActionRepository) ApplyTabletAction(ctx context.Context, actionName st // run the action ctx, cancel := context.WithTimeout(ctx, actionTimeout) - wr := wrangler.New(logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(ar.env, logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient()) output, err := action.method(ctx, wr, tabletAlias) cancel() if err != nil { diff --git a/go/vt/vtctld/api.go b/go/vt/vtctld/api.go index 43afcb29452..0452fce3c3d 100644 --- a/go/vt/vtctld/api.go +++ b/go/vt/vtctld/api.go @@ -487,7 +487,7 @@ func initAPI(ctx context.Context, ts *topo.Server, actions *ActionRepository) { logstream := logutil.NewMemoryLogger() - wr := wrangler.New(logstream, ts, tmClient) + wr := wrangler.New(actions.env, logstream, ts, tmClient) err := vtctl.RunCommand(r.Context(), wr, args) if err != nil { resp.Error = err.Error() @@ -523,7 +523,7 @@ func initAPI(ctx context.Context, ts *topo.Server, actions *ActionRepository) { logger := logutil.NewCallbackLogger(func(ev *logutilpb.Event) { w.Write([]byte(logutil.EventString(ev))) }) - wr := wrangler.New(logger, ts, tmClient) + wr := wrangler.New(actions.env, logger, ts, tmClient) apiCallUUID, err := schema.CreateUUID() if err != nil { @@ -531,7 +531,7 @@ func initAPI(ctx context.Context, ts *topo.Server, actions *ActionRepository) { } requestContext := fmt.Sprintf("vtctld/api:%s", apiCallUUID) - executor := schemamanager.NewTabletExecutor(requestContext, wr.TopoServer(), wr.TabletManagerClient(), wr.Logger(), time.Duration(req.ReplicaTimeoutSeconds)*time.Second, 0) + executor := schemamanager.NewTabletExecutor(requestContext, wr.TopoServer(), wr.TabletManagerClient(), wr.Logger(), time.Duration(req.ReplicaTimeoutSeconds)*time.Second, 0, actions.env.Parser()) if err := executor.SetDDLStrategy(req.DDLStrategy); err != nil { return fmt.Errorf("error setting DDL strategy: %v", err) } diff --git a/go/vt/vtctld/api_test.go b/go/vt/vtctld/api_test.go index 6443d89a56b..d8ac8beccc1 100644 --- a/go/vt/vtctld/api_test.go +++ b/go/vt/vtctld/api_test.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/vt/servenv/testutils" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/wrangler" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -47,7 +48,7 @@ func TestAPI(t *testing.T) { cells := []string{"cell1", "cell2"} ts := memorytopo.NewServer(ctx, cells...) defer ts.Close() - actionRepo := NewActionRepository(ts) + actionRepo := NewActionRepository(vtenv.NewTestEnv(), ts) server := testutils.HTTPTestServer() defer server.Close() @@ -237,7 +238,6 @@ func TestAPI(t *testing.T) { // Keyspaces {"GET", "keyspaces", "", `["ks1", "ks3"]`, http.StatusOK}, {"GET", "keyspaces/ks1", "", `{ - "served_froms": [], "keyspace_type":0, "base_keyspace":"", "snapshot_time":null, @@ -324,11 +324,11 @@ func TestAPI(t *testing.T) { // vtctl RunCommand {"POST", "vtctl/", `["GetKeyspace","ks1"]`, `{ "Error": "", - "Output": "{\n \"served_froms\": [],\n \"keyspace_type\": 0,\n \"base_keyspace\": \"\",\n \"snapshot_time\": null,\n \"durability_policy\": \"semi_sync\",\n \"throttler_config\": null,\n \"sidecar_db_name\": \"_vt_sidecar_ks1\"\n}\n\n" + "Output": "{\n \"keyspace_type\": 0,\n \"base_keyspace\": \"\",\n \"snapshot_time\": null,\n \"durability_policy\": \"semi_sync\",\n \"throttler_config\": null,\n \"sidecar_db_name\": \"_vt_sidecar_ks1\"\n}\n\n" }`, http.StatusOK}, {"POST", "vtctl/", `["GetKeyspace","ks3"]`, `{ "Error": "", - "Output": "{\n \"served_froms\": [],\n \"keyspace_type\": 1,\n \"base_keyspace\": \"ks1\",\n \"snapshot_time\": {\n \"seconds\": \"1136214245\",\n \"nanoseconds\": 0\n },\n \"durability_policy\": \"none\",\n \"throttler_config\": null,\n \"sidecar_db_name\": \"_vt\"\n}\n\n" + "Output": "{\n \"keyspace_type\": 1,\n \"base_keyspace\": \"ks1\",\n \"snapshot_time\": {\n \"seconds\": \"1136214245\",\n \"nanoseconds\": 0\n },\n \"durability_policy\": \"none\",\n \"throttler_config\": null,\n \"sidecar_db_name\": \"_vt\"\n}\n\n" }`, http.StatusOK}, {"POST", "vtctl/", `["GetVSchema","ks3"]`, `{ "Error": "", diff --git a/go/vt/vtctld/tablet_data_test.go b/go/vt/vtctld/tablet_data_test.go index d40c6647ef3..bbb03e3b878 100644 --- a/go/vt/vtctld/tablet_data_test.go +++ b/go/vt/vtctld/tablet_data_test.go @@ -29,6 +29,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" @@ -112,7 +113,7 @@ func TestTabletData(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") defer ts.Close() - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) if err := ts.CreateKeyspace(context.Background(), "ks", &topodatapb.Keyspace{}); err != nil { t.Fatalf("CreateKeyspace failed: %v", err) diff --git a/go/vt/vtctld/vtctld.go b/go/vt/vtctld/vtctld.go index ab9cf24c9a5..5ca3908c053 100644 --- a/go/vt/vtctld/vtctld.go +++ b/go/vt/vtctld/vtctld.go @@ -23,6 +23,8 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/acl" @@ -48,8 +50,8 @@ func registerVtctldFlags(fs *pflag.FlagSet) { } // InitVtctld initializes all the vtctld functionality. -func InitVtctld(ts *topo.Server) error { - actionRepo := NewActionRepository(ts) +func InitVtctld(env *vtenv.Environment, ts *topo.Server) error { + actionRepo := NewActionRepository(env, ts) // keyspace actions actionRepo.RegisterKeyspaceAction("ValidateKeyspace", diff --git a/go/vt/vtenv/cached_size.go b/go/vt/vtenv/cached_size.go new file mode 100644 index 00000000000..808cc4cdca3 --- /dev/null +++ b/go/vt/vtenv/cached_size.go @@ -0,0 +1,37 @@ +/* +Copyright 2021 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by Sizegen. DO NOT EDIT. + +package vtenv + +import hack "vitess.io/vitess/go/hack" + +func (cached *Environment) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field collationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.collationEnv.CachedSize(true) + // field parser *vitess.io/vitess/go/vt/sqlparser.Parser + size += cached.parser.CachedSize(true) + // field mysqlVersion string + size += hack.RuntimeAllocSize(int64(len(cached.mysqlVersion))) + return size +} diff --git a/go/vt/vtenv/vtenv.go b/go/vt/vtenv/vtenv.go new file mode 100644 index 00000000000..1371affff52 --- /dev/null +++ b/go/vt/vtenv/vtenv.go @@ -0,0 +1,97 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtenv + +import ( + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" + "vitess.io/vitess/go/vt/sqlparser" +) + +type Environment struct { + collationEnv *collations.Environment + parser *sqlparser.Parser + mysqlVersion string + truncateUILen int + truncateErrLen int +} + +type Options struct { + MySQLServerVersion string + TruncateUILen int + TruncateErrLen int +} + +func New(cfg Options) (*Environment, error) { + if cfg.MySQLServerVersion == "" { + cfg.MySQLServerVersion = config.DefaultMySQLVersion + } + parser, err := sqlparser.New(sqlparser.Options{ + MySQLServerVersion: cfg.MySQLServerVersion, + TruncateErrLen: cfg.TruncateErrLen, + TruncateUILen: cfg.TruncateUILen, + }) + if err != nil { + return nil, err + } + return &Environment{ + collationEnv: collations.NewEnvironment(cfg.MySQLServerVersion), + parser: parser, + mysqlVersion: cfg.MySQLServerVersion, + truncateUILen: cfg.TruncateUILen, + truncateErrLen: cfg.TruncateErrLen, + }, nil +} + +func NewTestEnv() *Environment { + return &Environment{ + collationEnv: collations.NewEnvironment(config.DefaultMySQLVersion), + parser: sqlparser.NewTestParser(), + mysqlVersion: config.DefaultMySQLVersion, + truncateUILen: 512, + truncateErrLen: 0, + } +} + +func (e *Environment) CollationEnv() *collations.Environment { + return e.collationEnv +} + +func (e *Environment) Parser() *sqlparser.Parser { + return e.parser +} + +func (e *Environment) MySQLVersion() string { + return e.mysqlVersion +} + +// TruncateForUI is used when displaying queries on various Vitess status pages +// to keep the pages small enough to load and render properly +func (e *Environment) TruncateForUI(query string) string { + return sqlparser.TruncateQuery(query, e.truncateUILen) +} + +// TruncateForLog is used when displaying queries as part of error logs +// to avoid overwhelming logging systems with potentially long queries and +// bind value data. +func (e *Environment) TruncateForLog(query string) string { + return sqlparser.TruncateQuery(query, e.truncateErrLen) +} + +func (e *Environment) TruncateErrLen() int { + return e.truncateErrLen +} diff --git a/go/vt/vtenv/vtenv_test.go b/go/vt/vtenv/vtenv_test.go new file mode 100644 index 00000000000..f0d15e5156b --- /dev/null +++ b/go/vt/vtenv/vtenv_test.go @@ -0,0 +1,49 @@ +package vtenv + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" + "vitess.io/vitess/go/vt/sqlparser" +) + +func TestNewDefaults(t *testing.T) { + e, err := New(Options{}) + assert.NoError(t, err) + assert.Equal(t, config.DefaultMySQLVersion, e.MySQLVersion()) + assert.Equal(t, collations.MySQL8(), e.CollationEnv()) + assert.Equal(t, 0, e.Parser().GetTruncateErrLen()) + assert.Equal(t, "foo", e.TruncateForLog("foo")) + assert.Equal(t, "foo", e.TruncateForUI("foo")) +} + +func TestNewCustom(t *testing.T) { + e, err := New(Options{ + MySQLServerVersion: "8.0.34", + TruncateErrLen: 15, + TruncateUILen: 16, + }) + assert.NoError(t, err) + assert.Equal(t, "8.0.34", e.MySQLVersion()) + assert.Equal(t, collations.MySQL8(), e.CollationEnv()) + assert.Equal(t, 15, e.Parser().GetTruncateErrLen()) + assert.Equal(t, "sel [TRUNCATED]", e.TruncateForLog("select 11111111111")) + assert.Equal(t, "sele [TRUNCATED]", e.TruncateForUI("select 11111111111")) +} + +func TestNewError(t *testing.T) { + _, err := New(Options{ + MySQLServerVersion: "invalid", + }) + assert.Error(t, err) +} + +func TestNewTestEnv(t *testing.T) { + e := NewTestEnv() + assert.Equal(t, config.DefaultMySQLVersion, e.MySQLVersion()) + assert.Equal(t, collations.MySQL8(), e.CollationEnv()) + assert.Equal(t, sqlparser.NewTestParser(), e.Parser()) +} diff --git a/go/vt/vterrors/code.go b/go/vt/vterrors/code.go index 6bc317db4ed..574ac7c2cdf 100644 --- a/go/vt/vterrors/code.go +++ b/go/vt/vterrors/code.go @@ -22,13 +22,16 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +// Errors added to the list of variables below must be added to the Errors slice a little below in this same file. +// This will enable the auto-documentation of error code in the website repository. + var ( VT03001 = errorWithState("VT03001", vtrpcpb.Code_INVALID_ARGUMENT, SyntaxError, "aggregate functions take a single argument '%s'", "This aggregation function only takes a single argument.") VT03002 = errorWithState("VT03002", vtrpcpb.Code_INVALID_ARGUMENT, ForbidSchemaChange, "changing schema from '%s' to '%s' is not allowed", "This schema change is not allowed. You cannot change the keyspace of a table.") VT03003 = errorWithState("VT03003", vtrpcpb.Code_INVALID_ARGUMENT, UnknownTable, "unknown table '%s' in MULTI DELETE", "The specified table in this DELETE statement is unknown.") VT03004 = errorWithState("VT03004", vtrpcpb.Code_INVALID_ARGUMENT, NonUpdateableTable, "the target table %s of the DELETE is not updatable", "You cannot delete something that is not a real MySQL table.") VT03005 = errorWithState("VT03005", vtrpcpb.Code_INVALID_ARGUMENT, WrongGroupField, "cannot group on '%s'", "The planner does not allow grouping on certain field. For instance, aggregation function.") - VT03006 = errorWithState("VT03006", vtrpcpb.Code_INVALID_ARGUMENT, WrongValueCountOnRow, "column count does not match value count at row 1", "The number of columns you want to insert do not match the number of columns of your SELECT query.") + VT03006 = errorWithState("VT03006", vtrpcpb.Code_INVALID_ARGUMENT, WrongValueCountOnRow, "column count does not match value count with the row", "The number of columns you want to insert do not match the number of columns of your SELECT query.") VT03007 = errorWithoutState("VT03007", vtrpcpb.Code_INVALID_ARGUMENT, "keyspace not specified", "You need to add a keyspace qualifier.") VT03008 = errorWithState("VT03008", vtrpcpb.Code_INVALID_ARGUMENT, CantUseOptionHere, "incorrect usage/placement of '%s'", "The given token is not usable in this situation. Please refer to the MySQL documentation to learn more about your token's syntax.") VT03009 = errorWithState("VT03009", vtrpcpb.Code_INVALID_ARGUMENT, WrongValueForVar, "unexpected value type for '%s': %v", "You cannot assign this type to the given variable.") @@ -36,7 +39,7 @@ var ( VT03011 = errorWithoutState("VT03011", vtrpcpb.Code_INVALID_ARGUMENT, "invalid value type: %v", "The given value type is not accepted.") VT03012 = errorWithoutState("VT03012", vtrpcpb.Code_INVALID_ARGUMENT, "invalid syntax: %s", "The syntax is invalid. Please refer to the MySQL documentation for the proper syntax.") VT03013 = errorWithState("VT03013", vtrpcpb.Code_INVALID_ARGUMENT, NonUniqTable, "not unique table/alias: '%s'", "This table or alias name is already use. Please use another one that is unique.") - VT03014 = errorWithState("VT03014", vtrpcpb.Code_INVALID_ARGUMENT, BadFieldError, "unknown column '%d' in '%s'", "The given column is unknown.") + VT03014 = errorWithState("VT03014", vtrpcpb.Code_INVALID_ARGUMENT, BadFieldError, "unknown column '%s' in '%s'", "The given column is unknown.") VT03015 = errorWithoutState("VT03015", vtrpcpb.Code_INVALID_ARGUMENT, "column has duplicate set values: '%v'", "Cannot assign multiple values to a column in an update statement.") VT03016 = errorWithoutState("VT03016", vtrpcpb.Code_INVALID_ARGUMENT, "unknown vindex column: '%s'", "The given column is unknown in the vindex table.") VT03017 = errorWithState("VT03017", vtrpcpb.Code_INVALID_ARGUMENT, SyntaxError, "where clause can only be of the type 'pos > '", "This vstream where clause can only be a greater than filter.") @@ -49,6 +52,12 @@ var ( VT03024 = errorWithoutState("VT03024", vtrpcpb.Code_INVALID_ARGUMENT, "'%s' user defined variable does not exists", "The query cannot be prepared using the user defined variable as it does not exists for this session.") VT03025 = errorWithState("VT03025", vtrpcpb.Code_INVALID_ARGUMENT, WrongArguments, "Incorrect arguments to %s", "The execute statement have wrong number of arguments") VT03026 = errorWithoutState("VT03024", vtrpcpb.Code_INVALID_ARGUMENT, "'%s' bind variable does not exists", "The query cannot be executed as missing the bind variable.") + VT03027 = errorWithState("VT03027", vtrpcpb.Code_INVALID_ARGUMENT, BadNullError, "Column '%s' cannot be null", "The column cannot have null value.") + VT03028 = errorWithState("VT03028", vtrpcpb.Code_INVALID_ARGUMENT, BadNullError, "Column '%s' cannot be null on row %d, col %d", "The column cannot have null value.") + VT03029 = errorWithState("VT03029", vtrpcpb.Code_INVALID_ARGUMENT, WrongValueCountOnRow, "column count does not match value count with the row for vindex '%s'", "The number of columns you want to insert do not match the number of columns of your SELECT query.") + VT03030 = errorWithState("VT03030", vtrpcpb.Code_INVALID_ARGUMENT, WrongValueCountOnRow, "lookup column count does not match value count with the row (columns, count): (%v, %d)", "The number of columns you want to insert do not match the number of columns of your SELECT query.") + VT03031 = errorWithoutState("VT03031", vtrpcpb.Code_INVALID_ARGUMENT, "EXPLAIN is only supported for single keyspace", "EXPLAIN has to be sent down as a single query to the underlying MySQL, and this is not possible if it uses tables from multiple keyspaces") + VT03032 = errorWithState("VT03031", vtrpcpb.Code_INVALID_ARGUMENT, NonUpdateableTable, "the target table %s of the UPDATE is not updatable", "You cannot update a table that is not a real MySQL table.") VT05001 = errorWithState("VT05001", vtrpcpb.Code_NOT_FOUND, DbDropExists, "cannot drop database '%s'; database does not exists", "The given database does not exist; Vitess cannot drop it.") VT05002 = errorWithState("VT05002", vtrpcpb.Code_NOT_FOUND, BadDb, "cannot alter database '%s'; unknown database", "The given database does not exist; Vitess cannot alter it.") @@ -80,7 +89,9 @@ var ( VT09016 = errorWithState("VT09016", vtrpcpb.Code_FAILED_PRECONDITION, RowIsReferenced2, "Cannot delete or update a parent row: a foreign key constraint fails", "SET DEFAULT is not supported by InnoDB") VT09017 = errorWithoutState("VT09017", vtrpcpb.Code_FAILED_PRECONDITION, "%s", "Invalid syntax for the statement type.") VT09018 = errorWithoutState("VT09018", vtrpcpb.Code_FAILED_PRECONDITION, "%s", "Invalid syntax for the vindex function statement.") - VT09019 = errorWithoutState("VT09019", vtrpcpb.Code_FAILED_PRECONDITION, "%s has cyclic foreign keys", "Vitess doesn't support cyclic foreign keys.") + VT09019 = errorWithoutState("VT09019", vtrpcpb.Code_FAILED_PRECONDITION, "keyspace '%s' has cyclic foreign keys", "Vitess doesn't support cyclic foreign keys.") + VT09020 = errorWithoutState("VT09020", vtrpcpb.Code_FAILED_PRECONDITION, "can not use multiple vindex hints for table %s", "Vitess does not allow using multiple vindex hints on the same table.") + VT09021 = errorWithState("VT09021", vtrpcpb.Code_FAILED_PRECONDITION, KeyDoesNotExist, "Vindex '%s' does not exist in table '%s'", "Vindex hints have to reference an existing vindex, and no such vindex could be found for the given table.") VT10001 = errorWithoutState("VT10001", vtrpcpb.Code_ABORTED, "foreign key constraints are not allowed", "Foreign key constraints are not allowed, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/.") @@ -97,6 +108,8 @@ var ( VT14004 = errorWithoutState("VT14004", vtrpcpb.Code_UNAVAILABLE, "cannot find keyspace for: %s", "The specified keyspace could not be found.") VT14005 = errorWithoutState("VT14005", vtrpcpb.Code_UNAVAILABLE, "cannot lookup sidecar database for keyspace: %s", "Failed to read sidecar database identifier.") + // Errors is a list of errors that must match all the variables + // defined above to enable auto-documentation of error codes. Errors = []func(args ...any) *VitessError{ VT03001, VT03002, @@ -124,6 +137,12 @@ var ( VT03024, VT03025, VT03026, + VT03027, + VT03028, + VT03029, + VT03030, + VT03031, + VT03032, VT05001, VT05002, VT05003, @@ -151,6 +170,7 @@ var ( VT09016, VT09017, VT09018, + VT09019, VT10001, VT12001, VT12002, diff --git a/go/vt/vterrors/state.go b/go/vt/vterrors/state.go index 5e3dcf22dfb..00d64dd39a5 100644 --- a/go/vt/vterrors/state.go +++ b/go/vt/vterrors/state.go @@ -47,6 +47,7 @@ const ( WrongValueCountOnRow WrongValue WrongArguments + BadNullError // failed precondition NoDB @@ -58,6 +59,7 @@ const ( RowIsReferenced2 NoReferencedRow2 UnknownStmtHandler + KeyDoesNotExist // not found BadDb diff --git a/go/vt/vtexplain/vtexplain.go b/go/vt/vtexplain/vtexplain.go index 55e76606e08..e16dea89d2b 100644 --- a/go/vt/vtexplain/vtexplain.go +++ b/go/vt/vtexplain/vtexplain.go @@ -20,7 +20,6 @@ limitations under the License. package vtexplain import ( - "bytes" "context" "fmt" "sort" @@ -29,6 +28,9 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtgate" @@ -43,9 +45,7 @@ import ( vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" ) -var ( - batchInterval = 10 * time.Millisecond -) +var batchInterval = 10 * time.Millisecond func init() { servenv.OnParseFor("vtexplain", func(fs *pflag.FlagSet) { @@ -54,7 +54,7 @@ func init() { } const ( - vtexplainCell = "explainCell" + Cell = "explainCell" // ModeMulti is the default mode with autocommit implemented at vtgate ModeMulti = "multi" @@ -147,6 +147,8 @@ type ( // time simulator batchTime *sync2.Batcher globalTabletEnv *tabletEnv + + env *vtenv.Environment } ) @@ -154,10 +156,11 @@ type ( func (tq *TabletQuery) MarshalJSON() ([]byte, error) { // Convert Bindvars to strings for nicer output bindVars := make(map[string]string) + var buf strings.Builder for k, v := range tq.BindVars { - var b strings.Builder - sqlparser.EncodeValue(&b, v) - bindVars[k] = b.String() + buf.Reset() + sqlparser.EncodeValue(&buf, v) + bindVars[k] = buf.String() } return jsonutil.MarshalNoEscape(&struct { @@ -181,27 +184,30 @@ type TabletActions struct { } // Init sets up the fake execution environment -func Init(ctx context.Context, vSchemaStr, sqlSchema, ksShardMapStr string, opts *Options) (*VTExplain, error) { +func Init(ctx context.Context, env *vtenv.Environment, ts *topo.Server, vSchemaStr, sqlSchema, ksShardMapStr string, opts *Options) (*VTExplain, error) { // Verify options if opts.ReplicationMode != "ROW" && opts.ReplicationMode != "STATEMENT" { return nil, fmt.Errorf("invalid replication mode \"%s\"", opts.ReplicationMode) } - parsedDDLs, err := parseSchema(sqlSchema, opts) + parsedDDLs, err := parseSchema(sqlSchema, opts, env.Parser()) if err != nil { return nil, fmt.Errorf("parseSchema: %v", err) } - tabletEnv, err := newTabletEnvironment(parsedDDLs, opts) + tabletEnv, err := newTabletEnvironment(parsedDDLs, opts, env.CollationEnv()) if err != nil { return nil, fmt.Errorf("initTabletEnvironment: %v", err) } - vte := &VTExplain{vtgateSession: &vtgatepb.Session{ - TargetString: "", - Autocommit: true, - }} + vte := &VTExplain{ + vtgateSession: &vtgatepb.Session{ + TargetString: "", + Autocommit: true, + }, + env: env, + } vte.setGlobalTabletEnv(tabletEnv) - err = vte.initVtgateExecutor(ctx, vSchemaStr, ksShardMapStr, opts) + err = vte.initVtgateExecutor(ctx, ts, vSchemaStr, ksShardMapStr, opts) if err != nil { return nil, fmt.Errorf("initVtgateExecutor: %v", err.Error()) } @@ -227,10 +233,10 @@ func (vte *VTExplain) Stop() { } } -func parseSchema(sqlSchema string, opts *Options) ([]sqlparser.DDLStatement, error) { +func parseSchema(sqlSchema string, opts *Options, parser *sqlparser.Parser) ([]sqlparser.DDLStatement, error) { parsedDDLs := make([]sqlparser.DDLStatement, 0, 16) for { - sql, rem, err := sqlparser.SplitStatement(sqlSchema) + sql, rem, err := parser.SplitStatement(sqlSchema) sqlSchema = rem if err != nil { return nil, err @@ -245,12 +251,12 @@ func parseSchema(sqlSchema string, opts *Options) ([]sqlparser.DDLStatement, err var stmt sqlparser.Statement if opts.StrictDDL { - stmt, err = sqlparser.ParseStrictDDL(sql) + stmt, err = parser.ParseStrictDDL(sql) if err != nil { return nil, err } } else { - stmt, err = sqlparser.Parse(sql) + stmt, err = parser.Parse(sql) if err != nil { log.Errorf("ERROR: failed to parse sql: %s, got error: %v", sql, err) continue @@ -294,7 +300,7 @@ func (vte *VTExplain) Run(sql string) ([]*Explain, error) { sql = s } - sql, rem, err = sqlparser.SplitStatement(sql) + sql, rem, err = vte.env.Parser().SplitStatement(sql) if err != nil { return nil, err } @@ -337,7 +343,7 @@ func (vte *VTExplain) explain(sql string) (*Explain, error) { // ExplainsAsText returns a text representation of the explains in logical time // order func (vte *VTExplain) ExplainsAsText(explains []*Explain) (string, error) { - var b bytes.Buffer + var b strings.Builder for _, explain := range explains { fmt.Fprintf(&b, "----------------------------------------------------------------------\n") fmt.Fprintf(&b, "%s\n\n", explain.SQL) @@ -381,7 +387,7 @@ func (vte *VTExplain) specialHandlingOfSavepoints(q *MysqlQuery) error { return nil } - stmt, err := sqlparser.Parse(q.SQL) + stmt, err := vte.env.Parser().Parse(q.SQL) if err != nil { return err } diff --git a/go/vt/vtexplain/vtexplain_test.go b/go/vt/vtexplain/vtexplain_test.go index 54f1efbc522..e7a6f4bdfc8 100644 --- a/go/vt/vtexplain/vtexplain_test.go +++ b/go/vt/vtexplain/vtexplain_test.go @@ -28,6 +28,9 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/key" @@ -49,7 +52,7 @@ type testopts struct { shardmap map[string]map[string]*topo.ShardInfo } -func initTest(ctx context.Context, mode string, opts *Options, topts *testopts, t *testing.T) *VTExplain { +func initTest(ctx context.Context, ts *topo.Server, mode string, opts *Options, topts *testopts, t *testing.T) *VTExplain { schema, err := os.ReadFile("testdata/test-schema.sql") require.NoError(t, err) @@ -65,7 +68,7 @@ func initTest(ctx context.Context, mode string, opts *Options, topts *testopts, } opts.ExecutionMode = mode - vte, err := Init(ctx, string(vSchema), string(schema), shardmap, opts) + vte, err := Init(ctx, vtenv.NewTestEnv(), ts, string(vSchema), string(schema), shardmap, opts) require.NoError(t, err, "vtexplain Init error\n%s", string(schema)) return vte } @@ -88,7 +91,8 @@ func runTestCase(testcase, mode string, opts *Options, topts *testopts, t *testi t.Run(testcase, func(t *testing.T) { ctx := utils.LeakCheckContext(t) - vte := initTest(ctx, mode, opts, topts, t) + ts := memorytopo.NewServer(ctx, Cell) + vte := initTest(ctx, ts, mode, opts, topts, t) defer vte.Stop() sqlFile := fmt.Sprintf("testdata/%s-queries.sql", testcase) @@ -154,8 +158,8 @@ func TestExplain(t *testing.T) { func TestErrors(t *testing.T) { ctx := utils.LeakCheckContext(t) - - vte := initTest(ctx, ModeMulti, defaultTestOpts(), &testopts{}, t) + ts := memorytopo.NewServer(ctx, Cell) + vte := initTest(ctx, ts, ModeMulti, defaultTestOpts(), &testopts{}, t) defer vte.Stop() tests := []struct { @@ -194,8 +198,8 @@ func TestErrors(t *testing.T) { func TestJSONOutput(t *testing.T) { ctx := utils.LeakCheckContext(t) - - vte := initTest(ctx, ModeMulti, defaultTestOpts(), &testopts{}, t) + ts := memorytopo.NewServer(ctx, Cell) + vte := initTest(ctx, ts, ModeMulti, defaultTestOpts(), &testopts{}, t) defer vte.Stop() sql := "select 1 from user where id = 1" explains, err := vte.Run(sql) @@ -344,7 +348,8 @@ func TestInit(t *testing.T) { } }` schema := "create table table_missing_primary_vindex (id int primary key)" - _, err := Init(ctx, vschema, schema, "", defaultTestOpts()) + ts := memorytopo.NewServer(ctx, Cell) + _, err := Init(ctx, vtenv.NewTestEnv(), ts, vschema, schema, "", defaultTestOpts()) require.Error(t, err) require.Contains(t, err.Error(), "missing primary col vindex") } diff --git a/go/vt/vtexplain/vtexplain_vtgate.go b/go/vt/vtexplain/vtexplain_vtgate.go index 8167c510b01..a5d9677f02a 100644 --- a/go/vt/vtexplain/vtexplain_vtgate.go +++ b/go/vt/vtexplain/vtexplain_vtgate.go @@ -22,26 +22,23 @@ package vtexplain import ( "context" "fmt" + "path" "sort" "strings" "vitess.io/vitess/go/cache/theine" - "vitess.io/vitess/go/vt/vtgate/logstats" - "vitess.io/vitess/go/vt/vtgate/vindexes" - - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/memorytopo" - - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/json2" "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate" "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/logstats" + "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/queryservice" querypb "vitess.io/vitess/go/vt/proto/query" @@ -50,14 +47,14 @@ import ( vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" ) -func (vte *VTExplain) initVtgateExecutor(ctx context.Context, vSchemaStr, ksShardMapStr string, opts *Options) error { +func (vte *VTExplain) initVtgateExecutor(ctx context.Context, ts *topo.Server, vSchemaStr, ksShardMapStr string, opts *Options) error { vte.explainTopo = &ExplainTopo{NumShards: opts.NumShards} - vte.explainTopo.TopoServer = memorytopo.NewServer(ctx, vtexplainCell) + vte.explainTopo.TopoServer = ts vte.healthCheck = discovery.NewFakeHealthCheck(nil) - resolver := vte.newFakeResolver(ctx, opts, vte.explainTopo, vtexplainCell) + resolver := vte.newFakeResolver(ctx, opts, vte.explainTopo, Cell) - err := vte.buildTopology(ctx, opts, vSchemaStr, ksShardMapStr, opts.NumShards) + err := vte.buildTopology(ctx, ts, opts, vSchemaStr, ksShardMapStr, opts.NumShards) if err != nil { return err } @@ -75,7 +72,7 @@ func (vte *VTExplain) initVtgateExecutor(ctx context.Context, vSchemaStr, ksShar var schemaTracker vtgate.SchemaInfo // no schema tracker for these tests queryLogBufferSize := 10 plans := theine.NewStore[vtgate.PlanCacheKey, *engine.Plan](4*1024*1024, false) - vte.vtgateExecutor = vtgate.NewExecutor(ctx, vte.explainTopo, vtexplainCell, resolver, opts.Normalize, false, streamSize, plans, schemaTracker, false, opts.PlannerVersion, 0) + vte.vtgateExecutor = vtgate.NewExecutor(ctx, vte.env, vte.explainTopo, Cell, resolver, opts.Normalize, false, streamSize, plans, schemaTracker, false, opts.PlannerVersion, 0) vte.vtgateExecutor.SetQueryLogger(streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize)) return nil @@ -95,7 +92,7 @@ func (vte *VTExplain) newFakeResolver(ctx context.Context, opts *Options, serv s return vtgate.NewResolver(srvResolver, serv, cell, sc) } -func (vte *VTExplain) buildTopology(ctx context.Context, opts *Options, vschemaStr string, ksShardMapStr string, numShardsPerKeyspace int) error { +func (vte *VTExplain) buildTopology(ctx context.Context, ts *topo.Server, opts *Options, vschemaStr string, ksShardMapStr string, numShardsPerKeyspace int) error { vte.explainTopo.Lock.Lock() defer vte.explainTopo.Lock.Unlock() @@ -107,7 +104,7 @@ func (vte *VTExplain) buildTopology(ctx context.Context, opts *Options, vschemaS if err != nil { return err } - schema := vindexes.BuildVSchema(&srvVSchema) + schema := vindexes.BuildVSchema(&srvVSchema, vte.env.Parser()) for ks, ksSchema := range schema.Keyspaces { if ksSchema.Error != nil { return vterrors.Wrapf(ksSchema.Error, "vschema failed to load on keyspace [%s]", ks) @@ -120,6 +117,10 @@ func (vte *VTExplain) buildTopology(ctx context.Context, opts *Options, vschemaS return err } + conn, err := ts.ConnForCell(ctx, Cell) + if err != nil { + return err + } vte.explainTopo.TabletConns = make(map[string]*explainTablet) vte.explainTopo.KeyspaceShards = make(map[string]map[string]*topodatapb.ShardReference) for ks, vschema := range vte.explainTopo.Keyspaces { @@ -130,6 +131,32 @@ func (vte *VTExplain) buildTopology(ctx context.Context, opts *Options, vschemaS vte.explainTopo.KeyspaceShards[ks] = make(map[string]*topodatapb.ShardReference) + srvPath := path.Join(topo.KeyspacesPath, ks, topo.SrvKeyspaceFile) + srvKeyspace := &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_PRIMARY, + ShardReferences: shards, + }, + { + ServedType: topodatapb.TabletType_REPLICA, + ShardReferences: shards, + }, + { + ServedType: topodatapb.TabletType_RDONLY, + ShardReferences: shards, + }, + }, + } + data, err := srvKeyspace.MarshalVT() + if err != nil { + return err + } + _, err = conn.Update(ctx, srvPath, data, nil) + if err != nil { + return err + } + for _, shard := range shards { // If the topology is in the middle of a reshard, there can be two shards covering the same key range (e.g. // both source shard 80- and target shard 80-c0 cover the keyrange 80-c0). For the purposes of explain, we @@ -142,14 +169,13 @@ func (vte *VTExplain) buildTopology(ctx context.Context, opts *Options, vschemaS hostname := fmt.Sprintf("%s/%s", ks, shard.Name) log.Infof("registering test tablet %s for keyspace %s shard %s", hostname, ks, shard.Name) - tablet := vte.healthCheck.AddFakeTablet(vtexplainCell, hostname, 1, ks, shard.Name, topodatapb.TabletType_PRIMARY, true, 1, nil, func(t *topodatapb.Tablet) queryservice.QueryService { - return vte.newTablet(ctx, opts, t) + tablet := vte.healthCheck.AddFakeTablet(Cell, hostname, 1, ks, shard.Name, topodatapb.TabletType_PRIMARY, true, 1, nil, func(t *topodatapb.Tablet) queryservice.QueryService { + return vte.newTablet(ctx, vte.env, opts, t, ts) }) vte.explainTopo.TabletConns[hostname] = tablet.(*explainTablet) vte.explainTopo.KeyspaceShards[ks][shard.Name] = shard } } - return err } diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index 85aa64037a7..b573fe29774 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -24,6 +24,8 @@ import ( "sync" "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/collations" @@ -34,7 +36,6 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -98,13 +99,15 @@ type explainTablet struct { mysqlQueries []*MysqlQuery currentTime int vte *VTExplain + + collationEnv *collations.Environment } var _ queryservice.QueryService = (*explainTablet)(nil) -func (vte *VTExplain) newTablet(ctx context.Context, opts *Options, t *topodatapb.Tablet) *explainTablet { +func (vte *VTExplain) newTablet(ctx context.Context, env *vtenv.Environment, opts *Options, t *topodatapb.Tablet, ts *topo.Server) *explainTablet { db := fakesqldb.New(nil) - sidecardb.AddSchemaInitQueries(db, true) + sidecardb.AddSchemaInitQueries(db, true, env.Parser()) config := tabletenv.NewCurrentConfig() config.TrackSchemaVersions = false @@ -117,9 +120,9 @@ func (vte *VTExplain) newTablet(ctx context.Context, opts *Options, t *topodatap config.EnableTableGC = false // XXX much of this is cloned from the tabletserver tests - tsv := tabletserver.NewTabletServer(ctx, topoproto.TabletAliasString(t.Alias), config, memorytopo.NewServer(ctx, ""), t.Alias) + tsv := tabletserver.NewTabletServer(ctx, env, topoproto.TabletAliasString(t.Alias), config, ts, t.Alias) - tablet := explainTablet{db: db, tsv: tsv, vte: vte} + tablet := explainTablet{db: db, tsv: tsv, vte: vte, collationEnv: env.CollationEnv()} db.Handler = &tablet tablet.QueryService = queryservice.Wrap( @@ -129,7 +132,7 @@ func (vte *VTExplain) newTablet(ctx context.Context, opts *Options, t *topodatap }, ) - params, _ := db.ConnParams().MysqlParams() + params := db.ConnParams() cp := *params dbcfgs := dbconfigs.NewTestDBConfigs(cp, cp, "") cnf := mysqlctl.NewMycnf(22222, 6802) @@ -280,7 +283,7 @@ func (t *explainTablet) Close(ctx context.Context) error { return t.tsv.Close(ctx) } -func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tabletEnv, error) { +func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options, collationEnv *collations.Environment) (*tabletEnv, error) { tEnv := newTabletEnv() schemaQueries := map[string]*sqltypes.Result{ "select unix_timestamp()": { @@ -302,6 +305,15 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet {sqltypes.NewVarChar("STRICT_TRANS_TABLES")}, }, }, + "select @@global.collation_server": { + Fields: []*querypb.Field{{ + Type: sqltypes.VarChar, + Charset: uint32(collations.SystemCollation.Collation), + }}, + Rows: [][]sqltypes.Value{ + {sqltypes.NewVarChar("utf8mb4_0900_ai_ci")}, + }, + }, "select @@session.sql_mode as sql_mode": { Fields: []*querypb.Field{{ Name: "sql_mode", @@ -442,10 +454,18 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet indexRows := make([][]sqltypes.Value, 0, 4) for _, ddl := range ddls { table := sqlparser.String(ddl.GetTable().Name) - backtickedTable := sqlescape.EscapeID(sqlescape.UnescapeID(table)) + sanitizedTable, err := sqlescape.UnescapeID(table) + if err != nil { + return nil, err + } + backtickedTable := sqlescape.EscapeID(sanitizedTable) if ddl.GetOptLike() != nil { likeTable := ddl.GetOptLike().LikeTable.Name.String() - backtickedLikeTable := sqlescape.EscapeID(sqlescape.UnescapeID(likeTable)) + sanitizedLikeTable, err := sqlescape.UnescapeID(likeTable) + if err != nil { + return nil, err + } + backtickedLikeTable := sqlescape.EscapeID(sanitizedLikeTable) likeQuery := "SELECT * FROM " + backtickedLikeTable + " WHERE 1 != 1" query := "SELECT * FROM " + backtickedTable + " WHERE 1 != 1" @@ -454,8 +474,8 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet } tEnv.addResult(query, tEnv.getResult(likeQuery)) - likeQuery = fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqlescape.UnescapeID(likeTable)) - query = fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqlescape.UnescapeID(table)) + likeQuery = fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqltypes.EncodeStringSQL(sanitizedLikeTable)) + query = fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqltypes.EncodeStringSQL(sanitizedTable)) if tEnv.getResult(likeQuery) == nil { return nil, fmt.Errorf("check your schema, table[%s] doesn't exist", likeTable) } @@ -479,7 +499,7 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet colType := &querypb.Field{ Name: "column_type", Type: sqltypes.VarChar, - Charset: uint32(collations.Default()), + Charset: uint32(collationEnv.DefaultConnectionCharset()), } colTypes = append(colTypes, colType) for _, col := range ddl.GetTableSpec().Columns { @@ -496,7 +516,7 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet tEnv.addResult("SELECT * FROM "+backtickedTable+" WHERE 1 != 1", &sqltypes.Result{ Fields: rowTypes, }) - query := fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqlescape.UnescapeID(table)) + query := fmt.Sprintf(mysqlctl.GetColumnNamesQuery, "database()", sqltypes.EncodeStringSQL(sanitizedTable)) tEnv.addResult(query, &sqltypes.Result{ Fields: colTypes, Rows: colValues, @@ -581,7 +601,7 @@ func (t *explainTablet) handleSelect(query string) (*sqltypes.Result, error) { // Parse the select statement to figure out the table and columns // that were referenced so that the synthetic response has the // expected field names and types. - stmt, err := sqlparser.Parse(query) + stmt, err := t.vte.env.Parser().Parse(query) if err != nil { return nil, err } @@ -598,7 +618,7 @@ func (t *explainTablet) handleSelect(query string) (*sqltypes.Result, error) { // Gen4 supports more complex queries so we now need to // handle multiple FROM clauses - tables := make([]*sqlparser.AliasedTableExpr, len(selStmt.From)) + tables := make([]*sqlparser.AliasedTableExpr, 0, len(selStmt.From)) for _, from := range selStmt.From { tables = append(tables, getTables(from)...) } @@ -646,7 +666,7 @@ func (t *explainTablet) handleSelect(query string) (*sqltypes.Result, error) { rows := make([][]sqltypes.Value, 0, rowCount) for i, col := range colNames { colType := colTypes[i] - cs := collations.DefaultCollationForType(colType) + cs := collations.CollationForType(colType, t.collationEnv.DefaultConnectionCharset()) fields[i] = &querypb.Field{ Name: col, Type: colType, @@ -734,7 +754,7 @@ func (t *explainTablet) analyzeWhere(selStmt *sqlparser.Select, tableColumnMap m // Check if we have a duplicate value isNewValue := true for _, v := range inVal { - result, err := evalengine.NullsafeCompare(v, value, collations.Default()) + result, err := evalengine.NullsafeCompare(v, value, t.collationEnv, t.collationEnv.DefaultConnectionCharset()) if err != nil { return "", nil, 0, nil, err } diff --git a/go/vt/vtexplain/vtexplain_vttablet_test.go b/go/vt/vtexplain/vtexplain_vttablet_test.go index 614ad186224..a4350316d82 100644 --- a/go/vt/vtexplain/vtexplain_vttablet_test.go +++ b/go/vt/vtexplain/vtexplain_vttablet_test.go @@ -20,10 +20,15 @@ import ( "context" "encoding/json" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -70,10 +75,15 @@ create table t2 ( ctx, cancel := context.WithCancel(context.Background()) defer cancel() - vte, err := Init(ctx, testVSchema, testSchema, "", opts) + ts := memorytopo.NewServer(ctx, Cell) + vte, err := Init(ctx, vtenv.NewTestEnv(), ts, testVSchema, testSchema, "", opts) require.NoError(t, err) defer vte.Stop() + // Check if the correct schema query is registered. + _, found := vte.globalTabletEnv.schemaQueries["SELECT COLUMN_NAME as column_name\n\t\tFROM INFORMATION_SCHEMA.COLUMNS\n\t\tWHERE TABLE_SCHEMA = database() AND TABLE_NAME = 't1'\n\t\tORDER BY ORDINAL_POSITION"] + assert.True(t, found) + sql := "SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id" _, err = vte.Run(sql) @@ -117,25 +127,30 @@ create table test_partitioned ( PARTITION p2018_06_16 VALUES LESS THAN (1529132400) ENGINE = InnoDB, PARTITION p2018_06_17 VALUES LESS THAN (1529218800) ENGINE = InnoDB)*/; ` - - ddls, err := parseSchema(testSchema, &Options{StrictDDL: false}) + env := vtenv.NewTestEnv() + ddls, err := parseSchema(testSchema, &Options{StrictDDL: false}, env.Parser()) if err != nil { t.Fatalf("parseSchema: %v", err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() - vte := initTest(ctx, ModeMulti, defaultTestOpts(), &testopts{}, t) + ts := memorytopo.NewServer(ctx, Cell) + vte := initTest(ctx, ts, ModeMulti, defaultTestOpts(), &testopts{}, t) defer vte.Stop() - tabletEnv, _ := newTabletEnvironment(ddls, defaultTestOpts()) + tabletEnv, _ := newTabletEnvironment(ddls, defaultTestOpts(), env.CollationEnv()) vte.setGlobalTabletEnv(tabletEnv) - tablet := vte.newTablet(ctx, defaultTestOpts(), &topodatapb.Tablet{ - Keyspace: "test_keyspace", + tablet := vte.newTablet(ctx, env, defaultTestOpts(), &topodatapb.Tablet{ + Keyspace: "ks_sharded", Shard: "-80", - Alias: &topodatapb.TabletAlias{}, - }) + Alias: &topodatapb.TabletAlias{ + Cell: Cell, + }, + }, ts) + + time.Sleep(10 * time.Millisecond) se := tablet.tsv.SchemaEngine() tables := se.GetSchema() @@ -181,9 +196,9 @@ create table test_partitioned ( func TestErrParseSchema(t *testing.T) { testSchema := `create table t1 like t2` - ddl, err := parseSchema(testSchema, &Options{StrictDDL: true}) + ddl, err := parseSchema(testSchema, &Options{StrictDDL: true}, sqlparser.NewTestParser()) require.NoError(t, err) - _, err = newTabletEnvironment(ddl, defaultTestOpts()) + _, err = newTabletEnvironment(ddl, defaultTestOpts(), collations.MySQL8()) require.Error(t, err, "check your schema, table[t2] doesn't exist") } diff --git a/go/vt/vtgate/buffer/buffer_test.go b/go/vt/vtgate/buffer/buffer_test.go index 7f32364d57f..c730a8336d1 100644 --- a/go/vt/vtgate/buffer/buffer_test.go +++ b/go/vt/vtgate/buffer/buffer_test.go @@ -20,9 +20,13 @@ import ( "context" "fmt" "strings" + "sync" + "sync/atomic" "testing" "time" + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" @@ -69,10 +73,18 @@ var ( ) func TestBuffering(t *testing.T) { - testAllImplementations(t, testBuffering1) + testAllImplementations(t, func(t *testing.T, fail failover) { + testBuffering1WithOptions(t, fail, 1) + }) +} + +func TestBufferingConcurrent(t *testing.T) { + testAllImplementations(t, func(t *testing.T, fail failover) { + testBuffering1WithOptions(t, fail, 2) + }) } -func testBuffering1(t *testing.T, fail failover) { +func testBuffering1WithOptions(t *testing.T, fail failover, concurrency int) { resetVariables() defer checkVariables(t) @@ -86,6 +98,7 @@ func testBuffering1(t *testing.T, fail failover) { topoproto.KeyspaceShardString(keyspace, shard): true, } cfg.now = func() time.Time { return now } + cfg.DrainConcurrency = concurrency b := New(cfg) @@ -782,3 +795,66 @@ func testShutdown1(t *testing.T, fail failover) { t.Fatal(err) } } + +func TestParallelRangeIndex(t *testing.T) { + suite := []struct { + max int + concurrency int + calls []int + }{ + { + max: 0, + concurrency: 0, + calls: []int{}, + }, + { + max: 100, + concurrency: 0, + calls: []int{}, + }, + { + max: 9, + concurrency: 3, + calls: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + { + max: 0, + concurrency: 10, + calls: []int{0}, + }, + { + max: 9, + concurrency: 9, + calls: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + } + + for idx, tc := range suite { + name := fmt.Sprintf("%d_max%d_concurrency%d", idx, tc.max, tc.concurrency) + t.Run(name, func(t *testing.T) { + var mu sync.Mutex + var wg sync.WaitGroup + var counter atomic.Int64 + + wg.Add(tc.concurrency) + var got []int + for i := 0; i < tc.concurrency; i++ { + go func() { + defer wg.Done() + for { + idx, ok := parallelRangeIndex(&counter, tc.max) + if !ok { + break + } + + mu.Lock() + got = append(got, idx) + mu.Unlock() + } + }() + } + wg.Wait() + assert.ElementsMatch(t, got, tc.calls, "must call passed function with matching indexes") + }) + } +} diff --git a/go/vt/vtgate/buffer/flags.go b/go/vt/vtgate/buffer/flags.go index a17cc09ccc3..b45f10a6e38 100644 --- a/go/vt/vtgate/buffer/flags.go +++ b/go/vt/vtgate/buffer/flags.go @@ -162,16 +162,6 @@ func NewDefaultConfig() *Config { } } -// EnableBuffering is used in tests where we require the keyspace event watcher to be created -func EnableBuffering() { - bufferEnabled = true -} - -// DisableBuffering is the counterpart of EnableBuffering -func DisableBuffering() { - bufferEnabled = false -} - func NewConfigFromFlags() *Config { if err := verifyFlags(); err != nil { log.Fatalf("Invalid buffer configuration: %v", err) diff --git a/go/vt/vtgate/buffer/shard_buffer.go b/go/vt/vtgate/buffer/shard_buffer.go index ae33aabb399..bb38dcd2caa 100644 --- a/go/vt/vtgate/buffer/shard_buffer.go +++ b/go/vt/vtgate/buffer/shard_buffer.go @@ -21,6 +21,7 @@ import ( "fmt" "runtime/debug" "sync" + "sync/atomic" "time" "vitess.io/vitess/go/vt/discovery" @@ -561,6 +562,18 @@ func (sb *shardBuffer) stopBufferingLocked(reason stopReason, details string) { go sb.drain(q, clientEntryError) } +// parallelRangeIndex uses counter to return a unique idx value up to the +// passed max and ok will be set to false if the counter exceeds the max +func parallelRangeIndex(counter *atomic.Int64, max int) (idx int, ok bool) { + next := counter.Add(1) + if next-1 > int64(max) { + return -1, false + } + // if this is a 32-bit platform, max won't exceed the 32-bit integer limit + // so a cast from a too-large 64-bit int to a 32-bit int will never happen + return int(next) - 1, true +} + func (sb *shardBuffer) drain(q []*entry, err error) { defer sb.wg.Done() @@ -569,10 +582,32 @@ func (sb *shardBuffer) drain(q []*entry, err error) { sb.timeoutThread.stop() start := sb.timeNow() - // TODO(mberlin): Parallelize the drain by pumping the data through a channel. - for _, e := range q { - sb.unblockAndWait(e, err, true /* releaseSlot */, true /* blockingWait */) + + entryCount := len(q) + parallelism := min(sb.buf.config.DrainConcurrency, entryCount) + + var wg sync.WaitGroup + var rangeCounter atomic.Int64 + + wg.Add(parallelism) + for i := 0; i < parallelism; i++ { + go func() { + defer wg.Done() + for { + idx, ok := parallelRangeIndex(&rangeCounter, entryCount-1) + if !ok { + break + } + // Shared access to the q slice is concurrency-safe because each goroutine receives + // a unique set of slice indices from parallelRangeIndex above and the slice remains + // immutable for the lifetime of this operation. + sb.unblockAndWait(q[idx], err, true /* releaseSlot */, true /* blockingWait */) + } + }() } + + wg.Wait() + d := sb.timeNow().Sub(start) log.Infof("Draining finished for shard: %s Took: %v for: %d requests.", topoproto.KeyspaceShardString(sb.keyspace, sb.shard), d, len(q)) requestsDrained.Add(sb.statsKey, int64(len(q))) diff --git a/go/vt/vtgate/endtoend/vstream_test.go b/go/vt/vtgate/endtoend/vstream_test.go index 42dd6e3d2a3..871e6cf98c3 100644 --- a/go/vt/vtgate/endtoend/vstream_test.go +++ b/go/vt/vtgate/endtoend/vstream_test.go @@ -493,7 +493,7 @@ func TestVStreamCopyResume(t *testing.T) { // Also, to ensure that the client can resume properly, make sure that // the Fields value is present in the sqltypes.Result field and not missing. // It's not guaranteed that BOTH shards have streamed a row yet as the order - // of events in the stream is non-determinstic. So we check to be sure that + // of events in the stream is non-deterministic. So we check to be sure that // at least one shard has copied rows and thus has a full TableLastPK proto // message. eventStr := ev.String() diff --git a/go/vt/vtgate/engine/aggregations.go b/go/vt/vtgate/engine/aggregations.go index dd7a259d1b6..ea10267a7e6 100644 --- a/go/vt/vtgate/engine/aggregations.go +++ b/go/vt/vtgate/engine/aggregations.go @@ -49,15 +49,17 @@ type AggregateParams struct { // This is based on the function passed in the select expression and // not what we use to aggregate at the engine primitive level. OrigOpcode AggregateOpcode + + CollationEnv *collations.Environment } -func NewAggregateParam(opcode AggregateOpcode, col int, alias string) *AggregateParams { +func NewAggregateParam(opcode AggregateOpcode, col int, alias string, collationEnv *collations.Environment) *AggregateParams { out := &AggregateParams{ - Opcode: opcode, - Col: col, - Alias: alias, - WCol: -1, - Type: evalengine.UnknownType(), + Opcode: opcode, + Col: col, + Alias: alias, + WCol: -1, + CollationEnv: collationEnv, } if opcode.NeedsComparableValues() { out.KeyCol = col @@ -74,8 +76,8 @@ func (ap *AggregateParams) String() string { if ap.WAssigned() { keyCol = fmt.Sprintf("%s|%d", keyCol, ap.WCol) } - if sqltypes.IsText(ap.Type.Type) && ap.Type.Coll != collations.Unknown { - keyCol += " COLLATE " + collations.Local().LookupName(ap.Type.Coll) + if sqltypes.IsText(ap.Type.Type()) && ap.CollationEnv.IsSupported(ap.Type.Collation()) { + keyCol += " COLLATE " + ap.CollationEnv.LookupName(ap.Type.Collation()) } dispOrigOp := "" if ap.OrigOpcode != AggregateUnassigned && ap.OrigOpcode != ap.Opcode { @@ -89,9 +91,9 @@ func (ap *AggregateParams) String() string { func (ap *AggregateParams) typ(inputType querypb.Type) querypb.Type { if ap.OrigOpcode != AggregateUnassigned { - return ap.OrigOpcode.Type(inputType) + return ap.OrigOpcode.SQLType(inputType) } - return ap.Opcode.Type(inputType) + return ap.Opcode.SQLType(inputType) } type aggregator interface { @@ -101,9 +103,10 @@ type aggregator interface { } type aggregatorDistinct struct { - column int - last sqltypes.Value - coll collations.ID + column int + last sqltypes.Value + coll collations.ID + collationEnv *collations.Environment } func (a *aggregatorDistinct) shouldReturn(row []sqltypes.Value) (bool, error) { @@ -112,7 +115,7 @@ func (a *aggregatorDistinct) shouldReturn(row []sqltypes.Value) (bool, error) { next := row[a.column] if !last.IsNull() { if last.TinyWeightCmp(next) == 0 { - cmp, err := evalengine.NullsafeCompare(last, next, a.coll) + cmp, err := evalengine.NullsafeCompare(last, next, a.collationEnv, a.coll) if err != nil { return true, err } @@ -380,8 +383,9 @@ func newAggregation(fields []*querypb.Field, aggregates []*AggregateParams) (agg ag = &aggregatorCount{ from: aggr.Col, distinct: aggregatorDistinct{ - column: distinct, - coll: aggr.Type.Coll, + column: distinct, + coll: aggr.Type.Collation(), + collationEnv: aggr.CollationEnv, }, } @@ -398,8 +402,9 @@ func newAggregation(fields []*querypb.Field, aggregates []*AggregateParams) (agg from: aggr.Col, sum: sum, distinct: aggregatorDistinct{ - column: distinct, - coll: aggr.Type.Coll, + column: distinct, + coll: aggr.Type.Collation(), + collationEnv: aggr.CollationEnv, }, } @@ -407,7 +412,7 @@ func newAggregation(fields []*querypb.Field, aggregates []*AggregateParams) (agg ag = &aggregatorMin{ aggregatorMinMax{ from: aggr.Col, - minmax: evalengine.NewAggregationMinMax(sourceType, aggr.Type.Coll), + minmax: evalengine.NewAggregationMinMax(sourceType, aggr.CollationEnv, aggr.Type.Collation()), }, } @@ -415,7 +420,7 @@ func newAggregation(fields []*querypb.Field, aggregates []*AggregateParams) (agg ag = &aggregatorMax{ aggregatorMinMax{ from: aggr.Col, - minmax: evalengine.NewAggregationMinMax(sourceType, aggr.Type.Coll), + minmax: evalengine.NewAggregationMinMax(sourceType, aggr.CollationEnv, aggr.Type.Collation()), }, } diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go index b70f83b192d..781c5904044 100644 --- a/go/vt/vtgate/engine/cached_size.go +++ b/go/vt/vtgate/engine/cached_size.go @@ -35,7 +35,7 @@ func (cached *AggregateParams) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(96) + size += int64(112) } // field Alias string size += hack.RuntimeAllocSize(int64(len(cached.Alias))) @@ -45,6 +45,8 @@ func (cached *AggregateParams) CachedSize(alloc bool) int64 { } // field Original *vitess.io/vitess/go/vt/sqlparser.AliasedExpr size += cached.Original.CachedSize(true) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *AlterVSchema) CachedSize(alloc bool) int64 { @@ -67,10 +69,12 @@ func (cached *CheckCol) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(24) + size += int64(48) } // field WsCol *int size += hack.RuntimeAllocSize(int64(8)) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } @@ -145,7 +149,7 @@ func (cached *DML) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(128) + size += int64(144) } // field Query string size += hack.RuntimeAllocSize(int64(len(cached.Query))) @@ -173,13 +177,35 @@ func (cached *DML) CachedSize(alloc bool) int64 { size += cached.RoutingParameters.CachedSize(true) return size } +func (cached *DMLWithInput) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(64) + } + // field DML vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.DML.(cachedObject); ok { + size += cc.CachedSize(true) + } + // field Input vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.Input.(cachedObject); ok { + size += cc.CachedSize(true) + } + // field OutputCols []int + { + size += hack.RuntimeAllocSize(int64(cap(cached.OutputCols)) * int64(8)) + } + return size +} func (cached *Delete) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) } size := int64(0) if alloc { - size += int64(8) + size += int64(16) } // field DML *vitess.io/vitess/go/vt/vtgate/engine.DML size += cached.DML.CachedSize(true) @@ -199,7 +225,7 @@ func (cached *Distinct) CachedSize(alloc bool) int64 { } // field CheckCols []vitess.io/vitess/go/vt/vtgate/engine.CheckCol { - size += hack.RuntimeAllocSize(int64(cap(cached.CheckCols)) * int64(23)) + size += hack.RuntimeAllocSize(int64(cap(cached.CheckCols)) * int64(40)) for _, elem := range cached.CheckCols { size += elem.CachedSize(false) } @@ -280,7 +306,7 @@ func (cached *FkChild) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(64) + size += int64(80) } // field BVName string size += hack.RuntimeAllocSize(int64(len(cached.BVName))) @@ -288,6 +314,13 @@ func (cached *FkChild) CachedSize(alloc bool) int64 { { size += hack.RuntimeAllocSize(int64(cap(cached.Cols)) * int64(8)) } + // field NonLiteralInfo []vitess.io/vitess/go/vt/vtgate/engine.NonLiteralUpdateInfo + { + size += hack.RuntimeAllocSize(int64(cap(cached.NonLiteralInfo)) * int64(32)) + for _, elem := range cached.NonLiteralInfo { + size += elem.CachedSize(false) + } + } // field Exec vitess.io/vitess/go/vt/vtgate/engine.Primitive if cc, ok := cached.Exec.(cachedObject); ok { size += cc.CachedSize(true) @@ -339,12 +372,14 @@ func (cached *GroupByParams) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(48) + size += int64(64) } // field Expr vitess.io/vitess/go/vt/sqlparser.Expr if cc, ok := cached.Expr.(cachedObject); ok { size += cc.CachedSize(true) } + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *HashJoin) CachedSize(alloc bool) int64 { @@ -371,6 +406,8 @@ func (cached *HashJoin) CachedSize(alloc bool) int64 { if cc, ok := cached.ASTPred.(cachedObject); ok { size += cc.CachedSize(true) } + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *Insert) CachedSize(alloc bool) int64 { @@ -379,10 +416,10 @@ func (cached *Insert) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(240) + size += int64(208) } - // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace - size += cached.Keyspace.CachedSize(true) + // field InsertCommon vitess.io/vitess/go/vt/vtgate/engine.InsertCommon + size += cached.InsertCommon.CachedSize(false) // field Query string size += hack.RuntimeAllocSize(int64(len(cached.Query))) // field VindexValues [][][]vitess.io/vitess/go/vt/vtgate/evalengine.Expr @@ -404,19 +441,6 @@ func (cached *Insert) CachedSize(alloc bool) int64 { } } } - // field ColVindexes []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex - { - size += hack.RuntimeAllocSize(int64(cap(cached.ColVindexes)) * int64(8)) - for _, elem := range cached.ColVindexes { - size += elem.CachedSize(true) - } - } - // field TableName string - size += hack.RuntimeAllocSize(int64(len(cached.TableName))) - // field Generate *vitess.io/vitess/go/vt/vtgate/engine.Generate - size += cached.Generate.CachedSize(true) - // field Prefix string - size += hack.RuntimeAllocSize(int64(len(cached.Prefix))) // field Mid vitess.io/vitess/go/vt/sqlparser.Values { size += hack.RuntimeAllocSize(int64(cap(cached.Mid)) * int64(24)) @@ -431,8 +455,54 @@ func (cached *Insert) CachedSize(alloc bool) int64 { } } } - // field Suffix string - size += hack.RuntimeAllocSize(int64(len(cached.Suffix))) + return size +} +func (cached *InsertCommon) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(144) + } + // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace + size += cached.Keyspace.CachedSize(true) + // field TableName string + size += hack.RuntimeAllocSize(int64(len(cached.TableName))) + // field Generate *vitess.io/vitess/go/vt/vtgate/engine.Generate + size += cached.Generate.CachedSize(true) + // field ColVindexes []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex + { + size += hack.RuntimeAllocSize(int64(cap(cached.ColVindexes)) * int64(8)) + for _, elem := range cached.ColVindexes { + size += elem.CachedSize(true) + } + } + // field Prefix string + size += hack.RuntimeAllocSize(int64(len(cached.Prefix))) + // field Suffix vitess.io/vitess/go/vt/sqlparser.OnDup + { + size += hack.RuntimeAllocSize(int64(cap(cached.Suffix)) * int64(8)) + for _, elem := range cached.Suffix { + size += elem.CachedSize(true) + } + } + return size +} +func (cached *InsertSelect) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(176) + } + // field InsertCommon vitess.io/vitess/go/vt/vtgate/engine.InsertCommon + size += cached.InsertCommon.CachedSize(false) + // field Input vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.Input.(cachedObject); ok { + size += cc.CachedSize(true) + } // field VindexValueOffset [][]int { size += hack.RuntimeAllocSize(int64(cap(cached.VindexValueOffset)) * int64(24)) @@ -442,10 +512,6 @@ func (cached *Insert) CachedSize(alloc bool) int64 { } } } - // field Input vitess.io/vitess/go/vt/vtgate/engine.Primitive - if cc, ok := cached.Input.(cachedObject); ok { - size += cc.CachedSize(true) - } return size } @@ -581,7 +647,10 @@ func (cached *MemorySort) CachedSize(alloc bool) int64 { } // field OrderBy vitess.io/vitess/go/vt/vtgate/evalengine.Comparison { - size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(27)) + size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(48)) + for _, elem := range cached.OrderBy { + size += elem.CachedSize(false) + } } // field Input vitess.io/vitess/go/vt/vtgate/engine.Primitive if cc, ok := cached.Input.(cachedObject); ok { @@ -608,8 +677,23 @@ func (cached *MergeSort) CachedSize(alloc bool) int64 { } // field OrderBy vitess.io/vitess/go/vt/vtgate/evalengine.Comparison { - size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(27)) + size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(48)) + for _, elem := range cached.OrderBy { + size += elem.CachedSize(false) + } + } + return size +} +func (cached *NonLiteralUpdateInfo) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(32) } + // field UpdateExprBvName string + size += hack.RuntimeAllocSize(int64(len(cached.UpdateExprBvName))) return size } func (cached *OnlineDDL) CachedSize(alloc bool) int64 { @@ -662,6 +746,8 @@ func (cached *OrderedAggregate) CachedSize(alloc bool) int64 { if cc, ok := cached.Input.(cachedObject); ok { size += cc.CachedSize(true) } + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *Plan) CachedSize(alloc bool) int64 { @@ -801,7 +887,10 @@ func (cached *Route) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.FieldQuery))) // field OrderBy vitess.io/vitess/go/vt/vtgate/evalengine.Comparison { - size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(27)) + size += hack.RuntimeAllocSize(int64(cap(cached.OrderBy)) * int64(48)) + for _, elem := range cached.OrderBy { + size += elem.CachedSize(false) + } } // field RoutingParameters *vitess.io/vitess/go/vt/vtgate/engine.RoutingParameters size += cached.RoutingParameters.CachedSize(true) @@ -988,6 +1077,25 @@ func (cached *Send) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Query))) return size } +func (cached *Sequential) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(24) + } + // field Sources []vitess.io/vitess/go/vt/vtgate/engine.Primitive + { + size += hack.RuntimeAllocSize(int64(cap(cached.Sources)) * int64(16)) + for _, elem := range cached.Sources { + if cc, ok := elem.(cachedObject); ok { + size += cc.CachedSize(true) + } + } + } + return size +} func (cached *SessionPrimitive) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1200,6 +1308,23 @@ func (cached *UpdateTarget) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.Target))) return size } +func (cached *Upsert) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(24) + } + // field Upserts []vitess.io/vitess/go/vt/vtgate/engine.upsert + { + size += hack.RuntimeAllocSize(int64(cap(cached.Upserts)) * int64(32)) + for _, elem := range cached.Upserts { + size += elem.CachedSize(false) + } + } + return size +} func (cached *UserDefinedVariable) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1411,3 +1536,21 @@ func (cached *shardRoute) CachedSize(alloc bool) int64 { } return size } +func (cached *upsert) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(32) + } + // field Insert vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.Insert.(cachedObject); ok { + size += cc.CachedSize(true) + } + // field Update vitess.io/vitess/go/vt/vtgate/engine.Primitive + if cc, ok := cached.Update.(cachedObject); ok { + size += cc.CachedSize(true) + } + return size +} diff --git a/go/vt/vtgate/engine/concatenate.go b/go/vt/vtgate/engine/concatenate.go index 1e8cb655547..27b35c32aa8 100644 --- a/go/vt/vtgate/engine/concatenate.go +++ b/go/vt/vtgate/engine/concatenate.go @@ -105,7 +105,7 @@ func (c *Concatenate) TryExecute(ctx context.Context, vcursor VCursor, bindVars err = c.coerceAndVisitResults(res, fields, func(result *sqltypes.Result) error { rows = append(rows, result.Rows...) return nil - }) + }, evalengine.ParseSQLMode(vcursor.SQLMode())) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func (c *Concatenate) TryExecute(ctx context.Context, vcursor VCursor, bindVars }, nil } -func (c *Concatenate) coerceValuesTo(row sqltypes.Row, fields []*querypb.Field) error { +func (c *Concatenate) coerceValuesTo(row sqltypes.Row, fields []*querypb.Field, sqlmode evalengine.SQLMode) error { if len(row) != len(fields) { return errWrongNumberOfColumnsInSelect } @@ -126,7 +126,7 @@ func (c *Concatenate) coerceValuesTo(row sqltypes.Row, fields []*querypb.Field) continue } if fields[i].Type != value.Type() { - newValue, err := evalengine.CoerceTo(value, fields[i].Type) + newValue, err := evalengine.CoerceTo(value, fields[i].Type, sqlmode) if err != nil { return err } @@ -228,16 +228,17 @@ func (c *Concatenate) sequentialExec(ctx context.Context, vcursor VCursor, bindV // TryStreamExecute performs a streaming exec. func (c *Concatenate) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool, callback func(*sqltypes.Result) error) error { + sqlmode := evalengine.ParseSQLMode(vcursor.SQLMode()) if vcursor.Session().InTransaction() { // as we are in a transaction, we need to execute all queries inside a single connection, // which holds the single transaction we have - return c.sequentialStreamExec(ctx, vcursor, bindVars, callback) + return c.sequentialStreamExec(ctx, vcursor, bindVars, callback, sqlmode) } // not in transaction, so execute in parallel. - return c.parallelStreamExec(ctx, vcursor, bindVars, callback) + return c.parallelStreamExec(ctx, vcursor, bindVars, callback, sqlmode) } -func (c *Concatenate) parallelStreamExec(inCtx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, in func(*sqltypes.Result) error) error { +func (c *Concatenate) parallelStreamExec(inCtx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, in func(*sqltypes.Result) error, sqlmode evalengine.SQLMode) error { // Scoped context; any early exit triggers cancel() to clean up ongoing work. ctx, cancel := context.WithCancel(inCtx) defer cancel() @@ -271,7 +272,7 @@ func (c *Concatenate) parallelStreamExec(inCtx context.Context, vcursor VCursor, // Apply type coercion if needed. if needsCoercion { for _, row := range res.Rows { - if err := c.coerceValuesTo(row, fields); err != nil { + if err := c.coerceValuesTo(row, fields, sqlmode); err != nil { return err } } @@ -340,7 +341,7 @@ func (c *Concatenate) parallelStreamExec(inCtx context.Context, vcursor VCursor, return wg.Wait() } -func (c *Concatenate) sequentialStreamExec(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error) error { +func (c *Concatenate) sequentialStreamExec(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error, sqlmode evalengine.SQLMode) error { // all the below fields ensure that the fields are sent only once. results := make([][]*sqltypes.Result, len(c.Sources)) @@ -374,7 +375,7 @@ func (c *Concatenate) sequentialStreamExec(ctx context.Context, vcursor VCursor, return err } for _, res := range results { - if err = c.coerceAndVisitResults(res, fields, callback); err != nil { + if err = c.coerceAndVisitResults(res, fields, callback, sqlmode); err != nil { return err } } @@ -386,6 +387,7 @@ func (c *Concatenate) coerceAndVisitResults( res []*sqltypes.Result, fields []*querypb.Field, callback func(*sqltypes.Result) error, + sqlmode evalengine.SQLMode, ) error { for _, r := range res { if len(r.Rows) > 0 && @@ -402,7 +404,7 @@ func (c *Concatenate) coerceAndVisitResults( } if needsCoercion { for _, row := range r.Rows { - err := c.coerceValuesTo(row, fields) + err := c.coerceValuesTo(row, fields, sqlmode) if err != nil { return err } diff --git a/go/vt/vtgate/engine/dbddl.go b/go/vt/vtgate/engine/dbddl.go index be0c5b049b7..60bb4a7202b 100644 --- a/go/vt/vtgate/engine/dbddl.go +++ b/go/vt/vtgate/engine/dbddl.go @@ -58,12 +58,12 @@ type DBDDLPlugin interface { // DBDDL is just a container around custom database provisioning plugins // The default behaviour is to just return an error type DBDDL struct { + noInputs + noTxNeeded + name string create bool queryTimeout int - - noInputs - noTxNeeded } // NewDBDDL creates the engine primitive diff --git a/go/vt/vtgate/engine/ddl.go b/go/vt/vtgate/engine/ddl.go index 17aa7945537..cfdaa5866dc 100644 --- a/go/vt/vtgate/engine/ddl.go +++ b/go/vt/vtgate/engine/ddl.go @@ -32,6 +32,9 @@ var _ Primitive = (*DDL)(nil) // DDL represents a DDL statement, either normal or online DDL type DDL struct { + noTxNeeded + noInputs + Keyspace *vindexes.Keyspace SQL string DDL sqlparser.DDLStatement @@ -43,10 +46,6 @@ type DDL struct { OnlineDDLEnabled bool CreateTempTable bool - - noTxNeeded - - noInputs } func (ddl *DDL) description() PrimitiveDescription { diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index 5f0f2408993..adcc11174fd 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -130,6 +130,7 @@ func (del *Delete) description() PrimitiveDescription { "OwnedVindexQuery": del.OwnedVindexQuery, "MultiShardAutocommit": del.MultiShardAutocommit, "QueryTimeout": del.QueryTimeout, + "NoAutoCommit": del.PreventAutoCommit, } addFieldsIfNotEmpty(del.DML, other) diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index be67c7fc9e6..d8485765ca9 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -89,7 +89,7 @@ func TestDeleteEqual(t *testing.T) { }) // Failure case - expr := evalengine.NewBindVar("aa", evalengine.UnknownType()) + expr := evalengine.NewBindVar("aa", evalengine.Type{}) del.Values = []evalengine.Expr{expr} _, err = del.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) require.EqualError(t, err, "query arguments missing for aa") @@ -121,7 +121,7 @@ func TestDeleteEqualMultiCol(t *testing.T) { }) // Failure case - expr := evalengine.NewBindVar("aa", evalengine.UnknownType()) + expr := evalengine.NewBindVar("aa", evalengine.Type{}) del.Values = []evalengine.Expr{expr} _, err = del.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) require.EqualError(t, err, "query arguments missing for aa") diff --git a/go/vt/vtgate/engine/distinct.go b/go/vt/vtgate/engine/distinct.go index cd6b93a9f32..e292d516d51 100644 --- a/go/vt/vtgate/engine/distinct.go +++ b/go/vt/vtgate/engine/distinct.go @@ -19,6 +19,7 @@ package engine import ( "context" "fmt" + "sync" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -38,13 +39,16 @@ type ( Truncate int } CheckCol struct { - Col int - WsCol *int - Type evalengine.Type + Col int + WsCol *int + Type evalengine.Type + CollationEnv *collations.Environment } probeTable struct { - seenRows map[evalengine.HashCode][]sqltypes.Row - checkCols []CheckCol + seenRows map[evalengine.HashCode][]sqltypes.Row + checkCols []CheckCol + sqlmode evalengine.SQLMode + collationEnv *collations.Environment } ) @@ -118,14 +122,14 @@ func (pt *probeTable) hashCodeForRow(inputRow sqltypes.Row) (evalengine.HashCode return 0, vterrors.VT13001("index out of range in row when creating the DISTINCT hash code") } col := inputRow[checkCol.Col] - hashcode, err := evalengine.NullsafeHashcode(col, checkCol.Type.Coll, col.Type()) + hashcode, err := evalengine.NullsafeHashcode(col, checkCol.Type.Collation(), col.Type(), pt.sqlmode) if err != nil { if err != evalengine.UnsupportedCollationHashError || checkCol.WsCol == nil { return 0, err } checkCol = checkCol.SwitchToWeightString() pt.checkCols[i] = checkCol - hashcode, err = evalengine.NullsafeHashcode(inputRow[checkCol.Col], checkCol.Type.Coll, col.Type()) + hashcode, err = evalengine.NullsafeHashcode(inputRow[checkCol.Col], checkCol.Type.Collation(), col.Type(), pt.sqlmode) if err != nil { return 0, err } @@ -137,7 +141,7 @@ func (pt *probeTable) hashCodeForRow(inputRow sqltypes.Row) (evalengine.HashCode func (pt *probeTable) equal(a, b sqltypes.Row) (bool, error) { for i, checkCol := range pt.checkCols { - cmp, err := evalengine.NullsafeCompare(a[i], b[i], checkCol.Type.Coll) + cmp, err := evalengine.NullsafeCompare(a[i], b[i], pt.collationEnv, checkCol.Type.Collation()) if err != nil { _, isCollErr := err.(evalengine.UnsupportedCollationError) if !isCollErr || checkCol.WsCol == nil { @@ -145,7 +149,7 @@ func (pt *probeTable) equal(a, b sqltypes.Row) (bool, error) { } checkCol = checkCol.SwitchToWeightString() pt.checkCols[i] = checkCol - cmp, err = evalengine.NullsafeCompare(a[i], b[i], checkCol.Type.Coll) + cmp, err = evalengine.NullsafeCompare(a[i], b[i], pt.collationEnv, checkCol.Type.Collation()) if err != nil { return false, err } @@ -157,12 +161,13 @@ func (pt *probeTable) equal(a, b sqltypes.Row) (bool, error) { return true, nil } -func newProbeTable(checkCols []CheckCol) *probeTable { +func newProbeTable(checkCols []CheckCol, collationEnv *collations.Environment) *probeTable { cols := make([]CheckCol, len(checkCols)) copy(cols, checkCols) return &probeTable{ - seenRows: map[evalengine.HashCode][]sqltypes.Row{}, - checkCols: cols, + seenRows: map[evalengine.HashCode][]sqltypes.Row{}, + checkCols: cols, + collationEnv: collationEnv, } } @@ -178,7 +183,7 @@ func (d *Distinct) TryExecute(ctx context.Context, vcursor VCursor, bindVars map InsertID: input.InsertID, } - pt := newProbeTable(d.CheckCols) + pt := newProbeTable(d.CheckCols, vcursor.Environment().CollationEnv()) for _, row := range input.Rows { exists, err := pt.exists(row) @@ -197,13 +202,16 @@ func (d *Distinct) TryExecute(ctx context.Context, vcursor VCursor, bindVars map // TryStreamExecute implements the Primitive interface func (d *Distinct) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { - pt := newProbeTable(d.CheckCols) + var mu sync.Mutex + pt := newProbeTable(d.CheckCols, vcursor.Environment().CollationEnv()) err := vcursor.StreamExecutePrimitive(ctx, d.Source, bindVars, wantfields, func(input *sqltypes.Result) error { result := &sqltypes.Result{ Fields: input.Fields, InsertID: input.InsertID, } + mu.Lock() + defer mu.Unlock() for _, row := range input.Rows { exists, err := pt.exists(row) if err != nil { @@ -272,16 +280,17 @@ func (d *Distinct) description() PrimitiveDescription { // SwitchToWeightString returns a new CheckCol that works on the weight string column instead func (cc CheckCol) SwitchToWeightString() CheckCol { return CheckCol{ - Col: *cc.WsCol, - WsCol: nil, - Type: evalengine.Type{Type: sqltypes.VarBinary, Coll: collations.CollationBinaryID}, + Col: *cc.WsCol, + WsCol: nil, + Type: evalengine.NewType(sqltypes.VarBinary, collations.CollationBinaryID), + CollationEnv: cc.CollationEnv, } } func (cc CheckCol) String() string { var collation string - if sqltypes.IsText(cc.Type.Type) && cc.Type.Coll != collations.Unknown { - collation = ": " + collations.Local().LookupName(cc.Type.Coll) + if sqltypes.IsText(cc.Type.Type()) && cc.Type.Collation() != collations.Unknown { + collation = ": " + cc.CollationEnv.LookupName(cc.Type.Collation()) } var column string diff --git a/go/vt/vtgate/engine/distinct_test.go b/go/vt/vtgate/engine/distinct_test.go index 65f8e5d430c..76e46496e21 100644 --- a/go/vt/vtgate/engine/distinct_test.go +++ b/go/vt/vtgate/engine/distinct_test.go @@ -88,14 +88,10 @@ func TestDistinct(t *testing.T) { if sqltypes.IsNumber(tc.inputs.Fields[i].Type) { collID = collations.CollationBinaryID } - t := evalengine.Type{ - Type: tc.inputs.Fields[i].Type, - Coll: collID, - Nullable: false, - } checkCols = append(checkCols, CheckCol{ - Col: i, - Type: t, + Col: i, + Type: evalengine.NewTypeEx(tc.inputs.Fields[i].Type, collID, false, 0, 0), + CollationEnv: collations.MySQL8(), }) } } @@ -135,12 +131,64 @@ func TestDistinct(t *testing.T) { } } +func TestDistinctStreamAsync(t *testing.T) { + distinct := &Distinct{ + Source: &fakePrimitive{ + results: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("myid|id|num|name", "varchar|int64|int64|varchar"), + "a|1|1|a", + "a|1|1|a", + "a|1|1|a", + "a|1|1|a", + "---", + "c|1|1|a", + "a|1|1|a", + "z|1|1|a", + "a|1|1|t", + "a|1|1|a", + "a|1|1|a", + "a|1|1|a", + "---", + "c|1|1|a", + "a|1|1|a", + "---", + "c|1|1|a", + "a|1|1|a", + "a|1|1|a", + "c|1|1|a", + "a|1|1|a", + "a|1|1|a", + "---", + "c|1|1|a", + "a|1|1|a", + ), + async: true, + }, + CheckCols: []CheckCol{ + {Col: 0, Type: evalengine.NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)}, + {Col: 1, Type: evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)}, + {Col: 2, Type: evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)}, + {Col: 3, Type: evalengine.NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)}, + }, + } + + qr := &sqltypes.Result{} + err := distinct.TryStreamExecute(context.Background(), &noopVCursor{}, nil, true, func(result *sqltypes.Result) error { + qr.Rows = append(qr.Rows, result.Rows...) + return nil + }) + require.NoError(t, err) + require.NoError(t, sqltypes.RowsEqualsStr(` +[[VARCHAR("c") INT64(1) INT64(1) VARCHAR("a")] +[VARCHAR("a") INT64(1) INT64(1) VARCHAR("a")] +[VARCHAR("z") INT64(1) INT64(1) VARCHAR("a")] +[VARCHAR("a") INT64(1) INT64(1) VARCHAR("t")]]`, qr.Rows)) +} + func TestWeightStringFallBack(t *testing.T) { offsetOne := 1 checkCols := []CheckCol{{ Col: 0, WsCol: &offsetOne, - Type: evalengine.UnknownType(), }} input := r("myid|weightstring(myid)", "varchar|varbinary", @@ -165,6 +213,5 @@ func TestWeightStringFallBack(t *testing.T) { utils.MustMatch(t, []CheckCol{{ Col: 0, WsCol: &offsetOne, - Type: evalengine.UnknownType(), }}, distinct.CheckCols, "checkCols should not be updated") } diff --git a/go/vt/vtgate/engine/dml.go b/go/vt/vtgate/engine/dml.go index 51177f41e08..463008a4433 100644 --- a/go/vt/vtgate/engine/dml.go +++ b/go/vt/vtgate/engine/dml.go @@ -36,6 +36,8 @@ import ( // DML contains the common elements between Update and Delete plans type DML struct { + txNeeded + // Query specifies the query to be executed. Query string @@ -61,10 +63,10 @@ type DML struct { // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query QueryTimeout int + PreventAutoCommit bool + // RoutingParameters parameters required for query routing. *RoutingParameters - - txNeeded } // NewDML returns and empty initialized DML struct. @@ -73,7 +75,7 @@ func NewDML() *DML { } func (dml *DML) execUnsharded(ctx context.Context, primitive Primitive, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) (*sqltypes.Result, error) { - return execShard(ctx, primitive, vcursor, dml.Query, bindVars, rss[0], true /* rollbackOnError */, true /* canAutocommit */) + return execShard(ctx, primitive, vcursor, dml.Query, bindVars, rss[0], true /* rollbackOnError */, !dml.PreventAutoCommit /* canAutocommit */) } func (dml *DML) execMultiDestination(ctx context.Context, primitive Primitive, vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard, dmlSpecialFunc func(context.Context, VCursor, map[string]*querypb.BindVariable, []*srvtopo.ResolvedShard) error) (*sqltypes.Result, error) { diff --git a/go/vt/vtgate/engine/dml_with_input.go b/go/vt/vtgate/engine/dml_with_input.go new file mode 100644 index 00000000000..87d7c1d9826 --- /dev/null +++ b/go/vt/vtgate/engine/dml_with_input.go @@ -0,0 +1,124 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vterrors" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" +) + +var _ Primitive = (*DMLWithInput)(nil) + +const DmlVals = "dml_vals" + +// DMLWithInput represents the instructions to perform a DML operation based on the input result. +type DMLWithInput struct { + txNeeded + + DML Primitive + Input Primitive + + OutputCols []int +} + +func (dml *DMLWithInput) RouteType() string { + return "DMLWithInput" +} + +func (dml *DMLWithInput) GetKeyspaceName() string { + return dml.Input.GetKeyspaceName() +} + +func (dml *DMLWithInput) GetTableName() string { + return dml.Input.GetTableName() +} + +func (dml *DMLWithInput) Inputs() ([]Primitive, []map[string]any) { + return []Primitive{dml.Input, dml.DML}, nil +} + +// TryExecute performs a non-streaming exec. +func (dml *DMLWithInput) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool) (*sqltypes.Result, error) { + inputRes, err := vcursor.ExecutePrimitive(ctx, dml.Input, bindVars, false) + if err != nil { + return nil, err + } + if inputRes == nil || len(inputRes.Rows) == 0 { + return &sqltypes.Result{}, nil + } + + var bv *querypb.BindVariable + if len(dml.OutputCols) == 1 { + bv = getBVSingle(inputRes, dml.OutputCols[0]) + } else { + bv = getBVMulti(inputRes, dml.OutputCols) + } + + bindVars[DmlVals] = bv + return vcursor.ExecutePrimitive(ctx, dml.DML, bindVars, false) +} + +func getBVSingle(res *sqltypes.Result, offset int) *querypb.BindVariable { + bv := &querypb.BindVariable{Type: querypb.Type_TUPLE} + for _, row := range res.Rows { + bv.Values = append(bv.Values, sqltypes.ValueToProto(row[offset])) + } + return bv +} + +func getBVMulti(res *sqltypes.Result, offsets []int) *querypb.BindVariable { + bv := &querypb.BindVariable{Type: querypb.Type_TUPLE} + outputVals := make([]sqltypes.Value, 0, len(offsets)) + for _, row := range res.Rows { + for _, offset := range offsets { + outputVals = append(outputVals, row[offset]) + } + bv.Values = append(bv.Values, sqltypes.TupleToProto(outputVals)) + outputVals = outputVals[:0] + } + return bv +} + +// TryStreamExecute performs a streaming exec. +func (dml *DMLWithInput) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + res, err := dml.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err + } + return callback(res) +} + +// GetFields fetches the field info. +func (dml *DMLWithInput) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.VT13001("unreachable code for DMLs") +} + +func (dml *DMLWithInput) description() PrimitiveDescription { + other := map[string]any{ + "Offset": dml.OutputCols, + } + return PrimitiveDescription{ + OperatorType: "DMLWithInput", + TargetTabletType: topodatapb.TabletType_PRIMARY, + Other: other, + } +} diff --git a/go/vt/vtgate/engine/dml_with_input_test.go b/go/vt/vtgate/engine/dml_with_input_test.go new file mode 100644 index 00000000000..fb75ae70f1d --- /dev/null +++ b/go/vt/vtgate/engine/dml_with_input_test.go @@ -0,0 +1,116 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +func TestDeleteWithInputSingleOffset(t *testing.T) { + input := &fakePrimitive{results: []*sqltypes.Result{ + sqltypes.MakeTestResult(sqltypes.MakeTestFields("id", "int64"), "1", "2", "3"), + }} + + del := &DMLWithInput{ + Input: input, + DML: &Delete{ + DML: &DML{ + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + }, + Query: "dummy_delete", + }, + }, + OutputCols: []int{0}, + } + + vc := newDMLTestVCursor("-20", "20-") + _, err := del.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ` + + `ks.-20: dummy_delete {dml_vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"3"}} ` + + `ks.20-: dummy_delete {dml_vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"3"}} true false`, + }) + + vc.Rewind() + input.rewind() + err = del.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { return nil }) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ` + + `ks.-20: dummy_delete {dml_vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"3"}} ` + + `ks.20-: dummy_delete {dml_vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"3"}} true false`, + }) +} + +func TestDeleteWithInputMultiOffset(t *testing.T) { + input := &fakePrimitive{results: []*sqltypes.Result{ + sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col", "int64|varchar"), "1|a", "2|b", "3|c"), + }} + + del := &DMLWithInput{ + Input: input, + DML: &Delete{ + DML: &DML{ + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + }, + Query: "dummy_delete", + }, + }, + OutputCols: []int{1, 0}, + } + + vc := newDMLTestVCursor("-20", "20-") + _, err := del.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ` + + `ks.-20: dummy_delete {dml_vals: type:TUPLE values:{type:TUPLE value:"\x950\x01a\x89\x02\x011"} values:{type:TUPLE value:"\x950\x01b\x89\x02\x012"} values:{type:TUPLE value:"\x950\x01c\x89\x02\x013"}} ` + + `ks.20-: dummy_delete {dml_vals: type:TUPLE values:{type:TUPLE value:"\x950\x01a\x89\x02\x011"} values:{type:TUPLE value:"\x950\x01b\x89\x02\x012"} values:{type:TUPLE value:"\x950\x01c\x89\x02\x013"}} true false`, + }) + + vc.Rewind() + input.rewind() + err = del.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { return nil }) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ` + + `ks.-20: dummy_delete {dml_vals: type:TUPLE values:{type:TUPLE value:"\x950\x01a\x89\x02\x011"} values:{type:TUPLE value:"\x950\x01b\x89\x02\x012"} values:{type:TUPLE value:"\x950\x01c\x89\x02\x013"}} ` + + `ks.20-: dummy_delete {dml_vals: type:TUPLE values:{type:TUPLE value:"\x950\x01a\x89\x02\x011"} values:{type:TUPLE value:"\x950\x01b\x89\x02\x012"} values:{type:TUPLE value:"\x950\x01c\x89\x02\x013"}} true false`, + }) +} diff --git a/go/vt/vtgate/engine/fake_primitive_test.go b/go/vt/vtgate/engine/fake_primitive_test.go index dcec32f1ffd..e992c2a4623 100644 --- a/go/vt/vtgate/engine/fake_primitive_test.go +++ b/go/vt/vtgate/engine/fake_primitive_test.go @@ -23,8 +23,9 @@ import ( "strings" "testing" - "vitess.io/vitess/go/sqltypes" + "golang.org/x/sync/errgroup" + "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -41,6 +42,8 @@ type fakePrimitive struct { log []string allResultsInOneCall bool + + async bool } func (f *fakePrimitive) Inputs() ([]Primitive, []map[string]any) { @@ -86,6 +89,13 @@ func (f *fakePrimitive) TryStreamExecute(ctx context.Context, vcursor VCursor, b return f.sendErr } + if f.async { + return f.asyncCall(callback) + } + return f.syncCall(wantfields, callback) +} + +func (f *fakePrimitive) syncCall(wantfields bool, callback func(*sqltypes.Result) error) error { readMoreResults := true for readMoreResults && f.curResult < len(f.results) { readMoreResults = f.allResultsInOneCall @@ -116,9 +126,46 @@ func (f *fakePrimitive) TryStreamExecute(ctx context.Context, vcursor VCursor, b } } } - return nil } + +func (f *fakePrimitive) asyncCall(callback func(*sqltypes.Result) error) error { + var g errgroup.Group + var fields []*querypb.Field + if len(f.results) > 0 { + fields = f.results[0].Fields + } + for _, res := range f.results { + qr := res + g.Go(func() error { + if qr == nil { + return f.sendErr + } + if err := callback(&sqltypes.Result{Fields: fields}); err != nil { + return err + } + result := &sqltypes.Result{} + for i := 0; i < len(qr.Rows); i++ { + result.Rows = append(result.Rows, qr.Rows[i]) + // Send only two rows at a time. + if i%2 == 1 { + if err := callback(result); err != nil { + return err + } + result = &sqltypes.Result{} + } + } + if len(result.Rows) != 0 { + if err := callback(result); err != nil { + return err + } + } + return nil + }) + } + return g.Wait() +} + func (f *fakePrimitive) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { f.log = append(f.log, fmt.Sprintf("GetFields %v", printBindVars(bindVars))) return f.TryExecute(ctx, vcursor, bindVars, true /* wantfields */) diff --git a/go/vt/vtgate/engine/fake_vcursor_test.go b/go/vt/vtgate/engine/fake_vcursor_test.go index 6c99af33313..08a40c0d835 100644 --- a/go/vt/vtgate/engine/fake_vcursor_test.go +++ b/go/vt/vtgate/engine/fake_vcursor_test.go @@ -21,18 +21,23 @@ import ( "context" "fmt" "reflect" + "slices" "sort" "strings" "sync" "testing" "time" + "github.com/google/go-cmp/cmp" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -52,6 +57,7 @@ type noopVCursor struct { inTx bool } +// MySQLVersion implements VCursor. func (t *noopVCursor) Commit(ctx context.Context) error { return nil } @@ -125,13 +131,22 @@ func (t *noopVCursor) SetContextWithValue(key, value interface{}) func() { // ConnCollation implements VCursor func (t *noopVCursor) ConnCollation() collations.ID { - return collations.Default() + return collations.MySQL8().DefaultConnectionCharset() +} + +// CollationEnv implements VCursor +func (t *noopVCursor) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() } func (t *noopVCursor) TimeZone() *time.Location { return nil } +func (t *noopVCursor) SQLMode() string { + return config.DefaultSQLMode +} + func (t *noopVCursor) ExecutePrimitive(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { return primitive.TryExecute(ctx, t, bindVars, wantfields) } @@ -404,6 +419,8 @@ type loggingVCursor struct { ksShardMap map[string][]string shardSession []*srvtopo.ResolvedShard + + parser *sqlparser.Parser } func (f *loggingVCursor) HasCreatedTempTable() { @@ -790,13 +807,21 @@ func (f *loggingVCursor) nextResult() (*sqltypes.Result, error) { } func (f *loggingVCursor) CanUseSetVar() bool { - useSetVar := sqlparser.IsMySQL80AndAbove() && !f.disableSetVar + useSetVar := f.SQLParser().IsMySQL80AndAbove() && !f.disableSetVar if useSetVar { f.log = append(f.log, "SET_VAR can be used") } return useSetVar } +// SQLParser implements VCursor +func (t *loggingVCursor) SQLParser() *sqlparser.Parser { + if t.parser == nil { + return sqlparser.NewTestParser() + } + return t.parser +} + func (t *noopVCursor) VExplainLogging() {} func (t *noopVCursor) DisableLogging() {} func (t *noopVCursor) GetVExplainLogs() []ExecuteEntry { @@ -806,18 +831,39 @@ func (t *noopVCursor) GetLogs() ([]ExecuteEntry, error) { return nil, nil } -func expectResult(t *testing.T, msg string, result, want *sqltypes.Result) { +func expectResult(t *testing.T, result, want *sqltypes.Result) { t.Helper() fieldsResult := fmt.Sprintf("%v", result.Fields) fieldsWant := fmt.Sprintf("%v", want.Fields) if fieldsResult != fieldsWant { - t.Errorf("%s (mismatch in Fields):\n%s\nwant:\n%s", msg, fieldsResult, fieldsWant) + t.Errorf("mismatch in Fields\n%s\nwant:\n%s", fieldsResult, fieldsWant) } rowsResult := fmt.Sprintf("%v", result.Rows) rowsWant := fmt.Sprintf("%v", want.Rows) if rowsResult != rowsWant { - t.Errorf("%s (mismatch in Rows):\n%s\nwant:\n%s", msg, rowsResult, rowsWant) + t.Errorf("mismatch in Rows:\n%s\nwant:\n%s", rowsResult, rowsWant) + } +} + +func expectResultAnyOrder(t *testing.T, result, want *sqltypes.Result) { + t.Helper() + f := func(a, b sqltypes.Row) int { + for i := range a { + l := a[i].RawStr() + r := b[i].RawStr() + x := strings.Compare(l, r) + if x == 0 { + continue + } + return x + } + return 0 + } + slices.SortFunc(result.Rows, f) + slices.SortFunc(want.Rows, f) + if diff := cmp.Diff(want, result); diff != "" { + t.Errorf("result: %+v, want %+v\ndiff: %s", result, want, diff) } } diff --git a/go/vt/vtgate/engine/filter.go b/go/vt/vtgate/engine/filter.go index c0a54f2b6ac..dc7af1acfeb 100644 --- a/go/vt/vtgate/engine/filter.go +++ b/go/vt/vtgate/engine/filter.go @@ -18,6 +18,7 @@ package engine import ( "context" + "sync" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" @@ -29,13 +30,13 @@ var _ Primitive = (*Filter)(nil) // Filter is a primitive that performs the FILTER operation. type Filter struct { + noTxNeeded + Predicate evalengine.Expr ASTPredicate sqlparser.Expr Input Primitive Truncate int - - noTxNeeded } // RouteType returns a description of the query routing type used by the primitive @@ -78,9 +79,14 @@ func (f *Filter) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[s // TryStreamExecute satisfies the Primitive interface. func (f *Filter) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + var mu sync.Mutex + env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) filter := func(results *sqltypes.Result) error { var rows [][]sqltypes.Value + + mu.Lock() + defer mu.Unlock() for _, row := range results.Rows { env.Row = row evalResult, err := env.Evaluate(f.Predicate) diff --git a/go/vt/vtgate/engine/filter_test.go b/go/vt/vtgate/engine/filter_test.go index 9a8335e4d7e..fc888019dfe 100644 --- a/go/vt/vtgate/engine/filter_test.go +++ b/go/vt/vtgate/engine/filter_test.go @@ -23,12 +23,15 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) func TestFilterPass(t *testing.T) { + collationEnv := collations.MySQL8() utf8mb4Bin := collationEnv.LookupByName("utf8mb4_bin") predicate := &sqlparser.ComparisonExpr{ Operator: sqlparser.GreaterThanOp, @@ -70,6 +73,7 @@ func TestFilterPass(t *testing.T) { pred, err := evalengine.Translate(predicate, &evalengine.Config{ Collation: utf8mb4Bin, ResolveColumn: evalengine.FieldResolver(tc.res.Fields).Column, + Environment: vtenv.NewTestEnv(), }) require.NoError(t, err) @@ -83,3 +87,65 @@ func TestFilterPass(t *testing.T) { }) } } + +func TestFilterStreaming(t *testing.T) { + collationEnv := collations.MySQL8() + utf8mb4Bin := collationEnv.LookupByName("utf8mb4_bin") + predicate := &sqlparser.ComparisonExpr{ + Operator: sqlparser.GreaterThanOp, + Left: sqlparser.NewColName("left"), + Right: sqlparser.NewColName("right"), + } + + tcases := []struct { + name string + res []*sqltypes.Result + expRes string + }{{ + name: "int32", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "int32|int32"), "0|1", "---", "1|0", "2|3"), + expRes: `[[INT32(1) INT32(0)]]`, + }, { + name: "uint16", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "uint16|uint16"), "0|1", "1|0", "---", "2|3"), + expRes: `[[UINT16(1) UINT16(0)]]`, + }, { + name: "uint64_int64", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "uint64|int64"), "0|1", "---", "1|0", "2|3"), + expRes: `[[UINT64(1) INT64(0)]]`, + }, { + name: "int32_uint32", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "int32|uint32"), "0|1", "---", "1|0", "---", "2|3"), + expRes: `[[INT32(1) UINT32(0)]]`, + }, { + name: "uint16_int8", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "uint16|int8"), "0|1", "1|0", "2|3", "---"), + expRes: `[[UINT16(1) INT8(0)]]`, + }, { + name: "uint64_int32", + res: sqltypes.MakeTestStreamingResults(sqltypes.MakeTestFields("left|right", "uint64|int32"), "0|1", "1|0", "2|3", "---", "0|1", "1|3", "5|3"), + expRes: `[[UINT64(1) INT32(0)] [UINT64(5) INT32(3)]]`, + }} + for _, tc := range tcases { + t.Run(tc.name, func(t *testing.T) { + pred, err := evalengine.Translate(predicate, &evalengine.Config{ + Collation: utf8mb4Bin, + ResolveColumn: evalengine.FieldResolver(tc.res[0].Fields).Column, + Environment: vtenv.NewTestEnv(), + }) + require.NoError(t, err) + + filter := &Filter{ + Predicate: pred, + Input: &fakePrimitive{results: tc.res, async: true}, + } + qr := &sqltypes.Result{} + err = filter.TryStreamExecute(context.Background(), &noopVCursor{}, nil, false, func(result *sqltypes.Result) error { + qr.Rows = append(qr.Rows, result.Rows...) + return nil + }) + require.NoError(t, err) + require.NoError(t, sqltypes.RowsEqualsStr(tc.expRes, qr.Rows)) + }) + } +} diff --git a/go/vt/vtgate/engine/fk_cascade.go b/go/vt/vtgate/engine/fk_cascade.go index d0bddbea8f9..35122ac9563 100644 --- a/go/vt/vtgate/engine/fk_cascade.go +++ b/go/vt/vtgate/engine/fk_cascade.go @@ -19,6 +19,7 @@ package engine import ( "context" "fmt" + "maps" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" @@ -29,22 +30,37 @@ import ( // FkChild contains the Child Primitive to be executed collecting the values from the Selection Primitive using the column indexes. // BVName is used to pass the value as bind variable to the Child Primitive. type FkChild struct { + // BVName is the bind variable name for the tuple bind variable used in the primitive. BVName string - Cols []int // indexes - Exec Primitive + // Cols are the indexes of the column that need to be selected from the SELECT query to create the tuple bind variable. + Cols []int + // NonLiteralInfo stores the information that is needed to run an update query with non-literal values. + NonLiteralInfo []NonLiteralUpdateInfo + Exec Primitive +} + +// NonLiteralUpdateInfo stores the information required to process non-literal update queries. +// It stores 4 information- +// 1. CompExprCol- The index of the comparison expression in the select query to know if the row value is actually being changed or not. +// 2. UpdateExprCol- The index of the updated expression in the select query. +// 3. UpdateExprBvName- The bind variable name to store the updated expression into. +type NonLiteralUpdateInfo struct { + CompExprCol int + UpdateExprCol int + UpdateExprBvName string } // FkCascade is a primitive that implements foreign key cascading using Selection as values required to execute the FkChild Primitives. // On success, it executes the Parent Primitive. type FkCascade struct { + txNeeded + // Selection is the Primitive that is used to find the rows that are going to be modified in the child tables. Selection Primitive // Children is a list of child foreign key Primitives that are executed using rows from the Selection Primitive. Children []*FkChild // Parent is the Primitive that is executed after the children are modified. Parent Primitive - - txNeeded } // RouteType implements the Primitive interface. @@ -82,81 +98,110 @@ func (fkc *FkCascade) TryExecute(ctx context.Context, vcursor VCursor, bindVars } for _, child := range fkc.Children { - // We create a bindVariable for each Child - // that stores the tuple of columns involved in the fk constraint. - bv := &querypb.BindVariable{ - Type: querypb.Type_TUPLE, - } - for _, row := range selectionRes.Rows { - var tupleValues []sqltypes.Value - for _, colIdx := range child.Cols { - tupleValues = append(tupleValues, row[colIdx]) - } - bv.Values = append(bv.Values, sqltypes.TupleToProto(tupleValues)) + // Having non-empty UpdateExprBvNames is an indication that we have an update query with non-literal expressions in it. + // We need to run this query differently because we need to run an update for each row we get back from the SELECT. + if len(child.NonLiteralInfo) > 0 { + err = fkc.executeNonLiteralExprFkChild(ctx, vcursor, bindVars, wantfields, selectionRes, child) + } else { + err = fkc.executeLiteralExprFkChild(ctx, vcursor, bindVars, wantfields, selectionRes, child, false) } - // Execute the child primitive, and bail out incase of failure. - // Since this Primitive is always executed in a transaction, the changes should - // be rolled back incase of an error. - bindVars[child.BVName] = bv - _, err = vcursor.ExecutePrimitive(ctx, child.Exec, bindVars, wantfields) if err != nil { return nil, err } - delete(bindVars, child.BVName) } // All the children are modified successfully, we can now execute the Parent Primitive. return vcursor.ExecutePrimitive(ctx, fkc.Parent, bindVars, wantfields) } -// TryStreamExecute implements the Primitive interface. -func (fkc *FkCascade) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { - // We create a bindVariable for each Child - // that stores the tuple of columns involved in the fk constraint. - var bindVariables []*querypb.BindVariable - for range fkc.Children { - bindVariables = append(bindVariables, &querypb.BindVariable{ - Type: querypb.Type_TUPLE, - }) +func (fkc *FkCascade) executeLiteralExprFkChild(ctx context.Context, vcursor VCursor, in map[string]*querypb.BindVariable, wantfields bool, selectionRes *sqltypes.Result, child *FkChild, isStreaming bool) error { + bindVars := maps.Clone(in) + // We create a bindVariable that stores the tuple of columns involved in the fk constraint. + bv := &querypb.BindVariable{ + Type: querypb.Type_TUPLE, } + for _, row := range selectionRes.Rows { + var tupleValues []sqltypes.Value - // Execute the Selection primitive to find the rows that are going to modified. - // This will be used to find the rows that need modification on the children. - err := vcursor.StreamExecutePrimitive(ctx, fkc.Selection, bindVars, wantfields, func(result *sqltypes.Result) error { - if len(result.Rows) == 0 { - return nil - } - for idx, child := range fkc.Children { - for _, row := range result.Rows { - var tupleValues []sqltypes.Value - for _, colIdx := range child.Cols { - tupleValues = append(tupleValues, row[colIdx]) - } - bindVariables[idx].Values = append(bindVariables[idx].Values, sqltypes.TupleToProto(tupleValues)) - } + for _, colIdx := range child.Cols { + tupleValues = append(tupleValues, row[colIdx]) } - return nil - }) - if err != nil { - return err + bv.Values = append(bv.Values, sqltypes.TupleToProto(tupleValues)) } - // Execute the child primitive, and bail out incase of failure. // Since this Primitive is always executed in a transaction, the changes should // be rolled back incase of an error. - for idx, child := range fkc.Children { - bindVars[child.BVName] = bindVariables[idx] - err = vcursor.StreamExecutePrimitive(ctx, child.Exec, bindVars, wantfields, func(result *sqltypes.Result) error { - return nil - }) + bindVars[child.BVName] = bv + var err error + if isStreaming { + err = vcursor.StreamExecutePrimitive(ctx, child.Exec, bindVars, wantfields, func(result *sqltypes.Result) error { return nil }) + } else { + _, err = vcursor.ExecutePrimitive(ctx, child.Exec, bindVars, wantfields) + } + if err != nil { + return err + } + return nil +} + +func (fkc *FkCascade) executeNonLiteralExprFkChild(ctx context.Context, vcursor VCursor, in map[string]*querypb.BindVariable, wantfields bool, selectionRes *sqltypes.Result, child *FkChild) error { + // For each row in the SELECT we need to run the child primitive. + for _, row := range selectionRes.Rows { + bindVars := maps.Clone(in) + // First we check if any of the columns is being updated at all. + skipRow := true + for _, info := range child.NonLiteralInfo { + // We use a null-safe comparison, so the value is guaranteed to be not null. + // We check if the column has updated or not. + isUnchanged, err := row[info.CompExprCol].ToBool() + if err != nil { + return err + } + if !isUnchanged { + // If any column has changed, then we can't skip this row. + // We need to execute the child primitive. + skipRow = false + break + } + } + // If none of the columns have changed, then there is no update to cascade, we can move on. + if skipRow { + continue + } + // We create a bindVariable that stores the tuple of columns involved in the fk constraint. + bv := &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + } + // Create a tuple from the Row. + var tupleValues []sqltypes.Value + for _, colIdx := range child.Cols { + tupleValues = append(tupleValues, row[colIdx]) + } + bv.Values = append(bv.Values, sqltypes.TupleToProto(tupleValues)) + // Execute the child primitive, and bail out incase of failure. + // Since this Primitive is always executed in a transaction, the changes should + // be rolled back in case of an error. + bindVars[child.BVName] = bv + + // Next, we need to copy the updated expressions value into the bind variables map. + for _, info := range child.NonLiteralInfo { + bindVars[info.UpdateExprBvName] = sqltypes.ValueBindVariable(row[info.UpdateExprCol]) + } + _, err := vcursor.ExecutePrimitive(ctx, child.Exec, bindVars, wantfields) if err != nil { return err } - delete(bindVars, child.BVName) } + return nil +} - // All the children are modified successfully, we can now execute the Parent Primitive. - return vcursor.StreamExecutePrimitive(ctx, fkc.Parent, bindVars, wantfields, callback) +// TryStreamExecute implements the Primitive interface. +func (fkc *FkCascade) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + res, err := fkc.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err + } + return callback(res) } // Inputs implements the Primitive interface. @@ -168,11 +213,15 @@ func (fkc *FkCascade) Inputs() ([]Primitive, []map[string]any) { inputName: "Selection", }) for idx, child := range fkc.Children { - inputsMap = append(inputsMap, map[string]any{ + childInfoMap := map[string]any{ inputName: fmt.Sprintf("CascadeChild-%d", idx+1), "BvName": child.BVName, "Cols": child.Cols, - }) + } + if len(child.NonLiteralInfo) > 0 { + childInfoMap["NonLiteralUpdateInfo"] = child.NonLiteralInfo + } + inputsMap = append(inputsMap, childInfoMap) inputs = append(inputs, child.Exec) } inputs = append(inputs, fkc.Parent) diff --git a/go/vt/vtgate/engine/fk_cascade_test.go b/go/vt/vtgate/engine/fk_cascade_test.go index ddd381003b1..942fe44a709 100644 --- a/go/vt/vtgate/engine/fk_cascade_test.go +++ b/go/vt/vtgate/engine/fk_cascade_test.go @@ -80,7 +80,7 @@ func TestDeleteCascade(t *testing.T) { require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `StreamExecuteMulti select cola, colb from parent where foo = 48 ks.0: {} `, + `ExecuteMultiShard ks.0: select cola, colb from parent where foo = 48 {} false false`, `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: delete from child where (ca, cb) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x011\x950\x01a"} values:{type:TUPLE value:"\x89\x02\x012\x950\x01b"}} true true`, `ResolveDestinations ks [] Destinations:DestinationAllShards()`, @@ -141,7 +141,7 @@ func TestUpdateCascade(t *testing.T) { require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `StreamExecuteMulti select cola, colb from parent where foo = 48 ks.0: {} `, + `ExecuteMultiShard ks.0: select cola, colb from parent where foo = 48 {} false false`, `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: update child set ca = :vtg1 where (ca, cb) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x011\x950\x01a"} values:{type:TUPLE value:"\x89\x02\x012\x950\x01b"}} true true`, `ResolveDestinations ks [] Destinations:DestinationAllShards()`, @@ -149,6 +149,82 @@ func TestUpdateCascade(t *testing.T) { }) } +// TestNonLiteralUpdateCascade tests that FkCascade executes the child and parent primitives for a non-literal update cascade. +func TestNonLiteralUpdateCascade(t *testing.T) { + fakeRes := sqltypes.MakeTestResult(sqltypes.MakeTestFields("cola|cola <=> colb + 2|colb + 2", "int64|int64|int64"), "1|1|3", "2|0|5", "3|0|7") + + inputP := &Route{ + Query: "select cola, cola <=> colb + 2, colb + 2, from parent where foo = 48", + RoutingParameters: &RoutingParameters{ + Opcode: Unsharded, + Keyspace: &vindexes.Keyspace{Name: "ks"}, + }, + } + childP := &Update{ + DML: &DML{ + Query: "update child set ca = :fkc_upd where (ca) in ::__vals", + RoutingParameters: &RoutingParameters{ + Opcode: Unsharded, + Keyspace: &vindexes.Keyspace{Name: "ks"}, + }, + }, + } + parentP := &Update{ + DML: &DML{ + Query: "update parent set cola = colb + 2 where foo = 48", + RoutingParameters: &RoutingParameters{ + Opcode: Unsharded, + Keyspace: &vindexes.Keyspace{Name: "ks"}, + }, + }, + } + fkc := &FkCascade{ + Selection: inputP, + Children: []*FkChild{{ + BVName: "__vals", + Cols: []int{0}, + NonLiteralInfo: []NonLiteralUpdateInfo{ + { + UpdateExprBvName: "fkc_upd", + UpdateExprCol: 2, + CompExprCol: 1, + }, + }, + Exec: childP, + }}, + Parent: parentP, + } + + vc := newDMLTestVCursor("0") + vc.results = []*sqltypes.Result{fakeRes} + _, err := fkc.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, true) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: select cola, cola <=> colb + 2, colb + 2, from parent where foo = 48 {} false false`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update child set ca = :fkc_upd where (ca) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x012"} fkc_upd: type:INT64 value:"5"} true true`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update child set ca = :fkc_upd where (ca) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x013"} fkc_upd: type:INT64 value:"7"} true true`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update parent set cola = colb + 2 where foo = 48 {} true true`, + }) + + vc.Rewind() + err = fkc.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, true, func(result *sqltypes.Result) error { return nil }) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: select cola, cola <=> colb + 2, colb + 2, from parent where foo = 48 {} false false`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update child set ca = :fkc_upd where (ca) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x012"} fkc_upd: type:INT64 value:"5"} true true`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update child set ca = :fkc_upd where (ca) in ::__vals {__vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x013"} fkc_upd: type:INT64 value:"7"} true true`, + `ResolveDestinations ks [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard ks.0: update parent set cola = colb + 2 where foo = 48 {} true true`, + }) +} + // TestNeedsTransactionInExecPrepared tests that if we have a foreign key cascade inside an ExecStmt plan, then we do mark the plan to require a transaction. func TestNeedsTransactionInExecPrepared(t *testing.T) { // Even if FkCascade is wrapped in ExecStmt, the plan should be marked such that it requires a transaction. diff --git a/go/vt/vtgate/engine/fk_verify.go b/go/vt/vtgate/engine/fk_verify.go index 350aeec59e0..7184e5d8381 100644 --- a/go/vt/vtgate/engine/fk_verify.go +++ b/go/vt/vtgate/engine/fk_verify.go @@ -35,10 +35,10 @@ type Verify struct { // FkVerify is a primitive that verifies that the foreign key constraints in parent tables are satisfied. // It does this by executing a select distinct query on the parent table with the values that are being inserted/updated. type FkVerify struct { + txNeeded + Verify []*Verify Exec Primitive - - txNeeded } // constants for verification type. @@ -83,18 +83,11 @@ func (f *FkVerify) TryExecute(ctx context.Context, vcursor VCursor, bindVars map // TryStreamExecute implements the Primitive interface func (f *FkVerify) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { - for _, v := range f.Verify { - err := vcursor.StreamExecutePrimitive(ctx, v.Exec, bindVars, wantfields, func(qr *sqltypes.Result) error { - if len(qr.Rows) > 0 { - return getError(v.Typ) - } - return nil - }) - if err != nil { - return err - } + res, err := f.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err } - return vcursor.StreamExecutePrimitive(ctx, f.Exec, bindVars, wantfields, callback) + return callback(res) } // Inputs implements the Primitive interface diff --git a/go/vt/vtgate/engine/fk_verify_test.go b/go/vt/vtgate/engine/fk_verify_test.go index 5635a32bc2c..5c9ff83c2ec 100644 --- a/go/vt/vtgate/engine/fk_verify_test.go +++ b/go/vt/vtgate/engine/fk_verify_test.go @@ -74,7 +74,7 @@ func TestFKVerifyUpdate(t *testing.T) { require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `StreamExecuteMulti select 1 from child c left join parent p on p.cola = 1 and p.colb = 'a' where p.cola is null and p.colb is null ks.0: {} `, + `ExecuteMultiShard ks.0: select 1 from child c left join parent p on p.cola = 1 and p.colb = 'a' where p.cola is null and p.colb is null {} false false`, `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: update child set cola = 1, colb = 'a' where foo = 48 {} true true`, }) @@ -97,7 +97,7 @@ func TestFKVerifyUpdate(t *testing.T) { require.ErrorContains(t, err, "Cannot add or update a child row: a foreign key constraint fails") vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `StreamExecuteMulti select 1 from child c left join parent p on p.cola = 1 and p.colb = 'a' where p.cola is null and p.colb is null ks.0: {} `, + `ExecuteMultiShard ks.0: select 1 from child c left join parent p on p.cola = 1 and p.colb = 'a' where p.cola is null and p.colb is null {} false false`, }) }) @@ -119,7 +119,7 @@ func TestFKVerifyUpdate(t *testing.T) { require.ErrorContains(t, err, "Cannot delete or update a parent row: a foreign key constraint fails") vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `StreamExecuteMulti select 1 from grandchild g join child c on g.cola = c.cola and g.colb = c.colb where c.foo = 48 ks.0: {} `, + `ExecuteMultiShard ks.0: select 1 from grandchild g join child c on g.cola = c.cola and g.colb = c.colb where c.foo = 48 {} false false`, }) }) } diff --git a/go/vt/vtgate/engine/hash_join.go b/go/vt/vtgate/engine/hash_join.go index a38fc21bf97..f7c9d87e1fb 100644 --- a/go/vt/vtgate/engine/hash_join.go +++ b/go/vt/vtgate/engine/hash_join.go @@ -20,48 +20,72 @@ import ( "context" "fmt" "strings" + "sync" + "sync/atomic" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vthash" ) var _ Primitive = (*HashJoin)(nil) -// HashJoin specifies the parameters for a join primitive -// Hash joins work by fetch all the input from the LHS, and building a hash map, known as the probe table, for this input. -// The key to the map is the hashcode of the value for column that we are joining by. -// Then the RHS is fetched, and we can check if the rows from the RHS matches any from the LHS. -// When they match by hash code, we double-check that we are not working with a false positive by comparing the values. -type HashJoin struct { - Opcode JoinOpcode - - // Left and Right are the LHS and RHS primitives - // of the Join. They can be any primitive. - Left, Right Primitive `json:",omitempty"` - - // Cols defines which columns from the left - // or right results should be used to build the - // return result. For results coming from the - // left query, the index values go as -1, -2, etc. - // For the right query, they're 1, 2, etc. - // If Cols is {-1, -2, 1, 2}, it means that - // the returned result will be {Left0, Left1, Right0, Right1}. - Cols []int `json:",omitempty"` - - // The keys correspond to the column offset in the inputs where - // the join columns can be found - LHSKey, RHSKey int - - // The join condition. Used for plan descriptions - ASTPred sqlparser.Expr - - // collation and type are used to hash the incoming values correctly - Collation collations.ID - ComparisonType querypb.Type -} +type ( + // HashJoin specifies the parameters for a join primitive + // Hash joins work by fetch all the input from the LHS, and building a hash map, known as the probe table, for this input. + // The key to the map is the hashcode of the value for column that we are joining by. + // Then the RHS is fetched, and we can check if the rows from the RHS matches any from the LHS. + // When they match by hash code, we double-check that we are not working with a false positive by comparing the values. + HashJoin struct { + Opcode JoinOpcode + + // Left and Right are the LHS and RHS primitives + // of the Join. They can be any primitive. + Left, Right Primitive `json:",omitempty"` + + // Cols defines which columns from the left + // or right results should be used to build the + // return result. For results coming from the + // left query, the index values go as -1, -2, etc. + // For the right query, they're 1, 2, etc. + // If Cols is {-1, -2, 1, 2}, it means that + // the returned result will be {Left0, Left1, Right0, Right1}. + Cols []int `json:",omitempty"` + + // The keys correspond to the column offset in the inputs where + // the join columns can be found + LHSKey, RHSKey int + + // The join condition. Used for plan descriptions + ASTPred sqlparser.Expr + + // collation and type are used to hash the incoming values correctly + Collation collations.ID + ComparisonType querypb.Type + + CollationEnv *collations.Environment + } + + hashJoinProbeTable struct { + innerMap map[vthash.Hash]*probeTableEntry + + coll collations.ID + typ querypb.Type + lhsKey, rhsKey int + cols []int + hasher vthash.Hasher + sqlmode evalengine.SQLMode + } + + probeTableEntry struct { + row sqltypes.Row + next *probeTableEntry + seen bool + } +) // TryExecute implements the Primitive interface func (hj *HashJoin) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { @@ -70,10 +94,13 @@ func (hj *HashJoin) TryExecute(ctx context.Context, vcursor VCursor, bindVars ma return nil, err } + pt := newHashJoinProbeTable(hj.Collation, hj.ComparisonType, hj.LHSKey, hj.RHSKey, hj.Cols) // build the probe table from the LHS result - probeTable, err := hj.buildProbeTable(lresult) - if err != nil { - return nil, err + for _, row := range lresult.Rows { + err := pt.addLeftRow(row) + if err != nil { + return nil, err + } } rresult, err := vcursor.ExecutePrimitive(ctx, hj.Right, bindVars, wantfields) @@ -86,68 +113,37 @@ func (hj *HashJoin) TryExecute(ctx context.Context, vcursor VCursor, bindVars ma } for _, currentRHSRow := range rresult.Rows { - joinVal := currentRHSRow[hj.RHSKey] - if joinVal.IsNull() { - continue - } - hashcode, err := evalengine.NullsafeHashcode(joinVal, hj.Collation, hj.ComparisonType) + matches, err := pt.get(currentRHSRow) if err != nil { return nil, err } - lftRows := probeTable[hashcode] - for _, currentLHSRow := range lftRows { - lhsVal := currentLHSRow[hj.LHSKey] - // hash codes can give false positives, so we need to check with a real comparison as well - cmp, err := evalengine.NullsafeCompare(joinVal, lhsVal, hj.Collation) - if err != nil { - return nil, err - } + result.Rows = append(result.Rows, matches...) + } - if cmp == 0 { - // we have a match! - result.Rows = append(result.Rows, joinRows(currentLHSRow, currentRHSRow, hj.Cols)) - } - } + if hj.Opcode == LeftJoin { + result.Rows = append(result.Rows, pt.notFetched()...) } return result, nil } -func (hj *HashJoin) buildProbeTable(lresult *sqltypes.Result) (map[evalengine.HashCode][]sqltypes.Row, error) { - probeTable := map[evalengine.HashCode][]sqltypes.Row{} - for _, current := range lresult.Rows { - joinVal := current[hj.LHSKey] - if joinVal.IsNull() { - continue - } - hashcode, err := evalengine.NullsafeHashcode(joinVal, hj.Collation, hj.ComparisonType) - if err != nil { - return nil, err - } - probeTable[hashcode] = append(probeTable[hashcode], current) - } - return probeTable, nil -} - // TryStreamExecute implements the Primitive interface func (hj *HashJoin) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { // build the probe table from the LHS result - probeTable := map[evalengine.HashCode][]sqltypes.Row{} + pt := newHashJoinProbeTable(hj.Collation, hj.ComparisonType, hj.LHSKey, hj.RHSKey, hj.Cols) var lfields []*querypb.Field + var mu sync.Mutex err := vcursor.StreamExecutePrimitive(ctx, hj.Left, bindVars, wantfields, func(result *sqltypes.Result) error { + mu.Lock() + defer mu.Unlock() if len(lfields) == 0 && len(result.Fields) != 0 { lfields = result.Fields } for _, current := range result.Rows { - joinVal := current[hj.LHSKey] - if joinVal.IsNull() { - continue - } - hashcode, err := evalengine.NullsafeHashcode(joinVal, hj.Collation, hj.ComparisonType) + err := pt.addLeftRow(current) if err != nil { return err } - probeTable[hashcode] = append(probeTable[hashcode], current) } return nil }) @@ -155,43 +151,50 @@ func (hj *HashJoin) TryStreamExecute(ctx context.Context, vcursor VCursor, bindV return err } - return vcursor.StreamExecutePrimitive(ctx, hj.Right, bindVars, wantfields, func(result *sqltypes.Result) error { + var sendFields atomic.Bool + sendFields.Store(wantfields) + + err = vcursor.StreamExecutePrimitive(ctx, hj.Right, bindVars, sendFields.Load(), func(result *sqltypes.Result) error { + mu.Lock() + defer mu.Unlock() // compare the results coming from the RHS with the probe-table res := &sqltypes.Result{} - if len(result.Fields) != 0 { - res = &sqltypes.Result{ - Fields: joinFields(lfields, result.Fields, hj.Cols), - } + if len(result.Fields) != 0 && sendFields.CompareAndSwap(true, false) { + res.Fields = joinFields(lfields, result.Fields, hj.Cols) } for _, currentRHSRow := range result.Rows { - joinVal := currentRHSRow[hj.RHSKey] - if joinVal.IsNull() { - continue - } - hashcode, err := evalengine.NullsafeHashcode(joinVal, hj.Collation, hj.ComparisonType) + results, err := pt.get(currentRHSRow) if err != nil { return err } - lftRows := probeTable[hashcode] - for _, currentLHSRow := range lftRows { - lhsVal := currentLHSRow[hj.LHSKey] - // hash codes can give false positives, so we need to check with a real comparison as well - cmp, err := evalengine.NullsafeCompare(joinVal, lhsVal, hj.Collation) - if err != nil { - return err - } - - if cmp == 0 { - // we have a match! - res.Rows = append(res.Rows, joinRows(currentLHSRow, currentRHSRow, hj.Cols)) - } - } + res.Rows = append(res.Rows, results...) } if len(res.Rows) != 0 || len(res.Fields) != 0 { return callback(res) } return nil }) + if err != nil { + return err + } + + if hj.Opcode == LeftJoin { + res := &sqltypes.Result{} + if sendFields.CompareAndSwap(true, false) { + // If we still have not sent the fields, we need to fetch + // the fields from the RHS to be able to build the result fields + rres, err := hj.Right.GetFields(ctx, vcursor, bindVars) + if err != nil { + return err + } + res.Fields = joinFields(lfields, rres.Fields, hj.Cols) + } + // this will only be called when all the concurrent access to the pt has + // ceased, so we don't need to lock it here + res.Rows = pt.notFetched() + return callback(res) + } + return nil } // RouteType implements the Primitive interface @@ -248,7 +251,7 @@ func (hj *HashJoin) description() PrimitiveDescription { } coll := hj.Collation if coll != collations.Unknown { - other["Collation"] = collations.Local().LookupName(coll) + other["Collation"] = hj.CollationEnv.LookupName(coll) } return PrimitiveDescription{ OperatorType: "Join", @@ -256,3 +259,69 @@ func (hj *HashJoin) description() PrimitiveDescription { Other: other, } } + +func newHashJoinProbeTable(coll collations.ID, typ querypb.Type, lhsKey, rhsKey int, cols []int) *hashJoinProbeTable { + return &hashJoinProbeTable{ + innerMap: map[vthash.Hash]*probeTableEntry{}, + coll: coll, + typ: typ, + lhsKey: lhsKey, + rhsKey: rhsKey, + cols: cols, + hasher: vthash.New(), + } +} + +func (pt *hashJoinProbeTable) addLeftRow(r sqltypes.Row) error { + hash, err := pt.hash(r[pt.lhsKey]) + if err != nil { + return err + } + pt.innerMap[hash] = &probeTableEntry{ + row: r, + next: pt.innerMap[hash], + } + + return nil +} + +func (pt *hashJoinProbeTable) hash(val sqltypes.Value) (vthash.Hash, error) { + err := evalengine.NullsafeHashcode128(&pt.hasher, val, pt.coll, pt.typ, pt.sqlmode) + if err != nil { + return vthash.Hash{}, err + } + + res := pt.hasher.Sum128() + pt.hasher.Reset() + return res, nil +} + +func (pt *hashJoinProbeTable) get(rrow sqltypes.Row) (result []sqltypes.Row, err error) { + val := rrow[pt.rhsKey] + if val.IsNull() { + return + } + + hash, err := pt.hash(val) + if err != nil { + return nil, err + } + + for e := pt.innerMap[hash]; e != nil; e = e.next { + e.seen = true + result = append(result, joinRows(e.row, rrow, pt.cols)) + } + + return +} + +func (pt *hashJoinProbeTable) notFetched() (rows []sqltypes.Row) { + for _, e := range pt.innerMap { + for ; e != nil; e = e.next { + if !e.seen { + rows = append(rows, joinRows(e.row, nil, pt.cols)) + } + } + } + return +} diff --git a/go/vt/vtgate/engine/hash_join_test.go b/go/vt/vtgate/engine/hash_join_test.go index 8add0b78fa2..d3271c643be 100644 --- a/go/vt/vtgate/engine/hash_join_test.go +++ b/go/vt/vtgate/engine/hash_join_test.go @@ -22,126 +22,153 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) -func TestHashJoinExecuteSameType(t *testing.T) { - leftPrim := &fakePrimitive{ - results: []*sqltypes.Result{ - sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col1|col2|col3", - "int64|varchar|varchar", +func TestHashJoinVariations(t *testing.T) { + // This test tries the different variations of hash-joins: + // comparing values of same type and different types, and both left and right outer joins + lhs := func() Primitive { + return &fakePrimitive{ + results: []*sqltypes.Result{ + sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "col1|col2", + "int64|varchar", + ), + "1|1", + "2|2", + "3|b", + "null|b", ), - "1|a|aa", - "2|b|bb", - "3|c|cc", - ), - }, + }, + } } - rightPrim := &fakePrimitive{ - results: []*sqltypes.Result{ - sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col4|col5|col6", - "int64|varchar|varchar", + rhs := func() Primitive { + return &fakePrimitive{ + results: []*sqltypes.Result{ + sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "col4|col5", + "int64|varchar", + ), + "1|1", + "3|2", + "5|null", + "4|b", ), - "1|d|dd", - "3|e|ee", - "4|f|ff", - "3|g|gg", - ), - }, + }, + } } - // Normal join - jn := &HashJoin{ - Opcode: InnerJoin, - Left: leftPrim, - Right: rightPrim, - Cols: []int{-1, -2, 1, 2}, - LHSKey: 0, - RHSKey: 0, - } - r, err := jn.TryExecute(context.Background(), &noopVCursor{}, map[string]*querypb.BindVariable{}, true) - require.NoError(t, err) - leftPrim.ExpectLog(t, []string{ - `Execute true`, - }) - rightPrim.ExpectLog(t, []string{ - `Execute true`, - }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col1|col2|col4|col5", - "int64|varchar|int64|varchar", - ), - "1|a|1|d", - "3|c|3|e", - "3|c|3|g", - )) -} + rows := func(r ...string) []string { return r } -func TestHashJoinExecuteDifferentType(t *testing.T) { - leftPrim := &fakePrimitive{ - results: []*sqltypes.Result{ - sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col1|col2|col3", - "int64|varchar|varchar", - ), - "1|a|aa", - "2|b|bb", - "3|c|cc", - "5|c|cc", - ), - }, - } - rightPrim := &fakePrimitive{ - results: []*sqltypes.Result{ - sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col4|col5|col6", - "varchar|varchar|varchar", - ), - "1.00|d|dd", - "3|e|ee", - "2.89|z|zz", - "4|f|ff", - "3|g|gg", - " 5.0toto|g|gg", - "w|ww|www", - ), - }, + tests := []struct { + name string + typ JoinOpcode + lhs, rhs int + expected []string + reverse bool + }{{ + name: "inner join, same type", + typ: InnerJoin, + lhs: 0, + rhs: 0, + expected: rows("1|1|1|1", "3|b|3|2"), + }, { + name: "inner join, coercion", + typ: InnerJoin, + lhs: 0, + rhs: 1, + expected: rows("1|1|1|1", "2|2|3|2"), + }, { + name: "left join, same type", + typ: LeftJoin, + lhs: 0, + rhs: 0, + expected: rows("1|1|1|1", "3|b|3|2", "2|2|null|null", "null|b|null|null"), + }, { + name: "left join, coercion", + typ: LeftJoin, + lhs: 0, + rhs: 1, + expected: rows("1|1|1|1", "2|2|3|2", "3|b|null|null", "null|b|null|null"), + }, { + name: "right join, same type", + typ: LeftJoin, + lhs: 0, + rhs: 0, + expected: rows("1|1|1|1", "3|2|3|b", "4|b|null|null", "5|null|null|null"), + reverse: true, + }, { + name: "right join, coercion", + typ: LeftJoin, + lhs: 0, + rhs: 1, + reverse: true, + expected: rows("1|1|1|1", "3|2|null|null", "4|b|null|null", "5|null|null|null"), + }} + + for _, tc := range tests { + + var fields []*querypb.Field + var first, last func() Primitive + if tc.reverse { + first, last = rhs, lhs + fields = sqltypes.MakeTestFields( + "col4|col5|col1|col2", + "int64|varchar|int64|varchar", + ) + } else { + first, last = lhs, rhs + fields = sqltypes.MakeTestFields( + "col1|col2|col4|col5", + "int64|varchar|int64|varchar", + ) + } + + expected := sqltypes.MakeTestResult(fields, tc.expected...) + + typ, err := evalengine.CoerceTypes(typeForOffset(tc.lhs), typeForOffset(tc.rhs), collations.MySQL8()) + require.NoError(t, err) + + jn := &HashJoin{ + Opcode: tc.typ, + Cols: []int{-1, -2, 1, 2}, + LHSKey: tc.lhs, + RHSKey: tc.rhs, + Collation: typ.Collation(), + ComparisonType: typ.Type(), + CollationEnv: collations.MySQL8(), + } + + t.Run(tc.name, func(t *testing.T) { + jn.Left = first() + jn.Right = last() + r, err := jn.TryExecute(context.Background(), &noopVCursor{}, map[string]*querypb.BindVariable{}, true) + require.NoError(t, err) + expectResultAnyOrder(t, r, expected) + }) + t.Run("Streaming "+tc.name, func(t *testing.T) { + jn.Left = first() + jn.Right = last() + r, err := wrapStreamExecute(jn, &noopVCursor{}, map[string]*querypb.BindVariable{}, true) + require.NoError(t, err) + expectResultAnyOrder(t, r, expected) + }) } +} - // Normal join - jn := &HashJoin{ - Opcode: InnerJoin, - Left: leftPrim, - Right: rightPrim, - Cols: []int{-1, -2, 1, 2}, - LHSKey: 0, - RHSKey: 0, - ComparisonType: querypb.Type_FLOAT64, +func typeForOffset(i int) evalengine.Type { + switch i { + case 0: + return evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID) + case 1: + return evalengine.NewType(sqltypes.VarChar, collations.MySQL8().DefaultConnectionCharset()) + default: + panic(i) } - r, err := jn.TryExecute(context.Background(), &noopVCursor{}, map[string]*querypb.BindVariable{}, true) - require.NoError(t, err) - leftPrim.ExpectLog(t, []string{ - `Execute true`, - }) - rightPrim.ExpectLog(t, []string{ - `Execute true`, - }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "col1|col2|col4|col5", - "int64|varchar|varchar|varchar", - ), - "1|a|1.00|d", - "3|c|3|e", - "3|c|3|g", - "5|c| 5.0toto|g", - )) } diff --git a/go/vt/vtgate/engine/insert.go b/go/vt/vtgate/engine/insert.go index fc65fbfbff9..332ccc92098 100644 --- a/go/vt/vtgate/engine/insert.go +++ b/go/vt/vtgate/engine/insert.go @@ -18,14 +18,10 @@ package engine import ( "context" - "encoding/json" "fmt" "strconv" "strings" - "sync" - "time" - "vitess.io/vitess/go/slice" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" @@ -40,93 +36,40 @@ import ( var _ Primitive = (*Insert)(nil) -type ( - // Insert represents the instructions to perform an insert operation. - Insert struct { - // Opcode is the execution opcode. - Opcode InsertOpcode +// Insert represents the instructions to perform an insert operation. +type Insert struct { + noInputs + InsertCommon - // Ignore is for INSERT IGNORE and INSERT...ON DUPLICATE KEY constructs - // for sharded cases. - Ignore bool + // Query specifies the query to be executed. + // For InsertSharded plans, this value is unused, + // and Prefix, Mid and Suffix are used instead. + Query string - // Keyspace specifies the keyspace to send the query to. - Keyspace *vindexes.Keyspace + // VindexValues specifies values for all the vindex columns. + // This is a three-dimensional data structure: + // Insert.Values[i] represents the values to be inserted for the i'th colvindex (i < len(Insert.Table.ColumnVindexes)) + // Insert.Values[i].Values[j] represents values for the j'th column of the given colVindex (j < len(colVindex[i].Columns) + // Insert.Values[i].Values[j].Values[k] represents the value pulled from row k for that column: (k < len(ins.rows)) + VindexValues [][][]evalengine.Expr - // Query specifies the query to be executed. - // For InsertSharded plans, this value is unused, - // and Prefix, Mid and Suffix are used instead. - Query string - - // VindexValues specifies values for all the vindex columns. - // This is a three-dimensional data structure: - // Insert.Values[i] represents the values to be inserted for the i'th colvindex (i < len(Insert.Table.ColumnVindexes)) - // Insert.Values[i].Values[j] represents values for the j'th column of the given colVindex (j < len(colVindex[i].Columns) - // Insert.Values[i].Values[j].Values[k] represents the value pulled from row k for that column: (k < len(ins.rows)) - VindexValues [][][]evalengine.Expr - - // ColVindexes are the vindexes that will use the VindexValues - ColVindexes []*vindexes.ColumnVindex - - // TableName is the name of the table on which row will be inserted. - TableName string - - // Generate is only set for inserts where a sequence must be generated. - Generate *Generate - - // Prefix, Mid and Suffix are for sharded insert plans. - Prefix string - Mid sqlparser.Values - Suffix string - - // Option to override the standard behavior and allow a multi-shard insert - // to use single round trip autocommit. - // - // This is a clear violation of the SQL semantics since it means the statement - // is not atomic in the presence of PK conflicts on one shard and not another. - // However some application use cases would prefer that the statement partially - // succeed in order to get the performance benefits of autocommit. - MultiShardAutocommit bool - - // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query - QueryTimeout int - - // VindexValueOffset stores the offset for each column in the ColumnVindex - // that will appear in the result set of the select query. - VindexValueOffset [][]int - - // Input is a select query plan to retrieve results for inserting data. - Input Primitive `json:",omitempty"` - - // ForceNonStreaming is true when the insert table and select table are same. - // This will avoid locking by the select table. - ForceNonStreaming bool - - // Insert needs tx handling - txNeeded - } - - ksID = []byte -) - -func (ins *Insert) Inputs() ([]Primitive, []map[string]any) { - if ins.Input == nil { - return nil, nil - } - return []Primitive{ins.Input}, nil + // Mid is the row values for the sharded insert plans. + Mid sqlparser.Values } -// NewQueryInsert creates an Insert with a query string. -func NewQueryInsert(opcode InsertOpcode, keyspace *vindexes.Keyspace, query string) *Insert { +// newQueryInsert creates an Insert with a query string. +func newQueryInsert(opcode InsertOpcode, keyspace *vindexes.Keyspace, query string) *Insert { return &Insert{ - Opcode: opcode, - Keyspace: keyspace, - Query: query, + InsertCommon: InsertCommon{ + Opcode: opcode, + Keyspace: keyspace, + }, + Query: query, } } -// NewInsert creates a new Insert. -func NewInsert( +// newInsert creates a new Insert. +func newInsert( opcode InsertOpcode, ignore bool, keyspace *vindexes.Keyspace, @@ -134,16 +77,18 @@ func NewInsert( table *vindexes.Table, prefix string, mid sqlparser.Values, - suffix string, + suffix sqlparser.OnDup, ) *Insert { ins := &Insert{ - Opcode: opcode, - Ignore: ignore, - Keyspace: keyspace, + InsertCommon: InsertCommon{ + Opcode: opcode, + Keyspace: keyspace, + Ignore: ignore, + Prefix: prefix, + Suffix: suffix, + }, VindexValues: vindexValues, - Prefix: prefix, Mid: mid, - Suffix: suffix, } if table != nil { ins.TableName = table.Name.String() @@ -157,208 +102,59 @@ func NewInsert( return ins } -// Generate represents the instruction to generate -// a value from a sequence. -type Generate struct { - Keyspace *vindexes.Keyspace - Query string - // Values are the supplied values for the column, which - // will be stored as a list within the expression. New - // values will be generated based on how many were not - // supplied (NULL). - Values evalengine.Expr - // Insert using Select, offset for auto increment column - Offset int -} - -// InsertOpcode is a number representing the opcode -// for the Insert primitive. -type InsertOpcode int - -const ( - // InsertUnsharded is for routing an insert statement - // to an unsharded keyspace. - InsertUnsharded = InsertOpcode(iota) - // InsertSharded is for routing an insert statement - // to individual shards. Requires: A list of Values, one - // for each ColVindex. If the table has an Autoinc column, - // A Generate subplan must be created. - InsertSharded - // InsertSelect is for routing an insert statement - // based on rows returned from the select statement. - InsertSelect -) - -var insName = map[InsertOpcode]string{ - InsertUnsharded: "InsertUnsharded", - InsertSharded: "InsertSharded", - InsertSelect: "InsertSelect", -} - -// String returns the opcode -func (code InsertOpcode) String() string { - return strings.ReplaceAll(insName[code], "Insert", "") -} - -// MarshalJSON serializes the InsertOpcode as a JSON string. -// It's used for testing and diagnostics. -func (code InsertOpcode) MarshalJSON() ([]byte, error) { - return json.Marshal(insName[code]) -} - // RouteType returns a description of the query routing type used by the primitive func (ins *Insert) RouteType() string { return insName[ins.Opcode] } -// GetKeyspaceName specifies the Keyspace that this primitive routes to. -func (ins *Insert) GetKeyspaceName() string { - return ins.Keyspace.Name -} - -// GetTableName specifies the table that this primitive routes to. -func (ins *Insert) GetTableName() string { - return ins.TableName -} - // TryExecute performs a non-streaming exec. -func (ins *Insert) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { +func (ins *Insert) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool) (*sqltypes.Result, error) { ctx, cancelFunc := addQueryTimeout(ctx, vcursor, ins.QueryTimeout) defer cancelFunc() switch ins.Opcode { case InsertUnsharded: - return ins.execInsertUnsharded(ctx, vcursor, bindVars) + return ins.insertIntoUnshardedTable(ctx, vcursor, bindVars) case InsertSharded: - return ins.execInsertSharded(ctx, vcursor, bindVars) - case InsertSelect: - return ins.execInsertFromSelect(ctx, vcursor, bindVars) + return ins.insertIntoShardedTable(ctx, vcursor, bindVars) default: - // Unreachable. - return nil, fmt.Errorf("unsupported query route: %v", ins) + return nil, vterrors.VT13001("unexpected query route: %v", ins.Opcode) } } // TryStreamExecute performs a streaming exec. func (ins *Insert) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { - if ins.Input == nil || ins.ForceNonStreaming { - res, err := ins.TryExecute(ctx, vcursor, bindVars, wantfields) - if err != nil { - return err - } - return callback(res) - } - if ins.QueryTimeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(ins.QueryTimeout)*time.Millisecond) - defer cancel() - } - - unsharded := ins.Opcode == InsertUnsharded - var mu sync.Mutex - output := &sqltypes.Result{} - - err := vcursor.StreamExecutePrimitiveStandalone(ctx, ins.Input, bindVars, false, func(result *sqltypes.Result) error { - if len(result.Rows) == 0 { - return nil - } - - // should process only one chunk at a time. - // as parallel chunk insert will try to use the same transaction in the vttablet - // this will cause transaction in use error. - mu.Lock() - defer mu.Unlock() - - var insertID int64 - var qr *sqltypes.Result - var err error - if unsharded { - insertID, qr, err = ins.insertIntoUnshardedTable(ctx, vcursor, bindVars, result) - } else { - insertID, qr, err = ins.insertIntoShardedTable(ctx, vcursor, bindVars, result) - } - if err != nil { - return err - } - - output.RowsAffected += qr.RowsAffected - // InsertID needs to be updated to the least insertID value in sqltypes.Result - if output.InsertID == 0 || output.InsertID > uint64(insertID) { - output.InsertID = uint64(insertID) - } - return nil - }) + res, err := ins.TryExecute(ctx, vcursor, bindVars, wantfields) if err != nil { return err } - return callback(output) + return callback(res) } -func (ins *Insert) insertIntoShardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, result *sqltypes.Result) (int64, *sqltypes.Result, error) { - insertID, err := ins.processGenerateFromRows(ctx, vcursor, result.Rows) +func (ins *Insert) insertIntoUnshardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + insertID, err := ins.processGenerateFromValues(ctx, vcursor, ins, bindVars) if err != nil { - return 0, nil, err - } - - rss, queries, err := ins.getInsertSelectQueries(ctx, vcursor, bindVars, result.Rows) - if err != nil { - return 0, nil, err - } - - qr, err := ins.executeInsertQueries(ctx, vcursor, rss, queries, insertID) - if err != nil { - return 0, nil, err - } - return insertID, qr, nil -} - -// GetFields fetches the field info. -func (ins *Insert) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unreachable code for %q", ins.Query) -} - -func (ins *Insert) execInsertUnsharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - query := ins.Query - if ins.Input != nil { - result, err := vcursor.ExecutePrimitive(ctx, ins.Input, bindVars, false) - if err != nil { - return nil, err - } - if len(result.Rows) == 0 { - return &sqltypes.Result{}, nil - } - query = ins.getInsertQueryForUnsharded(result, bindVars) + return nil, err } - _, qr, err := ins.executeUnshardedTableQuery(ctx, vcursor, bindVars, query) - return qr, err -} - -func (ins *Insert) getInsertQueryForUnsharded(result *sqltypes.Result, bindVars map[string]*querypb.BindVariable) string { - var mids sqlparser.Values - for r, inputRow := range result.Rows { - row := sqlparser.ValTuple{} - for c, value := range inputRow { - bvName := insertVarOffset(r, c) - bindVars[bvName] = sqltypes.ValueBindVariable(value) - row = append(row, sqlparser.NewArgument(bvName)) - } - mids = append(mids, row) - } - return ins.Prefix + sqlparser.String(mids) + ins.Suffix + return ins.executeUnshardedTableQuery(ctx, vcursor, ins, bindVars, ins.Query, uint64(insertID)) } -func (ins *Insert) execInsertSharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - insertID, err := ins.processGenerateFromValues(ctx, vcursor, bindVars) +func (ins *Insert) insertIntoShardedTable( + ctx context.Context, + vcursor VCursor, + bindVars map[string]*querypb.BindVariable, +) (*sqltypes.Result, error) { + insertID, err := ins.processGenerateFromValues(ctx, vcursor, ins, bindVars) if err != nil { return nil, err } - rss, queries, err := ins.getInsertShardedRoute(ctx, vcursor, bindVars) + rss, queries, err := ins.getInsertShardedQueries(ctx, vcursor, bindVars) if err != nil { return nil, err } - return ins.executeInsertQueries(ctx, vcursor, rss, queries, insertID) + return ins.executeInsertQueries(ctx, vcursor, rss, queries, uint64(insertID)) } func (ins *Insert) executeInsertQueries( @@ -366,7 +162,7 @@ func (ins *Insert) executeInsertQueries( vcursor VCursor, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, - insertID int64, + insertID uint64, ) (*sqltypes.Result, error) { autocommit := (len(rss) == 1 || ins.MultiShardAutocommit) && vcursor.AutocommitApproval() err := allowOnlyPrimary(rss...) @@ -379,347 +175,51 @@ func (ins *Insert) executeInsertQueries( } if insertID != 0 { - result.InsertID = uint64(insertID) + result.InsertID = insertID } return result, nil } -func (ins *Insert) getInsertSelectQueries( - ctx context.Context, - vcursor VCursor, - bindVars map[string]*querypb.BindVariable, - rows []sqltypes.Row, -) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { - colVindexes := ins.ColVindexes - if len(colVindexes) != len(ins.VindexValueOffset) { - return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex value offsets and vindex info do not match") - } - - // Here we go over the incoming rows and extract values for the vindexes we need to update - shardingCols := make([][]sqltypes.Row, len(colVindexes)) - for _, inputRow := range rows { - for colIdx := range colVindexes { - offsets := ins.VindexValueOffset[colIdx] - row := make(sqltypes.Row, 0, len(offsets)) - for _, offset := range offsets { - if offset == -1 { // value not provided from select query - row = append(row, sqltypes.NULL) - continue - } - row = append(row, inputRow[offset]) - } - shardingCols[colIdx] = append(shardingCols[colIdx], row) - } - } - - keyspaceIDs, err := ins.processPrimary(ctx, vcursor, shardingCols[0], colVindexes[0]) - if err != nil { - return nil, nil, err - } - - for vIdx := 1; vIdx < len(colVindexes); vIdx++ { - colVindex := colVindexes[vIdx] - var err error - if colVindex.Owned { - err = ins.processOwned(ctx, vcursor, shardingCols[vIdx], colVindex, keyspaceIDs) - } else { - err = ins.processUnowned(ctx, vcursor, shardingCols[vIdx], colVindex, keyspaceIDs) - } - if err != nil { - return nil, nil, err - } - } - - var indexes []*querypb.Value - var destinations []key.Destination - for i, ksid := range keyspaceIDs { - if ksid != nil { - indexes = append(indexes, &querypb.Value{ - Value: strconv.AppendInt(nil, int64(i), 10), - }) - destinations = append(destinations, key.DestinationKeyspaceID(ksid)) - } - } - if len(destinations) == 0 { - // In this case, all we have is nil KeyspaceIds, we don't do - // anything at all. - return nil, nil, nil - } - - rss, indexesPerRss, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, indexes, destinations) - if err != nil { - return nil, nil, err - } - - queries := make([]*querypb.BoundQuery, len(rss)) - for i := range rss { - bvs := sqltypes.CopyBindVariables(bindVars) // we don't want to create one huge bindvars for all values - var mids sqlparser.Values - for _, indexValue := range indexesPerRss[i] { - index, _ := strconv.Atoi(string(indexValue.Value)) - if keyspaceIDs[index] != nil { - row := sqlparser.ValTuple{} - for colOffset, value := range rows[index] { - bvName := insertVarOffset(index, colOffset) - bvs[bvName] = sqltypes.ValueBindVariable(value) - row = append(row, sqlparser.NewArgument(bvName)) - } - mids = append(mids, row) - } - } - rewritten := ins.Prefix + sqlparser.String(mids) + ins.Suffix - queries[i] = &querypb.BoundQuery{ - Sql: rewritten, - BindVariables: bvs, - } - } - - return rss, queries, nil -} - -func (ins *Insert) execInsertFromSelect(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - // run the SELECT query - if ins.Input == nil { - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "something went wrong planning INSERT SELECT") - } - - result, err := vcursor.ExecutePrimitive(ctx, ins.Input, bindVars, false) - if err != nil { - return nil, err - } - if len(result.Rows) == 0 { - return &sqltypes.Result{}, nil - } - - _, qr, err := ins.insertIntoShardedTable(ctx, vcursor, bindVars, result) - return qr, err -} - -// shouldGenerate determines if a sequence value should be generated for a given value -func shouldGenerate(v sqltypes.Value) bool { - if v.IsNull() { - return true - } - - // Unless the NO_AUTO_VALUE_ON_ZERO sql mode is active in mysql, it also - // treats 0 as a value that should generate a new sequence. - n, err := v.ToCastUint64() - if err == nil && n == 0 { - return true - } - - return false -} - -// processGenerateFromValues generates new values using a sequence if necessary. -// If no value was generated, it returns 0. Values are generated only -// for cases where none are supplied. -func (ins *Insert) processGenerateFromValues( - ctx context.Context, - vcursor VCursor, - bindVars map[string]*querypb.BindVariable, -) (insertID int64, err error) { - if ins.Generate == nil { - return 0, nil - } - - // Scan input values to compute the number of values to generate, and - // keep track of where they should be filled. - env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) - resolved, err := env.Evaluate(ins.Generate.Values) - if err != nil { - return 0, err - } - count := int64(0) - values := resolved.TupleValues() - for _, val := range values { - if shouldGenerate(val) { - count++ - } - } - - // If generation is needed, generate the requested number of values (as one call). - if count != 0 { - rss, _, err := vcursor.ResolveDestinations(ctx, ins.Generate.Keyspace.Name, nil, []key.Destination{key.DestinationAnyShard{}}) - if err != nil { - return 0, err - } - if len(rss) != 1 { - return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "auto sequence generation can happen through single shard only, it is getting routed to %d shards", len(rss)) - } - bindVars := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(count)} - qr, err := vcursor.ExecuteStandalone(ctx, ins, ins.Generate.Query, bindVars, rss[0]) - if err != nil { - return 0, err - } - // If no rows are returned, it's an internal error, and the code - // must panic, which will be caught and reported. - insertID, err = qr.Rows[0][0].ToCastInt64() - if err != nil { - return 0, err - } - } - - // Fill the holes where no value was supplied. - cur := insertID - for i, v := range values { - if shouldGenerate(v) { - bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.Int64BindVariable(cur) - cur++ - } else { - bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.ValueBindVariable(v) - } - } - return insertID, nil -} - -// processGenerateFromRows generates new values using a sequence if necessary. -// If no value was generated, it returns 0. Values are generated only -// for cases where none are supplied. -func (ins *Insert) processGenerateFromRows( - ctx context.Context, - vcursor VCursor, - rows []sqltypes.Row, -) (insertID int64, err error) { - if ins.Generate == nil { - return 0, nil - } - var count int64 - offset := ins.Generate.Offset - genColPresent := offset < len(rows[0]) - if genColPresent { - for _, val := range rows { - if val[offset].IsNull() { - count++ - } - } - } else { - count = int64(len(rows)) - } - - if count == 0 { - return 0, nil - } - - // If generation is needed, generate the requested number of values (as one call). - rss, _, err := vcursor.ResolveDestinations(ctx, ins.Generate.Keyspace.Name, nil, []key.Destination{key.DestinationAnyShard{}}) - if err != nil { - return 0, err - } - if len(rss) != 1 { - return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "auto sequence generation can happen through single shard only, it is getting routed to %d shards", len(rss)) - } - bindVars := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(count)} - qr, err := vcursor.ExecuteStandalone(ctx, ins, ins.Generate.Query, bindVars, rss[0]) - if err != nil { - return 0, err - } - // If no rows are returned, it's an internal error, and the code - // must panic, which will be caught and reported. - insertID, err = qr.Rows[0][0].ToCastInt64() - if err != nil { - return 0, err - } - - used := insertID - for idx, val := range rows { - if genColPresent { - if val[offset].IsNull() { - val[offset] = sqltypes.NewInt64(used) - used++ - } - } else { - rows[idx] = append(val, sqltypes.NewInt64(used)) - used++ - } - } - - return insertID, nil -} - -// getInsertShardedRoute performs all the vindex related work +// getInsertShardedQueries performs all the vindex related work // and returns a map of shard to queries. // Using the primary vindex, it computes the target keyspace ids. // For owned vindexes, it creates entries. // For unowned vindexes with no input values, it reverse maps. // For unowned vindexes with values, it validates. // If it's an IGNORE or ON DUPLICATE key insert, it drops unroutable rows. -func (ins *Insert) getInsertShardedRoute( +func (ins *Insert) getInsertShardedQueries( ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, ) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { + // vindexRowsValues builds the values of all vindex columns. // the 3-d structure indexes are colVindex, row, col. Note that // ins.Values indexes are colVindex, col, row. So, the conversion // involves a transpose. // The reason we need to transpose is that all the Vindex APIs // require inputs in that format. - vindexRowsValues := make([][]sqltypes.Row, len(ins.VindexValues)) - rowCount := 0 - env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) - colVindexes := ins.ColVindexes - for vIdx, vColValues := range ins.VindexValues { - if len(vColValues) != len(colVindexes[vIdx].Columns) { - return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] supplied vindex column values don't match vschema: %v %v", vColValues, colVindexes[vIdx].Columns) - } - for colIdx, colValues := range vColValues { - rowsResolvedValues := make(sqltypes.Row, 0, len(colValues)) - for _, colValue := range colValues { - result, err := env.Evaluate(colValue) - if err != nil { - return nil, nil, err - } - rowsResolvedValues = append(rowsResolvedValues, result.Value(vcursor.ConnCollation())) - } - // This is the first iteration: allocate for transpose. - if colIdx == 0 { - if len(rowsResolvedValues) == 0 { - return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] rowcount is zero for inserts: %v", rowsResolvedValues) - } - if rowCount == 0 { - rowCount = len(rowsResolvedValues) - } - if rowCount != len(rowsResolvedValues) { - return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] uneven row values for inserts: %d %d", rowCount, len(rowsResolvedValues)) - } - vindexRowsValues[vIdx] = make([]sqltypes.Row, rowCount) - } - // Perform the transpose. - for rowNum, colVal := range rowsResolvedValues { - vindexRowsValues[vIdx][rowNum] = append(vindexRowsValues[vIdx][rowNum], colVal) - } - } + vindexRowsValues, err := ins.buildVindexRowsValues(ctx, vcursor, bindVars) + if err != nil { + return nil, nil, err } // The output from the following 'process' functions is a list of // keyspace ids. For regular inserts, a failure to find a route // results in an error. For 'ignore' type inserts, the keyspace // id is returned as nil, which is used later to drop the corresponding rows. - if len(vindexRowsValues) == 0 || len(colVindexes) == 0 { + if len(vindexRowsValues) == 0 || len(ins.ColVindexes) == 0 { return nil, nil, vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.RequiresPrimaryKey, vterrors.PrimaryVindexNotSet, ins.TableName) } - keyspaceIDs, err := ins.processPrimary(ctx, vcursor, vindexRowsValues[0], colVindexes[0]) + + keyspaceIDs, err := ins.processVindexes(ctx, vcursor, vindexRowsValues) if err != nil { return nil, nil, err } - for vIdx := 1; vIdx < len(colVindexes); vIdx++ { - colVindex := colVindexes[vIdx] - var err error - if colVindex.Owned { - err = ins.processOwned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) - } else { - err = ins.processUnowned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) - } - if err != nil { - return nil, nil, err - } - } - // Build 3-d bindvars. Skip rows with nil keyspace ids in case // we're executing an insert ignore. - for vIdx, colVindex := range colVindexes { + for vIdx, colVindex := range ins.ColVindexes { for rowNum, rowColumnKeys := range vindexRowsValues[vIdx] { if keyspaceIDs[rowNum] == nil { // InsertIgnore: skip the row. @@ -764,25 +264,30 @@ func (ins *Insert) getInsertShardedRoute( for _, indexValue := range indexesPerRss[i] { index, _ := strconv.ParseInt(string(indexValue.Value), 0, 64) if keyspaceIDs[index] != nil { + walkFunc := func(node sqlparser.SQLNode) (kontinue bool, err error) { + if arg, ok := node.(*sqlparser.Argument); ok { + bv, exists := bindVars[arg.Name] + if !exists { + return false, vterrors.VT03026(arg.Name) + } + shardBindVars[arg.Name] = bv + } + return true, nil + } mids = append(mids, sqlparser.String(ins.Mid[index])) for _, expr := range ins.Mid[index] { - err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - if arg, ok := node.(*sqlparser.Argument); ok { - bv, exists := bindVars[arg.Name] - if !exists { - return false, vterrors.VT03026(arg.Name) - } - shardBindVars[arg.Name] = bv - } - return true, nil - }, expr, nil) + err = sqlparser.Walk(walkFunc, expr, nil) if err != nil { return nil, nil, err } } + err = sqlparser.Walk(walkFunc, ins.Suffix, nil) + if err != nil { + return nil, nil, err + } } } - rewritten := ins.Prefix + strings.Join(mids, ",") + ins.Suffix + rewritten := ins.Prefix + strings.Join(mids, ",") + sqlparser.String(ins.Suffix) queries[i] = &querypb.BoundQuery{ Sql: rewritten, BindVariables: shardBindVars, @@ -792,173 +297,50 @@ func (ins *Insert) getInsertShardedRoute( return rss, queries, nil } -// processPrimary maps the primary vindex values to the keyspace ids. -func (ins *Insert) processPrimary(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex) ([]ksID, error) { - destinations, err := vindexes.Map(ctx, colVindex.Vindex, vcursor, vindexColumnsKeys) - if err != nil { - return nil, err - } - - keyspaceIDs := make([]ksID, len(destinations)) - for i, destination := range destinations { - switch d := destination.(type) { - case key.DestinationKeyspaceID: - // This is a single keyspace id, we're good. - keyspaceIDs[i] = d - case key.DestinationNone: - // No valid keyspace id, we may return an error. - if !ins.Ignore { - return nil, fmt.Errorf("could not map %v to a keyspace id", vindexColumnsKeys[i]) - } - default: - return nil, fmt.Errorf("could not map %v to a unique keyspace id: %v", vindexColumnsKeys[i], destination) - } - } - - return keyspaceIDs, nil -} - -// processOwned creates vindex entries for the values of an owned column. -func (ins *Insert) processOwned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { - if !ins.Ignore { - return colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, vindexColumnsKeys, ksids, false /* ignoreMode */) - } - - // InsertIgnore - var createIndexes []int - var createKeys []sqltypes.Row - var createKsids []ksID - - for rowNum, rowColumnKeys := range vindexColumnsKeys { - if ksids[rowNum] == nil { - continue - } - createIndexes = append(createIndexes, rowNum) - createKeys = append(createKeys, rowColumnKeys) - createKsids = append(createKsids, ksids[rowNum]) - } - if createKeys == nil { - return nil - } - - err := colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, createKeys, createKsids, true) - if err != nil { - return err - } - // After creation, verify that the keys map to the keyspace ids. If not, remove - // those that don't map. - verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, createKeys, createKsids) - if err != nil { - return err - } - for i, v := range verified { - if !v { - ksids[createIndexes[i]] = nil - } - } - return nil -} - -// processUnowned either reverse maps or validates the values for an unowned column. -func (ins *Insert) processUnowned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { - var reverseIndexes []int - var reverseKsids []ksID - - var verifyIndexes []int - var verifyKeys []sqltypes.Row - var verifyKsids []ksID - - // Check if this VIndex is reversible or not. - reversibleVindex, isReversible := colVindex.Vindex.(vindexes.Reversible) - - for rowNum, rowColumnKeys := range vindexColumnsKeys { - // If we weren't able to determine a keyspace id from the primary VIndex, skip this row - if ksids[rowNum] == nil { - continue +func (ins *Insert) buildVindexRowsValues(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([][]sqltypes.Row, error) { + vindexRowsValues := make([][]sqltypes.Row, len(ins.VindexValues)) + rowCount := 0 + env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) + colVindexes := ins.ColVindexes + for vIdx, vColValues := range ins.VindexValues { + if len(vColValues) != len(colVindexes[vIdx].Columns) { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] supplied vindex column values don't match vschema: %v %v", vColValues, colVindexes[vIdx].Columns) } - - if rowColumnKeys[0].IsNull() { - // If the value of the column is `NULL`, but this is a reversible VIndex, - // we will try to generate the value from the keyspace id generated by the primary VIndex. - if isReversible { - reverseIndexes = append(reverseIndexes, rowNum) - reverseKsids = append(reverseKsids, ksids[rowNum]) + for colIdx, colValues := range vColValues { + rowsResolvedValues := make(sqltypes.Row, 0, len(colValues)) + for _, colValue := range colValues { + result, err := env.Evaluate(colValue) + if err != nil { + return nil, err + } + rowsResolvedValues = append(rowsResolvedValues, result.Value(vcursor.ConnCollation())) } - - // Otherwise, don't do anything. Whether `NULL` is a valid value for this column will be - // handled by MySQL. - } else { - // If a value for this column was specified, the keyspace id values from the - // secondary VIndex need to be verified against the keyspace id from the primary VIndex - verifyIndexes = append(verifyIndexes, rowNum) - verifyKeys = append(verifyKeys, rowColumnKeys) - verifyKsids = append(verifyKsids, ksids[rowNum]) - } - } - - // Reverse map values for secondary VIndex columns from the primary VIndex's keyspace id. - if reverseKsids != nil { - reverseKeys, err := reversibleVindex.ReverseMap(vcursor, reverseKsids) - if err != nil { - return err - } - - for i, reverseKey := range reverseKeys { - // Fill the first column with the reverse-mapped value. - vindexColumnsKeys[reverseIndexes[i]][0] = reverseKey - } - } - - // Verify that the keyspace ids generated by the primary and secondary VIndexes match - if verifyIndexes != nil { - // If values were supplied, we validate against keyspace id. - verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, verifyKeys, verifyKsids) - if err != nil { - return err - } - - var mismatchVindexKeys []sqltypes.Row - for i, v := range verified { - rowNum := verifyIndexes[i] - if !v { - if !ins.Ignore { - mismatchVindexKeys = append(mismatchVindexKeys, vindexColumnsKeys[rowNum]) - continue + // This is the first iteration: allocate for transpose. + if colIdx == 0 { + if len(rowsResolvedValues) == 0 { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] rowcount is zero for inserts: %v", rowsResolvedValues) } - - // Skip the whole row if this is a `INSERT IGNORE` or `INSERT ... ON DUPLICATE KEY ...` statement - // but the keyspace ids didn't match. - ksids[verifyIndexes[i]] = nil + if rowCount == 0 { + rowCount = len(rowsResolvedValues) + } + if rowCount != len(rowsResolvedValues) { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] uneven row values for inserts: %d %d", rowCount, len(rowsResolvedValues)) + } + vindexRowsValues[vIdx] = make([]sqltypes.Row, rowCount) + } + // Perform the transpose. + for rowNum, colVal := range rowsResolvedValues { + vindexRowsValues[vIdx][rowNum] = append(vindexRowsValues[vIdx][rowNum], colVal) } - } - - if mismatchVindexKeys != nil { - return fmt.Errorf("values %v for column %v does not map to keyspace ids", mismatchVindexKeys, colVindex.Columns) } } - - return nil -} - -// InsertVarName returns a name for the bind var for this column. This method is used by the planner and engine, -// to make sure they both produce the same names -func InsertVarName(col sqlparser.IdentifierCI, rowNum int) string { - return fmt.Sprintf("_%s_%d", col.CompliantName(), rowNum) -} - -func insertVarOffset(rowNum, colOffset int) string { - return fmt.Sprintf("_c%d_%d", rowNum, colOffset) + return vindexRowsValues, nil } func (ins *Insert) description() PrimitiveDescription { - other := map[string]any{ - "Query": ins.Query, - "TableName": ins.GetTableName(), - "MultiShardAutocommit": ins.MultiShardAutocommit, - "QueryTimeout": ins.QueryTimeout, - "InsertIgnore": ins.Ignore, - "InputAsNonStreaming": ins.ForceNonStreaming, - } + other := ins.commonDesc() + other["Query"] = ins.Query + other["TableName"] = ins.GetTableName() if len(ins.VindexValues) > 0 { valuesOffsets := map[string]string{} @@ -981,35 +363,6 @@ func (ins *Insert) description() PrimitiveDescription { other["VindexValues"] = valuesOffsets } - if ins.Generate != nil { - if ins.Generate.Values == nil { - other["AutoIncrement"] = fmt.Sprintf("%s:Offset(%d)", ins.Generate.Query, ins.Generate.Offset) - } else { - other["AutoIncrement"] = fmt.Sprintf("%s:Values::%s", ins.Generate.Query, sqlparser.String(ins.Generate.Values)) - } - } - - if len(ins.VindexValueOffset) > 0 { - valuesOffsets := map[string]string{} - for idx, ints := range ins.VindexValueOffset { - if len(ins.ColVindexes) < idx { - panic("ins.ColVindexes and ins.VindexValueOffset do not line up") - } - vindex := ins.ColVindexes[idx] - marshal, _ := json.Marshal(ints) - valuesOffsets[vindex.Name] = string(marshal) - } - other["VindexOffsetFromSelect"] = valuesOffsets - } - if len(ins.Mid) > 0 { - mids := slice.Map(ins.Mid, func(from sqlparser.ValTuple) string { - return sqlparser.String(from) - }) - shardQuery := fmt.Sprintf("%s%s%s", ins.Prefix, strings.Join(mids, ", "), ins.Suffix) - if shardQuery != ins.Query { - other["ShardedQuery"] = shardQuery - } - } return PrimitiveDescription{ OperatorType: "Insert", Keyspace: ins.Keyspace, @@ -1019,41 +372,8 @@ func (ins *Insert) description() PrimitiveDescription { } } -func (ins *Insert) insertIntoUnshardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, result *sqltypes.Result) (int64, *sqltypes.Result, error) { - query := ins.getInsertQueryForUnsharded(result, bindVars) - return ins.executeUnshardedTableQuery(ctx, vcursor, bindVars, query) -} - -func (ins *Insert) executeUnshardedTableQuery(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, query string) (int64, *sqltypes.Result, error) { - insertID, err := ins.processGenerateFromValues(ctx, vcursor, bindVars) - if err != nil { - return 0, nil, err - } - - rss, _, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, nil, []key.Destination{key.DestinationAllShards{}}) - if err != nil { - return 0, nil, err - } - if len(rss) != 1 { - return 0, nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) - } - err = allowOnlyPrimary(rss...) - if err != nil { - return 0, nil, err - } - qr, err := execShard(ctx, ins, vcursor, query, bindVars, rss[0], true, true /* canAutocommit */) - if err != nil { - return 0, nil, err - } - - // If processGenerateFromValues generated new values, it supercedes - // any ids that MySQL might have generated. If both generated - // values, we don't return an error because this behavior - // is required to support migration. - if insertID != 0 { - qr.InsertID = uint64(insertID) - } else { - insertID = int64(qr.InsertID) - } - return insertID, qr, nil +// InsertVarName returns a name for the bind var for this column. This method is used by the planner and engine, +// to make sure they both produce the same names +func InsertVarName(col sqlparser.IdentifierCI, rowNum int) string { + return fmt.Sprintf("_%s_%d", col.CompliantName(), rowNum) } diff --git a/go/vt/vtgate/engine/insert_common.go b/go/vt/vtgate/engine/insert_common.go new file mode 100644 index 00000000000..014fc7681c8 --- /dev/null +++ b/go/vt/vtgate/engine/insert_common.go @@ -0,0 +1,482 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + + "vitess.io/vitess/go/vt/sqlparser" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +type ( + InsertCommon struct { + // Insert needs tx handling + txNeeded + + // Opcode is the execution opcode. + Opcode InsertOpcode + + // Keyspace specifies the keyspace to send the query to. + Keyspace *vindexes.Keyspace + + // Ignore is for INSERT IGNORE and INSERT...ON DUPLICATE KEY constructs + // for sharded cases. + Ignore bool + + // TableName is the name of the table on which row will be inserted. + TableName string + + // Option to override the standard behavior and allow a multi-shard insert + // to use single round trip autocommit. + // + // This is a clear violation of the SQL semantics since it means the statement + // is not atomic in the presence of PK conflicts on one shard and not another. + // However, some application use cases would prefer that the statement partially + // succeed in order to get the performance benefits of autocommit. + MultiShardAutocommit bool + + // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query + QueryTimeout int + + // ForceNonStreaming is true when the insert table and select table are same. + // This will avoid locking by the select table. + ForceNonStreaming bool + + PreventAutoCommit bool + + // Generate is only set for inserts where a sequence must be generated. + Generate *Generate + + // ColVindexes are the vindexes that will use the VindexValues + ColVindexes []*vindexes.ColumnVindex + + // Prefix, Suffix are for sharded insert plans. + Prefix string + Suffix sqlparser.OnDup + } + + ksID = []byte + + // Generate represents the instruction to generate + // a value from a sequence. + Generate struct { + Keyspace *vindexes.Keyspace + Query string + // Values are the supplied values for the column, which + // will be stored as a list within the expression. New + // values will be generated based on how many were not + // supplied (NULL). + Values evalengine.Expr + // Insert using Select, offset for auto increment column + Offset int + } + + // InsertOpcode is a number representing the opcode + // for the Insert primitive. + InsertOpcode int +) + +const nextValBV = "n" + +const ( + // InsertUnsharded is for routing an insert statement + // to an unsharded keyspace. + InsertUnsharded = InsertOpcode(iota) + // InsertSharded is for routing an insert statement + // to individual shards. Requires: A list of Values, one + // for each ColVindex. If the table has an Autoinc column, + // A Generate subplan must be created. + InsertSharded +) + +var insName = map[InsertOpcode]string{ + InsertUnsharded: "InsertUnsharded", + InsertSharded: "InsertSharded", +} + +// String returns the opcode +func (code InsertOpcode) String() string { + return strings.ReplaceAll(insName[code], "Insert", "") +} + +// MarshalJSON serializes the InsertOpcode as a JSON string. +// It's used for testing and diagnostics. +func (code InsertOpcode) MarshalJSON() ([]byte, error) { + return json.Marshal(insName[code]) +} + +// GetKeyspaceName specifies the Keyspace that this primitive routes to. +func (ic *InsertCommon) GetKeyspaceName() string { + return ic.Keyspace.Name +} + +// GetTableName specifies the table that this primitive routes to. +func (ic *InsertCommon) GetTableName() string { + return ic.TableName +} + +// GetFields fetches the field info. +func (ic *InsertCommon) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.VT13001("unexpected fields call for insert query") +} + +func (ins *InsertCommon) executeUnshardedTableQuery(ctx context.Context, vcursor VCursor, loggingPrimitive Primitive, bindVars map[string]*querypb.BindVariable, query string, insertID uint64) (*sqltypes.Result, error) { + rss, _, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, nil, []key.Destination{key.DestinationAllShards{}}) + if err != nil { + return nil, err + } + if len(rss) != 1 { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) + } + err = allowOnlyPrimary(rss...) + if err != nil { + return nil, err + } + qr, err := execShard(ctx, loggingPrimitive, vcursor, query, bindVars, rss[0], true, !ins.PreventAutoCommit /* canAutocommit */) + if err != nil { + return nil, err + } + + // If processGenerateFromValues generated new values, it supersedes + // any ids that MySQL might have generated. If both generated + // values, we don't return an error because this behavior + // is required to support migration. + if insertID != 0 { + qr.InsertID = insertID + } + return qr, nil +} + +func (ins *InsertCommon) processVindexes(ctx context.Context, vcursor VCursor, vindexRowsValues [][]sqltypes.Row) ([]ksID, error) { + colVindexes := ins.ColVindexes + keyspaceIDs, err := ins.processPrimary(ctx, vcursor, vindexRowsValues[0], colVindexes[0]) + if err != nil { + return nil, err + } + + for vIdx := 1; vIdx < len(colVindexes); vIdx++ { + colVindex := colVindexes[vIdx] + if colVindex.Owned { + err = ins.processOwned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) + } else { + err = ins.processUnowned(ctx, vcursor, vindexRowsValues[vIdx], colVindex, keyspaceIDs) + } + if err != nil { + return nil, err + } + } + return keyspaceIDs, nil +} + +// processPrimary maps the primary vindex values to the keyspace ids. +func (ic *InsertCommon) processPrimary(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex) ([]ksID, error) { + destinations, err := vindexes.Map(ctx, colVindex.Vindex, vcursor, vindexColumnsKeys) + if err != nil { + return nil, err + } + + keyspaceIDs := make([]ksID, len(destinations)) + for i, destination := range destinations { + switch d := destination.(type) { + case key.DestinationKeyspaceID: + // This is a single keyspace id, we're good. + keyspaceIDs[i] = d + case key.DestinationNone: + // No valid keyspace id, we may return an error. + if !ic.Ignore { + return nil, fmt.Errorf("could not map %v to a keyspace id", vindexColumnsKeys[i]) + } + default: + return nil, fmt.Errorf("could not map %v to a unique keyspace id: %v", vindexColumnsKeys[i], destination) + } + } + + return keyspaceIDs, nil +} + +// processOwned creates vindex entries for the values of an owned column. +func (ic *InsertCommon) processOwned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { + if !ic.Ignore { + return colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, vindexColumnsKeys, ksids, false /* ignoreMode */) + } + + // InsertIgnore + var createIndexes []int + var createKeys []sqltypes.Row + var createKsids []ksID + + for rowNum, rowColumnKeys := range vindexColumnsKeys { + if ksids[rowNum] == nil { + continue + } + createIndexes = append(createIndexes, rowNum) + createKeys = append(createKeys, rowColumnKeys) + createKsids = append(createKsids, ksids[rowNum]) + } + if createKeys == nil { + return nil + } + + err := colVindex.Vindex.(vindexes.Lookup).Create(ctx, vcursor, createKeys, createKsids, true) + if err != nil { + return err + } + // After creation, verify that the keys map to the keyspace ids. If not, remove + // those that don't map. + verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, createKeys, createKsids) + if err != nil { + return err + } + for i, v := range verified { + if !v { + ksids[createIndexes[i]] = nil + } + } + return nil +} + +// processUnowned either reverse maps or validates the values for an unowned column. +func (ic *InsertCommon) processUnowned(ctx context.Context, vcursor VCursor, vindexColumnsKeys []sqltypes.Row, colVindex *vindexes.ColumnVindex, ksids []ksID) error { + var reverseIndexes []int + var reverseKsids []ksID + + var verifyIndexes []int + var verifyKeys []sqltypes.Row + var verifyKsids []ksID + + // Check if this VIndex is reversible or not. + reversibleVindex, isReversible := colVindex.Vindex.(vindexes.Reversible) + + for rowNum, rowColumnKeys := range vindexColumnsKeys { + // If we weren't able to determine a keyspace id from the primary VIndex, skip this row + if ksids[rowNum] == nil { + continue + } + + if rowColumnKeys[0].IsNull() { + // If the value of the column is `NULL`, but this is a reversible VIndex, + // we will try to generate the value from the keyspace id generated by the primary VIndex. + if isReversible { + reverseIndexes = append(reverseIndexes, rowNum) + reverseKsids = append(reverseKsids, ksids[rowNum]) + } + + // Otherwise, don't do anything. Whether `NULL` is a valid value for this column will be + // handled by MySQL. + } else { + // If a value for this column was specified, the keyspace id values from the + // secondary VIndex need to be verified against the keyspace id from the primary VIndex + verifyIndexes = append(verifyIndexes, rowNum) + verifyKeys = append(verifyKeys, rowColumnKeys) + verifyKsids = append(verifyKsids, ksids[rowNum]) + } + } + + // Reverse map values for secondary VIndex columns from the primary VIndex's keyspace id. + if reverseKsids != nil { + reverseKeys, err := reversibleVindex.ReverseMap(vcursor, reverseKsids) + if err != nil { + return err + } + + for i, reverseKey := range reverseKeys { + // Fill the first column with the reverse-mapped value. + vindexColumnsKeys[reverseIndexes[i]][0] = reverseKey + } + } + + // Verify that the keyspace ids generated by the primary and secondary VIndexes match + if verifyIndexes != nil { + // If values were supplied, we validate against keyspace id. + verified, err := vindexes.Verify(ctx, colVindex.Vindex, vcursor, verifyKeys, verifyKsids) + if err != nil { + return err + } + + var mismatchVindexKeys []sqltypes.Row + for i, v := range verified { + rowNum := verifyIndexes[i] + if !v { + if !ic.Ignore { + mismatchVindexKeys = append(mismatchVindexKeys, vindexColumnsKeys[rowNum]) + continue + } + + // Skip the whole row if this is a `INSERT IGNORE` or `INSERT ... ON DUPLICATE KEY ...` statement + // but the keyspace ids didn't match. + ksids[verifyIndexes[i]] = nil + } + } + + if mismatchVindexKeys != nil { + return fmt.Errorf("values %v for column %v does not map to keyspace ids", mismatchVindexKeys, colVindex.Columns) + } + } + + return nil +} + +// processGenerateFromSelect generates new values using a sequence if necessary. +// If no value was generated, it returns 0. Values are generated only +// for cases where none are supplied. +func (ic *InsertCommon) processGenerateFromSelect( + ctx context.Context, + vcursor VCursor, + loggingPrimitive Primitive, + rows []sqltypes.Row, +) (insertID int64, err error) { + if ic.Generate == nil { + return 0, nil + } + var count int64 + offset := ic.Generate.Offset + genColPresent := offset < len(rows[0]) + if genColPresent { + for _, row := range rows { + if shouldGenerate(row[offset], evalengine.ParseSQLMode(vcursor.SQLMode())) { + count++ + } + } + } else { + count = int64(len(rows)) + } + + if count == 0 { + return 0, nil + } + + insertID, err = ic.execGenerate(ctx, vcursor, loggingPrimitive, count) + if err != nil { + return 0, err + } + + used := insertID + for idx, val := range rows { + if genColPresent { + if shouldGenerate(val[offset], evalengine.ParseSQLMode(vcursor.SQLMode())) { + val[offset] = sqltypes.NewInt64(used) + used++ + } + } else { + rows[idx] = append(val, sqltypes.NewInt64(used)) + used++ + } + } + + return insertID, nil +} + +// processGenerateFromValues generates new values using a sequence if necessary. +// If no value was generated, it returns 0. Values are generated only +// for cases where none are supplied. +func (ic *InsertCommon) processGenerateFromValues( + ctx context.Context, + vcursor VCursor, + loggingPrimitive Primitive, + bindVars map[string]*querypb.BindVariable, +) (insertID int64, err error) { + if ic.Generate == nil { + return 0, nil + } + + // Scan input values to compute the number of values to generate, and + // keep track of where they should be filled. + env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) + resolved, err := env.Evaluate(ic.Generate.Values) + if err != nil { + return 0, err + } + count := int64(0) + values := resolved.TupleValues() + for _, val := range values { + if shouldGenerate(val, evalengine.ParseSQLMode(vcursor.SQLMode())) { + count++ + } + } + + // If generation is needed, generate the requested number of values (as one call). + if count != 0 { + insertID, err = ic.execGenerate(ctx, vcursor, loggingPrimitive, count) + if err != nil { + return 0, err + } + } + + // Fill the holes where no value was supplied. + cur := insertID + for i, v := range values { + if shouldGenerate(v, evalengine.ParseSQLMode(vcursor.SQLMode())) { + bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.Int64BindVariable(cur) + cur++ + } else { + bindVars[SeqVarName+strconv.Itoa(i)] = sqltypes.ValueBindVariable(v) + } + } + return insertID, nil +} + +func (ic *InsertCommon) execGenerate(ctx context.Context, vcursor VCursor, loggingPrimitive Primitive, count int64) (int64, error) { + // If generation is needed, generate the requested number of values (as one call). + rss, _, err := vcursor.ResolveDestinations(ctx, ic.Generate.Keyspace.Name, nil, []key.Destination{key.DestinationAnyShard{}}) + if err != nil { + return 0, err + } + if len(rss) != 1 { + return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "auto sequence generation can happen through single shard only, it is getting routed to %d shards", len(rss)) + } + bindVars := map[string]*querypb.BindVariable{nextValBV: sqltypes.Int64BindVariable(count)} + qr, err := vcursor.ExecuteStandalone(ctx, loggingPrimitive, ic.Generate.Query, bindVars, rss[0]) + if err != nil { + return 0, err + } + // If no rows are returned, it's an internal error, and the code + // must panic, which will be caught and reported. + return qr.Rows[0][0].ToCastInt64() +} + +// shouldGenerate determines if a sequence value should be generated for a given value +func shouldGenerate(v sqltypes.Value, sqlmode evalengine.SQLMode) bool { + if v.IsNull() { + return true + } + + // Unless the NO_AUTO_VALUE_ON_ZERO sql mode is active in mysql, it also + // treats 0 as a value that should generate a new sequence. + value, err := evalengine.CoerceTo(v, sqltypes.Uint64, sqlmode) + if err != nil { + return false + } + + id, err := value.ToCastUint64() + if err != nil { + return false + } + + return id == 0 +} diff --git a/go/vt/vtgate/engine/insert_select.go b/go/vt/vtgate/engine/insert_select.go new file mode 100644 index 00000000000..88767420508 --- /dev/null +++ b/go/vt/vtgate/engine/insert_select.go @@ -0,0 +1,423 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "sync" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +var _ Primitive = (*InsertSelect)(nil) + +type ( + // InsertSelect represents the instructions to perform an insert operation with input rows from a select. + InsertSelect struct { + InsertCommon + + // Input is a select query plan to retrieve results for inserting data. + Input Primitive + + // VindexValueOffset stores the offset for each column in the ColumnVindex + // that will appear in the result set of the select query. + VindexValueOffset [][]int + } +) + +// newInsertSelect creates a new InsertSelect. +func newInsertSelect( + ignore bool, + keyspace *vindexes.Keyspace, + table *vindexes.Table, + prefix string, + suffix sqlparser.OnDup, + vv [][]int, + input Primitive, +) *InsertSelect { + ins := &InsertSelect{ + InsertCommon: InsertCommon{ + Ignore: ignore, + Keyspace: keyspace, + Prefix: prefix, + Suffix: suffix, + }, + Input: input, + VindexValueOffset: vv, + } + if table != nil { + ins.TableName = table.Name.String() + for _, colVindex := range table.ColumnVindexes { + if colVindex.IsPartialVindex() { + continue + } + ins.ColVindexes = append(ins.ColVindexes, colVindex) + } + } + return ins +} + +func (ins *InsertSelect) Inputs() ([]Primitive, []map[string]any) { + return []Primitive{ins.Input}, nil +} + +// RouteType returns a description of the query routing type used by the primitive +func (ins *InsertSelect) RouteType() string { + return "InsertSelect" +} + +// TryExecute performs a non-streaming exec. +func (ins *InsertSelect) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool) (*sqltypes.Result, error) { + ctx, cancelFunc := addQueryTimeout(ctx, vcursor, ins.QueryTimeout) + defer cancelFunc() + + if ins.Keyspace.Sharded { + return ins.execInsertSharded(ctx, vcursor, bindVars) + } + return ins.execInsertUnsharded(ctx, vcursor, bindVars) +} + +// TryStreamExecute performs a streaming exec. +func (ins *InsertSelect) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + if ins.ForceNonStreaming { + res, err := ins.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err + } + return callback(res) + } + ctx, cancelFunc := addQueryTimeout(ctx, vcursor, ins.QueryTimeout) + defer cancelFunc() + + sharded := ins.Keyspace.Sharded + output := &sqltypes.Result{} + err := ins.execSelectStreaming(ctx, vcursor, bindVars, func(irr insertRowsResult) error { + if len(irr.rows) == 0 { + return nil + } + + var qr *sqltypes.Result + var err error + if sharded { + qr, err = ins.insertIntoShardedTable(ctx, vcursor, bindVars, irr) + } else { + qr, err = ins.insertIntoUnshardedTable(ctx, vcursor, bindVars, irr) + } + if err != nil { + return err + } + + output.RowsAffected += qr.RowsAffected + // InsertID needs to be updated to the least insertID value in sqltypes.Result + if output.InsertID == 0 || output.InsertID > qr.InsertID { + output.InsertID = qr.InsertID + } + return nil + }) + if err != nil { + return err + } + return callback(output) +} + +func (ins *InsertSelect) execInsertUnsharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + irr, err := ins.execSelect(ctx, vcursor, bindVars) + if err != nil { + return nil, err + } + if len(irr.rows) == 0 { + return &sqltypes.Result{}, nil + } + return ins.insertIntoUnshardedTable(ctx, vcursor, bindVars, irr) +} + +func (ins *InsertSelect) insertIntoUnshardedTable(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, irr insertRowsResult) (*sqltypes.Result, error) { + query := ins.getInsertUnshardedQuery(irr.rows, bindVars) + return ins.executeUnshardedTableQuery(ctx, vcursor, ins, bindVars, query, irr.insertID) +} + +func (ins *InsertSelect) getInsertUnshardedQuery(rows []sqltypes.Row, bindVars map[string]*querypb.BindVariable) string { + var mids sqlparser.Values + for r, inputRow := range rows { + row := sqlparser.ValTuple{} + for c, value := range inputRow { + bvName := insertVarOffset(r, c) + bindVars[bvName] = sqltypes.ValueBindVariable(value) + row = append(row, sqlparser.NewArgument(bvName)) + } + mids = append(mids, row) + } + return ins.Prefix + sqlparser.String(mids) + sqlparser.String(ins.Suffix) +} + +func (ins *InsertSelect) insertIntoShardedTable( + ctx context.Context, + vcursor VCursor, + bindVars map[string]*querypb.BindVariable, + irr insertRowsResult, +) (*sqltypes.Result, error) { + rss, queries, err := ins.getInsertShardedQueries(ctx, vcursor, bindVars, irr.rows) + if err != nil { + return nil, err + } + + qr, err := ins.executeInsertQueries(ctx, vcursor, rss, queries, irr.insertID) + if err != nil { + return nil, err + } + qr.InsertID = uint64(irr.insertID) + return qr, nil +} + +func (ins *InsertSelect) executeInsertQueries( + ctx context.Context, + vcursor VCursor, + rss []*srvtopo.ResolvedShard, + queries []*querypb.BoundQuery, + insertID uint64, +) (*sqltypes.Result, error) { + autocommit := (len(rss) == 1 || ins.MultiShardAutocommit) && vcursor.AutocommitApproval() + err := allowOnlyPrimary(rss...) + if err != nil { + return nil, err + } + result, errs := vcursor.ExecuteMultiShard(ctx, ins, rss, queries, true /* rollbackOnError */, autocommit) + if errs != nil { + return nil, vterrors.Aggregate(errs) + } + + if insertID != 0 { + result.InsertID = insertID + } + return result, nil +} + +func (ins *InsertSelect) getInsertShardedQueries( + ctx context.Context, + vcursor VCursor, + bindVars map[string]*querypb.BindVariable, + rows []sqltypes.Row, +) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { + vindexRowsValues, err := ins.buildVindexRowsValues(rows) + if err != nil { + return nil, nil, err + } + + keyspaceIDs, err := ins.processVindexes(ctx, vcursor, vindexRowsValues) + if err != nil { + return nil, nil, err + } + + var indexes []*querypb.Value + var destinations []key.Destination + for i, ksid := range keyspaceIDs { + if ksid != nil { + indexes = append(indexes, &querypb.Value{ + Value: strconv.AppendInt(nil, int64(i), 10), + }) + destinations = append(destinations, key.DestinationKeyspaceID(ksid)) + } + } + if len(destinations) == 0 { + // In this case, all we have is nil KeyspaceIds, we don't do + // anything at all. + return nil, nil, nil + } + + rss, indexesPerRss, err := vcursor.ResolveDestinations(ctx, ins.Keyspace.Name, indexes, destinations) + if err != nil { + return nil, nil, err + } + + queries := make([]*querypb.BoundQuery, len(rss)) + for i := range rss { + bvs := sqltypes.CopyBindVariables(bindVars) // we don't want to create one huge bindvars for all values + var mids sqlparser.Values + for _, indexValue := range indexesPerRss[i] { + index, _ := strconv.Atoi(string(indexValue.Value)) + if keyspaceIDs[index] != nil { + row := sqlparser.ValTuple{} + for colOffset, value := range rows[index] { + bvName := insertVarOffset(index, colOffset) + bvs[bvName] = sqltypes.ValueBindVariable(value) + row = append(row, sqlparser.NewArgument(bvName)) + } + mids = append(mids, row) + } + } + rewritten := ins.Prefix + sqlparser.String(mids) + sqlparser.String(ins.Suffix) + queries[i] = &querypb.BoundQuery{ + Sql: rewritten, + BindVariables: bvs, + } + } + + return rss, queries, nil +} + +func (ins *InsertSelect) buildVindexRowsValues(rows []sqltypes.Row) ([][]sqltypes.Row, error) { + colVindexes := ins.ColVindexes + if len(colVindexes) != len(ins.VindexValueOffset) { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex value offsets and vindex info do not match") + } + + // Here we go over the incoming rows and extract values for the vindexes we need to update + vindexRowsValues := make([][]sqltypes.Row, len(colVindexes)) + for _, inputRow := range rows { + for colIdx := range colVindexes { + offsets := ins.VindexValueOffset[colIdx] + row := make(sqltypes.Row, 0, len(offsets)) + for _, offset := range offsets { + if offset == -1 { // value not provided from select query + row = append(row, sqltypes.NULL) + continue + } + row = append(row, inputRow[offset]) + } + vindexRowsValues[colIdx] = append(vindexRowsValues[colIdx], row) + } + } + return vindexRowsValues, nil +} + +func (ins *InsertSelect) execInsertSharded(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + result, err := ins.execSelect(ctx, vcursor, bindVars) + if err != nil { + return nil, err + } + if len(result.rows) == 0 { + return &sqltypes.Result{}, nil + } + + return ins.insertIntoShardedTable(ctx, vcursor, bindVars, result) +} + +func (ins *InsertSelect) description() PrimitiveDescription { + other := ins.commonDesc() + other["TableName"] = ins.GetTableName() + + if len(ins.VindexValueOffset) > 0 { + valuesOffsets := map[string]string{} + for idx, ints := range ins.VindexValueOffset { + if len(ins.ColVindexes) < idx { + panic("ins.ColVindexes and ins.VindexValueOffset do not line up") + } + vindex := ins.ColVindexes[idx] + marshal, _ := json.Marshal(ints) + valuesOffsets[vindex.Name] = string(marshal) + } + other["VindexOffsetFromSelect"] = valuesOffsets + } + + return PrimitiveDescription{ + OperatorType: "Insert", + Keyspace: ins.Keyspace, + Variant: "Select", + TargetTabletType: topodatapb.TabletType_PRIMARY, + Other: other, + } +} + +func (ic *InsertCommon) commonDesc() map[string]any { + other := map[string]any{ + "MultiShardAutocommit": ic.MultiShardAutocommit, + "QueryTimeout": ic.QueryTimeout, + "InsertIgnore": ic.Ignore, + "InputAsNonStreaming": ic.ForceNonStreaming, + "NoAutoCommit": ic.PreventAutoCommit, + } + + if ic.Generate != nil { + if ic.Generate.Values == nil { + other["AutoIncrement"] = fmt.Sprintf("%s:Offset(%d)", ic.Generate.Query, ic.Generate.Offset) + } else { + other["AutoIncrement"] = fmt.Sprintf("%s:Values::%s", ic.Generate.Query, sqlparser.String(ic.Generate.Values)) + } + } + return other +} + +func insertVarOffset(rowNum, colOffset int) string { + return fmt.Sprintf("_c%d_%d", rowNum, colOffset) +} + +type insertRowsResult struct { + rows []sqltypes.Row + insertID uint64 +} + +func (ins *InsertSelect) execSelect( + ctx context.Context, + vcursor VCursor, + bindVars map[string]*querypb.BindVariable, +) (insertRowsResult, error) { + res, err := vcursor.ExecutePrimitive(ctx, ins.Input, bindVars, false) + if err != nil || len(res.Rows) == 0 { + return insertRowsResult{}, err + } + + insertID, err := ins.processGenerateFromSelect(ctx, vcursor, ins, res.Rows) + if err != nil { + return insertRowsResult{}, err + } + + return insertRowsResult{ + rows: res.Rows, + insertID: uint64(insertID), + }, nil +} + +func (ins *InsertSelect) execSelectStreaming( + ctx context.Context, + vcursor VCursor, + bindVars map[string]*querypb.BindVariable, + callback func(irr insertRowsResult) error, +) error { + var mu sync.Mutex + return vcursor.StreamExecutePrimitiveStandalone(ctx, ins.Input, bindVars, false, func(result *sqltypes.Result) error { + if len(result.Rows) == 0 { + return nil + } + + // should process only one chunk at a time. + // as parallel chunk insert will try to use the same transaction in the vttablet + // this will cause transaction in use error out with "transaction in use" error. + mu.Lock() + defer mu.Unlock() + + insertID, err := ins.processGenerateFromSelect(ctx, vcursor, ins, result.Rows) + if err != nil { + return err + } + + return callback(insertRowsResult{ + rows: result.Rows, + insertID: uint64(insertID), + }) + }) +} diff --git a/go/vt/vtgate/engine/insert_test.go b/go/vt/vtgate/engine/insert_test.go index 014654f37d6..e870ffa18c0 100644 --- a/go/vt/vtgate/engine/insert_test.go +++ b/go/vt/vtgate/engine/insert_test.go @@ -33,7 +33,7 @@ import ( ) func TestInsertUnsharded(t *testing.T) { - ins := NewQueryInsert( + ins := newQueryInsert( InsertUnsharded, &vindexes.Keyspace{ Name: "ks", @@ -55,7 +55,7 @@ func TestInsertUnsharded(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: dummy_insert {} true true`, }) - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 4}) + expectResult(t, result, &sqltypes.Result{InsertID: 4}) // Failure cases vc = &loggingVCursor{shardErr: errors.New("shard_error")} @@ -68,7 +68,7 @@ func TestInsertUnsharded(t *testing.T) { } func TestInsertUnshardedGenerate(t *testing.T) { - ins := NewQueryInsert( + ins := newQueryInsert( InsertUnsharded, &vindexes.Keyspace{ Name: "ks", @@ -117,11 +117,11 @@ func TestInsertUnshardedGenerate(t *testing.T) { }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 4}) + expectResult(t, result, &sqltypes.Result{InsertID: 4}) } func TestInsertUnshardedGenerate_Zeros(t *testing.T) { - ins := NewQueryInsert( + ins := newQueryInsert( InsertUnsharded, &vindexes.Keyspace{ Name: "ks", @@ -170,7 +170,7 @@ func TestInsertUnshardedGenerate_Zeros(t *testing.T) { }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 4}) + expectResult(t, result, &sqltypes.Result{InsertID: 4}) } func TestInsertShardedSimple(t *testing.T) { @@ -194,11 +194,11 @@ func TestInsertShardedSimple(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] // A single row insert should be autocommitted - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -213,7 +213,7 @@ func TestInsertShardedSimple(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -227,12 +227,12 @@ func TestInsertShardedSimple(t *testing.T) { `ResolveDestinations sharded [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, // Row 2 will go to -20, rows 1 & 3 will go to 20- `ExecuteMultiShard ` + - `sharded.20-: prefix(:_id_0 /* INT64 */) suffix {_id_0: type:INT64 value:"1"} ` + + `sharded.20-: prefix(:_id_0 /* INT64 */) {_id_0: type:INT64 value:"1"} ` + `true true`, }) // Multiple rows are not autocommitted by default - ins = NewInsert( + ins = newInsert( InsertSharded, false, ks.Keyspace, @@ -252,7 +252,7 @@ func TestInsertShardedSimple(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc = newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -266,13 +266,13 @@ func TestInsertShardedSimple(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, // Row 2 will go to -20, rows 1 & 3 will go to 20- `ExecuteMultiShard ` + - `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) suffix {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + - `sharded.-20: prefix(:_id_1 /* INT64 */) suffix {_id_1: type:INT64 value:"2"} ` + + `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + + `sharded.-20: prefix(:_id_1 /* INT64 */) {_id_1: type:INT64 value:"2"} ` + `true false`, }) // Optional flag overrides autocommit - ins = NewInsert( + ins = newInsert( InsertSharded, false, ks.Keyspace, @@ -293,7 +293,7 @@ func TestInsertShardedSimple(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) ins.MultiShardAutocommit = true @@ -309,8 +309,156 @@ func TestInsertShardedSimple(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, // Row 2 will go to -20, rows 1 & 3 will go to 20- `ExecuteMultiShard ` + - `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) suffix {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + - `sharded.-20: prefix(:_id_1 /* INT64 */) suffix {_id_1: type:INT64 value:"2"} ` + + `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + + `sharded.-20: prefix(:_id_1 /* INT64 */) {_id_1: type:INT64 value:"2"} ` + + `true true`, + }) +} + +func TestInsertShardWithONDuplicateKey(t *testing.T) { + invschema := &vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "sharded": { + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "hash": { + Type: "hash", + }, + }, + Tables: map[string]*vschemapb.Table{ + "t1": { + ColumnVindexes: []*vschemapb.ColumnVindex{{ + Name: "hash", + Columns: []string{"id"}, + }}, + }, + }, + }, + }, + } + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) + ks := vs.Keyspaces["sharded"] + + // A single row insert should be autocommitted + ins := newInsert( + InsertSharded, + false, + ks.Keyspace, + [][][]evalengine.Expr{{ + // colVindex columns: id + { + evalengine.NewLiteralInt(1), + }, + }}, + ks.Tables["t1"], + "prefix", + sqlparser.Values{ + {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + }, + sqlparser.OnDup{ + &sqlparser.UpdateExpr{Name: sqlparser.NewColName("suffix"), Expr: &sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + }, + ) + vc := newDMLTestVCursor("-20", "20-") + vc.shardForKsid = []string{"20-", "-20", "20-"} + + _, err := ins.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + // Based on shardForKsid, values returned will be 20-. + `ResolveDestinations sharded [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, + // Row 2 will go to -20, rows 1 & 3 will go to 20- + `ExecuteMultiShard ` + + `sharded.20-: prefix(:_id_0 /* INT64 */) on duplicate key update suffix = :_id_0 /* INT64 */ {_id_0: type:INT64 value:"1"} ` + + `true true`, + }) + + // Multiple rows are not autocommitted by default + ins = newInsert( + InsertSharded, + false, + ks.Keyspace, + [][][]evalengine.Expr{{ + // colVindex columns: id + // 3 rows. + { + evalengine.NewLiteralInt(1), + evalengine.NewLiteralInt(2), + evalengine.NewLiteralInt(3), + }, + }}, + ks.Tables["t1"], + "prefix", + sqlparser.Values{ + {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}}, + {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}}, + }, + sqlparser.OnDup{ + &sqlparser.UpdateExpr{Name: sqlparser.NewColName("suffix"), Expr: &sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + }, + ) + vc = newDMLTestVCursor("-20", "20-") + vc.shardForKsid = []string{"20-", "-20", "20-"} + + _, err = ins.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + // Based on shardForKsid, values returned will be 20-, -20, 20-. + `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, + // Row 2 will go to -20, rows 1 & 3 will go to 20- + `ExecuteMultiShard ` + + `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) on duplicate key update suffix = :_id_0 /* INT64 */ {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + + `sharded.-20: prefix(:_id_1 /* INT64 */) on duplicate key update suffix = :_id_0 /* INT64 */ {_id_0: type:INT64 value:"1" _id_1: type:INT64 value:"2"} ` + + `true false`, + }) + + // Optional flag overrides autocommit + ins = newInsert( + InsertSharded, + false, + ks.Keyspace, + [][][]evalengine.Expr{{ + // colVindex columns: id + // 3 rows. + { + evalengine.NewLiteralInt(1), + evalengine.NewLiteralInt(2), + evalengine.NewLiteralInt(3), + }, + }}, + + ks.Tables["t1"], + "prefix", + sqlparser.Values{ + {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}}, + {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}}, + }, + sqlparser.OnDup{ + &sqlparser.UpdateExpr{Name: sqlparser.NewColName("suffix"), Expr: &sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, + }, + ) + ins.MultiShardAutocommit = true + + vc = newDMLTestVCursor("-20", "20-") + vc.shardForKsid = []string{"20-", "-20", "20-"} + + _, err = ins.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + // Based on shardForKsid, values returned will be 20-, -20, 20-. + `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, + // Row 2 will go to -20, rows 1 & 3 will go to 20- + `ExecuteMultiShard ` + + `sharded.20-: prefix(:_id_0 /* INT64 */),(:_id_2 /* INT64 */) on duplicate key update suffix = :_id_0 /* INT64 */ {_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + + `sharded.-20: prefix(:_id_1 /* INT64 */) on duplicate key update suffix = :_id_0 /* INT64 */ {_id_0: type:INT64 value:"1" _id_1: type:INT64 value:"2"} ` + `true true`, }) } @@ -341,10 +489,10 @@ func TestInsertShardedFail(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -360,7 +508,7 @@ func TestInsertShardedFail(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc := &loggingVCursor{} @@ -391,10 +539,10 @@ func TestInsertShardedGenerate(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -414,7 +562,7 @@ func TestInsertShardedGenerate(t *testing.T) { {&sqlparser.Argument{Name: "__seq1", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "__seq2", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) ins.Generate = &Generate{ @@ -454,15 +602,15 @@ func TestInsertShardedGenerate(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, // Row 2 will go to -20, rows 1 & 3 will go to 20- `ExecuteMultiShard ` + - `sharded.20-: prefix(:__seq0 /* INT64 */),(:__seq2 /* INT64 */) suffix ` + + `sharded.20-: prefix(:__seq0 /* INT64 */),(:__seq2 /* INT64 */) ` + `{__seq0: type:INT64 value:"1" __seq2: type:INT64 value:"3"} ` + - `sharded.-20: prefix(:__seq1 /* INT64 */) suffix ` + + `sharded.-20: prefix(:__seq1 /* INT64 */) ` + `{__seq1: type:INT64 value:"2"} ` + `true false`, }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 2}) + expectResult(t, result, &sqltypes.Result{InsertID: 2}) } func TestInsertShardedOwned(t *testing.T) { @@ -510,10 +658,10 @@ func TestInsertShardedOwned(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -552,7 +700,7 @@ func TestInsertShardedOwned(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_1", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_2", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") @@ -574,12 +722,12 @@ func TestInsertShardedOwned(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + `sharded.20-: prefix(:_id_0 /* INT64 */, :_c1_0 /* INT64 */, :_c2_0 /* INT64 */, :_c3_0 /* INT64 */)` + - `,(:_id_2 /* INT64 */, :_c1_2 /* INT64 */, :_c2_2 /* INT64 */, :_c3_2 /* INT64 */) suffix ` + + `,(:_id_2 /* INT64 */, :_c1_2 /* INT64 */, :_c2_2 /* INT64 */, :_c3_2 /* INT64 */) ` + `{_c1_0: type:INT64 value:"4" _c1_2: type:INT64 value:"6" ` + `_c2_0: type:INT64 value:"7" _c2_2: type:INT64 value:"9" ` + `_c3_0: type:INT64 value:"10" _c3_2: type:INT64 value:"12" ` + `_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + - `sharded.-20: prefix(:_id_1 /* INT64 */, :_c1_1 /* INT64 */, :_c2_1 /* INT64 */, :_c3_1 /* INT64 */) suffix ` + + `sharded.-20: prefix(:_id_1 /* INT64 */, :_c1_1 /* INT64 */, :_c2_1 /* INT64 */, :_c3_1 /* INT64 */) ` + `{_c1_1: type:INT64 value:"5" _c2_1: type:INT64 value:"8" _c3_1: type:INT64 value:"11" ` + `_id_1: type:INT64 value:"2"} ` + `true false`, @@ -620,10 +768,10 @@ func TestInsertShardedOwnedWithNull(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -644,7 +792,7 @@ func TestInsertShardedOwnedWithNull(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_0", Type: sqltypes.Null}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") @@ -656,7 +804,7 @@ func TestInsertShardedOwnedWithNull(t *testing.T) { } vc.ExpectLog(t, []string{ `ResolveDestinations sharded [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sharded.20-: prefix(:_id_0 /* INT64 */, :_c3_0 /* NULL_TYPE */) suffix ` + + `ExecuteMultiShard sharded.20-: prefix(:_id_0 /* INT64 */, :_c3_0 /* NULL_TYPE */) ` + `{_c3_0: _id_0: type:INT64 value:"1"} true true`, }) } @@ -697,10 +845,10 @@ func TestInsertShardedGeo(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -730,7 +878,7 @@ func TestInsertShardedGeo(t *testing.T) { {&sqlparser.Argument{Name: "_region_0", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_region_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") @@ -745,9 +893,9 @@ func TestInsertShardedGeo(t *testing.T) { `id_0: type:INT64 value:"1" id_1: type:INT64 value:"1" ` + `keyspace_id_0: type:VARBINARY value:"\x01\x16k@\xb4J\xbaK\xd6" keyspace_id_1: type:VARBINARY value:"\xff\x16k@\xb4J\xbaK\xd6" true`, `ResolveDestinations sharded [value:"0" value:"1"] Destinations:DestinationKeyspaceID(01166b40b44aba4bd6),DestinationKeyspaceID(ff166b40b44aba4bd6)`, - `ExecuteMultiShard sharded.20-: prefix(:_region_0 /* INT64 */, :_id_0 /* INT64 */) suffix ` + + `ExecuteMultiShard sharded.20-: prefix(:_region_0 /* INT64 */, :_id_0 /* INT64 */) ` + `{_id_0: type:INT64 value:"1" _region_0: type:INT64 value:"1"} ` + - `sharded.-20: prefix(:_region_1 /* INT64 */, :_id_1 /* INT64 */) suffix ` + + `sharded.-20: prefix(:_region_1 /* INT64 */, :_id_1 /* INT64 */) ` + `{_id_1: type:INT64 value:"1" _region_1: type:INT64 value:"255"} ` + `true false`, }) @@ -803,10 +951,10 @@ func TestInsertShardedIgnoreOwned(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, true, ks.Keyspace, @@ -854,7 +1002,7 @@ func TestInsertShardedIgnoreOwned(t *testing.T) { {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_2", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_id_3", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_3", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_3", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_3", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) ksid0Lookup := sqltypes.MakeTestResult( @@ -917,9 +1065,9 @@ func TestInsertShardedIgnoreOwned(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"3"] Destinations:DestinationKeyspaceID(00),DestinationKeyspaceID(00)`, // Bind vars for rows 2 & 3 may be missing because they were not sent. `ExecuteMultiShard ` + - `sharded.20-: prefix(:_id_0 /* INT64 */, :_c1_0 /* INT64 */, :_c2_0 /* INT64 */, :_c3_0 /* INT64 */) suffix ` + + `sharded.20-: prefix(:_id_0 /* INT64 */, :_c1_0 /* INT64 */, :_c2_0 /* INT64 */, :_c3_0 /* INT64 */) ` + `{_c1_0: type:INT64 value:"5" _c2_0: type:INT64 value:"9" _c3_0: type:INT64 value:"13" _id_0: type:INT64 value:"1"} ` + - `sharded.-20: prefix(:_id_3 /* INT64 */, :_c1_3 /* INT64 */, :_c2_3 /* INT64 */, :_c3_3 /* INT64 */) suffix ` + + `sharded.-20: prefix(:_id_3 /* INT64 */, :_c1_3 /* INT64 */, :_c2_3 /* INT64 */, :_c3_3 /* INT64 */) ` + `{_c1_3: type:INT64 value:"8" _c2_3: type:INT64 value:"12" _c3_3: type:INT64 value:"16" _id_3: type:INT64 value:"4"} ` + `true false`, }) @@ -959,10 +1107,10 @@ func TestInsertShardedIgnoreOwnedWithNull(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, true, ks.Keyspace, @@ -984,7 +1132,7 @@ func TestInsertShardedIgnoreOwnedWithNull(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_0", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) ksid0 := sqltypes.MakeTestResult( @@ -1009,7 +1157,7 @@ func TestInsertShardedIgnoreOwnedWithNull(t *testing.T) { vc.ExpectLog(t, []string{ `Execute select from from lkp1 where from = :from and toc = :toc from: toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" false`, `ResolveDestinations sharded [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sharded.-20: prefix(:_id_0 /* INT64 */, :_c3_0 /* INT64 */) suffix ` + + `ExecuteMultiShard sharded.-20: prefix(:_id_0 /* INT64 */, :_c3_0 /* INT64 */) ` + `{_c3_0: _id_0: type:INT64 value:"1"} true true`, }) } @@ -1057,10 +1205,10 @@ func TestInsertShardedUnownedVerify(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -1102,7 +1250,7 @@ func TestInsertShardedUnownedVerify(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_1", Type: sqltypes.Int64}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c2_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_2", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) // nonemptyResult will cause the lookup verify queries to succeed. @@ -1141,12 +1289,12 @@ func TestInsertShardedUnownedVerify(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + `sharded.20-: prefix(:_id_0 /* INT64 */, :_c1_0 /* INT64 */, :_c2_0 /* INT64 */, :_c3_0 /* INT64 */),` + - `(:_id_2 /* INT64 */, :_c1_2 /* INT64 */, :_c2_2 /* INT64 */, :_c3_2 /* INT64 */) suffix ` + + `(:_id_2 /* INT64 */, :_c1_2 /* INT64 */, :_c2_2 /* INT64 */, :_c3_2 /* INT64 */) ` + `{_c1_0: type:INT64 value:"4" _c1_2: type:INT64 value:"6" ` + `_c2_0: type:INT64 value:"7" _c2_2: type:INT64 value:"9" ` + `_c3_0: type:INT64 value:"10" _c3_2: type:INT64 value:"12" ` + `_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + - `sharded.-20: prefix(:_id_1 /* INT64 */, :_c1_1 /* INT64 */, :_c2_1 /* INT64 */, :_c3_1 /* INT64 */) suffix ` + + `sharded.-20: prefix(:_id_1 /* INT64 */, :_c1_1 /* INT64 */, :_c2_1 /* INT64 */, :_c3_1 /* INT64 */) ` + `{_c1_1: type:INT64 value:"5" _c2_1: type:INT64 value:"8" ` + `_c3_1: type:INT64 value:"11" _id_1: type:INT64 value:"2"} ` + `true false`, @@ -1185,10 +1333,10 @@ func TestInsertShardedIgnoreUnownedVerify(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, true, ks.Keyspace, @@ -1216,7 +1364,7 @@ func TestInsertShardedIgnoreUnownedVerify(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "v2", Type: sqltypes.VarChar}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "v3", Type: sqltypes.VarChar}}, }, - " suffix", + nil, ) // nonemptyResult will cause the lookup verify queries to succeed. @@ -1251,9 +1399,9 @@ func TestInsertShardedIgnoreUnownedVerify(t *testing.T) { // Based on shardForKsid, values returned will be 20-, -20. `ResolveDestinations sharded [value:"0" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + - `sharded.20-: prefix(:_id_0 /* INT64 */, :_c3_0 /* INT64 */, :v1 /* VARCHAR */) suffix ` + + `sharded.20-: prefix(:_id_0 /* INT64 */, :_c3_0 /* INT64 */, :v1 /* VARCHAR */) ` + `{_c3_0: type:INT64 value:"10" _id_0: type:INT64 value:"1" v1: type:VARCHAR value:"a"} ` + - `sharded.-20: prefix(:_id_2 /* INT64 */, :_c3_2 /* INT64 */, :v3 /* VARCHAR */) suffix ` + + `sharded.-20: prefix(:_id_2 /* INT64 */, :_c3_2 /* INT64 */, :v3 /* VARCHAR */) ` + `{_c3_2: type:INT64 value:"12" _id_2: type:INT64 value:"3" v3: type:VARCHAR value:"c"} ` + `true false`, }) @@ -1291,10 +1439,10 @@ func TestInsertShardedIgnoreUnownedVerifyFail(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -1316,7 +1464,7 @@ func TestInsertShardedIgnoreUnownedVerifyFail(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_0", Type: sqltypes.Int64}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") @@ -1368,10 +1516,10 @@ func TestInsertShardedUnownedReverseMap(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -1413,7 +1561,7 @@ func TestInsertShardedUnownedReverseMap(t *testing.T) { {&sqlparser.Argument{Name: "_id_1", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_1", Type: sqltypes.Null}, &sqlparser.Argument{Name: "_c2_1", Type: sqltypes.Null}, &sqlparser.Argument{Name: "_c3_1", Type: sqltypes.Null}}, {&sqlparser.Argument{Name: "_id_2", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c1_2", Type: sqltypes.Null}, &sqlparser.Argument{Name: "_c2_2", Type: sqltypes.Null}, &sqlparser.Argument{Name: "_c3_2", Type: sqltypes.Null}}, }, - " suffix", + nil, ) // nonemptyResult will cause the lookup verify queries to succeed. @@ -1439,13 +1587,13 @@ func TestInsertShardedUnownedReverseMap(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard sharded.20-: ` + `prefix(:_id_0 /* INT64 */, :_c1_0 /* NULL_TYPE */, :_c2_0 /* NULL_TYPE */, :_c3_0 /* NULL_TYPE */),` + - `(:_id_2 /* INT64 */, :_c1_2 /* NULL_TYPE */, :_c2_2 /* NULL_TYPE */, :_c3_2 /* NULL_TYPE */) suffix ` + + `(:_id_2 /* INT64 */, :_c1_2 /* NULL_TYPE */, :_c2_2 /* NULL_TYPE */, :_c3_2 /* NULL_TYPE */) ` + `{_c1_0: type:UINT64 value:"1" _c1_2: type:UINT64 value:"3" ` + `_c2_0: _c2_2: ` + `_c3_0: type:UINT64 value:"1" _c3_2: type:UINT64 value:"3" ` + `_id_0: type:INT64 value:"1" _id_2: type:INT64 value:"3"} ` + `sharded.-20: ` + - `prefix(:_id_1 /* INT64 */, :_c1_1 /* NULL_TYPE */, :_c2_1 /* NULL_TYPE */, :_c3_1 /* NULL_TYPE */) suffix ` + + `prefix(:_id_1 /* INT64 */, :_c1_1 /* NULL_TYPE */, :_c2_1 /* NULL_TYPE */, :_c3_1 /* NULL_TYPE */) ` + `{_c1_1: type:UINT64 value:"2" _c2_1: _c3_1: type:UINT64 value:"2" _id_1: type:INT64 value:"2"} true false`, }) } @@ -1482,10 +1630,10 @@ func TestInsertShardedUnownedReverseMapSuccess(t *testing.T) { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( + ins := newInsert( InsertSharded, false, ks.Keyspace, @@ -1507,7 +1655,7 @@ func TestInsertShardedUnownedReverseMapSuccess(t *testing.T) { sqlparser.Values{ {&sqlparser.Argument{Name: "_id_0", Type: sqltypes.Int64}, &sqlparser.Argument{Name: "_c3_0", Type: sqltypes.Null}}, }, - " suffix", + nil, ) vc := newDMLTestVCursor("-20", "20-") @@ -1529,25 +1677,17 @@ func TestInsertSelectSimple(t *testing.T) { Name: "hash", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] // A single row insert should be autocommitted - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{{1}}, - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - - ins.ColVindexes = append(ins.ColVindexes, ks.Tables["t1"].ColumnVindexes...) - ins.Prefix = "prefix " - ins.Suffix = " suffix" + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + ins := newInsertSelect(false, ks.Keyspace, ks.Tables["t1"], "prefix ", nil, [][]int{{1}}, rb) vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -1571,10 +1711,10 @@ func TestInsertSelectSimple(t *testing.T) { // two rows go to the 20- shard, and one row go to the -20 shard `ExecuteMultiShard ` + - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1"` + ` _c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"2"} ` + - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix` + + `sharded.-20: prefix values (:_c1_0, :_c1_1)` + ` {_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"3"} true false`}) vc.Rewind() @@ -1591,10 +1731,10 @@ func TestInsertSelectSimple(t *testing.T) { // two rows go to the 20- shard, and one row go to the -20 shard `ExecuteMultiShard ` + - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1"` + ` _c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"2"} ` + - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix` + + `sharded.-20: prefix values (:_c1_0, :_c1_1)` + ` {_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"3"} true false`}) } @@ -1620,26 +1760,27 @@ func TestInsertSelectOwned(t *testing.T) { Name: "onecol", Columns: []string{"c3"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{ + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t1"], + "prefix ", + nil, + [][]int{ {1}, // The primary vindex has a single column as sharding key {0}}, // the onecol vindex uses the 'name' column - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - - ins.ColVindexes = append(ins.ColVindexes, ks.Tables["t1"].ColumnVindexes...) - ins.Prefix = "prefix " - ins.Suffix = " suffix" + rb, + ) vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -1669,11 +1810,11 @@ func TestInsertSelectOwned(t *testing.T) { // insert values into the main table `ExecuteMultiShard ` + // first we insert two rows on the 20- shard - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1" _c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"2"} ` + // next we insert one row on the -20 shard - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"3"} ` + `true false`}) @@ -1697,11 +1838,11 @@ func TestInsertSelectOwned(t *testing.T) { // insert values into the main table `ExecuteMultiShard ` + // first we insert two rows on the 20- shard - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1" _c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"2"} ` + // next we insert one row on the -20 shard - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"3"} ` + `true false`}) } @@ -1720,27 +1861,25 @@ func TestInsertSelectGenerate(t *testing.T) { Name: "hash", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := NewInsert( - InsertSelect, - false, - ks.Keyspace, - nil, - ks.Tables["t1"], - "prefix ", - nil, - " suffix") - ins.Query = "dummy_insert" - ins.VindexValueOffset = [][]int{{1}} // The primary vindex has a single column as sharding key - ins.Input = &Route{ + rb := &Route{ Query: "dummy_select", FieldQuery: "dummy_field_query", RoutingParameters: &RoutingParameters{ Opcode: Scatter, Keyspace: ks.Keyspace}} + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t1"], + "prefix ", + nil, + [][]int{{1}}, // The primary vindex has a single column as sharding key + rb, + ) ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -1760,7 +1899,7 @@ func TestInsertSelectGenerate(t *testing.T) { "varchar|int64"), "a|1", "a|null", - "b|null"), + "b|0"), // This is the result for the sequence query sqltypes.MakeTestResult( sqltypes.MakeTestFields( @@ -1785,19 +1924,19 @@ func TestInsertSelectGenerate(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + // first we send the insert to the 20- shard - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" ` + `_c0_1: type:INT64 value:"1" ` + `_c2_0: type:VARCHAR value:"b" ` + `_c2_1: type:INT64 value:"3"} ` + // next we send the insert to the -20 shard - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"2"} ` + `true false`, }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 2}) + expectResult(t, result, &sqltypes.Result{InsertID: 2}) } func TestStreamingInsertSelectGenerate(t *testing.T) { @@ -1814,23 +1953,26 @@ func TestStreamingInsertSelectGenerate(t *testing.T) { Name: "hash", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{ - {1}}, // The primary vindex has a single column as sharding key - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - ins.ColVindexes = ks.Tables["t1"].ColumnVindexes + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t1"], + "prefix ", + nil, + [][]int{ + {1}}, // The primary vindex has a single column as sharding key + rb, + ) ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -1839,8 +1981,6 @@ func TestStreamingInsertSelectGenerate(t *testing.T) { Query: "dummy_generate", Offset: 1, } - ins.Prefix = "prefix " - ins.Suffix = " suffix" vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -1881,19 +2021,19 @@ func TestStreamingInsertSelectGenerate(t *testing.T) { `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + // first we send the insert to the 20- shard - `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1), (:_c2_0, :_c2_1) ` + `{_c0_0: type:VARCHAR value:"a" ` + `_c0_1: type:INT64 value:"1" ` + `_c2_0: type:VARCHAR value:"b" ` + `_c2_1: type:INT64 value:"3"} ` + // next we send the insert to the -20 shard - `sharded.-20: prefix values (:_c1_0, :_c1_1) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"2"} ` + `true false`, }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", output, &sqltypes.Result{InsertID: 2}) + expectResult(t, output, &sqltypes.Result{InsertID: 2}) } func TestInsertSelectGenerateNotProvided(t *testing.T) { @@ -1910,23 +2050,24 @@ func TestInsertSelectGenerateNotProvided(t *testing.T) { Name: "hash", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{ - {1}}, // The primary vindex has a single column as sharding key - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - - ins.ColVindexes = ks.Tables["t1"].ColumnVindexes + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t1"], + "prefix ", + nil, + [][]int{{1}}, // The primary vindex has a single column as sharding key, + rb, + ) ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -1935,8 +2076,6 @@ func TestInsertSelectGenerateNotProvided(t *testing.T) { Query: "dummy_generate", Offset: 2, } - ins.Prefix = "prefix " - ins.Suffix = " suffix" vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -1972,16 +2111,16 @@ func TestInsertSelectGenerateNotProvided(t *testing.T) { `ExecuteStandalone dummy_generate n: type:INT64 value:"3" ks2 -20`, `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + - `sharded.20-: prefix values (:_c0_0, :_c0_1, :_c0_2), (:_c2_0, :_c2_1, :_c2_2) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1, :_c0_2), (:_c2_0, :_c2_1, :_c2_2) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1" _c0_2: type:INT64 value:"10" ` + `_c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"3" _c2_2: type:INT64 value:"12"} ` + - `sharded.-20: prefix values (:_c1_0, :_c1_1, :_c1_2) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1, :_c1_2) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"2" _c1_2: type:INT64 value:"11"} ` + `true false`, }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", result, &sqltypes.Result{InsertID: 10}) + expectResult(t, result, &sqltypes.Result{InsertID: 10}) } func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { @@ -1998,23 +2137,24 @@ func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { Name: "hash", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{ - {1}}, // The primary vindex has a single column as sharding key - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - - ins.ColVindexes = ks.Tables["t1"].ColumnVindexes + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t1"], + "prefix ", + nil, + [][]int{{1}}, // The primary vindex has a single column as sharding key, + rb, + ) ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -2023,8 +2163,6 @@ func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { Query: "dummy_generate", Offset: 2, } - ins.Prefix = "prefix " - ins.Suffix = " suffix" vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -2064,16 +2202,16 @@ func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { `ExecuteStandalone dummy_generate n: type:INT64 value:"3" ks2 -20`, `ResolveDestinations sharded [value:"0" value:"1" value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(4eb190c9a2fa169c)`, `ExecuteMultiShard ` + - `sharded.20-: prefix values (:_c0_0, :_c0_1, :_c0_2), (:_c2_0, :_c2_1, :_c2_2) suffix ` + + `sharded.20-: prefix values (:_c0_0, :_c0_1, :_c0_2), (:_c2_0, :_c2_1, :_c2_2) ` + `{_c0_0: type:VARCHAR value:"a" _c0_1: type:INT64 value:"1" _c0_2: type:INT64 value:"10" ` + `_c2_0: type:VARCHAR value:"b" _c2_1: type:INT64 value:"3" _c2_2: type:INT64 value:"12"} ` + - `sharded.-20: prefix values (:_c1_0, :_c1_1, :_c1_2) suffix ` + + `sharded.-20: prefix values (:_c1_0, :_c1_1, :_c1_2) ` + `{_c1_0: type:VARCHAR value:"a" _c1_1: type:INT64 value:"2" _c1_2: type:INT64 value:"11"} ` + `true false`, }) // The insert id returned by ExecuteMultiShard should be overwritten by processGenerateFromValues. - expectResult(t, "Execute", output, &sqltypes.Result{InsertID: 10}) + expectResult(t, output, &sqltypes.Result{InsertID: 10}) } func TestInsertSelectUnowned(t *testing.T) { @@ -2096,25 +2234,24 @@ func TestInsertSelectUnowned(t *testing.T) { Name: "onecol", Columns: []string{"id"}}}}}}}} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - VindexValueOffset: [][]int{ - {0}}, // the onecol vindex as unowned lookup sharding column - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} - - ins.ColVindexes = append(ins.ColVindexes, ks.Tables["t2"].ColumnVindexes...) - ins.Prefix = "prefix " - ins.Suffix = " suffix" + rb := &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} + ins := newInsertSelect( + false, + ks.Keyspace, + ks.Tables["t2"], + "prefix ", + nil, + [][]int{{0}}, // // the onecol vindex as unowned lookup sharding column + rb, + ) vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -2141,11 +2278,11 @@ func TestInsertSelectUnowned(t *testing.T) { // insert values into the main table `ExecuteMultiShard ` + // first we insert two rows on the 20- shard - `sharded.20-: prefix values (:_c0_0), (:_c2_0) suffix ` + + `sharded.20-: prefix values (:_c0_0), (:_c2_0) ` + `{_c0_0: type:INT64 value:"1" _c2_0: type:INT64 value:"2"} ` + // next we insert one row on the -20 shard - `sharded.-20: prefix values (:_c1_0) suffix ` + + `sharded.-20: prefix values (:_c1_0) ` + `{_c1_0: type:INT64 value:"3"} ` + `true false`}) @@ -2169,11 +2306,11 @@ func TestInsertSelectUnowned(t *testing.T) { // insert values into the main table `ExecuteMultiShard ` + // first we insert two rows on the 20- shard - `sharded.20-: prefix values (:_c0_0), (:_c2_0) suffix ` + + `sharded.20-: prefix values (:_c0_0), (:_c2_0) ` + `{_c0_0: type:INT64 value:"1" _c2_0: type:INT64 value:"2"} ` + // next we insert one row on the -20 shard - `sharded.-20: prefix values (:_c1_0) suffix ` + + `sharded.-20: prefix values (:_c1_0) ` + `{_c1_0: type:INT64 value:"3"} ` + `true false`}) } @@ -2201,7 +2338,7 @@ func TestInsertSelectShardingCases(t *testing.T) { "uks2": {Tables: map[string]*vschemapb.Table{"u2": {}}}, }} - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) sks1 := vs.Keyspaces["sks1"] sks2 := vs.Keyspaces["sks2"] uks1 := vs.Keyspaces["uks1"] @@ -2220,16 +2357,15 @@ func TestInsertSelectShardingCases(t *testing.T) { RoutingParameters: &RoutingParameters{Opcode: Unsharded, Keyspace: uks2.Keyspace}} // sks1 and sks2 - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: sks1.Keyspace, - Query: "dummy_insert", - Prefix: "prefix ", - Suffix: " suffix", - ColVindexes: sks1.Tables["s1"].ColumnVindexes, - VindexValueOffset: [][]int{{0}}, - Input: sRoute, - } + ins := newInsertSelect( + false, + sks1.Keyspace, + sks1.Tables["s1"], + "prefix ", + nil, + [][]int{{0}}, + sRoute, + ) vc := &loggingVCursor{ resolvedTargetTabletType: topodatapb.TabletType_PRIMARY, @@ -2252,7 +2388,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations sks1 [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) vc.Rewind() err = ins.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { @@ -2266,7 +2402,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations sks1 [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) // sks1 and uks2 ins.Input = uRoute @@ -2281,7 +2417,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations sks1 [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) vc.Rewind() err = ins.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { @@ -2295,17 +2431,18 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations sks1 [value:"0"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, - `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard sks1.-20: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) // uks1 and sks2 - ins = &Insert{ - Opcode: InsertUnsharded, - Keyspace: uks1.Keyspace, - Query: "dummy_insert", - Prefix: "prefix ", - Suffix: " suffix", - Input: sRoute, - } + ins = newInsertSelect( + false, + uks1.Keyspace, + nil, + "prefix ", + nil, + nil, + sRoute, + ) vc.Rewind() _, err = ins.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) @@ -2317,7 +2454,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations uks1 [] Destinations:DestinationAllShards()`, - `ExecuteMultiShard uks1.0: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard uks1.0: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) vc.Rewind() err = ins.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { @@ -2331,7 +2468,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations uks1 [] Destinations:DestinationAllShards()`, - `ExecuteMultiShard uks1.0: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard uks1.0: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) // uks1 and uks2 ins.Input = uRoute @@ -2346,7 +2483,7 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations uks1 [] Destinations:DestinationAllShards()`, - `ExecuteMultiShard uks1.0: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard uks1.0: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) vc.Rewind() err = ins.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { @@ -2360,5 +2497,5 @@ func TestInsertSelectShardingCases(t *testing.T) { // the query exec `ResolveDestinations uks1 [] Destinations:DestinationAllShards()`, - `ExecuteMultiShard uks1.0: prefix values (:_c0_0) suffix {_c0_0: type:INT64 value:"1"} true true`}) + `ExecuteMultiShard uks1.0: prefix values (:_c0_0) {_c0_0: type:INT64 value:"1"} true true`}) } diff --git a/go/vt/vtgate/engine/join.go b/go/vt/vtgate/engine/join.go index ef50389c989..45b0d182dd7 100644 --- a/go/vt/vtgate/engine/join.go +++ b/go/vt/vtgate/engine/join.go @@ -225,7 +225,7 @@ func joinFields(lfields, rfields []*querypb.Field, cols []int) []*querypb.Field return fields } -func joinRows(lrow, rrow []sqltypes.Value, cols []int) []sqltypes.Value { +func joinRows(lrow, rrow sqltypes.Row, cols []int) sqltypes.Row { row := make([]sqltypes.Value, len(cols)) for i, index := range cols { if index < 0 { diff --git a/go/vt/vtgate/engine/join_test.go b/go/vt/vtgate/engine/join_test.go index 2df507f9512..eef5810ce69 100644 --- a/go/vt/vtgate/engine/join_test.go +++ b/go/vt/vtgate/engine/join_test.go @@ -89,7 +89,7 @@ func TestJoinExecute(t *testing.T) { `Execute a: type:INT64 value:"10" bv: type:VARCHAR value:"b" false`, `Execute a: type:INT64 value:"10" bv: type:VARCHAR value:"c" false`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col4|col5", "int64|varchar|int64|varchar", @@ -116,7 +116,7 @@ func TestJoinExecute(t *testing.T) { `Execute a: type:INT64 value:"10" bv: type:VARCHAR value:"b" false`, `Execute a: type:INT64 value:"10" bv: type:VARCHAR value:"c" false`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col4|col5", "int64|varchar|int64|varchar", @@ -251,7 +251,7 @@ func TestJoinExecuteNoResult(t *testing.T) { "int64|varchar|int64|varchar", ), ) - expectResult(t, "jn.Execute", r, wantResult) + expectResult(t, r, wantResult) } func TestJoinExecuteErrors(t *testing.T) { @@ -389,7 +389,7 @@ func TestJoinStreamExecute(t *testing.T) { `StreamExecute bv: type:VARCHAR value:"b" false`, `StreamExecute bv: type:VARCHAR value:"c" false`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col4|col5", "int64|varchar|int64|varchar", @@ -418,7 +418,7 @@ func TestJoinStreamExecute(t *testing.T) { `StreamExecute bv: type:VARCHAR value:"b" false`, `StreamExecute bv: type:VARCHAR value:"c" false`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col4|col5", "int64|varchar|int64|varchar", @@ -475,7 +475,7 @@ func TestGetFields(t *testing.T) { `GetFields bv: `, `Execute bv: true`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col4|col5", "int64|varchar|int64|varchar", diff --git a/go/vt/vtgate/engine/limit.go b/go/vt/vtgate/engine/limit.go index 4ef809ad1fa..8be186f66bd 100644 --- a/go/vt/vtgate/engine/limit.go +++ b/go/vt/vtgate/engine/limit.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "strconv" + "sync" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -97,8 +98,11 @@ func (l *Limit) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars // the offset in memory from the result of the scatter query with count + offset. bindVars["__upper_limit"] = sqltypes.Int64BindVariable(int64(count + offset)) + var mu sync.Mutex err = vcursor.StreamExecutePrimitive(ctx, l.Input, bindVars, wantfields, func(qr *sqltypes.Result) error { - if len(qr.Fields) != 0 { + mu.Lock() + defer mu.Unlock() + if wantfields && len(qr.Fields) != 0 { if err := callback(&sqltypes.Result{Fields: qr.Fields}); err != nil { return err } diff --git a/go/vt/vtgate/engine/limit_test.go b/go/vt/vtgate/engine/limit_test.go index d5c6602f820..8b91dadecb5 100644 --- a/go/vt/vtgate/engine/limit_test.go +++ b/go/vt/vtgate/engine/limit_test.go @@ -130,7 +130,7 @@ func TestLimitExecute(t *testing.T) { results: []*sqltypes.Result{inputResult}, } l = &Limit{ - Count: evalengine.NewBindVar("l", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + Count: evalengine.NewBindVar("l", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)), Input: fp, } @@ -343,8 +343,8 @@ func TestLimitOffsetExecute(t *testing.T) { } l = &Limit{ - Count: evalengine.NewBindVar("l", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), - Offset: evalengine.NewBindVar("o", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + Count: evalengine.NewBindVar("l", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)), + Offset: evalengine.NewBindVar("o", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)), Input: fp, } result, err = l.TryExecute(context.Background(), &noopVCursor{}, map[string]*querypb.BindVariable{"l": sqltypes.Int64BindVariable(1), "o": sqltypes.Int64BindVariable(1)}, false) @@ -396,7 +396,7 @@ func TestLimitStreamExecute(t *testing.T) { // Test with bind vars. fp.rewind() - l.Count = evalengine.NewBindVar("l", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + l.Count = evalengine.NewBindVar("l", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) results = nil err = l.TryStreamExecute(context.Background(), &noopVCursor{}, map[string]*querypb.BindVariable{"l": sqltypes.Int64BindVariable(2)}, true, func(qr *sqltypes.Result) error { results = append(results, qr) @@ -451,6 +451,73 @@ func TestLimitStreamExecute(t *testing.T) { } } +func TestLimitStreamExecuteAsync(t *testing.T) { + bindVars := make(map[string]*querypb.BindVariable) + fields := sqltypes.MakeTestFields( + "col1|col2", + "int64|varchar", + ) + inputResults := sqltypes.MakeTestStreamingResults( + fields, + "a|1", + "b|2", + "d|3", + "e|4", + "a|1", + "b|2", + "d|3", + "e|4", + "---", + "c|7", + "x|8", + "y|9", + "c|7", + "x|8", + "y|9", + "c|7", + "x|8", + "y|9", + "---", + "l|4", + "m|5", + "n|6", + "l|4", + "m|5", + "n|6", + "l|4", + "m|5", + "n|6", + ) + fp := &fakePrimitive{ + results: inputResults, + async: true, + } + + const maxCount = 26 + for i := 0; i <= maxCount*20; i++ { + expRows := i + l := &Limit{ + Count: evalengine.NewLiteralInt(int64(expRows)), + Input: fp, + } + // Test with limit smaller than input. + results := &sqltypes.Result{} + + err := l.TryStreamExecute(context.Background(), &noopVCursor{}, bindVars, true, func(qr *sqltypes.Result) error { + if qr != nil { + results.Rows = append(results.Rows, qr.Rows...) + } + return nil + }) + require.NoError(t, err) + if expRows > maxCount { + expRows = maxCount + } + require.Len(t, results.Rows, expRows) + } + +} + func TestOffsetStreamExecute(t *testing.T) { bindVars := make(map[string]*querypb.BindVariable) fields := sqltypes.MakeTestFields( @@ -540,7 +607,7 @@ func TestLimitInputFail(t *testing.T) { func TestLimitInvalidCount(t *testing.T) { l := &Limit{ - Count: evalengine.NewBindVar("l", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + Count: evalengine.NewBindVar("l", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)), } _, _, err := l.getCountAndOffset(context.Background(), &noopVCursor{}, nil) assert.EqualError(t, err, "query arguments missing for l") diff --git a/go/vt/vtgate/engine/lock.go b/go/vt/vtgate/engine/lock.go index c1701f6c166..7739cbcd0cc 100644 --- a/go/vt/vtgate/engine/lock.go +++ b/go/vt/vtgate/engine/lock.go @@ -38,6 +38,9 @@ var _ Primitive = (*Lock)(nil) // Lock primitive will execute sql containing lock functions type Lock struct { + noInputs + noTxNeeded + // Keyspace specifies the keyspace to send the query to. Keyspace *vindexes.Keyspace @@ -47,10 +50,6 @@ type Lock struct { FieldQuery string LockFunctions []*LockFunc - - noInputs - - noTxNeeded } type LockFunc struct { diff --git a/go/vt/vtgate/engine/memory_sort.go b/go/vt/vtgate/engine/memory_sort.go index b896b303923..4e222498f26 100644 --- a/go/vt/vtgate/engine/memory_sort.go +++ b/go/vt/vtgate/engine/memory_sort.go @@ -23,6 +23,7 @@ import ( "reflect" "strconv" "strings" + "sync" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" @@ -101,7 +102,11 @@ func (ms *MemorySort) TryStreamExecute(ctx context.Context, vcursor VCursor, bin Compare: ms.OrderBy, Limit: count, } + + var mu sync.Mutex err = vcursor.StreamExecutePrimitive(ctx, ms.Input, bindVars, wantfields, func(qr *sqltypes.Result) error { + mu.Lock() + defer mu.Unlock() if len(qr.Fields) != 0 { if err := cb(&sqltypes.Result{Fields: qr.Fields}); err != nil { return err @@ -138,7 +143,7 @@ func (ms *MemorySort) NeedsTransaction() bool { func (ms *MemorySort) fetchCount(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (int, error) { if ms.UpperLimit == nil { - return math.MaxInt64, nil + return math.MaxInt, nil } env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) resolved, err := env.Evaluate(ms.UpperLimit) diff --git a/go/vt/vtgate/engine/memory_sort_test.go b/go/vt/vtgate/engine/memory_sort_test.go index bc9369c57af..21d73613158 100644 --- a/go/vt/vtgate/engine/memory_sort_test.go +++ b/go/vt/vtgate/engine/memory_sort_test.go @@ -26,17 +26,9 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) -func init() { - // We require MySQL 8.0 collations for the comparisons in the tests - mySQLVersion := "8.0.0" - servenv.SetMySQLServerVersionForTest(mySQLVersion) - collationEnv = collations.NewEnvironment(mySQLVersion) -} - func TestMemorySortExecute(t *testing.T) { fields := sqltypes.MakeTestFields( "c1|c2", @@ -75,7 +67,7 @@ func TestMemorySortExecute(t *testing.T) { utils.MustMatch(t, wantResult, result) fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} result, err = ms.TryExecute(context.Background(), &noopVCursor{}, bv, false) @@ -136,7 +128,7 @@ func TestMemorySortStreamExecuteWeightString(t *testing.T) { t.Run("Limit test", func(t *testing.T) { fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} results = nil @@ -194,7 +186,7 @@ func TestMemorySortExecuteWeightString(t *testing.T) { utils.MustMatch(t, wantResult, result) fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} result, err = ms.TryExecute(context.Background(), &noopVCursor{}, bv, false) @@ -225,11 +217,11 @@ func TestMemorySortStreamExecuteCollation(t *testing.T) { )}, } - collationID, _ := collations.Local().LookupID("utf8mb4_hu_0900_ai_ci") + collationID, _ := collations.MySQL8().LookupID("utf8mb4_hu_0900_ai_ci") ms := &MemorySort{ OrderBy: []evalengine.OrderByParams{{ Col: 0, - Type: evalengine.Type{Type: sqltypes.VarChar, Coll: collationID}, + Type: evalengine.NewType(sqltypes.VarChar, collationID), }}, Input: fp, } @@ -277,7 +269,7 @@ func TestMemorySortStreamExecuteCollation(t *testing.T) { t.Run("Limit test", func(t *testing.T) { fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} results = nil @@ -313,11 +305,11 @@ func TestMemorySortExecuteCollation(t *testing.T) { )}, } - collationID, _ := collations.Local().LookupID("utf8mb4_hu_0900_ai_ci") + collationID, _ := collations.MySQL8().LookupID("utf8mb4_hu_0900_ai_ci") ms := &MemorySort{ OrderBy: []evalengine.OrderByParams{{ Col: 0, - Type: evalengine.Type{Type: sqltypes.VarChar, Coll: collationID}, + Type: evalengine.NewType(sqltypes.VarChar, collationID), }}, Input: fp, } @@ -336,7 +328,7 @@ func TestMemorySortExecuteCollation(t *testing.T) { utils.MustMatch(t, wantResult, result) fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} result, err = ms.TryExecute(context.Background(), &noopVCursor{}, bv, false) @@ -393,7 +385,7 @@ func TestMemorySortStreamExecute(t *testing.T) { utils.MustMatch(t, wantResults, results) fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} results = nil @@ -552,7 +544,7 @@ func TestMemorySortMultiColumn(t *testing.T) { utils.MustMatch(t, wantResult, result) fp.rewind() - ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}) + ms.UpperLimit = evalengine.NewBindVar("__upper_limit", evalengine.NewType(sqltypes.Int64, collations.CollationBinaryID)) bv := map[string]*querypb.BindVariable{"__upper_limit": sqltypes.Int64BindVariable(3)} result, err = ms.TryExecute(context.Background(), &noopVCursor{}, bv, false) @@ -657,3 +649,57 @@ func TestMemorySortExecuteNoVarChar(t *testing.T) { t.Errorf("StreamExecute err: %v, want %v", err, want) } } + +func TestMemorySortStreamAsync(t *testing.T) { + fields := sqltypes.MakeTestFields( + "c1|c2", + "varbinary|decimal", + ) + fp := &fakePrimitive{ + results: sqltypes.MakeTestStreamingResults( + fields, + "a|1", + "g|2", + "a|1", + "---", + "c|3", + "g|2", + "a|1", + "---", + "c|4", + "c|3", + "g|2", + "a|1", + "---", + "c|4", + "c|3", + "g|2", + "a|1", + "---", + "c|4", + "c|3", + ), + async: true, + } + + ms := &MemorySort{ + OrderBy: []evalengine.OrderByParams{{ + WeightStringCol: -1, + Col: 1, + }}, + Input: fp, + } + + qr := &sqltypes.Result{} + err := ms.TryStreamExecute(context.Background(), &noopVCursor{}, nil, true, func(res *sqltypes.Result) error { + qr.Rows = append(qr.Rows, res.Rows...) + return nil + }) + require.NoError(t, err) + require.NoError(t, sqltypes.RowsEqualsStr( + `[[VARBINARY("a") DECIMAL(1)] [VARBINARY("a") DECIMAL(1)] [VARBINARY("a") DECIMAL(1)] [VARBINARY("a") DECIMAL(1)] [VARBINARY("a") DECIMAL(1)] +[VARBINARY("g") DECIMAL(2)] [VARBINARY("g") DECIMAL(2)] [VARBINARY("g") DECIMAL(2)] [VARBINARY("g") DECIMAL(2)] +[VARBINARY("c") DECIMAL(3)] [VARBINARY("c") DECIMAL(3)] [VARBINARY("c") DECIMAL(3)] [VARBINARY("c") DECIMAL(3)] +[VARBINARY("c") DECIMAL(4)] [VARBINARY("c") DECIMAL(4)] [VARBINARY("c") DECIMAL(4)]]`, + qr.Rows)) +} diff --git a/go/vt/vtgate/engine/merge_sort.go b/go/vt/vtgate/engine/merge_sort.go index 3c26a383594..fac57c37ccb 100644 --- a/go/vt/vtgate/engine/merge_sort.go +++ b/go/vt/vtgate/engine/merge_sort.go @@ -48,11 +48,12 @@ var _ Primitive = (*MergeSort)(nil) // be used like other Primitives in VTGate. However, it satisfies the Primitive API // so that vdiff can use it. In that situation, only StreamExecute is used. type MergeSort struct { + noInputs + noTxNeeded + Primitives []StreamExecutor OrderBy evalengine.Comparison ScatterErrorsAsWarnings bool - noInputs - noTxNeeded } // RouteType satisfies Primitive. diff --git a/go/vt/vtgate/engine/merge_sort_test.go b/go/vt/vtgate/engine/merge_sort_test.go index 803c70ca463..6b383e12572 100644 --- a/go/vt/vtgate/engine/merge_sort_test.go +++ b/go/vt/vtgate/engine/merge_sort_test.go @@ -179,10 +179,10 @@ func TestMergeSortCollation(t *testing.T) { ), }} - collationID, _ := collations.Local().LookupID("utf8mb4_hu_0900_ai_ci") + collationID, _ := collations.MySQL8().LookupID("utf8mb4_hu_0900_ai_ci") orderBy := []evalengine.OrderByParams{{ Col: 0, - Type: evalengine.Type{Type: sqltypes.VarChar, Coll: collationID}, + Type: evalengine.NewType(sqltypes.VarChar, collationID), }} var results []*sqltypes.Result diff --git a/go/vt/vtgate/engine/mstream.go b/go/vt/vtgate/engine/mstream.go index 033196ef576..af24199026b 100644 --- a/go/vt/vtgate/engine/mstream.go +++ b/go/vt/vtgate/engine/mstream.go @@ -30,6 +30,9 @@ var _ Primitive = (*MStream)(nil) // MStream is an operator for message streaming from specific keyspace, destination type MStream struct { + noTxNeeded + noInputs + // Keyspace specifies the keyspace to stream messages from Keyspace *vindexes.Keyspace @@ -38,10 +41,6 @@ type MStream struct { // TableName specifies the table on which stream will be executed. TableName string - - noTxNeeded - - noInputs } // RouteType implements the Primitive interface diff --git a/go/vt/vtgate/engine/online_ddl.go b/go/vt/vtgate/engine/online_ddl.go index c972fee66e9..9acf55869bc 100644 --- a/go/vt/vtgate/engine/online_ddl.go +++ b/go/vt/vtgate/engine/online_ddl.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" @@ -35,16 +34,15 @@ var _ Primitive = (*OnlineDDL)(nil) // OnlineDDL represents the instructions to perform an online schema change via vtctld type OnlineDDL struct { + noTxNeeded + noInputs + Keyspace *vindexes.Keyspace DDL sqlparser.DDLStatement SQL string DDLStrategySetting *schema.DDLStrategySetting // TargetDestination specifies an explicit target destination to send the query to. TargetDestination key.Destination - - noTxNeeded - - noInputs } func (v *OnlineDDL) description() PrimitiveDescription { @@ -79,7 +77,7 @@ func (v *OnlineDDL) TryExecute(ctx context.Context, vcursor VCursor, bindVars ma { Name: "uuid", Type: sqltypes.VarChar, - Charset: uint32(collations.Default()), + Charset: uint32(vcursor.ConnCollation()), }, }, Rows: [][]sqltypes.Value{}, @@ -90,7 +88,7 @@ func (v *OnlineDDL) TryExecute(ctx context.Context, vcursor VCursor, bindVars ma migrationContext = fmt.Sprintf("vtgate:%s", vcursor.Session().GetSessionUUID()) } onlineDDLs, err := schema.NewOnlineDDLs(v.GetKeyspaceName(), v.SQL, v.DDL, - v.DDLStrategySetting, migrationContext, "", + v.DDLStrategySetting, migrationContext, "", vcursor.Environment().Parser(), ) if err != nil { return result, err diff --git a/go/vt/vtgate/engine/opcode/constants.go b/go/vt/vtgate/engine/opcode/constants.go index dd73a78974d..2fa0e9446a4 100644 --- a/go/vt/vtgate/engine/opcode/constants.go +++ b/go/vt/vtgate/engine/opcode/constants.go @@ -19,8 +19,10 @@ package opcode import ( "fmt" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) // PulloutOpcode is a number representing the opcode @@ -74,6 +76,7 @@ const ( AggregateAnyValue AggregateCountStar AggregateGroupConcat + AggregateAvg _NumOfOpCodes // This line must be last of the opcodes! ) @@ -85,6 +88,7 @@ var ( AggregateCountStar: sqltypes.Int64, AggregateSumDistinct: sqltypes.Decimal, AggregateSum: sqltypes.Decimal, + AggregateAvg: sqltypes.Decimal, AggregateGtid: sqltypes.VarChar, } ) @@ -96,6 +100,7 @@ var SupportedAggregates = map[string]AggregateOpcode{ "sum": AggregateSum, "min": AggregateMin, "max": AggregateMax, + "avg": AggregateAvg, // These functions don't exist in mysql, but are used // to display the plan. "count_distinct": AggregateCountDistinct, @@ -117,6 +122,7 @@ var AggregateName = map[AggregateOpcode]string{ AggregateCountStar: "count_star", AggregateGroupConcat: "group_concat", AggregateAnyValue: "any_value", + AggregateAvg: "avg", } func (code AggregateOpcode) String() string { @@ -134,7 +140,7 @@ func (code AggregateOpcode) MarshalJSON() ([]byte, error) { } // Type returns the opcode return sql type, and a bool telling is we are sure about this type or not -func (code AggregateOpcode) Type(typ querypb.Type) querypb.Type { +func (code AggregateOpcode) SQLType(typ querypb.Type) querypb.Type { switch code { case AggregateUnassigned: return sqltypes.Null @@ -148,7 +154,7 @@ func (code AggregateOpcode) Type(typ querypb.Type) querypb.Type { return sqltypes.Text case AggregateMax, AggregateMin, AggregateAnyValue: return typ - case AggregateSumDistinct, AggregateSum: + case AggregateSumDistinct, AggregateSum, AggregateAvg: if typ == sqltypes.Unknown { return sqltypes.Unknown } @@ -165,6 +171,28 @@ func (code AggregateOpcode) Type(typ querypb.Type) querypb.Type { } } +func (code AggregateOpcode) Nullable() bool { + switch code { + case AggregateCount, AggregateCountStar: + return false + default: + return true + } +} + +func (code AggregateOpcode) ResolveType(t evalengine.Type, env *collations.Environment) evalengine.Type { + sqltype := code.SQLType(t.Type()) + collation := collations.CollationForType(sqltype, env.DefaultConnectionCharset()) + nullable := code.Nullable() + size := t.Size() + + scale := t.Scale() + if code == AggregateAvg { + scale += 4 + } + return evalengine.NewTypeEx(sqltype, collation, nullable, size, scale) +} + func (code AggregateOpcode) NeedsComparableValues() bool { switch code { case AggregateCountDistinct, AggregateSumDistinct, AggregateMin, AggregateMax: diff --git a/go/vt/vtgate/engine/opcode/constants_test.go b/go/vt/vtgate/engine/opcode/constants_test.go index 50cfc49a71c..5687a42433d 100644 --- a/go/vt/vtgate/engine/opcode/constants_test.go +++ b/go/vt/vtgate/engine/opcode/constants_test.go @@ -17,14 +17,147 @@ limitations under the License. package opcode import ( + "encoding/json" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" ) func TestCheckAllAggrOpCodes(t *testing.T) { // This test is just checking that we never reach the panic when using Type() on valid opcodes for i := AggregateOpcode(0); i < _NumOfOpCodes; i++ { - i.Type(sqltypes.Null) + i.SQLType(sqltypes.Null) + } +} + +func TestType(t *testing.T) { + tt := []struct { + opcode AggregateOpcode + typ querypb.Type + out querypb.Type + }{ + {AggregateUnassigned, sqltypes.VarChar, sqltypes.Null}, + {AggregateGroupConcat, sqltypes.VarChar, sqltypes.Text}, + {AggregateGroupConcat, sqltypes.Blob, sqltypes.Blob}, + {AggregateGroupConcat, sqltypes.Unknown, sqltypes.Unknown}, + {AggregateMax, sqltypes.Int64, sqltypes.Int64}, + {AggregateMax, sqltypes.Float64, sqltypes.Float64}, + {AggregateSumDistinct, sqltypes.Unknown, sqltypes.Unknown}, + {AggregateSumDistinct, sqltypes.Int64, sqltypes.Decimal}, + {AggregateSumDistinct, sqltypes.Decimal, sqltypes.Decimal}, + {AggregateCount, sqltypes.Int32, sqltypes.Int64}, + {AggregateCountStar, sqltypes.Int64, sqltypes.Int64}, + {AggregateGtid, sqltypes.VarChar, sqltypes.VarChar}, + } + + for _, tc := range tt { + t.Run(tc.opcode.String()+"_"+tc.typ.String(), func(t *testing.T) { + out := tc.opcode.SQLType(tc.typ) + assert.Equal(t, tc.out, out) + }) + } +} + +func TestType_Panic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + errMsg, ok := r.(string) + assert.True(t, ok, "Expected a string panic message") + assert.Contains(t, errMsg, "ERROR", "Expected panic message containing 'ERROR'") + } + }() + AggregateOpcode(999).SQLType(sqltypes.VarChar) +} + +func TestNeedsListArg(t *testing.T) { + tt := []struct { + opcode PulloutOpcode + out bool + }{ + {PulloutValue, false}, + {PulloutIn, true}, + {PulloutNotIn, true}, + {PulloutExists, false}, + {PulloutNotExists, false}, + } + + for _, tc := range tt { + t.Run(tc.opcode.String(), func(t *testing.T) { + out := tc.opcode.NeedsListArg() + assert.Equal(t, tc.out, out) + }) + } +} + +func TestPulloutOpcode_MarshalJSON(t *testing.T) { + tt := []struct { + opcode PulloutOpcode + out string + }{ + {PulloutValue, "\"PulloutValue\""}, + {PulloutIn, "\"PulloutIn\""}, + {PulloutNotIn, "\"PulloutNotIn\""}, + {PulloutExists, "\"PulloutExists\""}, + {PulloutNotExists, "\"PulloutNotExists\""}, + } + + for _, tc := range tt { + t.Run(tc.opcode.String(), func(t *testing.T) { + out, err := json.Marshal(tc.opcode) + require.NoError(t, err, "Unexpected error") + assert.Equal(t, tc.out, string(out)) + }) + } +} + +func TestAggregateOpcode_MarshalJSON(t *testing.T) { + tt := []struct { + opcode AggregateOpcode + out string + }{ + {AggregateCount, "\"count\""}, + {AggregateSum, "\"sum\""}, + {AggregateMin, "\"min\""}, + {AggregateMax, "\"max\""}, + {AggregateCountDistinct, "\"count_distinct\""}, + {AggregateSumDistinct, "\"sum_distinct\""}, + {AggregateGtid, "\"vgtid\""}, + {AggregateCountStar, "\"count_star\""}, + {AggregateGroupConcat, "\"group_concat\""}, + {AggregateAnyValue, "\"any_value\""}, + {AggregateAvg, "\"avg\""}, + {999, "\"ERROR\""}, + } + + for _, tc := range tt { + t.Run(tc.opcode.String(), func(t *testing.T) { + out, err := json.Marshal(tc.opcode) + require.NoError(t, err, "Unexpected error") + assert.Equal(t, tc.out, string(out)) + }) + } +} + +func TestNeedsComparableValues(t *testing.T) { + for i := AggregateOpcode(0); i < _NumOfOpCodes; i++ { + if i == AggregateCountDistinct || i == AggregateSumDistinct || i == AggregateMin || i == AggregateMax { + assert.True(t, i.NeedsComparableValues()) + } else { + assert.False(t, i.NeedsComparableValues()) + } + } +} + +func TestIsDistinct(t *testing.T) { + for i := AggregateOpcode(0); i < _NumOfOpCodes; i++ { + if i == AggregateCountDistinct || i == AggregateSumDistinct { + assert.True(t, i.IsDistinct()) + } else { + assert.False(t, i.IsDistinct()) + } } } diff --git a/go/vt/vtgate/engine/ordered_aggregate.go b/go/vt/vtgate/engine/ordered_aggregate.go index 07ea06fa5fd..ade8cd00299 100644 --- a/go/vt/vtgate/engine/ordered_aggregate.go +++ b/go/vt/vtgate/engine/ordered_aggregate.go @@ -28,13 +28,6 @@ import ( "vitess.io/vitess/go/vt/vtgate/evalengine" ) -var ( - // Some predefined values - countZero = sqltypes.MakeTrusted(sqltypes.Int64, []byte("0")) - countOne = sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")) - sumZero = sqltypes.MakeTrusted(sqltypes.Decimal, []byte("0")) -) - var _ Primitive = (*OrderedAggregate)(nil) // OrderedAggregate is a primitive that expects the underlying primitive @@ -58,6 +51,8 @@ type OrderedAggregate struct { // Input is the primitive that will feed into this Primitive. Input Primitive + + CollationEnv *collations.Environment } // GroupByParams specify the grouping key to be used. @@ -67,6 +62,7 @@ type GroupByParams struct { Expr sqlparser.Expr FromGroupBy bool Type evalengine.Type + CollationEnv *collations.Environment } // String returns a string. Used for plan descriptions @@ -78,8 +74,8 @@ func (gbp GroupByParams) String() string { out = fmt.Sprintf("(%d|%d)", gbp.KeyCol, gbp.WeightStringCol) } - if sqltypes.IsText(gbp.Type.Type) && gbp.Type.Coll != collations.Unknown { - out += " COLLATE " + collations.Local().LookupName(gbp.Type.Coll) + if sqltypes.IsText(gbp.Type.Type()) && gbp.Type.Collation() != collations.Unknown { + out += " COLLATE " + gbp.CollationEnv.LookupName(gbp.Type.Collation()) } return out @@ -348,14 +344,14 @@ func (oa *OrderedAggregate) nextGroupBy(currentKey, nextRow []sqltypes.Value) (n return nextRow, true, nil } - cmp, err := evalengine.NullsafeCompare(v1, v2, gb.Type.Coll) + cmp, err := evalengine.NullsafeCompare(v1, v2, oa.CollationEnv, gb.Type.Collation()) if err != nil { _, isCollationErr := err.(evalengine.UnsupportedCollationError) if !isCollationErr || gb.WeightStringCol == -1 { return nil, false, err } gb.KeyCol = gb.WeightStringCol - cmp, err = evalengine.NullsafeCompare(currentKey[gb.WeightStringCol], nextRow[gb.WeightStringCol], gb.Type.Coll) + cmp, err = evalengine.NullsafeCompare(currentKey[gb.WeightStringCol], nextRow[gb.WeightStringCol], oa.CollationEnv, gb.Type.Collation()) if err != nil { return nil, false, err } diff --git a/go/vt/vtgate/engine/ordered_aggregate_test.go b/go/vt/vtgate/engine/ordered_aggregate_test.go index 2eca4fc7ba9..3eaa63819e4 100644 --- a/go/vt/vtgate/engine/ordered_aggregate_test.go +++ b/go/vt/vtgate/engine/ordered_aggregate_test.go @@ -32,19 +32,9 @@ import ( "vitess.io/vitess/go/test/utils" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/servenv" . "vitess.io/vitess/go/vt/vtgate/engine/opcode" ) -var collationEnv *collations.Environment - -func init() { - // We require MySQL 8.0 collations for the comparisons in the tests - mySQLVersion := "8.0.0" - servenv.SetMySQLServerVersionForTest(mySQLVersion) - collationEnv = collations.NewEnvironment(mySQLVersion) -} - func TestOrderedAggregateExecute(t *testing.T) { fields := sqltypes.MakeTestFields( "col|count(*)", @@ -62,7 +52,7 @@ func TestOrderedAggregateExecute(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -94,7 +84,7 @@ func TestOrderedAggregateExecuteTruncate(t *testing.T) { )}, } - aggr := NewAggregateParam(AggregateSum, 1, "") + aggr := NewAggregateParam(AggregateSum, 1, "", collations.MySQL8()) aggr.OrigOpcode = AggregateCountStar oa := &OrderedAggregate{ @@ -134,7 +124,7 @@ func TestMinMaxFailsCorrectly(t *testing.T) { )}, } - aggr := NewAggregateParam(AggregateMax, 0, "") + aggr := NewAggregateParam(AggregateMax, 0, "", collations.MySQL8()) aggr.WCol = 1 oa := &ScalarAggregate{ Aggregates: []*AggregateParams{aggr}, @@ -163,7 +153,7 @@ func TestOrderedAggregateStreamExecute(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -202,7 +192,7 @@ func TestOrderedAggregateStreamExecuteTruncate(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 2}}, TruncateColumnCount: 2, Input: fp, @@ -305,8 +295,8 @@ func TestOrderedAggregateExecuteCountDistinct(t *testing.T) { )}, } - aggr1 := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct col2)") - aggr2 := NewAggregateParam(AggregateSum, 2, "") + aggr1 := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct col2)", collations.MySQL8()) + aggr2 := NewAggregateParam(AggregateSum, 2, "", collations.MySQL8()) aggr2.OrigOpcode = AggregateCountStar oa := &OrderedAggregate{ Aggregates: []*AggregateParams{aggr1, aggr2}, @@ -374,12 +364,12 @@ func TestOrderedAggregateStreamCountDistinct(t *testing.T) { )}, } - aggr2 := NewAggregateParam(AggregateSum, 2, "") + aggr2 := NewAggregateParam(AggregateSum, 2, "", collations.MySQL8()) aggr2.OrigOpcode = AggregateCountDistinct oa := &OrderedAggregate{ Aggregates: []*AggregateParams{ - NewAggregateParam(AggregateCountDistinct, 1, "count(distinct col2)"), + NewAggregateParam(AggregateCountDistinct, 1, "count(distinct col2)", collations.MySQL8()), aggr2}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, @@ -460,8 +450,8 @@ func TestOrderedAggregateSumDistinctGood(t *testing.T) { oa := &OrderedAggregate{ Aggregates: []*AggregateParams{ - NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct col2)"), - NewAggregateParam(AggregateSum, 2, ""), + NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct col2)", collations.MySQL8()), + NewAggregateParam(AggregateSum, 2, "", collations.MySQL8()), }, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, @@ -504,7 +494,7 @@ func TestOrderedAggregateSumDistinctTolerateError(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct col2)")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct col2)", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -536,7 +526,7 @@ func TestOrderedAggregateKeysFail(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -566,7 +556,7 @@ func TestOrderedAggregateMergeFail(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -627,7 +617,7 @@ func TestOrderedAggregateExecuteGtid(t *testing.T) { } oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateGtid, 1, "vgtid")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateGtid, 1, "vgtid", collations.MySQL8())}, TruncateColumnCount: 2, Input: fp, } @@ -660,7 +650,7 @@ func TestCountDistinctOnVarchar(t *testing.T) { )}, } - aggr := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)") + aggr := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)", collations.MySQL8()) aggr.WCol = 2 oa := &OrderedAggregate{ Aggregates: []*AggregateParams{aggr}, @@ -720,7 +710,7 @@ func TestCountDistinctOnVarcharWithNulls(t *testing.T) { )}, } - aggr := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)") + aggr := NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)", collations.MySQL8()) aggr.WCol = 2 oa := &OrderedAggregate{ Aggregates: []*AggregateParams{aggr}, @@ -782,7 +772,7 @@ func TestSumDistinctOnVarcharWithNulls(t *testing.T) { )}, } - aggr := NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct c2)") + aggr := NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct c2)", collations.MySQL8()) aggr.WCol = 2 oa := &OrderedAggregate{ Aggregates: []*AggregateParams{aggr}, @@ -848,8 +838,8 @@ func TestMultiDistinct(t *testing.T) { oa := &OrderedAggregate{ Aggregates: []*AggregateParams{ - NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)"), - NewAggregateParam(AggregateSumDistinct, 2, "sum(distinct c3)"), + NewAggregateParam(AggregateCountDistinct, 1, "count(distinct c2)", collations.MySQL8()), + NewAggregateParam(AggregateSumDistinct, 2, "sum(distinct c3)", collations.MySQL8()), }, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, @@ -904,10 +894,11 @@ func TestOrderedAggregateCollate(t *testing.T) { )}, } + collationEnv := collations.MySQL8() collationID, _ := collationEnv.LookupID("utf8mb4_0900_ai_ci") oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, - GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.Type{Coll: collationID}}}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collationEnv)}, + GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.NewType(sqltypes.Unknown, collationID)}}, Input: fp, } @@ -942,10 +933,11 @@ func TestOrderedAggregateCollateAS(t *testing.T) { )}, } + collationEnv := collations.MySQL8() collationID, _ := collationEnv.LookupID("utf8mb4_0900_as_ci") oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, - GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.Type{Coll: collationID}}}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collationEnv)}, + GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.NewType(sqltypes.Unknown, collationID)}}, Input: fp, } @@ -982,10 +974,11 @@ func TestOrderedAggregateCollateKS(t *testing.T) { )}, } + collationEnv := collations.MySQL8() collationID, _ := collationEnv.LookupID("utf8mb4_ja_0900_as_cs_ks") oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "")}, - GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.Type{Coll: collationID}}}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateSum, 1, "", collationEnv)}, + GroupByKeys: []*GroupByParams{{KeyCol: 0, Type: evalengine.NewType(sqltypes.Unknown, collationID)}}, Input: fp, } @@ -1066,7 +1059,7 @@ func TestGroupConcatWithAggrOnEngine(t *testing.T) { t.Run(tcase.name, func(t *testing.T) { fp := &fakePrimitive{results: []*sqltypes.Result{tcase.inputResult}} oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "group_concat(c2)")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "group_concat(c2)", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } @@ -1145,7 +1138,7 @@ func TestGroupConcat(t *testing.T) { t.Run(tcase.name, func(t *testing.T) { fp := &fakePrimitive{results: []*sqltypes.Result{tcase.inputResult}} oa := &OrderedAggregate{ - Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "")}, + Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "", collations.MySQL8())}, GroupByKeys: []*GroupByParams{{KeyCol: 0}}, Input: fp, } diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go index a3a37f97fe4..1c0e7de7a19 100644 --- a/go/vt/vtgate/engine/primitive.go +++ b/go/vt/vtgate/engine/primitive.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -87,7 +88,9 @@ type ( Session() SessionActions ConnCollation() collations.ID + Environment() *vtenv.Environment TimeZone() *time.Location + SQLMode() string ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) diff --git a/go/vt/vtgate/engine/projection.go b/go/vt/vtgate/engine/projection.go index e0055baa757..77e07203476 100644 --- a/go/vt/vtgate/engine/projection.go +++ b/go/vt/vtgate/engine/projection.go @@ -31,10 +31,11 @@ var _ Primitive = (*Projection)(nil) // Projection can evaluate expressions and project the results type Projection struct { + noTxNeeded + Cols []string Exprs []evalengine.Expr Input Primitive - noTxNeeded } // RouteType implements the Primitive interface @@ -88,8 +89,11 @@ func (p *Projection) TryStreamExecute(ctx context.Context, vcursor VCursor, bind env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) var once sync.Once var fields []*querypb.Field + var mu sync.Mutex return vcursor.StreamExecutePrimitive(ctx, p.Input, bindVars, wantfields, func(qr *sqltypes.Result) error { var err error + mu.Lock() + defer mu.Unlock() if wantfields { once.Do(func() { fields, err = p.evalFields(env, qr.Fields) @@ -149,15 +153,17 @@ func (p *Projection) evalFields(env *evalengine.ExpressionEnv, infields []*query if err != nil { return nil, err } - fl := mysql.FlagsForColumn(typ.Type, typ.Coll) - if !sqltypes.IsNull(typ.Type) && !typ.Nullable { + fl := mysql.FlagsForColumn(typ.Type(), typ.Collation()) + if !sqltypes.IsNull(typ.Type()) && !typ.Nullable() { fl |= uint32(querypb.MySqlFlag_NOT_NULL_FLAG) } fields = append(fields, &querypb.Field{ - Name: col, - Type: typ.Type, - Charset: uint32(typ.Coll), - Flags: fl, + Name: col, + Type: typ.Type(), + Charset: uint32(typ.Collation()), + ColumnLength: uint32(typ.Size()), + Decimals: uint32(typ.Scale()), + Flags: fl, }) } return fields, nil diff --git a/go/vt/vtgate/engine/projection_test.go b/go/vt/vtgate/engine/projection_test.go index 37d1730e2e1..af3b23fdf03 100644 --- a/go/vt/vtgate/engine/projection_test.go +++ b/go/vt/vtgate/engine/projection_test.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -38,7 +39,10 @@ func TestMultiply(t *testing.T) { Left: &sqlparser.Offset{V: 0}, Right: &sqlparser.Offset{V: 1}, } - evalExpr, err := evalengine.Translate(expr, nil) + evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ + Environment: vtenv.NewTestEnv(), + Collation: collations.MySQL8().DefaultConnectionCharset(), + }) require.NoError(t, err) fp := &fakePrimitive{ results: []*sqltypes.Result{sqltypes.MakeTestResult( @@ -72,13 +76,62 @@ func TestMultiply(t *testing.T) { assert.Equal(t, "[[UINT64(6)] [UINT64(0)] [UINT64(2)]]", fmt.Sprintf("%v", qr.Rows)) } +func TestProjectionStreaming(t *testing.T) { + expr := &sqlparser.BinaryExpr{ + Operator: sqlparser.MultOp, + Left: &sqlparser.Offset{V: 0}, + Right: &sqlparser.Offset{V: 1}, + } + evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ + Environment: vtenv.NewTestEnv(), + Collation: collations.MySQL8().DefaultConnectionCharset(), + }) + require.NoError(t, err) + fp := &fakePrimitive{ + results: sqltypes.MakeTestStreamingResults( + sqltypes.MakeTestFields("a|b", "uint64|uint64"), + "3|2", + "1|0", + "6|2", + "---", + "3|2", + "---", + "1|0", + "---", + "1|2", + "4|2", + "---", + "5|5", + "4|10", + ), + async: true, + } + proj := &Projection{ + Cols: []string{"apa"}, + Exprs: []evalengine.Expr{evalExpr}, + Input: fp, + } + + qr := &sqltypes.Result{} + err = proj.TryStreamExecute(context.Background(), &noopVCursor{}, nil, true, func(result *sqltypes.Result) error { + qr.Rows = append(qr.Rows, result.Rows...) + return nil + }) + require.NoError(t, err) + require.NoError(t, sqltypes.RowsEqualsStr(`[[UINT64(25)] [UINT64(40)] [UINT64(6)] [UINT64(2)] [UINT64(8)] [UINT64(0)] [UINT64(6)] [UINT64(0)] [UINT64(12)]]`, + qr.Rows)) +} + func TestEmptyInput(t *testing.T) { expr := &sqlparser.BinaryExpr{ Operator: sqlparser.MultOp, Left: &sqlparser.Offset{V: 0}, Right: &sqlparser.Offset{V: 1}, } - evalExpr, err := evalengine.Translate(expr, nil) + evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ + Environment: vtenv.NewTestEnv(), + Collation: collations.MySQL8().DefaultConnectionCharset(), + }) require.NoError(t, err) fp := &fakePrimitive{ results: []*sqltypes.Result{sqltypes.MakeTestResult(sqltypes.MakeTestFields("a|b", "uint64|uint64"))}, @@ -93,22 +146,25 @@ func TestEmptyInput(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[]", fmt.Sprintf("%v", qr.Rows)) - //fp = &fakePrimitive{ + // fp = &fakePrimitive{ // results: []*sqltypes.Result{sqltypes.MakeTestResult( // sqltypes.MakeTestFields("a|b", "uint64|uint64"), // "3|2", // "1|0", // "1|2", // )}, - //} - //proj.Input = fp - //qr, err = wrapStreamExecute(proj, newNoopVCursor(context.Background()), nil, true) - //require.NoError(t, err) - //assert.Equal(t, "[[UINT64(6)] [UINT64(0)] [UINT64(2)]]", fmt.Sprintf("%v", qr.Rows)) + // } + // proj.Input = fp + // qr, err = wrapStreamExecute(proj, newNoopVCursor(context.Background()), nil, true) + // require.NoError(t, err) + // assert.Equal(t, "[[UINT64(6)] [UINT64(0)] [UINT64(2)]]", fmt.Sprintf("%v", qr.Rows)) } func TestHexAndBinaryArgument(t *testing.T) { - hexExpr, err := evalengine.Translate(sqlparser.NewArgument("vtg1"), nil) + hexExpr, err := evalengine.Translate(sqlparser.NewArgument("vtg1"), &evalengine.Config{ + Environment: vtenv.NewTestEnv(), + Collation: collations.MySQL8().DefaultConnectionCharset(), + }) require.NoError(t, err) proj := &Projection{ Cols: []string{"hex"}, @@ -140,7 +196,7 @@ func TestFields(t *testing.T) { name: `string`, bindVar: sqltypes.StringBindVariable("test"), typ: querypb.Type_VARCHAR, - collation: collations.Default(), + collation: collations.MySQL8().DefaultConnectionCharset(), }, { name: `binary`, @@ -152,7 +208,10 @@ func TestFields(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - bindExpr, err := evalengine.Translate(sqlparser.NewArgument("vtg1"), nil) + bindExpr, err := evalengine.Translate(sqlparser.NewArgument("vtg1"), &evalengine.Config{ + Environment: vtenv.NewTestEnv(), + Collation: collations.MySQL8().DefaultConnectionCharset(), + }) require.NoError(t, err) proj := &Projection{ Cols: []string{"col"}, diff --git a/go/vt/vtgate/engine/rename_fields.go b/go/vt/vtgate/engine/rename_fields.go index e1dc7cbbb43..3fdab364468 100644 --- a/go/vt/vtgate/engine/rename_fields.go +++ b/go/vt/vtgate/engine/rename_fields.go @@ -28,10 +28,11 @@ var _ Primitive = (*RenameFields)(nil) // RenameFields is a primitive that renames the fields type RenameFields struct { + noTxNeeded + Cols []string Indices []int Input Primitive - noTxNeeded } // NewRenameField creates a new rename field diff --git a/go/vt/vtgate/engine/replace_variables.go b/go/vt/vtgate/engine/replace_variables.go index 66375266427..d3184d8756b 100644 --- a/go/vt/vtgate/engine/replace_variables.go +++ b/go/vt/vtgate/engine/replace_variables.go @@ -27,8 +27,8 @@ var _ Primitive = (*ReplaceVariables)(nil) // ReplaceVariables is used in SHOW VARIABLES statements so that it replaces the values for vitess-aware variables type ReplaceVariables struct { - Input Primitive noTxNeeded + Input Primitive } // NewReplaceVariables is used to create a new ReplaceVariables primitive diff --git a/go/vt/vtgate/engine/revert_migration.go b/go/vt/vtgate/engine/revert_migration.go index e7237d01da4..a7690d07f42 100644 --- a/go/vt/vtgate/engine/revert_migration.go +++ b/go/vt/vtgate/engine/revert_migration.go @@ -34,14 +34,13 @@ var _ Primitive = (*RevertMigration)(nil) // RevertMigration represents the instructions to perform an online schema change via vtctld type RevertMigration struct { + noTxNeeded + noInputs + Keyspace *vindexes.Keyspace Stmt *sqlparser.RevertMigration Query string TargetDestination key.Destination - - noTxNeeded - - noInputs } func (v *RevertMigration) description() PrimitiveDescription { @@ -88,7 +87,7 @@ func (v *RevertMigration) TryExecute(ctx context.Context, vcursor VCursor, bindV return nil, err } ddlStrategySetting.Strategy = schema.DDLStrategyOnline // and we keep the options as they were - onlineDDL, err := schema.NewOnlineDDL(v.GetKeyspaceName(), "", sql, ddlStrategySetting, fmt.Sprintf("vtgate:%s", vcursor.Session().GetSessionUUID()), "") + onlineDDL, err := schema.NewOnlineDDL(v.GetKeyspaceName(), "", sql, ddlStrategySetting, fmt.Sprintf("vtgate:%s", vcursor.Session().GetSessionUUID()), "", vcursor.Environment().Parser()) if err != nil { return result, err } diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index 30713f45f91..1a0c17d6be5 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -52,6 +52,12 @@ var ( // Route represents the instructions to route a read query to // one or many vttablets. type Route struct { + // Route does not take inputs + noInputs + + // Route does not need transaction handling + noTxNeeded + // TargetTabletType specifies an explicit target destination tablet type // this is only used in conjunction with TargetDestination TargetTabletType topodatapb.TabletType @@ -89,12 +95,6 @@ type Route struct { // select count(*) from tbl where lookupColumn = 'not there' // select exists() NoRoutesSpecialHandling bool - - // Route does not take inputs - noInputs - - // Route does not need transaction handling - noTxNeeded } // NewRoute creates a Route. diff --git a/go/vt/vtgate/engine/route_test.go b/go/vt/vtgate/engine/route_test.go index 274ac58c7d4..7120ba53172 100644 --- a/go/vt/vtgate/engine/route_test.go +++ b/go/vt/vtgate/engine/route_test.go @@ -31,7 +31,6 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -46,13 +45,6 @@ var defaultSelectResult = sqltypes.MakeTestResult( "1", ) -func init() { - // We require MySQL 8.0 collations for the comparisons in the tests - mySQLVersion := "8.0.0" - servenv.SetMySQLServerVersionForTest(mySQLVersion) - collationEnv = collations.NewEnvironment(mySQLVersion) -} - func TestSelectUnsharded(t *testing.T) { sel := NewRoute( Unsharded, @@ -74,7 +66,7 @@ func TestSelectUnsharded(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -83,7 +75,7 @@ func TestSelectUnsharded(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `StreamExecuteMulti dummy_select ks.0: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T) { @@ -219,7 +211,7 @@ func TestSelectScatter(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -228,7 +220,7 @@ func TestSelectScatter(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectEqualUnique(t *testing.T) { @@ -257,7 +249,7 @@ func TestSelectEqualUnique(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -266,7 +258,7 @@ func TestSelectEqualUnique(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectNone(t *testing.T) { @@ -290,7 +282,7 @@ func TestSelectNone(t *testing.T) { result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) require.NoError(t, err) require.Empty(t, vc.log) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -308,7 +300,7 @@ func TestSelectNone(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -317,7 +309,7 @@ func TestSelectNone(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) } func TestSelectEqualUniqueScatter(t *testing.T) { @@ -351,7 +343,7 @@ func TestSelectEqualUniqueScatter(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyRange(-)`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -360,7 +352,7 @@ func TestSelectEqualUniqueScatter(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyRange(-)`, `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectEqual(t *testing.T) { @@ -403,7 +395,7 @@ func TestSelectEqual(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceIDs(00,80)`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -413,7 +405,7 @@ func TestSelectEqual(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceIDs(00,80)`, `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectEqualNoRoute(t *testing.T) { @@ -443,7 +435,7 @@ func TestSelectEqualNoRoute(t *testing.T) { `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, }) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -466,7 +458,7 @@ func TestSelectEqualNoRoute(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -477,7 +469,7 @@ func TestSelectEqualNoRoute(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) } func TestINUnique(t *testing.T) { @@ -513,7 +505,7 @@ func TestINUnique(t *testing.T) { `ks.20-: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"4"}} ` + `false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -522,7 +514,7 @@ func TestINUnique(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, `StreamExecuteMulti dummy_select ks.-20: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ks.20-: {__vals: type:TUPLE values:{type:INT64 value:"4"}} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestINNonUnique(t *testing.T) { @@ -579,7 +571,7 @@ func TestINNonUnique(t *testing.T) { `ks.20-: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"4"}} ` + `false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -589,7 +581,7 @@ func TestINNonUnique(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceIDs(00,80),DestinationKeyspaceIDs(00),DestinationKeyspaceIDs(80)`, `StreamExecuteMulti dummy_select ks.-20: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ks.20-: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"4"}} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestMultiEqual(t *testing.T) { @@ -623,7 +615,7 @@ func TestMultiEqual(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -632,7 +624,7 @@ func TestMultiEqual(t *testing.T) { `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectLike(t *testing.T) { @@ -670,7 +662,7 @@ func TestSelectLike(t *testing.T) { `ResolveDestinations ks [type:VARCHAR value:"a%"] Destinations:DestinationKeyRange(0c-0d)`, `ExecuteMultiShard ks.-0c80: dummy_select {} ks.0c80-0d: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() @@ -681,7 +673,7 @@ func TestSelectLike(t *testing.T) { `ResolveDestinations ks [type:VARCHAR value:"a%"] Destinations:DestinationKeyRange(0c-0d)`, `StreamExecuteMulti dummy_select ks.-0c80: {} ks.0c80-0d: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() @@ -700,7 +692,7 @@ func TestSelectLike(t *testing.T) { `ResolveDestinations ks [type:VARCHAR value:"ab%"] Destinations:DestinationKeyRange(0c92-0c93)`, `ExecuteMultiShard ks.0c80-0d: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() @@ -711,7 +703,7 @@ func TestSelectLike(t *testing.T) { `ResolveDestinations ks [type:VARCHAR value:"ab%"] Destinations:DestinationKeyRange(0c92-0c93)`, `StreamExecuteMulti dummy_select ks.0c80-0d: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } @@ -736,7 +728,7 @@ func TestSelectNext(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -744,7 +736,7 @@ func TestSelectNext(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `StreamExecuteMulti dummy_select ks.-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectDBA(t *testing.T) { @@ -768,7 +760,7 @@ func TestSelectDBA(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -776,7 +768,7 @@ func TestSelectDBA(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectReference(t *testing.T) { @@ -800,7 +792,7 @@ func TestSelectReference(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -808,7 +800,7 @@ func TestSelectReference(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestRouteGetFields(t *testing.T) { @@ -840,7 +832,7 @@ func TestRouteGetFields(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select_field {} false false`, }) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, true) @@ -851,7 +843,7 @@ func TestRouteGetFields(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select_field {} false false`, }) - expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() // test with special no-routes handling @@ -864,7 +856,7 @@ func TestRouteGetFields(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, true) @@ -875,7 +867,7 @@ func TestRouteGetFields(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) + expectResult(t, result, &sqltypes.Result{}) } func TestRouteSort(t *testing.T) { @@ -924,7 +916,7 @@ func TestRouteSort(t *testing.T) { "2", "3", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) sel.OrderBy[0].Desc = true vc.Rewind() @@ -940,7 +932,7 @@ func TestRouteSort(t *testing.T) { "1", "1", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) vc = &loggingVCursor{ shards: []string{"0"}, @@ -1013,7 +1005,7 @@ func TestRouteSortWeightStrings(t *testing.T) { "g|d", "v|x", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) }) t.Run("Descending ordering using weighted strings", func(t *testing.T) { @@ -1032,7 +1024,7 @@ func TestRouteSortWeightStrings(t *testing.T) { "c|t", "a|a", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) }) t.Run("Error when no weight string set", func(t *testing.T) { @@ -1073,11 +1065,11 @@ func TestRouteSortCollation(t *testing.T) { "dummy_select_field", ) - collationID, _ := collations.Local().LookupID("utf8mb4_hu_0900_ai_ci") + collationID, _ := collations.MySQL8().LookupID("utf8mb4_hu_0900_ai_ci") sel.OrderBy = []evalengine.OrderByParams{{ Col: 0, - Type: evalengine.Type{Type: sqltypes.VarChar, Coll: collationID}, + Type: evalengine.NewType(sqltypes.VarChar, collationID), }} vc := &loggingVCursor{ @@ -1118,7 +1110,7 @@ func TestRouteSortCollation(t *testing.T) { "cs", "d", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) }) t.Run("Descending ordering using Collation", func(t *testing.T) { @@ -1137,13 +1129,12 @@ func TestRouteSortCollation(t *testing.T) { "c", "c", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) }) t.Run("Error when Unknown Collation", func(t *testing.T) { sel.OrderBy = []evalengine.OrderByParams{{ - Col: 0, - Type: evalengine.UnknownType(), + Col: 0, }} vc := &loggingVCursor{ @@ -1169,7 +1160,7 @@ func TestRouteSortCollation(t *testing.T) { t.Run("Error when Unsupported Collation", func(t *testing.T) { sel.OrderBy = []evalengine.OrderByParams{{ Col: 0, - Type: evalengine.Type{Coll: 1111}, + Type: evalengine.NewType(sqltypes.Unknown, 1111), }} vc := &loggingVCursor{ @@ -1239,7 +1230,7 @@ func TestRouteSortTruncate(t *testing.T) { "2", "3", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) } func TestRouteStreamTruncate(t *testing.T) { @@ -1281,7 +1272,7 @@ func TestRouteStreamTruncate(t *testing.T) { "1", "2", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) } func TestRouteStreamSortTruncate(t *testing.T) { @@ -1330,7 +1321,7 @@ func TestRouteStreamSortTruncate(t *testing.T) { "1", "2", ) - expectResult(t, "sel.Execute", result, wantResult) + expectResult(t, result, wantResult) } func TestParamsFail(t *testing.T) { @@ -1432,7 +1423,7 @@ func TestExecFail(t *testing.T) { `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() vc.resultErr = sqlerror.NewSQLError(sqlerror.ERQueryInterrupted, "", "query timeout -20") @@ -1474,7 +1465,7 @@ func TestSelectEqualUniqueMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, `ExecuteMultiShard ks.-20: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -1483,7 +1474,7 @@ func TestSelectEqualUniqueMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, `StreamExecuteMulti dummy_select ks.-20: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestSelectEqualMultiColumnVindex(t *testing.T) { @@ -1511,7 +1502,7 @@ func TestSelectEqualMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(32)]] Destinations:DestinationKeyRange(20-21)`, `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -1520,7 +1511,7 @@ func TestSelectEqualMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(32)]] Destinations:DestinationKeyRange(20-21)`, `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestINMultiColumnVindex(t *testing.T) { @@ -1557,7 +1548,7 @@ func TestINMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)] [INT64(2) INT64(3)] [INT64(2) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe),DestinationKeyspaceID(024eb190c9a2fa169c),DestinationKeyspaceID(02d2fd8867d50d2dfe)`, `ExecuteMultiShard ks.-20: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -1566,7 +1557,7 @@ func TestINMultiColumnVindex(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)] [INT64(2) INT64(3)] [INT64(2) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe),DestinationKeyspaceID(024eb190c9a2fa169c),DestinationKeyspaceID(02d2fd8867d50d2dfe)`, `StreamExecuteMulti dummy_select ks.-20: {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestINMixedMultiColumnComparision(t *testing.T) { @@ -1600,7 +1591,7 @@ func TestINMixedMultiColumnComparision(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`, `ExecuteMultiShard ks.-20: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"4"}} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -1609,7 +1600,7 @@ func TestINMixedMultiColumnComparision(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`, `StreamExecuteMulti dummy_select ks.-20: {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals1: type:TUPLE values:{type:INT64 value:"4"}} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestMultiEqualMultiCol(t *testing.T) { @@ -1643,7 +1634,7 @@ func TestMultiEqualMultiCol(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`, `ExecuteMultiShard ks.-20: dummy_select {} ks.40-: dummy_select {} false false`, }) - expectResult(t, "sel.Execute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) vc.Rewind() result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) @@ -1652,7 +1643,7 @@ func TestMultiEqualMultiCol(t *testing.T) { `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`, `StreamExecuteMulti dummy_select ks.-20: {} ks.40-: {} `, }) - expectResult(t, "sel.StreamExecute", result, defaultSelectResult) + expectResult(t, result, defaultSelectResult) } func TestBuildRowColValues(t *testing.T) { @@ -1717,3 +1708,46 @@ func TestBuildMultiColumnVindexValues(t *testing.T) { }) } } + +// TestSelectTupleMultiCol tests route execution having bind variable with multi column tuple. +func TestSelectTupleMultiCol(t *testing.T) { + vindex, _ := vindexes.CreateVindex("multicol", "", map[string]string{ + "column_count": "2", + "column_vindex": "hash,binary", + }) + + sel := NewRoute( + MultiEqual, + &vindexes.Keyspace{Name: "user", Sharded: true}, + "select 1 from multicol_tbl where (colb, colx, cola) in ::vals", + "select 1 from multicol_tbl where 1 != 1", + ) + sel.Vindex = vindex + sel.Values = []evalengine.Expr{ + &evalengine.TupleBindVariable{Key: "vals", Index: 0}, + &evalengine.TupleBindVariable{Key: "vals", Index: 1}, + } + + v1 := sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a")) + v2 := sqltypes.TestTuple(sqltypes.NewInt64(4), sqltypes.NewVarChar("b")) + tupleBV := &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: append([]*querypb.Value{sqltypes.ValueToProto(v1)}, sqltypes.ValueToProto(v2)), + } + vc := &loggingVCursor{ + shards: []string{"-20", "20-"}, + } + _, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{"vals": tupleBV}, false) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinationsMultiCol user [[INT64(1) VARCHAR("a")] [INT64(4) VARCHAR("b")]] Destinations:DestinationKeyspaceID(166b40b461),DestinationKeyspaceID(d2fd886762)`, + `ExecuteMultiShard user.-20: select 1 from multicol_tbl where (colb, colx, cola) in ::vals {vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x011\x950\x01a"} values:{type:TUPLE value:"\x89\x02\x014\x950\x01b"}} false false`, + }) + + vc.Rewind() + _, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{"vals": tupleBV}, false) + vc.ExpectLog(t, []string{ + `ResolveDestinationsMultiCol user [[INT64(1) VARCHAR("a")] [INT64(4) VARCHAR("b")]] Destinations:DestinationKeyspaceID(166b40b461),DestinationKeyspaceID(d2fd886762)`, + `StreamExecuteMulti select 1 from multicol_tbl where (colb, colx, cola) in ::vals user.-20: {vals: type:TUPLE values:{type:TUPLE value:"\x89\x02\x011\x950\x01a"} values:{type:TUPLE value:"\x89\x02\x014\x950\x01b"}} `, + }) +} diff --git a/go/vt/vtgate/engine/rows.go b/go/vt/vtgate/engine/rows.go index 2b81c85145f..424d5585a36 100644 --- a/go/vt/vtgate/engine/rows.go +++ b/go/vt/vtgate/engine/rows.go @@ -27,14 +27,14 @@ var _ Primitive = (*Rows)(nil) // Rows simply returns a number or rows type Rows struct { - rows [][]sqltypes.Value - fields []*querypb.Field - noInputs noTxNeeded + + rows [][]sqltypes.Value + fields []*querypb.Field } -// NewRowsPrimitive returns a new Rows primitie +// NewRowsPrimitive returns a new Rows primitive func NewRowsPrimitive(rows [][]sqltypes.Value, fields []*querypb.Field) Primitive { return &Rows{rows: rows, fields: fields} } diff --git a/go/vt/vtgate/engine/scalar_aggregation_test.go b/go/vt/vtgate/engine/scalar_aggregation_test.go index 3329fc72d39..99031c95f34 100644 --- a/go/vt/vtgate/engine/scalar_aggregation_test.go +++ b/go/vt/vtgate/engine/scalar_aggregation_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" . "vitess.io/vitess/go/vt/vtgate/engine/opcode" @@ -273,8 +274,8 @@ func TestScalarDistinctAggrOnEngine(t *testing.T) { oa := &ScalarAggregate{ Aggregates: []*AggregateParams{ - NewAggregateParam(AggregateCountDistinct, 0, "count(distinct value)"), - NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct value)"), + NewAggregateParam(AggregateCountDistinct, 0, "count(distinct value)", collations.MySQL8()), + NewAggregateParam(AggregateSumDistinct, 1, "sum(distinct value)", collations.MySQL8()), }, Input: fp, } @@ -311,9 +312,9 @@ func TestScalarDistinctPushedDown(t *testing.T) { "8|90", )}} - countAggr := NewAggregateParam(AggregateSum, 0, "count(distinct value)") + countAggr := NewAggregateParam(AggregateSum, 0, "count(distinct value)", collations.MySQL8()) countAggr.OrigOpcode = AggregateCountDistinct - sumAggr := NewAggregateParam(AggregateSum, 1, "sum(distinct value)") + sumAggr := NewAggregateParam(AggregateSum, 1, "sum(distinct value)", collations.MySQL8()) sumAggr.OrigOpcode = AggregateSumDistinct oa := &ScalarAggregate{ Aggregates: []*AggregateParams{ diff --git a/go/vt/vtgate/engine/semi_join.go b/go/vt/vtgate/engine/semi_join.go index d291b348da9..8ab0465249c 100644 --- a/go/vt/vtgate/engine/semi_join.go +++ b/go/vt/vtgate/engine/semi_join.go @@ -43,7 +43,7 @@ type SemiJoin struct { // Vars defines the list of SemiJoinVars that need to // be built from the LHS result before invoking - // the RHS subqquery. + // the RHS subquery. Vars map[string]int `json:",omitempty"` } diff --git a/go/vt/vtgate/engine/semi_join_test.go b/go/vt/vtgate/engine/semi_join_test.go index ca89882ab8a..9cf55d4f78f 100644 --- a/go/vt/vtgate/engine/semi_join_test.go +++ b/go/vt/vtgate/engine/semi_join_test.go @@ -152,7 +152,7 @@ func TestSemiJoinStreamExecute(t *testing.T) { `StreamExecute bv: type:VARCHAR value:"c" false`, `StreamExecute bv: type:VARCHAR value:"d" false`, }) - expectResult(t, "jn.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col2|col3", "int64|varchar|varchar", diff --git a/go/vt/vtgate/engine/send.go b/go/vt/vtgate/engine/send.go index 1a95d8f93fa..53f12cd3549 100644 --- a/go/vt/vtgate/engine/send.go +++ b/go/vt/vtgate/engine/send.go @@ -33,6 +33,8 @@ var _ Primitive = (*Send)(nil) // Send is an operator to send query to the specific keyspace, tabletType and destination type Send struct { + noInputs + // Keyspace specifies the keyspace to send the query to. Keyspace *vindexes.Keyspace @@ -54,7 +56,7 @@ type Send struct { // MultishardAutocommit specifies that a multishard transaction query can autocommit MultishardAutocommit bool - noInputs + ReservedConnectionNeeded bool } // ShardName as key for setting shard name in bind variables map @@ -88,19 +90,12 @@ func (s *Send) GetTableName() string { func (s *Send) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { ctx, cancelFunc := addQueryTimeout(ctx, vcursor, 0) defer cancelFunc() - rss, _, err := vcursor.ResolveDestinations(ctx, s.Keyspace.Name, nil, []key.Destination{s.TargetDestination}) + + rss, err := s.checkAndReturnShards(ctx, vcursor) if err != nil { return nil, err } - if !s.Keyspace.Sharded && len(rss) != 1 { - return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) - } - - if s.SingleShardOnly && len(rss) != 1 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %s, got: %v", s.Query, s.TargetDestination) - } - queries := make([]*querypb.BoundQuery, len(rss)) for i, rs := range rss { bv := bindVars @@ -123,6 +118,26 @@ func (s *Send) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[str return result, nil } +func (s *Send) checkAndReturnShards(ctx context.Context, vcursor VCursor) ([]*srvtopo.ResolvedShard, error) { + rss, _, err := vcursor.ResolveDestinations(ctx, s.Keyspace.Name, nil, []key.Destination{s.TargetDestination}) + if err != nil { + return nil, err + } + + if !s.Keyspace.Sharded && len(rss) != 1 { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) + } + + if s.SingleShardOnly && len(rss) != 1 { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %s, got: %v", s.Query, s.TargetDestination) + } + + if s.ReservedConnectionNeeded { + vcursor.Session().NeedsReservedConn() + } + return rss, nil +} + func (s *Send) canAutoCommit(vcursor VCursor, rss []*srvtopo.ResolvedShard) bool { if s.IsDML { return (len(rss) == 1 || s.MultishardAutocommit) && vcursor.AutocommitApproval() @@ -140,19 +155,11 @@ func copyBindVars(in map[string]*querypb.BindVariable) map[string]*querypb.BindV // TryStreamExecute implements Primitive interface func (s *Send) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { - rss, _, err := vcursor.ResolveDestinations(ctx, s.Keyspace.Name, nil, []key.Destination{s.TargetDestination}) + rss, err := s.checkAndReturnShards(ctx, vcursor) if err != nil { return err } - if !s.Keyspace.Sharded && len(rss) != 1 { - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) - } - - if s.SingleShardOnly && len(rss) != 1 { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %s, got: %v", s.Query, s.TargetDestination) - } - multiBindVars := make([]map[string]*querypb.BindVariable, len(rss)) for i, rs := range rss { bv := bindVars @@ -178,20 +185,13 @@ func (s *Send) GetFields(ctx context.Context, vcursor VCursor, bindVars map[stri func (s *Send) description() PrimitiveDescription { other := map[string]any{ - "Query": s.Query, - "Table": s.GetTableName(), - } - if s.IsDML { - other["IsDML"] = true - } - if s.SingleShardOnly { - other["SingleShardOnly"] = true - } - if s.ShardNameNeeded { - other["ShardNameNeeded"] = true - } - if s.MultishardAutocommit { - other["MultishardAutocommit"] = true + "Query": s.Query, + "Table": s.GetTableName(), + "IsDML": s.IsDML, + "SingleShardOnly": s.SingleShardOnly, + "ShardNameNeeded": s.ShardNameNeeded, + "MultishardAutocommit": s.MultishardAutocommit, + "ReservedConnectionNeeded": s.ReservedConnectionNeeded, } return PrimitiveDescription{ OperatorType: "Send", diff --git a/go/vt/vtgate/engine/sequential.go b/go/vt/vtgate/engine/sequential.go new file mode 100644 index 00000000000..ecf74d663a2 --- /dev/null +++ b/go/vt/vtgate/engine/sequential.go @@ -0,0 +1,107 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" +) + +// Sequential Primitive is used to execute DML statements in a fixed order. +// Any failure, stops the execution and returns. +type Sequential struct { + txNeeded + Sources []Primitive +} + +var _ Primitive = (*Sequential)(nil) + +// NewSequential creates a Sequential primitive. +func NewSequential(Sources []Primitive) *Sequential { + return &Sequential{ + Sources: Sources, + } +} + +// RouteType returns a description of the query routing type used by the primitive +func (s *Sequential) RouteType() string { + return "Sequential" +} + +// GetKeyspaceName specifies the Keyspace that this primitive routes to +func (s *Sequential) GetKeyspaceName() string { + res := s.Sources[0].GetKeyspaceName() + for i := 1; i < len(s.Sources); i++ { + res = formatTwoOptionsNicely(res, s.Sources[i].GetKeyspaceName()) + } + return res +} + +// GetTableName specifies the table that this primitive routes to. +func (s *Sequential) GetTableName() string { + res := s.Sources[0].GetTableName() + for i := 1; i < len(s.Sources); i++ { + res = formatTwoOptionsNicely(res, s.Sources[i].GetTableName()) + } + return res +} + +// TryExecute performs a non-streaming exec. +func (s *Sequential) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantFields bool) (*sqltypes.Result, error) { + finalRes := &sqltypes.Result{} + for _, source := range s.Sources { + res, err := vcursor.ExecutePrimitive(ctx, source, bindVars, wantFields) + if err != nil { + return nil, err + } + finalRes.RowsAffected += res.RowsAffected + if finalRes.InsertID == 0 { + finalRes.InsertID = res.InsertID + } + if res.Info != "" { + finalRes.Info = res.Info + } + } + return finalRes, nil +} + +// TryStreamExecute performs a streaming exec. +func (s *Sequential) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantFields bool, callback func(*sqltypes.Result) error) error { + qr, err := s.TryExecute(ctx, vcursor, bindVars, wantFields) + if err != nil { + return err + } + return callback(qr) +} + +// GetFields fetches the field info. +func (s *Sequential) GetFields(context.Context, VCursor, map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unreachable code for Sequential engine") +} + +// Inputs returns the input primitives for this +func (s *Sequential) Inputs() ([]Primitive, []map[string]any) { + return s.Sources, nil +} + +func (s *Sequential) description() PrimitiveDescription { + return PrimitiveDescription{OperatorType: s.RouteType()} +} diff --git a/go/vt/vtgate/engine/session_primitive.go b/go/vt/vtgate/engine/session_primitive.go index 834f335dd6f..c9c39c39e2b 100644 --- a/go/vt/vtgate/engine/session_primitive.go +++ b/go/vt/vtgate/engine/session_primitive.go @@ -27,11 +27,11 @@ import ( // SessionPrimitive the session primitive is a very small primitive used // when we have simple engine code that needs to interact with the Session type SessionPrimitive struct { - action func(sa SessionActions) (*sqltypes.Result, error) - name string - noInputs noTxNeeded + + action func(sa SessionActions) (*sqltypes.Result, error) + name string } var _ Primitive = (*SessionPrimitive)(nil) diff --git a/go/vt/vtgate/engine/set.go b/go/vt/vtgate/engine/set.go index df56fc04ed2..9d370b6ec36 100644 --- a/go/vt/vtgate/engine/set.go +++ b/go/vt/vtgate/engine/set.go @@ -17,7 +17,6 @@ limitations under the License. package engine import ( - "bytes" "context" "encoding/json" "fmt" @@ -46,10 +45,10 @@ import ( type ( // Set contains the instructions to perform set. Set struct { + noTxNeeded + Ops []SetOp Input Primitive - - noTxNeeded } // SetOp is an interface that different type of set operations implements. @@ -181,7 +180,6 @@ func (u *UserDefinedVariable) MarshalJSON() ([]byte, error) { Name: u.Name, Expr: sqlparser.String(u.Expr), }) - } // VariableName implements the SetOp interface method. @@ -209,7 +207,6 @@ func (svi *SysVarIgnore) MarshalJSON() ([]byte, error) { Type: "SysVarIgnore", SysVarIgnore: *svi, }) - } // VariableName implements the SetOp interface method. @@ -219,7 +216,6 @@ func (svi *SysVarIgnore) VariableName() string { // Execute implements the SetOp interface method. func (svi *SysVarIgnore) Execute(context.Context, VCursor, *evalengine.ExpressionEnv) error { - log.Infof("Ignored inapplicable SET %v = %v", svi.Name, svi.Expr) return nil } @@ -234,7 +230,6 @@ func (svci *SysVarCheckAndIgnore) MarshalJSON() ([]byte, error) { Type: "SysVarCheckAndIgnore", SysVarCheckAndIgnore: *svci, }) - } // VariableName implements the SetOp interface method @@ -253,7 +248,7 @@ func (svci *SysVarCheckAndIgnore) Execute(ctx context.Context, vcursor VCursor, return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %v", svci.TargetDestination) } checkSysVarQuery := fmt.Sprintf("select 1 from dual where @@%s = %s", svci.Name, svci.Expr) - result, err := execShard(ctx, nil, vcursor, checkSysVarQuery, env.BindVars, rss[0], false /* rollbackOnError */, false /* canAutocommit */) + _, err = execShard(ctx, nil, vcursor, checkSysVarQuery, env.BindVars, rss[0], false /* rollbackOnError */, false /* canAutocommit */) if err != nil { // Rather than returning the error, we will just log the error // as the intention for executing the query it to validate the current setting and eventually ignore it anyways. @@ -261,9 +256,6 @@ func (svci *SysVarCheckAndIgnore) Execute(ctx context.Context, vcursor VCursor, log.Warningf("unable to validate the current settings for '%s': %s", svci.Name, err.Error()) return nil } - if len(result.Rows) == 0 { - log.Infof("Ignored inapplicable SET %v = %v", svci.Name, svci.Expr) - } return nil } @@ -278,7 +270,6 @@ func (svs *SysVarReservedConn) MarshalJSON() ([]byte, error) { Type: "SysVarSet", SysVarReservedConn: *svs, }) - } // VariableName implements the SetOp interface method @@ -363,8 +354,8 @@ func (svs *SysVarReservedConn) checkAndUpdateSysVar(ctx context.Context, vcursor } else { value = qr.Rows[0][0] } - buf := new(bytes.Buffer) - value.EncodeSQL(buf) + var buf strings.Builder + value.EncodeSQL(&buf) s := buf.String() vcursor.Session().SetSysVar(svs.Name, s) diff --git a/go/vt/vtgate/engine/set_test.go b/go/vt/vtgate/engine/set_test.go index 9245e9a618d..0677ee40bd8 100644 --- a/go/vt/vtgate/engine/set_test.go +++ b/go/vt/vtgate/engine/set_test.go @@ -107,7 +107,7 @@ func TestSetTable(t *testing.T) { setOps: []SetOp{ &UserDefinedVariable{ Name: "x", - Expr: evalengine.NewColumn(0, evalengine.UnknownType(), nil), + Expr: evalengine.NewColumn(0, evalengine.Type{}, nil), }, }, qr: []*sqltypes.Result{sqltypes.MakeTestResult( @@ -363,7 +363,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change - changed additional - MySQL57", - mysqlVersion: "50709", + mysqlVersion: "5.7.9", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -383,7 +383,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change - changed less - MySQL57", - mysqlVersion: "50709", + mysqlVersion: "5.7.9", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -420,7 +420,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change - empty orig - MySQL57", - mysqlVersion: "50709", + mysqlVersion: "5.7.9", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -459,7 +459,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change - empty orig - MySQL80", - mysqlVersion: "80000", + mysqlVersion: "8.0.0", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -479,7 +479,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change to empty - non empty orig - MySQL80 - should use reserved conn", - mysqlVersion: "80000", + mysqlVersion: "8.0.0", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -499,7 +499,7 @@ func TestSetTable(t *testing.T) { )}, }, { testName: "sql_mode change - empty orig - MySQL80 - SET_VAR disabled", - mysqlVersion: "80000", + mysqlVersion: "8.0.0", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -520,7 +520,7 @@ func TestSetTable(t *testing.T) { disableSetVar: true, }, { testName: "sql_mode set an unsupported mode", - mysqlVersion: "80000", + mysqlVersion: "8.0.0", setOps: []SetOp{ &SysVarReservedConn{ Name: "sql_mode", @@ -540,7 +540,7 @@ func TestSetTable(t *testing.T) { disableSetVar: true, }, { testName: "default_week_format change - empty orig - MySQL80", - mysqlVersion: "80000", + mysqlVersion: "8.0.0", setOps: []SetOp{ &SysVarReservedConn{ Name: "default_week_format", @@ -565,23 +565,22 @@ func TestSetTable(t *testing.T) { tc.input = &SingleRow{} } - oldMySQLVersion := sqlparser.GetParserVersion() - defer func() { sqlparser.SetParserVersion(oldMySQLVersion) }() - if tc.mysqlVersion != "" { - sqlparser.SetParserVersion(tc.mysqlVersion) - } - set := &Set{ Ops: tc.setOps, Input: tc.input, } + parser, err := sqlparser.New(sqlparser.Options{ + MySQLServerVersion: tc.mysqlVersion, + }) + require.NoError(t, err) vc := &loggingVCursor{ shards: []string{"-20", "20-"}, results: tc.qr, multiShardErrs: []error{tc.execErr}, disableSetVar: tc.disableSetVar, + parser: parser, } - _, err := set.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) + _, err = set.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) if tc.expectedError == "" { require.NoError(t, err) } else { diff --git a/go/vt/vtgate/engine/show_exec.go b/go/vt/vtgate/engine/show_exec.go index f0c251aa376..d09733d0ec1 100644 --- a/go/vt/vtgate/engine/show_exec.go +++ b/go/vt/vtgate/engine/show_exec.go @@ -28,11 +28,11 @@ var _ Primitive = (*ShowExec)(nil) // ShowExec is a primitive to call into executor via vcursor. type ShowExec struct { - Command sqlparser.ShowCommandType - ShowFilter *sqlparser.ShowFilter - noInputs noTxNeeded + + Command sqlparser.ShowCommandType + ShowFilter *sqlparser.ShowFilter } func (s *ShowExec) RouteType() string { diff --git a/go/vt/vtgate/engine/simple_projection_test.go b/go/vt/vtgate/engine/simple_projection_test.go index 99d644c93af..6fdc288095c 100644 --- a/go/vt/vtgate/engine/simple_projection_test.go +++ b/go/vt/vtgate/engine/simple_projection_test.go @@ -59,7 +59,7 @@ func TestSubqueryExecute(t *testing.T) { prim.ExpectLog(t, []string{ `Execute a: type:INT64 value:"1" true`, }) - expectResult(t, "sq.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col3", "int64|varchar", @@ -108,7 +108,7 @@ func TestSubqueryStreamExecute(t *testing.T) { prim.ExpectLog(t, []string{ `StreamExecute a: type:INT64 value:"1" true`, }) - expectResult(t, "sq.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col3", "int64|varchar", @@ -158,7 +158,7 @@ func TestSubqueryGetFields(t *testing.T) { `GetFields a: type:INT64 value:"1"`, `Execute a: type:INT64 value:"1" true`, }) - expectResult(t, "sq.Execute", r, sqltypes.MakeTestResult( + expectResult(t, r, sqltypes.MakeTestResult( sqltypes.MakeTestFields( "col1|col3", "int64|varchar", diff --git a/go/vt/vtgate/engine/throttle_app.go b/go/vt/vtgate/engine/throttle_app.go index db485e6bec3..8b2370699cf 100644 --- a/go/vt/vtgate/engine/throttle_app.go +++ b/go/vt/vtgate/engine/throttle_app.go @@ -31,12 +31,11 @@ var _ Primitive = (*ThrottleApp)(nil) // ThrottleApp represents the instructions to perform an online schema change via vtctld type ThrottleApp struct { - Keyspace *vindexes.Keyspace - ThrottledAppRule *topodatapb.ThrottledAppRule - noTxNeeded - noInputs + + Keyspace *vindexes.Keyspace + ThrottledAppRule *topodatapb.ThrottledAppRule } func (v *ThrottleApp) description() PrimitiveDescription { diff --git a/go/vt/vtgate/engine/uncorrelated_subquery_test.go b/go/vt/vtgate/engine/uncorrelated_subquery_test.go index 3e80c6369a7..085fe09238f 100644 --- a/go/vt/vtgate/engine/uncorrelated_subquery_test.go +++ b/go/vt/vtgate/engine/uncorrelated_subquery_test.go @@ -65,7 +65,7 @@ func TestPulloutSubqueryValueGood(t *testing.T) { require.NoError(t, err) sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`}) ufp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" sq: type:INT64 value:"1" false`}) - expectResult(t, "ps.Execute", result, underlyingResult) + expectResult(t, result, underlyingResult) } func TestPulloutSubqueryValueNone(t *testing.T) { @@ -279,7 +279,7 @@ func TestPulloutSubqueryStream(t *testing.T) { require.NoError(t, err) sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`}) ufp.ExpectLog(t, []string{`StreamExecute aa: type:INT64 value:"1" sq: type:INT64 value:"1" true`}) - expectResult(t, "ps.StreamExecute", result, underlyingResult) + expectResult(t, result, underlyingResult) } func TestPulloutSubqueryGetFields(t *testing.T) { diff --git a/go/vt/vtgate/engine/unlock.go b/go/vt/vtgate/engine/unlock.go new file mode 100644 index 00000000000..5addbb957fa --- /dev/null +++ b/go/vt/vtgate/engine/unlock.go @@ -0,0 +1,79 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vterrors" +) + +var _ Primitive = (*Unlock)(nil) + +// Unlock primitive will execute unlock tables to all connections in the session. +type Unlock struct { + noTxNeeded + noInputs +} + +const unlockTables = "unlock tables" + +func (u *Unlock) RouteType() string { + return "UNLOCK" +} + +func (u *Unlock) GetKeyspaceName() string { + return "" +} + +func (u *Unlock) GetTableName() string { + return "" +} + +func (u *Unlock) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.VT13001("GetFields should not be called for unlock tables") +} + +func (u *Unlock) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + rss := vcursor.Session().ShardSession() + + if len(rss) == 0 { + return &sqltypes.Result{}, nil + } + bqs := make([]*querypb.BoundQuery, len(rss)) + for i := 0; i < len(rss); i++ { + bqs[i] = &querypb.BoundQuery{Sql: unlockTables} + } + qr, errs := vcursor.ExecuteMultiShard(ctx, u, rss, bqs, true, false) + return qr, vterrors.Aggregate(errs) +} + +func (u *Unlock) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + qr, err := u.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err + } + return callback(qr) +} + +func (u *Unlock) description() PrimitiveDescription { + return PrimitiveDescription{ + OperatorType: "UnlockTables", + } +} diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 3db7972fba5..323b7ab40ac 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -42,13 +42,13 @@ type VindexValues struct { // Update represents the instructions to perform an update. type Update struct { + // Update does not take inputs + noInputs + *DML // ChangedVindexValues contains values for updated Vindexes during an update statement. ChangedVindexValues map[string]*VindexValues - - // Update does not take inputs - noInputs } // TryExecute performs a non-streaming exec. @@ -204,6 +204,7 @@ func (upd *Update) description() PrimitiveDescription { "OwnedVindexQuery": upd.OwnedVindexQuery, "MultiShardAutocommit": upd.MultiShardAutocommit, "QueryTimeout": upd.QueryTimeout, + "NoAutoCommit": upd.PreventAutoCommit, } addFieldsIfNotEmpty(upd.DML, other) diff --git a/go/vt/vtgate/engine/update_target.go b/go/vt/vtgate/engine/update_target.go index a15d47b9915..9f47199079a 100644 --- a/go/vt/vtgate/engine/update_target.go +++ b/go/vt/vtgate/engine/update_target.go @@ -30,12 +30,11 @@ var _ Primitive = (*UpdateTarget)(nil) // UpdateTarget is an operator to update target string. type UpdateTarget struct { - // Target string to be updated - Target string - noInputs - noTxNeeded + + // Target string to be updated + Target string } func (updTarget *UpdateTarget) description() PrimitiveDescription { diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index 22c2b90d60e..9d583cdcfcf 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -21,6 +21,7 @@ import ( "errors" "testing" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/evalengine" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -93,7 +94,7 @@ func TestUpdateEqual(t *testing.T) { }) // Failure case - upd.Values = []evalengine.Expr{evalengine.NewBindVar("aa", evalengine.UnknownType())} + upd.Values = []evalengine.Expr{evalengine.NewBindVar("aa", evalengine.Type{})} _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) require.EqualError(t, err, `query arguments missing for aa`) } @@ -1023,7 +1024,7 @@ func buildTestVSchema() *vindexes.VSchema { }, }, } - vs := vindexes.BuildVSchema(invschema) + vs := vindexes.BuildVSchema(invschema, sqlparser.NewTestParser()) return vs } diff --git a/go/vt/vtgate/engine/upsert.go b/go/vt/vtgate/engine/upsert.go new file mode 100644 index 00000000000..2e42452a7a4 --- /dev/null +++ b/go/vt/vtgate/engine/upsert.go @@ -0,0 +1,137 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "context" + "fmt" + + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" +) + +var _ Primitive = (*Upsert)(nil) + +// Upsert Primitive will execute the insert primitive first and +// if there is `Duplicate Key` error, it executes the update primitive. +type Upsert struct { + txNeeded + Upserts []upsert +} + +type upsert struct { + Insert Primitive + Update Primitive +} + +// AddUpsert appends to the Upsert Primitive. +func (u *Upsert) AddUpsert(ins, upd Primitive) { + u.Upserts = append(u.Upserts, upsert{ + Insert: ins, + Update: upd, + }) +} + +// RouteType implements Primitive interface type. +func (u *Upsert) RouteType() string { + return "UPSERT" +} + +// GetKeyspaceName implements Primitive interface type. +func (u *Upsert) GetKeyspaceName() string { + if len(u.Upserts) > 0 { + return u.Upserts[0].Insert.GetKeyspaceName() + } + return "" +} + +// GetTableName implements Primitive interface type. +func (u *Upsert) GetTableName() string { + if len(u.Upserts) > 0 { + return u.Upserts[0].Insert.GetTableName() + } + return "" +} + +// GetFields implements Primitive interface type. +func (u *Upsert) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.VT13001("unexpected to receive GetFields call for insert on duplicate key update query") +} + +// TryExecute implements Primitive interface type. +func (u *Upsert) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + result := &sqltypes.Result{} + for _, up := range u.Upserts { + qr, err := execOne(ctx, vcursor, bindVars, wantfields, up) + if err != nil { + return nil, err + } + result.RowsAffected += qr.RowsAffected + } + return result, nil +} + +func execOne(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, up upsert) (*sqltypes.Result, error) { + insQr, err := vcursor.ExecutePrimitive(ctx, up.Insert, bindVars, wantfields) + if err == nil { + return insQr, nil + } + if vterrors.Code(err) != vtrpcpb.Code_ALREADY_EXISTS { + return nil, err + } + updQr, err := vcursor.ExecutePrimitive(ctx, up.Update, bindVars, wantfields) + if err != nil { + return nil, err + } + // To match mysql, need to report +1 on rows affected if there is any change. + if updQr.RowsAffected > 0 { + updQr.RowsAffected += 1 + } + return updQr, nil +} + +// TryStreamExecute implements Primitive interface type. +func (u *Upsert) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + qr, err := u.TryExecute(ctx, vcursor, bindVars, wantfields) + if err != nil { + return err + } + return callback(qr) +} + +// Inputs implements Primitive interface type. +func (u *Upsert) Inputs() ([]Primitive, []map[string]any) { + var inputs []Primitive + var inputsMap []map[string]any + for i, up := range u.Upserts { + inputs = append(inputs, up.Insert, up.Update) + inputsMap = append(inputsMap, + map[string]any{inputName: fmt.Sprintf("Insert-%d", i+1)}, + map[string]any{inputName: fmt.Sprintf("Update-%d", i+1)}) + } + return inputs, inputsMap +} + +func (u *Upsert) description() PrimitiveDescription { + return PrimitiveDescription{ + OperatorType: "Upsert", + TargetTabletType: topodatapb.TabletType_PRIMARY, + } +} diff --git a/go/vt/vtgate/engine/vexplain.go b/go/vt/vtgate/engine/vexplain.go index ad540f96c9c..010901021fa 100644 --- a/go/vt/vtgate/engine/vexplain.go +++ b/go/vt/vtgate/engine/vexplain.go @@ -122,7 +122,7 @@ func (v *VExplain) convertToVExplainAllResult(ctx context.Context, vcursor VCurs explainQuery := fmt.Sprintf("explain format = json %v", entry.Query) // We rely on the parser to see if the query we have is explainable or not // If we get an error in parsing then we can't execute explain on the given query, and we skip it - _, err := sqlparser.Parse(explainQuery) + _, err := vcursor.Environment().Parser().Parse(explainQuery) if err != nil { continue } diff --git a/go/vt/vtgate/engine/vindex_func.go b/go/vt/vtgate/engine/vindex_func.go index ccc3d366c20..ecd83baeaad 100644 --- a/go/vt/vtgate/engine/vindex_func.go +++ b/go/vt/vtgate/engine/vindex_func.go @@ -38,6 +38,12 @@ var _ Primitive = (*VindexFunc)(nil) // VindexFunc is a primitive that performs vindex functions. type VindexFunc struct { + // VindexFunc does not take inputs + noInputs + + // VindexFunc does not need to work inside a tx + noTxNeeded + Opcode VindexOpcode // Fields is the field info for the result. Fields []*querypb.Field @@ -46,12 +52,6 @@ type VindexFunc struct { // TODO(sougou): add support for MultiColumn. Vindex vindexes.SingleColumn Value evalengine.Expr - - // VindexFunc does not take inputs - noInputs - - // VindexFunc does not need to work inside a tx - noTxNeeded } // VindexOpcode is the opcode for a VindexFunc. diff --git a/go/vt/vtgate/engine/vindex_lookup.go b/go/vt/vtgate/engine/vindex_lookup.go index aaf49feea95..8bf8755c40e 100644 --- a/go/vt/vtgate/engine/vindex_lookup.go +++ b/go/vt/vtgate/engine/vindex_lookup.go @@ -227,10 +227,6 @@ func (vr *VindexLookup) executeBatch(ctx context.Context, vcursor VCursor, ids [ } else { result, err = vcursor.ExecutePrimitive(ctx, vr.Lookup, bindVars, false) } - if err != nil { - return nil, err - } - if err != nil { return nil, vterrors.Wrapf(err, "failed while running the lookup query") } diff --git a/go/vt/vtgate/engine/vschema_ddl.go b/go/vt/vtgate/engine/vschema_ddl.go index 1e385269c1d..cb107eecb58 100644 --- a/go/vt/vtgate/engine/vschema_ddl.go +++ b/go/vt/vtgate/engine/vschema_ddl.go @@ -30,13 +30,11 @@ var _ Primitive = (*AlterVSchema)(nil) // AlterVSchema operator applies changes to VSchema type AlterVSchema struct { - Keyspace *vindexes.Keyspace - - AlterVschemaDDL *sqlparser.AlterVschema - noTxNeeded - noInputs + + Keyspace *vindexes.Keyspace + AlterVschemaDDL *sqlparser.AlterVschema } func (v *AlterVSchema) description() PrimitiveDescription { diff --git a/go/vt/vtgate/engine/vstream.go b/go/vt/vtgate/engine/vstream.go index 2ad8286dfcc..5d3c92c4d98 100644 --- a/go/vt/vtgate/engine/vstream.go +++ b/go/vt/vtgate/engine/vstream.go @@ -35,14 +35,14 @@ var _ Primitive = (*VStream)(nil) // VStream is an operator for streaming specific keyspace, destination type VStream struct { + noTxNeeded + noInputs + Keyspace *vindexes.Keyspace TargetDestination key.Destination TableName string Position string Limit int - - noTxNeeded - noInputs } // RouteType implements the Primitive interface diff --git a/go/vt/vtgate/evalengine/api_aggregation.go b/go/vt/vtgate/evalengine/api_aggregation.go index c0d490ced22..0566f477a3c 100644 --- a/go/vt/vtgate/evalengine/api_aggregation.go +++ b/go/vt/vtgate/evalengine/api_aggregation.go @@ -389,6 +389,7 @@ func (s *aggregationDecimal) Min(value sqltypes.Value) error { } if !s.dec.IsInitialized() || dec.Cmp(s.dec) < 0 { s.dec = dec + s.prec = -dec.Exponent() } return nil } @@ -403,6 +404,7 @@ func (s *aggregationDecimal) Max(value sqltypes.Value) error { } if !s.dec.IsInitialized() || dec.Cmp(s.dec) > 0 { s.dec = dec + s.prec = -dec.Exponent() } return nil } @@ -443,8 +445,9 @@ func NewAggregationSum(type_ sqltypes.Type) Sum { // The aggregation is performed using the slow NullSafeComparison path of the // evaluation engine. type aggregationMinMax struct { - current sqltypes.Value - collation collations.ID + current sqltypes.Value + collation collations.ID + collationEnv *collations.Environment } func (a *aggregationMinMax) minmax(value sqltypes.Value, max bool) (err error) { @@ -455,7 +458,7 @@ func (a *aggregationMinMax) minmax(value sqltypes.Value, max bool) (err error) { a.current = value return nil } - n, err := compare(a.current, value, a.collation) + n, err := compare(a.current, value, a.collationEnv, a.collation) if err != nil { return err } @@ -481,17 +484,17 @@ func (a *aggregationMinMax) Reset() { a.current = sqltypes.NULL } -func NewAggregationMinMax(type_ sqltypes.Type, collation collations.ID) MinMax { +func NewAggregationMinMax(typ sqltypes.Type, collationEnv *collations.Environment, collation collations.ID) MinMax { switch { - case sqltypes.IsSigned(type_): - return &aggregationInt{t: type_} - case sqltypes.IsUnsigned(type_): - return &aggregationUint{t: type_} - case sqltypes.IsFloat(type_): - return &aggregationFloat{t: type_} - case sqltypes.IsDecimal(type_): + case sqltypes.IsSigned(typ): + return &aggregationInt{t: typ} + case sqltypes.IsUnsigned(typ): + return &aggregationUint{t: typ} + case sqltypes.IsFloat(typ): + return &aggregationFloat{t: typ} + case sqltypes.IsDecimal(typ): return &aggregationDecimal{} default: - return &aggregationMinMax{collation: collation} + return &aggregationMinMax{collation: collation, collationEnv: collationEnv} } } diff --git a/go/vt/vtgate/evalengine/api_aggregation_test.go b/go/vt/vtgate/evalengine/api_aggregation_test.go index aab49541e71..e5dae47017e 100644 --- a/go/vt/vtgate/evalengine/api_aggregation_test.go +++ b/go/vt/vtgate/evalengine/api_aggregation_test.go @@ -72,6 +72,12 @@ func TestMinMax(t *testing.T) { min: sqltypes.NewVarBinary("a"), max: sqltypes.NewVarBinary("b"), }, + { + type_: sqltypes.Decimal, + values: []sqltypes.Value{sqltypes.NewDecimal("1.001"), sqltypes.NewDecimal("2.1")}, + min: sqltypes.NewDecimal("1.001"), + max: sqltypes.NewDecimal("2.1"), + }, { // accent insensitive type_: sqltypes.VarChar, @@ -131,7 +137,7 @@ func TestMinMax(t *testing.T) { for i, tcase := range tcases { t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run("Min", func(t *testing.T) { - agg := NewAggregationMinMax(tcase.type_, tcase.coll) + agg := NewAggregationMinMax(tcase.type_, collations.MySQL8(), tcase.coll) for _, v := range tcase.values { err := agg.Min(v) @@ -147,7 +153,7 @@ func TestMinMax(t *testing.T) { }) t.Run("Max", func(t *testing.T) { - agg := NewAggregationMinMax(tcase.type_, tcase.coll) + agg := NewAggregationMinMax(tcase.type_, collations.MySQL8(), tcase.coll) for _, v := range tcase.values { err := agg.Max(v) diff --git a/go/vt/vtgate/evalengine/api_arithmetic_test.go b/go/vt/vtgate/evalengine/api_arithmetic_test.go index 40373423aa5..37f79d08c6c 100644 --- a/go/vt/vtgate/evalengine/api_arithmetic_test.go +++ b/go/vt/vtgate/evalengine/api_arithmetic_test.go @@ -24,16 +24,14 @@ import ( "strconv" "testing" - "vitess.io/vitess/go/test/utils" - "vitess.io/vitess/go/vt/vthash" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" - + "vitess.io/vitess/go/test/utils" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vthash" ) var ( @@ -85,11 +83,11 @@ func TestArithmetics(t *testing.T) { // testing for int64 overflow with min negative value v1: NewInt64(math.MinInt64), v2: NewInt64(1), - err: dataOutOfRangeError(math.MinInt64, 1, "BIGINT", "-").Error(), + err: dataOutOfRangeError(int64(math.MinInt64), int64(1), "BIGINT", "-").Error(), }, { v1: NewUint64(4), v2: NewInt64(5), - err: dataOutOfRangeError(4, 5, "BIGINT UNSIGNED", "-").Error(), + err: dataOutOfRangeError(uint64(4), int64(5), "BIGINT UNSIGNED", "-").Error(), }, { // testing uint - int v1: NewUint64(7), @@ -103,7 +101,7 @@ func TestArithmetics(t *testing.T) { // testing for int64 overflow v1: NewInt64(math.MinInt64), v2: NewUint64(0), - err: dataOutOfRangeError(math.MinInt64, 0, "BIGINT UNSIGNED", "-").Error(), + err: dataOutOfRangeError(int64(math.MinInt64), uint64(0), "BIGINT UNSIGNED", "-").Error(), }, { v1: TestValue(sqltypes.VarChar, "c"), v2: NewInt64(1), @@ -140,7 +138,7 @@ func TestArithmetics(t *testing.T) { }, { v1: NewInt64(-1), v2: NewUint64(2), - err: dataOutOfRangeError(-1, 2, "BIGINT UNSIGNED", "-").Error(), + err: dataOutOfRangeError(int64(-1), int64(2), "BIGINT UNSIGNED", "-").Error(), }, { v1: NewInt64(2), v2: NewUint64(1), @@ -169,7 +167,7 @@ func TestArithmetics(t *testing.T) { // testing uint - uint if v2 > v1 v1: NewUint64(2), v2: NewUint64(4), - err: dataOutOfRangeError(2, 4, "BIGINT UNSIGNED", "-").Error(), + err: dataOutOfRangeError(uint64(2), uint64(4), "BIGINT UNSIGNED", "-").Error(), }, { // testing uint - (- int) v1: NewUint64(1), @@ -207,7 +205,7 @@ func TestArithmetics(t *testing.T) { }, { v1: NewInt64(-2), v2: NewUint64(1), - err: dataOutOfRangeError(1, -2, "BIGINT UNSIGNED", "+").Error(), + err: dataOutOfRangeError(uint64(1), int64(-2), "BIGINT UNSIGNED", "+").Error(), }, { v1: NewInt64(math.MaxInt64), v2: NewInt64(-2), @@ -221,12 +219,12 @@ func TestArithmetics(t *testing.T) { // testing for overflow uint64 v1: NewUint64(maxUint64), v2: NewUint64(2), - err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(), + err: dataOutOfRangeError(maxUint64, uint64(2), "BIGINT UNSIGNED", "+").Error(), }, { // int64 underflow v1: NewInt64(math.MinInt64), v2: NewInt64(-2), - err: dataOutOfRangeError(math.MinInt64, -2, "BIGINT", "+").Error(), + err: dataOutOfRangeError(int64(math.MinInt64), int64(-2), "BIGINT", "+").Error(), }, { // checking int64 max value can be returned v1: NewInt64(math.MaxInt64), @@ -261,7 +259,7 @@ func TestArithmetics(t *testing.T) { // testing for uint64 overflow with max uint64 + int value v1: NewUint64(maxUint64), v2: NewInt64(2), - err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(), + err: dataOutOfRangeError(maxUint64, int64(2), "BIGINT UNSIGNED", "+").Error(), }, { v1: sqltypes.NewHexNum([]byte("0x9")), v2: NewInt64(1), @@ -309,7 +307,7 @@ func TestArithmetics(t *testing.T) { // Lower bound for int64 v1: NewInt64(math.MinInt64), v2: NewInt64(1), - out: NewDecimal(strconv.Itoa(math.MinInt64) + ".0000"), + out: NewDecimal(strconv.FormatInt(math.MinInt64, 10) + ".0000"), }, { // upper bound for uint64 v1: NewUint64(math.MaxUint64), @@ -413,12 +411,12 @@ func TestArithmetics(t *testing.T) { // testing for overflow of int64 v1: NewInt64(math.MaxInt64), v2: NewInt64(2), - err: dataOutOfRangeError(math.MaxInt64, 2, "BIGINT", "*").Error(), + err: dataOutOfRangeError(int64(math.MaxInt64), int64(2), "BIGINT", "*").Error(), }, { // testing for underflow of uint64*max.uint64 v1: NewInt64(2), v2: NewUint64(maxUint64), - err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "*").Error(), + err: dataOutOfRangeError(maxUint64, int64(2), "BIGINT UNSIGNED", "*").Error(), }, { v1: NewUint64(math.MaxUint64), v2: NewUint64(1), @@ -427,7 +425,7 @@ func TestArithmetics(t *testing.T) { // Checking whether maxInt value can be passed as uint value v1: NewUint64(math.MaxInt64), v2: NewInt64(3), - err: dataOutOfRangeError(math.MaxInt64, 3, "BIGINT UNSIGNED", "*").Error(), + err: dataOutOfRangeError(uint64(math.MaxInt64), int64(3), "BIGINT UNSIGNED", "*").Error(), }}, }} @@ -492,7 +490,7 @@ func TestNullSafeAdd(t *testing.T) { }, { v1: NewInt64(-100), v2: NewUint64(10), - err: dataOutOfRangeError(10, -100, "BIGINT UNSIGNED", "+"), + err: dataOutOfRangeError(uint64(10), int64(-100), "BIGINT UNSIGNED", "+"), }, { // Make sure underlying error is returned while converting. v1: NewFloat64(1), @@ -594,12 +592,12 @@ func TestAddNumeric(t *testing.T) { // Int64 overflow. v1: newEvalInt64(9223372036854775807), v2: newEvalInt64(2), - err: dataOutOfRangeError(9223372036854775807, 2, "BIGINT", "+"), + err: dataOutOfRangeError(int64(9223372036854775807), int64(2), "BIGINT", "+"), }, { // Int64 underflow. v1: newEvalInt64(-9223372036854775807), v2: newEvalInt64(-2), - err: dataOutOfRangeError(-9223372036854775807, -2, "BIGINT", "+"), + err: dataOutOfRangeError(int64(-9223372036854775807), int64(-2), "BIGINT", "+"), }, { v1: newEvalInt64(-1), v2: newEvalUint64(2), @@ -608,7 +606,7 @@ func TestAddNumeric(t *testing.T) { // Uint64 overflow. v1: newEvalUint64(18446744073709551615), v2: newEvalUint64(2), - err: dataOutOfRangeError(uint64(18446744073709551615), 2, "BIGINT UNSIGNED", "+"), + err: dataOutOfRangeError(uint64(18446744073709551615), uint64(2), "BIGINT UNSIGNED", "+"), }} for _, tcase := range tcases { got, err := addNumericWithError(tcase.v1, tcase.v2) @@ -740,6 +738,23 @@ func TestToSqlValue(t *testing.T) { typ: sqltypes.Decimal, v: newEvalFloat(1.2e-16), out: TestValue(sqltypes.Decimal, "0.00000000000000012"), + }, { + // null in should return null out no matter what type + typ: sqltypes.Int64, + v: nil, + out: sqltypes.NULL, + }, { + typ: sqltypes.Uint64, + v: nil, + out: sqltypes.NULL, + }, { + typ: sqltypes.Float64, + v: nil, + out: sqltypes.NULL, + }, { + typ: sqltypes.VarChar, + v: nil, + out: sqltypes.NULL, }} for _, tcase := range tcases { got := evalToSQLValueWithType(tcase.v, tcase.typ) diff --git a/go/vt/vtgate/evalengine/api_coerce.go b/go/vt/vtgate/evalengine/api_coerce.go index 130727d8f31..2730cedff07 100644 --- a/go/vt/vtgate/evalengine/api_coerce.go +++ b/go/vt/vtgate/evalengine/api_coerce.go @@ -19,12 +19,80 @@ package evalengine import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" ) -func CoerceTo(value sqltypes.Value, typ sqltypes.Type) (sqltypes.Value, error) { - cast, err := valueToEvalCast(value, value.Type(), collations.Unknown) +func CoerceTo(value sqltypes.Value, typ sqltypes.Type, sqlmode SQLMode) (sqltypes.Value, error) { + cast, err := valueToEvalCast(value, value.Type(), collations.Unknown, sqlmode) if err != nil { return sqltypes.Value{}, err } return evalToSQLValueWithType(cast, typ), nil } + +// CoerceTypes takes two input types, and decides how they should be coerced before compared +func CoerceTypes(v1, v2 Type, collationEnv *collations.Environment) (out Type, err error) { + if v1 == v2 { + return v1, nil + } + if sqltypes.IsNull(v1.Type()) || sqltypes.IsNull(v2.Type()) { + return NewType(sqltypes.Null, collations.CollationBinaryID), nil + } + + out = Type{ + init: true, + nullable: v1.Nullable() || v2.Nullable(), + } + + switch { + case sqltypes.IsTextOrBinary(v1.Type()) && sqltypes.IsTextOrBinary(v2.Type()): + mergedCollation, _, _, ferr := mergeCollations(typedCoercionCollation(v1.Type(), v1.Collation()), typedCoercionCollation(v2.Type(), v2.Collation()), v1.Type(), v2.Type(), collationEnv) + if ferr != nil { + return Type{}, ferr + } + out.collation = mergedCollation.Collation + out.typ = sqltypes.VarChar + return + + case sqltypes.IsDateOrTime(v1.Type()): + out.collation = collations.CollationBinaryID + out.typ = v1.Type() + return + + case sqltypes.IsDateOrTime(v2.Type()): + out.collation = collations.CollationBinaryID + out.typ = v2.Type() + return + + case sqltypes.IsNumber(v1.Type()) || sqltypes.IsNumber(v2.Type()): + out.collation = collations.CollationBinaryID + switch { + case sqltypes.IsTextOrBinary(v1.Type()) || sqltypes.IsFloat(v1.Type()) || sqltypes.IsDecimal(v1.Type()) || + sqltypes.IsTextOrBinary(v2.Type()) || sqltypes.IsFloat(v2.Type()) || sqltypes.IsDecimal(v2.Type()): + out.typ = sqltypes.Float64 + return + case sqltypes.IsSigned(v1.Type()): + switch { + case sqltypes.IsUnsigned(v2.Type()): + out.typ = sqltypes.Uint64 + return + case sqltypes.IsSigned(v2.Type()): + out.typ = sqltypes.Int64 + return + default: + return Type{}, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot coerce SIGNED into %v", v2.Type()) + } + case sqltypes.IsUnsigned(v1.Type()): + switch { + case sqltypes.IsSigned(v2.Type()) || sqltypes.IsUnsigned(v2.Type()): + out.typ = sqltypes.Uint64 + return + default: + return Type{}, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot coerce UNSIGNED into %v", v2.Type()) + } + } + } + + return Type{}, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot coerce %v into %v", v1.Type(), v2.Type()) +} diff --git a/go/vt/vtgate/evalengine/api_compare.go b/go/vt/vtgate/evalengine/api_compare.go index 1f30a17b9d5..c6278264a47 100644 --- a/go/vt/vtgate/evalengine/api_compare.go +++ b/go/vt/vtgate/evalengine/api_compare.go @@ -43,7 +43,7 @@ func (err UnsupportedCollationError) Error() string { // UnsupportedCollationHashError is returned when we try to get the hash value and are missing the collation to use var UnsupportedCollationHashError = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "text type with an unknown/unsupported collation cannot be hashed") -func compare(v1, v2 sqltypes.Value, collationID collations.ID) (int, error) { +func compare(v1, v2 sqltypes.Value, collationEnv *collations.Environment, collationID collations.ID) (int, error) { v1t := v1.Type() // We have a fast path here for the case where both values are @@ -129,7 +129,7 @@ func compare(v1, v2 sqltypes.Value, collationID collations.ID) (int, error) { return 0, err } - out, err := evalCompare(v1eval, v2eval) + out, err := evalCompare(v1eval, v2eval, collationEnv) if err != nil { return 0, err } @@ -147,7 +147,7 @@ func compare(v1, v2 sqltypes.Value, collationID collations.ID) (int, error) { // numeric, then a numeric comparison is performed after // necessary conversions. If none are numeric, then it's // a simple binary comparison. Uncomparable values return an error. -func NullsafeCompare(v1, v2 sqltypes.Value, collationID collations.ID) (int, error) { +func NullsafeCompare(v1, v2 sqltypes.Value, collationEnv *collations.Environment, collationID collations.ID) (int, error) { // Based on the categorization defined for the types, // we're going to allow comparison of the following: // Null, isNumber, IsBinary. This will exclude IsQuoted @@ -161,7 +161,7 @@ func NullsafeCompare(v1, v2 sqltypes.Value, collationID collations.ID) (int, err if v2.IsNull() { return 1, nil } - return compare(v1, v2, collationID) + return compare(v1, v2, collationEnv, collationID) } // OrderByParams specifies the parameters for ordering. @@ -176,6 +176,8 @@ type ( // Type for knowing if the collation is relevant Type Type + + CollationEnv *collations.Environment } Comparison []OrderByParams @@ -198,8 +200,8 @@ func (obp *OrderByParams) String() string { val += " ASC" } - if sqltypes.IsText(obp.Type.Type) && obp.Type.Coll != collations.Unknown { - val += " COLLATE " + collations.Local().LookupName(obp.Type.Coll) + if sqltypes.IsText(obp.Type.Type()) && obp.Type.Collation() != collations.Unknown { + val += " COLLATE " + obp.CollationEnv.LookupName(obp.Type.Collation()) } return val } @@ -211,7 +213,7 @@ func (obp *OrderByParams) Compare(r1, r2 []sqltypes.Value) int { if cmp == 0 { var err error - cmp, err = NullsafeCompare(v1, v2, obp.Type.Coll) + cmp, err = NullsafeCompare(v1, v2, obp.CollationEnv, obp.Type.Collation()) if err != nil { _, isCollationErr := err.(UnsupportedCollationError) if !isCollationErr || obp.WeightStringCol == -1 { @@ -220,7 +222,7 @@ func (obp *OrderByParams) Compare(r1, r2 []sqltypes.Value) int { // in case of a comparison or collation error switch to using the weight string column for ordering obp.Col = obp.WeightStringCol obp.WeightStringCol = -1 - cmp, err = NullsafeCompare(r1[obp.Col], r2[obp.Col], obp.Type.Coll) + cmp, err = NullsafeCompare(r1[obp.Col], r2[obp.Col], obp.CollationEnv, obp.Type.Collation()) if err != nil { panic(err) } @@ -236,7 +238,7 @@ func (obp *OrderByParams) Compare(r1, r2 []sqltypes.Value) int { func (cmp Comparison) tinyWeighters(fields []*querypb.Field) []tinyWeighter { weights := make([]tinyWeighter, 0, len(cmp)) for _, c := range cmp { - if apply := TinyWeighter(fields[c.Col], c.Type.Coll); apply != nil { + if apply := TinyWeighter(fields[c.Col], c.Type.Collation()); apply != nil { weights = append(weights, tinyWeighter{c.Col, apply}) } } diff --git a/go/vt/vtgate/evalengine/api_compare_test.go b/go/vt/vtgate/evalengine/api_compare_test.go index 3f97d9d18e9..b483f8e52c0 100644 --- a/go/vt/vtgate/evalengine/api_compare_test.go +++ b/go/vt/vtgate/evalengine/api_compare_test.go @@ -31,8 +31,8 @@ import ( "vitess.io/vitess/go/mysql/collations" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/sqltypes" @@ -51,18 +51,11 @@ type testCase struct { } var ( - T = true - F = false - collationEnv *collations.Environment + T = true + F = false + collationEnv *collations.Environment = collations.MySQL8() ) -func init() { - // We require MySQL 8.0 collations for the comparisons in the tests - mySQLVersion := "8.0.0" - servenv.SetMySQLServerVersionForTest(mySQLVersion) - collationEnv = collations.NewEnvironment(mySQLVersion) -} - func defaultCollation() collations.TypedCollation { return collations.TypedCollation{ Collation: collationEnv.LookupByName("utf8mb4_bin"), @@ -78,11 +71,13 @@ func (tc testCase) run(t *testing.T) { for i, value := range tc.row { fields[i] = &querypb.Field{Type: value.Type()} } - env := NewExpressionEnv(context.Background(), tc.bv, nil) + venv := vtenv.NewTestEnv() + env := NewExpressionEnv(context.Background(), tc.bv, NewEmptyVCursor(venv, time.UTC)) env.Row = tc.row ast := &astCompiler{ cfg: &Config{ - Collation: collations.CollationUtf8mb4ID, + Collation: collations.CollationUtf8mb4ID, + Environment: venv, }, } cmp, err := ast.translateComparisonExpr2(tc.op, tc.v1, tc.v2) @@ -110,7 +105,7 @@ func TestCompareIntegers(t *testing.T) { tests := []testCase{ { name: "integers are equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Int64, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewInt64(18)}, }, @@ -131,25 +126,25 @@ func TestCompareIntegers(t *testing.T) { }, { name: "integers are not equal (3)", - v1: newColumn(0, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Int64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewInt64(18), sqltypes.NewInt64(98)}, }, { name: "unsigned integers are equal", - v1: newColumn(0, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Uint64, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Uint64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewUint64(18)}, }, { name: "unsigned integer and integer are equal", - v1: newColumn(0, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Uint64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewUint64(18), sqltypes.NewInt64(18)}, }, { name: "unsigned integer and integer are not equal", - v1: newColumn(0, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Uint64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewUint64(18), sqltypes.NewInt64(42)}, }, @@ -207,7 +202,7 @@ func TestCompareFloats(t *testing.T) { tests := []testCase{ { name: "floats are equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(18)}, }, @@ -228,7 +223,7 @@ func TestCompareFloats(t *testing.T) { }, { name: "floats are not equal (3)", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(16516.84), sqltypes.NewFloat64(219541.01)}, }, @@ -286,37 +281,37 @@ func TestCompareDecimals(t *testing.T) { tests := []testCase{ { name: "decimals are equal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("12.9019")}, }, { name: "decimals are not equal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("12.9019"), sqltypes.NewDecimal("489.156849")}, }, { name: "decimal is greater than decimal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewDecimal("192.128")}, }, { name: "decimal is not greater than decimal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.128"), sqltypes.NewDecimal("192.129")}, }, { name: "decimal is less than decimal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.128"), sqltypes.NewDecimal("192.129")}, }, { name: "decimal is not less than decimal", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &F, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewDecimal("192.128")}, }, @@ -334,151 +329,151 @@ func TestCompareNumerics(t *testing.T) { tests := []testCase{ { name: "decimal and float are equal", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(189.6), sqltypes.NewDecimal("189.6")}, }, { name: "decimal and float with negative values are equal", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(-98.1839), sqltypes.NewDecimal("-98.1839")}, }, { name: "decimal and float with negative values are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(-98.9381), sqltypes.NewDecimal("-98.1839")}, }, { name: "decimal and float with negative values are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Float64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewFloat64(-98.9381), sqltypes.NewDecimal("-98.1839")}, }, { name: "decimal and integer are equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Int64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewInt64(8979), sqltypes.NewDecimal("8979")}, }, { name: "decimal and integer are equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("8979.0000"), sqltypes.NewInt64(8979)}, }, { name: "decimal and unsigned integer are equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Uint64, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Decimal, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewUint64(901), sqltypes.NewDecimal("901")}, }, { name: "decimal and unsigned integer are equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Uint64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("901.00"), sqltypes.NewUint64(901)}, }, { name: "decimal and unsigned integer are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Uint64, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewUint64(192)}, }, { name: "decimal and unsigned integer are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Uint64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Uint64, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewUint64(192)}, }, { name: "decimal is greater than integer", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("1.01"), sqltypes.NewInt64(1)}, }, { name: "decimal is greater-equal to integer", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("1.00"), sqltypes.NewInt64(1)}, }, { name: "decimal is less than integer", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDecimal(".99"), sqltypes.NewInt64(1)}, }, { name: "decimal is less-equal to integer", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("1.00"), sqltypes.NewInt64(1)}, }, { name: "decimal is greater than float", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("849.896"), sqltypes.NewFloat64(86.568)}, }, { name: "decimal is not greater than float", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("15.23"), sqltypes.NewFloat64(8689.5)}, }, { name: "decimal is greater-equal to float (1)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("65"), sqltypes.NewFloat64(65)}, }, { name: "decimal is greater-equal to float (2)", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("65"), sqltypes.NewFloat64(60)}, }, { name: "decimal is less than float", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDecimal("0.998"), sqltypes.NewFloat64(0.999)}, }, { name: "decimal is less-equal to float", - v1: newColumn(0, Type{Type: sqltypes.Decimal, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Float64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Decimal, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Float64, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDecimal("1.000101"), sqltypes.NewFloat64(1.00101)}, }, { name: "different int types are equal for 8 bit", - v1: newColumn(0, Type{Type: sqltypes.Int8, Coll: collations.CollationBinaryID}), v2: NewLiteralInt(0), + v1: newColumn(0, NewType(sqltypes.Int8, collations.CollationBinaryID)), v2: NewLiteralInt(0), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewInt8(0)}, }, { name: "different int types are equal for 32 bit", - v1: newColumn(0, Type{Type: sqltypes.Int32, Coll: collations.CollationBinaryID}), v2: NewLiteralInt(0), + v1: newColumn(0, NewType(sqltypes.Int32, collations.CollationBinaryID)), v2: NewLiteralInt(0), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewInt32(0)}, }, { name: "different int types are equal for float32 bit", - v1: newColumn(0, Type{Type: sqltypes.Float32, Coll: collations.CollationBinaryID}), v2: NewLiteralFloat(1.0), + v1: newColumn(0, NewType(sqltypes.Float32, collations.CollationBinaryID)), v2: NewLiteralFloat(1.0), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.Float32, []byte("1.0"))}, }, { name: "different unsigned int types are equal for 8 bit", - v1: newColumn(0, Type{Type: sqltypes.Uint8, Coll: collations.CollationBinaryID}), v2: NewLiteralInt(0), + v1: newColumn(0, NewType(sqltypes.Uint8, collations.CollationBinaryID)), v2: NewLiteralInt(0), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.Uint8, []byte("0"))}, }, { name: "different unsigned int types are equal for 32 bit", - v1: newColumn(0, Type{Type: sqltypes.Uint32, Coll: collations.CollationBinaryID}), v2: NewLiteralInt(0), + v1: newColumn(0, NewType(sqltypes.Uint32, collations.CollationBinaryID)), v2: NewLiteralInt(0), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewUint32(0)}, }, @@ -496,73 +491,73 @@ func TestCompareDatetime(t *testing.T) { tests := []testCase{ { name: "datetimes are equal", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00")}, }, { name: "datetimes are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00"), sqltypes.NewDatetime("2020-10-22 12:00:00")}, }, { name: "datetimes are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00"), sqltypes.NewDatetime("2021-10-22 10:23:56")}, }, { name: "datetimes are not equal (3)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 00:00:00"), sqltypes.NewDatetime("2021-02-01 00:00:00")}, }, { name: "datetime is greater than datetime", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")}, }, { name: "datetime is not greater than datetime", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")}, }, { name: "datetime is less than datetime", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")}, }, { name: "datetime is not less than datetime", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &F, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")}, }, { name: "datetime is greater-equal to datetime (1)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-30 10:42:50")}, }, { name: "datetime is greater-equal to datetime (2)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")}, }, { name: "datetime is less-equal to datetime (1)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-30 10:42:50")}, }, { name: "datetime is less-equal to datetime (2)", - v1: newColumn(0, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Datetime, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")}, }, @@ -580,73 +575,73 @@ func TestCompareTimestamp(t *testing.T) { tests := []testCase{ { name: "timestamps are equal", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00")}, }, { name: "timestamps are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00"), sqltypes.NewTimestamp("2020-10-22 12:00:00")}, }, { name: "timestamps are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00"), sqltypes.NewTimestamp("2021-10-22 10:23:56")}, }, { name: "timestamps are not equal (3)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 00:00:00"), sqltypes.NewTimestamp("2021-02-01 00:00:00")}, }, { name: "timestamp is greater than timestamp", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")}, }, { name: "timestamp is not greater than timestamp", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")}, }, { name: "timestamp is less than timestamp", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")}, }, { name: "timestamp is not less than timestamp", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &F, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")}, }, { name: "timestamp is greater-equal to timestamp (1)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-30 10:42:50")}, }, { name: "timestamp is greater-equal to timestamp (2)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")}, }, { name: "timestamp is less-equal to timestamp (1)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-30 10:42:50")}, }, { name: "timestamp is less-equal to timestamp (2)", - v1: newColumn(0, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")}, }, @@ -668,67 +663,67 @@ func TestCompareDate(t *testing.T) { tests := []testCase{ { name: "dates are equal", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22")}, }, { name: "dates are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDate("2020-10-21")}, }, { name: "dates are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-02-01")}, }, { name: "date is greater than date", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")}, }, { name: "date is not greater than date", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")}, }, { name: "date is less than date", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")}, }, { name: "date is not less than date", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &F, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")}, }, { name: "date is greater-equal to date (1)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-30")}, }, { name: "date is greater-equal to date (2)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")}, }, { name: "date is less-equal to date (1)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-30")}, }, { name: "date is less-equal to date (2)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")}, }, @@ -746,79 +741,79 @@ func TestCompareTime(t *testing.T) { tests := []testCase{ { name: "times are equal", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewTime("12:00:00")}, }, { name: "times are not equal (1)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewTime("12:00:00"), sqltypes.NewTime("10:23:56")}, }, { name: "times are not equal (2)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewTime("00:00:00"), sqltypes.NewTime("10:15:00")}, }, { name: "time is greater than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTime("18:14:35"), sqltypes.NewTime("13:01:38")}, }, { name: "time is not greater than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTime("02:46:02"), sqltypes.NewTime("10:42:50")}, }, { name: "time is greater than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTime("101:14:35"), sqltypes.NewTime("13:01:38")}, }, { name: "time is not greater than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &F, op: sqlparser.GreaterThanOp, row: []sqltypes.Value{sqltypes.NewTime("24:46:02"), sqltypes.NewTime("101:42:50")}, }, { name: "time is less than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewTime("04:30:00"), sqltypes.NewTime("09:23:48")}, }, { name: "time is not less than time", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &F, op: sqlparser.LessThanOp, row: []sqltypes.Value{sqltypes.NewTime("15:21:00"), sqltypes.NewTime("10:00:00")}, }, { name: "time is greater-equal to time (1)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewTime("10:42:50"), sqltypes.NewTime("10:42:50")}, }, { name: "time is greater-equal to time (2)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.GreaterEqualOp, row: []sqltypes.Value{sqltypes.NewTime("19:42:50"), sqltypes.NewTime("13:10:02")}, }, { name: "time is less-equal to time (1)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewTime("10:42:50"), sqltypes.NewTime("10:42:50")}, }, { name: "time is less-equal to time (2)", - v1: newColumn(0, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Time, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.LessEqualOp, row: []sqltypes.Value{sqltypes.NewTime("10:10:02"), sqltypes.NewTime("10:42:50")}, }, @@ -836,13 +831,13 @@ func TestCompareDates(t *testing.T) { tests := []testCase{ { name: "date equal datetime", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDatetime("2021-10-22 00:00:00")}, }, { name: "date equal datetime through bind variables", - v1: NewBindVar("k1", Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: NewBindVar("k2", Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: NewBindVar("k1", NewType(sqltypes.Date, collations.CollationBinaryID)), v2: NewBindVar("k2", NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, bv: map[string]*querypb.BindVariable{ "k1": {Type: sqltypes.Date, Value: []byte("2021-10-22")}, @@ -851,7 +846,7 @@ func TestCompareDates(t *testing.T) { }, { name: "date not equal datetime through bind variables", - v1: NewBindVar("k1", Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: NewBindVar("k2", Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: NewBindVar("k1", NewType(sqltypes.Date, collations.CollationBinaryID)), v2: NewBindVar("k2", NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, bv: map[string]*querypb.BindVariable{ "k1": {Type: sqltypes.Date, Value: []byte("2021-02-20")}, @@ -860,73 +855,73 @@ func TestCompareDates(t *testing.T) { }, { name: "date not equal datetime", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDatetime("2021-10-20 00:06:00")}, }, { name: "date equal timestamp", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewTimestamp("2021-10-22 00:00:00")}, }, { name: "date not equal timestamp", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewTimestamp("2021-10-22 16:00:00")}, }, { name: "date equal time", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &F, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewDate(time.Now().Format("2006-01-02")), sqltypes.NewTime("00:00:00")}, }, { name: "date not equal time", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDate(time.Now().Format("2006-01-02")), sqltypes.NewTime("12:00:00")}, }, { name: "string equal datetime", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Datetime, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Datetime, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22"), sqltypes.NewDatetime("2021-10-22 00:00:00")}, }, { name: "string equal timestamp", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22 00:00:00"), sqltypes.NewTimestamp("2021-10-22 00:00:00")}, }, { name: "string not equal timestamp", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Timestamp, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Timestamp, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22 06:00:30"), sqltypes.NewTimestamp("2021-10-20 15:02:10")}, }, { name: "string equal time", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Time, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Time, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("00:05:12"), sqltypes.NewTime("00:05:12")}, }, { name: "string equal date", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("2021-02-22"), sqltypes.NewDate("2021-02-22")}, }, { name: "string not equal date (1, date on the RHS)", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), v2: newColumn(1, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), v2: newColumn(1, NewType(sqltypes.Date, collations.CollationBinaryID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("2021-02-20"), sqltypes.NewDate("2021-03-30")}, }, { name: "string not equal date (2, date on the LHS)", - v1: newColumn(0, Type{Type: sqltypes.Date, Coll: collations.CollationBinaryID}), v2: newColumn(1, Type{Type: sqltypes.VarChar, Coll: collations.CollationUtf8mb4ID}), + v1: newColumn(0, NewType(sqltypes.Date, collations.CollationBinaryID)), v2: newColumn(1, NewType(sqltypes.VarChar, collations.CollationUtf8mb4ID)), out: &T, op: sqlparser.NotEqualOp, row: []sqltypes.Value{sqltypes.NewDate("2021-03-30"), sqltypes.NewVarChar("2021-02-20")}, }, @@ -944,13 +939,13 @@ func TestCompareStrings(t *testing.T) { tests := []testCase{ { name: "string equal string", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.Default()}), v2: newColumn(1, Type{Type: sqltypes.VarChar, Coll: collations.Default()}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.MySQL8().DefaultConnectionCharset())), v2: newColumn(1, NewType(sqltypes.VarChar, collations.MySQL8().DefaultConnectionCharset())), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("toto"), sqltypes.NewVarChar("toto")}, }, { name: "string equal number", - v1: newColumn(0, Type{Type: sqltypes.VarChar, Coll: collations.Default()}), v2: newColumn(1, Type{Type: sqltypes.Int64, Coll: collations.CollationBinaryID}), + v1: newColumn(0, NewType(sqltypes.VarChar, collations.MySQL8().DefaultConnectionCharset())), v2: newColumn(1, NewType(sqltypes.Int64, collations.CollationBinaryID)), out: &T, op: sqlparser.EqualOp, row: []sqltypes.Value{sqltypes.NewVarChar("1"), sqltypes.NewInt64(1)}, }, @@ -1153,7 +1148,7 @@ func TestNullsafeCompare(t *testing.T) { } for _, tcase := range tcases { t.Run(fmt.Sprintf("%v/%v", tcase.v1, tcase.v2), func(t *testing.T) { - got, err := NullsafeCompare(tcase.v1, tcase.v2, collation) + got, err := NullsafeCompare(tcase.v1, tcase.v2, collations.MySQL8(), collation) if tcase.err != nil { require.EqualError(t, err, tcase.err.Error()) return @@ -1242,7 +1237,7 @@ func TestNullsafeCompareCollate(t *testing.T) { } for _, tcase := range tcases { t.Run(fmt.Sprintf("%v/%v", tcase.v1, tcase.v2), func(t *testing.T) { - got, err := NullsafeCompare(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), tcase.collation) + got, err := NullsafeCompare(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), collations.MySQL8(), tcase.collation) if tcase.err == nil { require.NoError(t, err) } else { @@ -1293,7 +1288,7 @@ func BenchmarkNullSafeComparison(b *testing.B) { for i := 0; i < b.N; i++ { for _, lhs := range inputs { for _, rhs := range inputs { - _, _ = NullsafeCompare(lhs, rhs, collid) + _, _ = NullsafeCompare(lhs, rhs, collations.MySQL8(), collid) } } } @@ -1323,7 +1318,7 @@ func BenchmarkNullSafeComparison(b *testing.B) { for i := 0; i < b.N; i++ { for _, lhs := range inputs { for _, rhs := range inputs { - _, _ = NullsafeCompare(lhs, rhs, collations.CollationUtf8mb4ID) + _, _ = NullsafeCompare(lhs, rhs, collations.MySQL8(), collations.CollationUtf8mb4ID) } } } @@ -1341,30 +1336,30 @@ func TestCompareSorter(t *testing.T) { Count: 100, Limit: 10, Random: sqltypes.RandomGenerators[sqltypes.Int64], - Cmp: Comparison{{Col: 0, Desc: false, Type: Type{Type: sqltypes.Int64}}}, + Cmp: Comparison{{Col: 0, Desc: false, Type: NewType(sqltypes.Int64, collations.Unknown)}}, }, { Count: 100, Limit: 10, Random: sqltypes.RandomGenerators[sqltypes.Int64], - Cmp: Comparison{{Col: 0, Desc: true, Type: Type{Type: sqltypes.Int64}}}, + Cmp: Comparison{{Col: 0, Desc: true, Type: NewType(sqltypes.Int64, collations.Unknown)}}, }, { Count: 100, Limit: math.MaxInt, Random: sqltypes.RandomGenerators[sqltypes.Int64], - Cmp: Comparison{{Col: 0, Desc: false, Type: Type{Type: sqltypes.Int64}}}, + Cmp: Comparison{{Col: 0, Desc: false, Type: NewType(sqltypes.Int64, collations.Unknown)}}, }, { Count: 100, Limit: math.MaxInt, Random: sqltypes.RandomGenerators[sqltypes.Int64], - Cmp: Comparison{{Col: 0, Desc: true, Type: Type{Type: sqltypes.Int64}}}, + Cmp: Comparison{{Col: 0, Desc: true, Type: NewType(sqltypes.Int64, collations.Unknown)}}, }, } for _, tc := range cases { - t.Run(fmt.Sprintf("%s-%d-%d", tc.Cmp[0].Type.Type, tc.Count, tc.Limit), func(t *testing.T) { + t.Run(fmt.Sprintf("%s-%d-%d", tc.Cmp[0].Type.Type(), tc.Count, tc.Limit), func(t *testing.T) { unsorted := make([]sqltypes.Row, 0, tc.Count) for i := 0; i < tc.Count; i++ { unsorted = append(unsorted, []sqltypes.Value{tc.Random()}) diff --git a/go/vt/vtgate/evalengine/api_hash.go b/go/vt/vtgate/evalengine/api_hash.go index 209f766840d..3bce100839c 100644 --- a/go/vt/vtgate/evalengine/api_hash.go +++ b/go/vt/vtgate/evalengine/api_hash.go @@ -34,8 +34,8 @@ type HashCode = uint64 // NullsafeHashcode returns an int64 hashcode that is guaranteed to be the same // for two values that are considered equal by `NullsafeCompare`. -func NullsafeHashcode(v sqltypes.Value, collation collations.ID, coerceType sqltypes.Type) (HashCode, error) { - e, err := valueToEvalCast(v, coerceType, collation) +func NullsafeHashcode(v sqltypes.Value, collation collations.ID, coerceType sqltypes.Type, sqlmode SQLMode) (HashCode, error) { + e, err := valueToEvalCast(v, coerceType, collation, sqlmode) if err != nil { return 0, err } @@ -75,7 +75,7 @@ var ErrHashCoercionIsNotExact = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, " // for two values that are considered equal by `NullsafeCompare`. // This can be used to avoid having to do comparison checks after a hash, // since we consider the 128 bits of entropy enough to guarantee uniqueness. -func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collations.ID, coerceTo sqltypes.Type) error { +func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collations.ID, coerceTo sqltypes.Type, sqlmode SQLMode) error { switch { case v.IsNull(), sqltypes.IsNull(coerceTo): hash.Write16(hashPrefixNil) @@ -97,7 +97,7 @@ func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collat case v.IsText(), v.IsBinary(): f, _ = fastparse.ParseFloat64(v.RawStr()) default: - return nullsafeHashcode128Default(hash, v, collation, coerceTo) + return nullsafeHashcode128Default(hash, v, collation, coerceTo, sqlmode) } if err != nil { return err @@ -137,7 +137,7 @@ func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collat } neg = i < 0 default: - return nullsafeHashcode128Default(hash, v, collation, coerceTo) + return nullsafeHashcode128Default(hash, v, collation, coerceTo, sqlmode) } if err != nil { return err @@ -180,7 +180,7 @@ func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collat u, err = uint64(fval), nil } default: - return nullsafeHashcode128Default(hash, v, collation, coerceTo) + return nullsafeHashcode128Default(hash, v, collation, coerceTo, sqlmode) } if err != nil { return err @@ -223,20 +223,20 @@ func NullsafeHashcode128(hash *vthash.Hasher, v sqltypes.Value, collation collat fval, _ := fastparse.ParseFloat64(v.RawStr()) dec = decimal.NewFromFloat(fval) default: - return nullsafeHashcode128Default(hash, v, collation, coerceTo) + return nullsafeHashcode128Default(hash, v, collation, coerceTo, sqlmode) } hash.Write16(hashPrefixDecimal) dec.Hash(hash) default: - return nullsafeHashcode128Default(hash, v, collation, coerceTo) + return nullsafeHashcode128Default(hash, v, collation, coerceTo, sqlmode) } return nil } -func nullsafeHashcode128Default(hash *vthash.Hasher, v sqltypes.Value, collation collations.ID, coerceTo sqltypes.Type) error { +func nullsafeHashcode128Default(hash *vthash.Hasher, v sqltypes.Value, collation collations.ID, coerceTo sqltypes.Type, sqlmode SQLMode) error { // Slow path to handle all other types. This uses the generic // logic for value casting to ensure we match MySQL here. - e, err := valueToEvalCast(v, coerceTo, collation) + e, err := valueToEvalCast(v, coerceTo, collation, sqlmode) if err != nil { return err } diff --git a/go/vt/vtgate/evalengine/api_hash_test.go b/go/vt/vtgate/evalengine/api_hash_test.go index 832a1ed3b88..7a680892712 100644 --- a/go/vt/vtgate/evalengine/api_hash_test.go +++ b/go/vt/vtgate/evalengine/api_hash_test.go @@ -24,13 +24,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vthash" - - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" ) func TestHashCodes(t *testing.T) { @@ -54,14 +52,14 @@ func TestHashCodes(t *testing.T) { for _, tc := range cases { t.Run(fmt.Sprintf("%v %s %v", tc.static, equality(tc.equal).Operator(), tc.dynamic), func(t *testing.T) { - cmp, err := NullsafeCompare(tc.static, tc.dynamic, collations.CollationUtf8mb4ID) + cmp, err := NullsafeCompare(tc.static, tc.dynamic, collations.MySQL8(), collations.CollationUtf8mb4ID) require.NoError(t, err) require.Equalf(t, tc.equal, cmp == 0, "got %v %s %v (expected %s)", tc.static, equality(cmp == 0).Operator(), tc.dynamic, equality(tc.equal)) - h1, err := NullsafeHashcode(tc.static, collations.CollationUtf8mb4ID, tc.static.Type()) + h1, err := NullsafeHashcode(tc.static, collations.CollationUtf8mb4ID, tc.static.Type(), 0) require.NoError(t, err) - h2, err := NullsafeHashcode(tc.dynamic, collations.CollationUtf8mb4ID, tc.static.Type()) + h2, err := NullsafeHashcode(tc.dynamic, collations.CollationUtf8mb4ID, tc.static.Type(), 0) require.ErrorIs(t, err, tc.err) assert.Equalf(t, tc.equal, h1 == h2, "HASH(%v) %s HASH(%v) (expected %s)", tc.static, equality(h1 == h2).Operator(), tc.dynamic, equality(tc.equal)) @@ -74,19 +72,19 @@ func TestHashCodes(t *testing.T) { func TestHashCodesRandom(t *testing.T) { tested := 0 equal := 0 - collation := collations.Local().LookupByName("utf8mb4_general_ci") + collation := collations.MySQL8().LookupByName("utf8mb4_general_ci") endTime := time.Now().Add(1 * time.Second) for time.Now().Before(endTime) { tested++ v1, v2 := sqltypes.TestRandomValues() - cmp, err := NullsafeCompare(v1, v2, collation) + cmp, err := NullsafeCompare(v1, v2, collations.MySQL8(), collation) require.NoErrorf(t, err, "%s compared with %s", v1.String(), v2.String()) typ, err := coerceTo(v1.Type(), v2.Type()) require.NoError(t, err) - hash1, err := NullsafeHashcode(v1, collation, typ) + hash1, err := NullsafeHashcode(v1, collation, typ, 0) require.NoError(t, err) - hash2, err := NullsafeHashcode(v2, collation, typ) + hash2, err := NullsafeHashcode(v2, collation, typ, 0) require.NoError(t, err) if cmp == 0 { equal++ @@ -139,16 +137,16 @@ func TestHashCodes128(t *testing.T) { for _, tc := range cases { t.Run(fmt.Sprintf("%v %s %v", tc.static, equality(tc.equal).Operator(), tc.dynamic), func(t *testing.T) { - cmp, err := NullsafeCompare(tc.static, tc.dynamic, collations.CollationUtf8mb4ID) + cmp, err := NullsafeCompare(tc.static, tc.dynamic, collations.MySQL8(), collations.CollationUtf8mb4ID) require.NoError(t, err) require.Equalf(t, tc.equal, cmp == 0, "got %v %s %v (expected %s)", tc.static, equality(cmp == 0).Operator(), tc.dynamic, equality(tc.equal)) hasher1 := vthash.New() - err = NullsafeHashcode128(&hasher1, tc.static, collations.CollationUtf8mb4ID, tc.static.Type()) + err = NullsafeHashcode128(&hasher1, tc.static, collations.CollationUtf8mb4ID, tc.static.Type(), 0) require.NoError(t, err) hasher2 := vthash.New() - err = NullsafeHashcode128(&hasher2, tc.dynamic, collations.CollationUtf8mb4ID, tc.static.Type()) + err = NullsafeHashcode128(&hasher2, tc.dynamic, collations.CollationUtf8mb4ID, tc.static.Type(), 0) require.ErrorIs(t, err, tc.err) h1 := hasher1.Sum128() @@ -163,21 +161,21 @@ func TestHashCodes128(t *testing.T) { func TestHashCodesRandom128(t *testing.T) { tested := 0 equal := 0 - collation := collations.Local().LookupByName("utf8mb4_general_ci") + collation := collations.MySQL8().LookupByName("utf8mb4_general_ci") endTime := time.Now().Add(1 * time.Second) for time.Now().Before(endTime) { tested++ v1, v2 := sqltypes.TestRandomValues() - cmp, err := NullsafeCompare(v1, v2, collation) + cmp, err := NullsafeCompare(v1, v2, collations.MySQL8(), collation) require.NoErrorf(t, err, "%s compared with %s", v1.String(), v2.String()) typ, err := coerceTo(v1.Type(), v2.Type()) require.NoError(t, err) hasher1 := vthash.New() - err = NullsafeHashcode128(&hasher1, v1, collation, typ) + err = NullsafeHashcode128(&hasher1, v1, collation, typ, 0) require.NoError(t, err) hasher2 := vthash.New() - err = NullsafeHashcode128(&hasher2, v2, collation, typ) + err = NullsafeHashcode128(&hasher2, v2, collation, typ, 0) require.NoError(t, err) if cmp == 0 { equal++ @@ -197,7 +195,7 @@ func coerceTo(v1, v2 sqltypes.Type) (sqltypes.Type, error) { if sqltypes.IsNull(v1) || sqltypes.IsNull(v2) { return sqltypes.Null, nil } - if (sqltypes.IsText(v1) || sqltypes.IsBinary(v1)) && (sqltypes.IsText(v2) || sqltypes.IsBinary(v2)) { + if (sqltypes.IsTextOrBinary(v1)) && (sqltypes.IsTextOrBinary(v2)) { return sqltypes.VarChar, nil } if sqltypes.IsDateOrTime(v1) { @@ -209,7 +207,7 @@ func coerceTo(v1, v2 sqltypes.Type) (sqltypes.Type, error) { if sqltypes.IsNumber(v1) || sqltypes.IsNumber(v2) { switch { - case sqltypes.IsText(v1) || sqltypes.IsBinary(v1) || sqltypes.IsText(v2) || sqltypes.IsBinary(v2): + case sqltypes.IsTextOrBinary(v1) || sqltypes.IsTextOrBinary(v2): return sqltypes.Float64, nil case sqltypes.IsFloat(v2) || v2 == sqltypes.Decimal || sqltypes.IsFloat(v1) || v1 == sqltypes.Decimal: return sqltypes.Float64, nil diff --git a/go/vt/vtgate/evalengine/api_literal.go b/go/vt/vtgate/evalengine/api_literal.go index f12988233e8..64d0cf5c1c3 100644 --- a/go/vt/vtgate/evalengine/api_literal.go +++ b/go/vt/vtgate/evalengine/api_literal.go @@ -203,8 +203,8 @@ func NewLiteralBinaryFromBit(val []byte) (*Literal, error) { func NewBindVar(key string, typ Type) *BindVariable { return &BindVariable{ Key: key, - Type: typ.Type, - Collation: typ.Coll, + Type: typ.Type(), + Collation: typ.Collation(), dynamicTypeOffset: -1, } } @@ -222,9 +222,12 @@ func NewBindVarTuple(key string, coll collations.ID) *BindVariable { func NewColumn(offset int, typ Type, original sqlparser.Expr) *Column { return &Column{ Offset: offset, - Type: typ.Type, - Collation: typedCoercionCollation(typ.Type, typ.Coll), + Type: typ.Type(), + Size: typ.size, + Scale: typ.scale, + Collation: typedCoercionCollation(typ.Type(), typ.Collation()), Original: original, + Nullable: typ.nullable, dynamicTypeOffset: -1, } } diff --git a/go/vt/vtgate/evalengine/api_type_aggregation.go b/go/vt/vtgate/evalengine/api_type_aggregation.go index 5ab1d2e5338..83703d4532c 100644 --- a/go/vt/vtgate/evalengine/api_type_aggregation.go +++ b/go/vt/vtgate/evalengine/api_type_aggregation.go @@ -16,7 +16,10 @@ limitations under the License. package evalengine -import "vitess.io/vitess/go/sqltypes" +import ( + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" +) type typeAggregation struct { double uint16 @@ -42,17 +45,29 @@ type typeAggregation struct { geometry uint16 blob uint16 total uint16 + + nullable bool } -func AggregateTypes(types []sqltypes.Type) sqltypes.Type { +func AggregateEvalTypes(types []Type, env *collations.Environment) (Type, error) { var typeAgg typeAggregation + var collAgg collationAggregation + var size, scale int32 for _, typ := range types { - var flag typeFlag - if typ == sqltypes.HexVal || typ == sqltypes.HexNum { - typ = sqltypes.Binary - flag = flagHex + typeAgg.addNullable(typ.typ, typ.nullable) + if err := collAgg.add(typedCoercionCollation(typ.typ, typ.collation), env); err != nil { + return Type{}, err } - typeAgg.add(typ, flag) + size = max(typ.size, size) + scale = max(typ.scale, scale) + } + return NewTypeEx(typeAgg.result(), collAgg.result().Collation, typeAgg.nullable, size, scale), nil +} + +func AggregateTypes(types []sqltypes.Type) sqltypes.Type { + var typeAgg typeAggregation + for _, typ := range types { + typeAgg.addNullable(typ, false) } return typeAgg.result() } @@ -63,6 +78,7 @@ func (ta *typeAggregation) addEval(e eval) { switch e := e.(type) { case nil: t = sqltypes.Null + ta.nullable = true case *evalBytes: t = sqltypes.Type(e.tt) f = e.flag @@ -72,7 +88,22 @@ func (ta *typeAggregation) addEval(e eval) { ta.add(t, f) } +func (ta *typeAggregation) addNullable(typ sqltypes.Type, nullable bool) { + var flag typeFlag + if typ == sqltypes.HexVal || typ == sqltypes.HexNum { + typ = sqltypes.Binary + flag |= flagHex + } + if nullable { + flag |= flagNullable + } + ta.add(typ, flag) +} + func (ta *typeAggregation) add(tt sqltypes.Type, f typeFlag) { + if f&flagNullable != 0 { + ta.nullable = true + } switch tt { case sqltypes.Float32, sqltypes.Float64: ta.double++ @@ -122,6 +153,23 @@ func (ta *typeAggregation) add(tt sqltypes.Type, f typeFlag) { ta.total++ } +func nextSignedTypeForUnsigned(t sqltypes.Type) sqltypes.Type { + switch t { + case sqltypes.Uint8: + return sqltypes.Int16 + case sqltypes.Uint16: + return sqltypes.Int24 + case sqltypes.Uint24: + return sqltypes.Int32 + case sqltypes.Uint32: + return sqltypes.Int64 + case sqltypes.Uint64: + return sqltypes.Decimal + default: + panic("bad unsigned integer type") + } +} + func (ta *typeAggregation) result() sqltypes.Type { /* If all types are numeric, the aggregated type is also numeric: @@ -175,11 +223,14 @@ func (ta *typeAggregation) result() sqltypes.Type { if ta.unsigned == ta.total { return ta.unsignedMax } - if ta.unsignedMax == sqltypes.Uint64 && ta.signed > 0 { - return sqltypes.Decimal + if ta.signed == 0 { + panic("bad type aggregation for signed/unsigned types") + } + agtype := nextSignedTypeForUnsigned(ta.unsignedMax) + if sqltypes.IsSigned(agtype) { + return max(agtype, ta.signedMax) } - // TODO - return sqltypes.Uint64 + return agtype } if ta.char == ta.total { diff --git a/go/vt/vtgate/evalengine/api_type_aggregation_test.go b/go/vt/vtgate/evalengine/api_type_aggregation_test.go new file mode 100644 index 00000000000..1bf29eaffb3 --- /dev/null +++ b/go/vt/vtgate/evalengine/api_type_aggregation_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package evalengine + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" +) + +var aggregationCases = []struct { + types []sqltypes.Type + result sqltypes.Type +}{ + {[]sqltypes.Type{sqltypes.Int64, sqltypes.Int32, sqltypes.Float64}, sqltypes.Float64}, + {[]sqltypes.Type{sqltypes.Int64, sqltypes.Decimal, sqltypes.Float64}, sqltypes.Float64}, + {[]sqltypes.Type{sqltypes.Int64, sqltypes.Int32, sqltypes.Decimal}, sqltypes.Decimal}, + {[]sqltypes.Type{sqltypes.Int64, sqltypes.Int32, sqltypes.Int64}, sqltypes.Int64}, + {[]sqltypes.Type{sqltypes.Int32, sqltypes.Int16, sqltypes.Int8}, sqltypes.Int32}, + {[]sqltypes.Type{sqltypes.Int32, sqltypes.Uint16, sqltypes.Uint8}, sqltypes.Int32}, + {[]sqltypes.Type{sqltypes.Int32, sqltypes.Uint16, sqltypes.Uint32}, sqltypes.Int64}, + {[]sqltypes.Type{sqltypes.Int32, sqltypes.Uint16, sqltypes.Uint64}, sqltypes.Decimal}, + {[]sqltypes.Type{sqltypes.Bit, sqltypes.Bit, sqltypes.Bit}, sqltypes.Bit}, + {[]sqltypes.Type{sqltypes.Bit, sqltypes.Int32, sqltypes.Float64}, sqltypes.Float64}, + {[]sqltypes.Type{sqltypes.Bit, sqltypes.Decimal, sqltypes.Float64}, sqltypes.Float64}, + {[]sqltypes.Type{sqltypes.Bit, sqltypes.Int32, sqltypes.Decimal}, sqltypes.Decimal}, + {[]sqltypes.Type{sqltypes.Bit, sqltypes.Int32, sqltypes.Int64}, sqltypes.Int64}, + {[]sqltypes.Type{sqltypes.Char, sqltypes.VarChar}, sqltypes.VarChar}, + {[]sqltypes.Type{sqltypes.Char, sqltypes.Char}, sqltypes.VarChar}, + {[]sqltypes.Type{sqltypes.Char, sqltypes.VarChar, sqltypes.VarBinary}, sqltypes.VarBinary}, + {[]sqltypes.Type{sqltypes.Char, sqltypes.Char, sqltypes.Set, sqltypes.Enum}, sqltypes.VarChar}, + {[]sqltypes.Type{sqltypes.TypeJSON, sqltypes.TypeJSON}, sqltypes.TypeJSON}, + {[]sqltypes.Type{sqltypes.Geometry, sqltypes.Geometry}, sqltypes.Geometry}, +} + +func TestTypeAggregations(t *testing.T) { + for i, tc := range aggregationCases { + t.Run(fmt.Sprintf("%d.%v", i, tc.result), func(t *testing.T) { + res := AggregateTypes(tc.types) + require.Equalf(t, tc.result, res, "expected aggregate(%v) = %v, got %v", tc.types, tc.result, res) + }) + } +} + +func TestEvalengineTypeAggregations(t *testing.T) { + for i, tc := range aggregationCases { + t.Run(fmt.Sprintf("%d.%v", i, tc.result), func(t *testing.T) { + var types []Type + for _, tt := range tc.types { + // this test only aggregates binary collations because textual collation + // aggregation is tested in the `mysql/collations` package + types = append(types, NewType(tt, collations.CollationBinaryID)) + } + + res, err := AggregateEvalTypes(types, collations.MySQL8()) + require.NoError(t, err) + require.Equalf(t, tc.result, res.Type(), "expected aggregate(%v) = %v, got %v", tc.types, tc.result, res) + }) + } +} diff --git a/go/vt/vtgate/evalengine/arithmetic.go b/go/vt/vtgate/evalengine/arithmetic.go index c258dab1672..031b387d275 100644 --- a/go/vt/vtgate/evalengine/arithmetic.go +++ b/go/vt/vtgate/evalengine/arithmetic.go @@ -25,7 +25,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" ) -func dataOutOfRangeError[N1, N2 int | int64 | uint64 | float64](v1 N1, v2 N2, typ, sign string) error { +func dataOutOfRangeError[N1, N2 int64 | uint64 | float64](v1 N1, v2 N2, typ, sign string) error { return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in '(%v %s %v)'", typ, v1, sign, v2) } diff --git a/go/vt/vtgate/evalengine/cached_size.go b/go/vt/vtgate/evalengine/cached_size.go index d227af1a237..b386d3dc915 100644 --- a/go/vt/vtgate/evalengine/cached_size.go +++ b/go/vt/vtgate/evalengine/cached_size.go @@ -145,10 +145,12 @@ func (cached *CollateExpr) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(24) + size += int64(32) } // field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr size += cached.UnaryExpr.CachedSize(false) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *Column) CachedSize(alloc bool) int64 { @@ -157,7 +159,7 @@ func (cached *Column) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(48) + size += int64(64) } // field Original vitess.io/vitess/go/vt/sqlparser.Expr if cc, ok := cached.Original.(cachedObject); ok { @@ -187,7 +189,7 @@ func (cached *CompiledExpr) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(64) + size += int64(80) } // field code []vitess.io/vitess/go/vt/vtgate/evalengine.frame { @@ -211,6 +213,12 @@ func (cached *ConvertExpr) CachedSize(alloc bool) int64 { size += cached.UnaryExpr.CachedSize(false) // field Type string size += hack.RuntimeAllocSize(int64(len(cached.Type))) + // field Length *int + size += hack.RuntimeAllocSize(int64(8)) + // field Scale *int + size += hack.RuntimeAllocSize(int64(8)) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *ConvertUsingExpr) CachedSize(alloc bool) int64 { @@ -219,10 +227,12 @@ func (cached *ConvertUsingExpr) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(24) + size += int64(32) } // field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr size += cached.UnaryExpr.CachedSize(false) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *InExpr) CachedSize(alloc bool) int64 { @@ -255,10 +265,12 @@ func (cached *IntroducerExpr) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(24) + size += int64(32) } // field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr size += cached.UnaryExpr.CachedSize(false) + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) return size } func (cached *IsExpr) CachedSize(alloc bool) int64 { @@ -343,6 +355,30 @@ func (cached *NotExpr) CachedSize(alloc bool) int64 { size += cached.UnaryExpr.CachedSize(false) return size } +func (cached *OrderByParams) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CollationEnv *vitess.io/vitess/go/mysql/collations.Environment + size += cached.CollationEnv.CachedSize(true) + return size +} +func (cached *TupleBindVariable) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field Key string + size += hack.RuntimeAllocSize(int64(len(cached.Key))) + return size +} func (cached *UnaryExpr) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -363,8 +399,10 @@ func (cached *UntypedExpr) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(80) + size += int64(96) } + // field env *vitess.io/vitess/go/vt/vtenv.Environment + size += cached.env.CachedSize(true) // field ir vitess.io/vitess/go/vt/vtgate/evalengine.IR if cc, ok := cached.ir.(cachedObject); ok { size += cc.CachedSize(true) @@ -537,6 +575,18 @@ func (cached *builtinChangeCase) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinChar) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinCharLength) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -801,6 +851,18 @@ func (cached *builtinFromBase64) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinFromDays) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinFromUnixtime) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -885,6 +947,18 @@ func (cached *builtinInetNtoa) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinInsert) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinIsIPV4) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1041,6 +1115,18 @@ func (cached *builtinJSONUnquote) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinLastDay) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinLeftRight) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1077,6 +1163,18 @@ func (cached *builtinLn) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinLocate) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinLog) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1365,6 +1463,30 @@ func (cached *builtinRepeat) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinReplace) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} +func (cached *builtinReverse) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinRound) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1437,6 +1559,18 @@ func (cached *builtinSin) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinSpace) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinSqrt) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1461,6 +1595,18 @@ func (cached *builtinStrcmp) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinSubstring) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinSysdate) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1497,6 +1643,18 @@ func (cached *builtinTime) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinTimeToSec) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinToBase64) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1509,6 +1667,18 @@ func (cached *builtinToBase64) CachedSize(alloc bool) int64 { size += cached.CallExpr.CachedSize(false) return size } +func (cached *builtinToDays) CachedSize(alloc bool) int64 { + if cached == nil { + return int64(0) + } + size := int64(0) + if alloc { + size += int64(48) + } + // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr + size += cached.CallExpr.CachedSize(false) + return size +} func (cached *builtinTrim) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -1659,12 +1829,14 @@ func (cached *builtinWeightString) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(80) + size += int64(64) } // field CallExpr vitess.io/vitess/go/vt/vtgate/evalengine.CallExpr size += cached.CallExpr.CachedSize(false) // field Cast string size += hack.RuntimeAllocSize(int64(len(cached.Cast))) + // field Len *int + size += hack.RuntimeAllocSize(int64(8)) return size } func (cached *builtinYear) CachedSize(alloc bool) int64 { @@ -1711,7 +1883,7 @@ func (cached *evalDecimal) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(16) + size += int64(24) } // field dec vitess.io/vitess/go/mysql/decimal.Decimal size += cached.dec.CachedSize(false) @@ -1786,7 +1958,7 @@ func (cached *typedExpr) CachedSize(alloc bool) int64 { } // field types []vitess.io/vitess/go/vt/vtgate/evalengine.ctype { - size += hack.RuntimeAllocSize(int64(cap(cached.types)) * int64(10)) + size += hack.RuntimeAllocSize(int64(cap(cached.types)) * int64(20)) } // field compiled *vitess.io/vitess/go/vt/vtgate/evalengine.CompiledExpr size += cached.compiled.CachedSize(true) diff --git a/go/vt/vtgate/evalengine/casting_test.go b/go/vt/vtgate/evalengine/casting_test.go index 93c04d74539..1d75a9b24ab 100644 --- a/go/vt/vtgate/evalengine/casting_test.go +++ b/go/vt/vtgate/evalengine/casting_test.go @@ -21,6 +21,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" ) func TestEvalResultToBooleanStrict(t *testing.T) { @@ -43,7 +45,7 @@ func TestEvalResultToBooleanStrict(t *testing.T) { for _, res := range trueValues { name := evalToSQLValue(res).String() t.Run(fmt.Sprintf("ToBooleanStrict() %s expected true (success)", name), func(t *testing.T) { - result, err := (&EvalResult{res}).ToBooleanStrict() + result, err := (&EvalResult{v: res, collationEnv: collations.MySQL8()}).ToBooleanStrict() require.NoError(t, err, name) require.Equal(t, true, result, name) }) @@ -51,7 +53,7 @@ func TestEvalResultToBooleanStrict(t *testing.T) { for _, res := range falseValues { name := evalToSQLValue(res).String() t.Run(fmt.Sprintf("ToBooleanStrict() %s expected false (success)", name), func(t *testing.T) { - result, err := (&EvalResult{res}).ToBooleanStrict() + result, err := (&EvalResult{v: res, collationEnv: collations.MySQL8()}).ToBooleanStrict() require.NoError(t, err, name) require.Equal(t, false, result, name) }) @@ -59,7 +61,7 @@ func TestEvalResultToBooleanStrict(t *testing.T) { for _, res := range invalid { name := evalToSQLValue(res).String() t.Run(fmt.Sprintf("ToBooleanStrict() %s expected fail", name), func(t *testing.T) { - _, err := (&EvalResult{res}).ToBooleanStrict() + _, err := (&EvalResult{v: res, collationEnv: collations.MySQL8()}).ToBooleanStrict() require.Error(t, err) }) } diff --git a/go/vt/vtgate/evalengine/collation.go b/go/vt/vtgate/evalengine/collation.go index 7cb341f52b0..c0feca87556 100644 --- a/go/vt/vtgate/evalengine/collation.go +++ b/go/vt/vtgate/evalengine/collation.go @@ -54,13 +54,13 @@ func evalCollation(e eval) collations.TypedCollation { } } -func mergeCollations(c1, c2 collations.TypedCollation, t1, t2 sqltypes.Type) (collations.TypedCollation, colldata.Coercion, colldata.Coercion, error) { +func mergeCollations(c1, c2 collations.TypedCollation, t1, t2 sqltypes.Type, env *collations.Environment) (collations.TypedCollation, colldata.Coercion, colldata.Coercion, error) { if c1.Collation == c2.Collation { return c1, nil, nil, nil } - lt := sqltypes.IsText(t1) || sqltypes.IsBinary(t1) - rt := sqltypes.IsText(t2) || sqltypes.IsBinary(t2) + lt := sqltypes.IsTextOrBinary(t1) + rt := sqltypes.IsTextOrBinary(t2) if !lt || !rt { if lt { return c1, nil, nil, nil @@ -71,18 +71,17 @@ func mergeCollations(c1, c2 collations.TypedCollation, t1, t2 sqltypes.Type) (co return collationBinary, nil, nil, nil } - env := collations.Local() return colldata.Merge(env, c1, c2, colldata.CoercionOptions{ ConvertToSuperset: true, ConvertWithCoercion: true, }) } -func mergeAndCoerceCollations(left, right eval) (eval, eval, collations.TypedCollation, error) { +func mergeAndCoerceCollations(left, right eval, env *collations.Environment) (eval, eval, collations.TypedCollation, error) { lt := left.SQLType() rt := right.SQLType() - mc, coerceLeft, coerceRight, err := mergeCollations(evalCollation(left), evalCollation(right), lt, rt) + mc, coerceLeft, coerceRight, err := mergeCollations(evalCollation(left), evalCollation(right), lt, rt, env) if err != nil { return nil, nil, collations.TypedCollation{}, err } @@ -112,7 +111,7 @@ type collationAggregation struct { cur collations.TypedCollation } -func (ca *collationAggregation) add(env *collations.Environment, tc collations.TypedCollation) error { +func (ca *collationAggregation) add(tc collations.TypedCollation, env *collations.Environment) error { if ca.cur.Collation == collations.Unknown { ca.cur = tc } else { diff --git a/go/vt/vtgate/evalengine/compare.go b/go/vt/vtgate/evalengine/compare.go index aa452c61729..102d6142321 100644 --- a/go/vt/vtgate/evalengine/compare.go +++ b/go/vt/vtgate/evalengine/compare.go @@ -19,6 +19,7 @@ package evalengine import ( "bytes" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/colldata" "vitess.io/vitess/go/mysql/decimal" "vitess.io/vitess/go/mysql/json" @@ -133,8 +134,8 @@ func compareDateAndString(l, r eval) int { // More on string collations coercibility on MySQL documentation: // - https://dev.mysql.com/doc/refman/8.0/en/charset-collation-coercibility.html -func compareStrings(l, r eval) (int, error) { - l, r, col, err := mergeAndCoerceCollations(l, r) +func compareStrings(l, r eval, env *collations.Environment) (int, error) { + l, r, col, err := mergeAndCoerceCollations(l, r, env) if err != nil { return 0, err } diff --git a/go/vt/vtgate/evalengine/compiler.go b/go/vt/vtgate/evalengine/compiler.go index 21a25ad3163..8c5700d751d 100644 --- a/go/vt/vtgate/evalengine/compiler.go +++ b/go/vt/vtgate/evalengine/compiler.go @@ -24,6 +24,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" ) @@ -33,6 +34,8 @@ type compiler struct { collation collations.ID dynamicTypes []ctype asm assembler + sqlmode SQLMode + env *vtenv.Environment } type CompilerLog interface { @@ -47,19 +50,64 @@ type compiledCoercion struct { } type ctype struct { - Type sqltypes.Type - Flag typeFlag - Col collations.TypedCollation + Type sqltypes.Type + Flag typeFlag + Size, Scale int32 + Col collations.TypedCollation } type Type struct { - Type sqltypes.Type - Coll collations.ID - Nullable bool + typ sqltypes.Type + collation collations.ID + nullable bool + init bool + size, scale int32 } -func UnknownType() Type { - return Type{Type: sqltypes.Unknown, Coll: collations.Unknown} +func NewType(t sqltypes.Type, collation collations.ID) Type { + // New types default to being nullable + return NewTypeEx(t, collation, true, 0, 0) +} + +func NewTypeEx(t sqltypes.Type, collation collations.ID, nullable bool, size, scale int32) Type { + return Type{ + typ: t, + collation: collation, + nullable: nullable, + init: true, + size: size, + scale: scale, + } +} + +func (t *Type) Type() sqltypes.Type { + if t.init { + return t.typ + } + return sqltypes.Unknown +} + +func (t *Type) Collation() collations.ID { + return t.collation +} + +func (t *Type) Size() int32 { + return t.size +} + +func (t *Type) Scale() int32 { + return t.scale +} + +func (t *Type) Nullable() bool { + if t.init { + return t.nullable + } + return true // nullable by default for unknown types +} + +func (t *Type) Valid() bool { + return t.init } func (ct ctype) nullable() bool { @@ -67,7 +115,7 @@ func (ct ctype) nullable() bool { } func (ct ctype) isTextual() bool { - return sqltypes.IsText(ct.Type) || sqltypes.IsBinary(ct.Type) + return sqltypes.IsTextOrBinary(ct.Type) } func (ct ctype) isHexOrBitLiteral() bool { @@ -102,36 +150,40 @@ func (c *compiler) compileToNumeric(ct ctype, offset int, fallback sqltypes.Type if ct.Type == sqltypes.VarBinary { if (ct.Flag & flagHex) != 0 { c.asm.Convert_hex(offset) - return ctype{sqltypes.Uint64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Uint64, Flag: ct.Flag, Col: collationNumeric} } if (ct.Flag & flagBit) != 0 { c.asm.Convert_bit(offset) - return ctype{sqltypes.Int64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Int64, Flag: ct.Flag, Col: collationNumeric} } } if sqltypes.IsDateOrTime(ct.Type) { if preciseDatetime { - c.asm.Convert_Ti(offset) - return ctype{sqltypes.Int64, ct.Flag, collationNumeric} + if ct.Size == 0 { + c.asm.Convert_Ti(offset) + return ctype{Type: sqltypes.Int64, Flag: ct.Flag, Col: collationNumeric} + } + c.asm.Convert_Td(offset) + return ctype{Type: sqltypes.Decimal, Flag: ct.Flag, Col: collationNumeric, Size: ct.Size} } c.asm.Convert_Tf(offset) - return ctype{sqltypes.Float64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Float64, Flag: ct.Flag, Col: collationNumeric} } switch fallback { case sqltypes.Int64: c.asm.Convert_xi(offset) - return ctype{sqltypes.Int64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Int64, Flag: ct.Flag, Col: collationNumeric} case sqltypes.Uint64: c.asm.Convert_xu(offset) - return ctype{sqltypes.Uint64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Uint64, Flag: ct.Flag, Col: collationNumeric} case sqltypes.Decimal: c.asm.Convert_xd(offset, 0, 0) - return ctype{sqltypes.Decimal, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Decimal, Flag: ct.Flag, Col: collationNumeric} } c.asm.Convert_xf(offset) - return ctype{sqltypes.Float64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Float64, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToInt64(ct ctype, offset int) ctype { @@ -144,7 +196,7 @@ func (c *compiler) compileToInt64(ct ctype, offset int) ctype { default: c.asm.Convert_xi(offset) } - return ctype{sqltypes.Int64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Int64, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToUint64(ct ctype, offset int) ctype { @@ -157,7 +209,7 @@ func (c *compiler) compileToUint64(ct ctype, offset int) ctype { default: c.asm.Convert_xu(offset) } - return ctype{sqltypes.Uint64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Uint64, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToBitwiseUint64(ct ctype, offset int) ctype { @@ -172,7 +224,7 @@ func (c *compiler) compileToBitwiseUint64(ct ctype, offset int) ctype { default: c.asm.Convert_xu(offset) } - return ctype{sqltypes.Uint64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Uint64, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToFloat(ct ctype, offset int) ctype { @@ -189,7 +241,7 @@ func (c *compiler) compileToFloat(ct ctype, offset int) ctype { default: c.asm.Convert_xf(offset) } - return ctype{sqltypes.Float64, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Float64, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToDecimal(ct ctype, offset int) ctype { @@ -204,7 +256,7 @@ func (c *compiler) compileToDecimal(ct ctype, offset int) ctype { default: c.asm.Convert_xd(offset, 0, 0) } - return ctype{sqltypes.Decimal, ct.Flag, collationNumeric} + return ctype{Type: sqltypes.Decimal, Flag: ct.Flag, Col: collationNumeric} } func (c *compiler) compileToDate(doct ctype, offset int) ctype { @@ -212,7 +264,7 @@ func (c *compiler) compileToDate(doct ctype, offset int) ctype { case sqltypes.Date: return doct default: - c.asm.Convert_xD(offset) + c.asm.Convert_xD(offset, c.sqlmode.AllowZeroDate()) } return ctype{Type: sqltypes.Date, Col: collationBinary, Flag: flagNullable} } @@ -223,9 +275,9 @@ func (c *compiler) compileToDateTime(doct ctype, offset, prec int) ctype { c.asm.Convert_tp(offset, prec) return doct default: - c.asm.Convert_xDT(offset, prec) + c.asm.Convert_xDT(offset, prec, c.sqlmode.AllowZeroDate()) } - return ctype{Type: sqltypes.Datetime, Col: collationBinary, Flag: flagNullable} + return ctype{Type: sqltypes.Datetime, Size: int32(prec), Col: collationBinary, Flag: flagNullable} } func (c *compiler) compileToTime(doct ctype, offset, prec int) ctype { @@ -236,7 +288,7 @@ func (c *compiler) compileToTime(doct ctype, offset, prec int) ctype { default: c.asm.Convert_xT(offset, prec) } - return ctype{Type: sqltypes.Time, Col: collationBinary, Flag: flagNullable} + return ctype{Type: sqltypes.Time, Size: int32(prec), Col: collationBinary, Flag: flagNullable} } func (c *compiler) compileNullCheck1(ct ctype) *jump { @@ -275,6 +327,15 @@ func (c *compiler) compileNullCheck3(arg1, arg2, arg3 ctype) *jump { return nil } +func (c *compiler) compileNullCheck4(arg1, arg2, arg3, arg4 ctype) *jump { + if arg1.nullable() || arg2.nullable() || arg3.nullable() || arg4.nullable() { + j := c.asm.jumpFrom() + c.asm.NullCheck4(j) + return j + } + return nil +} + func (c *compiler) compileNullCheckArg(ct ctype, offset int) *jump { if ct.nullable() { j := c.asm.jumpFrom() @@ -369,7 +430,7 @@ func (c *compiler) compareNumericTypes(lt ctype, rt ctype) (swapped bool) { } func (c *compiler) compareAsStrings(lt ctype, rt ctype) error { - merged, coerceLeft, coerceRight, err := mergeCollations(lt.Col, rt.Col, lt.Type, rt.Type) + merged, coerceLeft, coerceRight, err := mergeCollations(lt.Col, rt.Col, lt.Type, rt.Type, c.env.CollationEnv()) if err != nil { return err } @@ -473,7 +534,7 @@ func (c *compiler) compileToJSONKey(key ctype) error { if key.Type == sqltypes.VarBinary { return nil } - c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, nil) return nil } diff --git a/go/vt/vtgate/evalengine/compiler_asm.go b/go/vt/vtgate/evalengine/compiler_asm.go index 6230627c26a..5eeb9a6300d 100644 --- a/go/vt/vtgate/evalengine/compiler_asm.go +++ b/go/vt/vtgate/evalengine/compiler_asm.go @@ -35,6 +35,7 @@ import ( "github.com/google/uuid" + "vitess.io/vitess/go/mysql/collations/charset/types" "vitess.io/vitess/go/mysql/collations/colldata" "vitess.io/vitess/go/hack" @@ -50,7 +51,6 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vthash" ) @@ -288,13 +288,13 @@ func (asm *assembler) BitShiftLeft_bu() { r := env.vm.stack[env.vm.sp-1].(*evalUint64) var ( - bits = int(r.u & 7) - bytes = int(r.u >> 3) - length = len(l.bytes) + bits = int64(r.u & 7) + bytes = int64(r.u >> 3) + length = int64(len(l.bytes)) out = make([]byte, length) ) - for i := 0; i < length; i++ { + for i := int64(0); i < length; i++ { pos := i + bytes + 1 switch { case pos < length: @@ -332,9 +332,9 @@ func (asm *assembler) BitShiftRight_bu() { r := env.vm.stack[env.vm.sp-1].(*evalUint64) var ( - bits = int(r.u & 7) - bytes = int(r.u >> 3) - length = len(l.bytes) + bits = int64(r.u & 7) + bytes = int64(r.u >> 3) + length = int64(len(l.bytes)) out = make([]byte, length) ) @@ -516,7 +516,7 @@ func (asm *assembler) Cmp_ne_n() { }, "CMPFLAG NE [NULL]") } -func (asm *assembler) CmpCase(cases int, hasElse bool, tt sqltypes.Type, cc collations.TypedCollation) { +func (asm *assembler) CmpCase(cases int, hasElse bool, tt sqltypes.Type, cc collations.TypedCollation, allowZeroDate bool) { elseOffset := 0 if hasElse { elseOffset = 1 @@ -528,13 +528,13 @@ func (asm *assembler) CmpCase(cases int, hasElse bool, tt sqltypes.Type, cc coll asm.emit(func(env *ExpressionEnv) int { end := env.vm.sp - elseOffset for sp := env.vm.sp - stackDepth; sp < end; sp += 2 { - if env.vm.stack[sp].(*evalInt64).i != 0 { - env.vm.stack[env.vm.sp-stackDepth], env.vm.err = evalCoerce(env.vm.stack[sp+1], tt, cc.Collation, env.now) + if env.vm.stack[sp] != nil && env.vm.stack[sp].(*evalInt64).i != 0 { + env.vm.stack[env.vm.sp-stackDepth], env.vm.err = evalCoerce(env.vm.stack[sp+1], tt, cc.Collation, env.now, allowZeroDate) goto done } } if elseOffset != 0 { - env.vm.stack[env.vm.sp-stackDepth], env.vm.err = evalCoerce(env.vm.stack[env.vm.sp-1], tt, cc.Collation, env.now) + env.vm.stack[env.vm.sp-stackDepth], env.vm.err = evalCoerce(env.vm.stack[env.vm.sp-1], tt, cc.Collation, env.now, allowZeroDate) } else { env.vm.stack[env.vm.sp-stackDepth] = nil } @@ -717,25 +717,25 @@ func (asm *assembler) CmpJSON() { }, "CMP JSON(SP-2), JSON(SP-1)") } -func (asm *assembler) CmpTuple(fullEquality bool) { +func (asm *assembler) CmpTuple(collationEnv *collations.Environment, fullEquality bool) { asm.adjustStack(-2) asm.emit(func(env *ExpressionEnv) int { l := env.vm.stack[env.vm.sp-2].(*evalTuple) r := env.vm.stack[env.vm.sp-1].(*evalTuple) env.vm.sp -= 2 - env.vm.flags.cmp, env.vm.flags.null, env.vm.err = evalCompareMany(l.t, r.t, fullEquality) + env.vm.flags.cmp, env.vm.flags.null, env.vm.err = evalCompareMany(l.t, r.t, fullEquality, collationEnv) return 1 }, "CMP TUPLE(SP-2), TUPLE(SP-1)") } -func (asm *assembler) CmpTupleNullsafe() { +func (asm *assembler) CmpTupleNullsafe(collationsEnv *collations.Environment) { asm.adjustStack(-1) asm.emit(func(env *ExpressionEnv) int { l := env.vm.stack[env.vm.sp-2].(*evalTuple) r := env.vm.stack[env.vm.sp-1].(*evalTuple) var equals int - equals, env.vm.err = evalCompareTuplesNullSafe(l.t, r.t) + equals, env.vm.err = evalCompareTuplesNullSafe(l.t, r.t, collationsEnv) env.vm.stack[env.vm.sp-2] = env.vm.arena.newEvalBool(equals == 0) env.vm.sp -= 1 @@ -782,8 +782,8 @@ func (asm *assembler) Convert_bB(offset int) { var f float64 if arg != nil { f, _ = fastparse.ParseFloat64(arg.(*evalBytes).string()) + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(f != 0.0) } - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(f != 0.0) return 1 }, "CONV VARBINARY(SP-%d), BOOL", offset) } @@ -791,7 +791,9 @@ func (asm *assembler) Convert_bB(offset int) { func (asm *assembler) Convert_TB(offset int) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-offset] - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg != nil && !arg.(*evalTemporal).isZero()) + if arg != nil { + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(!arg.(*evalTemporal).isZero()) + } return 1 }, "CONV SQLTYPES(SP-%d), BOOL", offset) } @@ -839,7 +841,9 @@ func (asm *assembler) Convert_Tj(offset int) { func (asm *assembler) Convert_dB(offset int) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-offset] - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg != nil && !arg.(*evalDecimal).dec.IsZero()) + if arg != nil { + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(!arg.(*evalDecimal).dec.IsZero()) + } return 1 }, "CONV DECIMAL(SP-%d), BOOL", offset) } @@ -859,7 +863,9 @@ func (asm *assembler) Convert_dbit(offset int) { func (asm *assembler) Convert_fB(offset int) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-offset] - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg != nil && arg.(*evalFloat).f != 0.0) + if arg != nil { + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg.(*evalFloat).f != 0.0) + } return 1 }, "CONV FLOAT64(SP-%d), BOOL", offset) } @@ -898,7 +904,7 @@ func (asm *assembler) Convert_Ti(offset int) { asm.emit(func(env *ExpressionEnv) int { v := env.vm.stack[env.vm.sp-offset].(*evalTemporal) if v.prec != 0 { - env.vm.err = errDeoptimize + env.vm.err = vterrors.NewErrorf(vtrpc.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "temporal type with non-zero precision") return 1 } env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalInt64(v.toInt64()) @@ -914,10 +920,24 @@ func (asm *assembler) Convert_Tf(offset int) { }, "CONV SQLTIME(SP-%d), FLOAT64", offset) } +func (asm *assembler) Convert_Td(offset int) { + asm.emit(func(env *ExpressionEnv) int { + v := env.vm.stack[env.vm.sp-offset].(*evalTemporal) + if v.prec == 0 { + env.vm.err = vterrors.NewErrorf(vtrpc.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "temporal type with zero precision") + return 1 + } + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalDecimalWithPrec(v.toDecimal(), int32(v.prec)) + return 1 + }, "CONV SQLTIME(SP-%d), DECIMAL", offset) +} + func (asm *assembler) Convert_iB(offset int) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-offset] - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg != nil && arg.(*evalInt64).i != 0) + if arg != nil { + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg.(*evalInt64).i != 0) + } return 1 }, "CONV INT64(SP-%d), BOOL", offset) } @@ -997,7 +1017,9 @@ func (asm *assembler) Convert_Nj(offset int) { func (asm *assembler) Convert_uB(offset int) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-offset] - env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg != nil && arg.(*evalUint64).u != 0) + if arg != nil { + env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalBool(arg.(*evalUint64).u != 0) + } return 1 }, "CONV UINT64(SP-%d), BOOL", offset) } @@ -1026,15 +1048,16 @@ func (asm *assembler) Convert_ui(offset int) { }, "CONV UINT64(SP-%d), INT64", offset) } -func (asm *assembler) Convert_xb(offset int, t sqltypes.Type, length int, hasLength bool) { - if hasLength { +func (asm *assembler) Convert_xb(offset int, t sqltypes.Type, length *int) { + if length != nil { + l := *length asm.emit(func(env *ExpressionEnv) int { arg := evalToBinary(env.vm.stack[env.vm.sp-offset]) - arg.truncateInPlace(length) + arg.truncateInPlace(l) arg.tt = int16(t) env.vm.stack[env.vm.sp-offset] = arg return 1 - }, "CONV (SP-%d), VARBINARY[%d]", offset, length) + }, "CONV (SP-%d), VARBINARY[%d]", offset, l) } else { asm.emit(func(env *ExpressionEnv) int { arg := evalToBinary(env.vm.stack[env.vm.sp-offset]) @@ -1045,19 +1068,20 @@ func (asm *assembler) Convert_xb(offset int, t sqltypes.Type, length int, hasLen } } -func (asm *assembler) Convert_xc(offset int, t sqltypes.Type, collation collations.ID, length int, hasLength bool) { - if hasLength { +func (asm *assembler) Convert_xc(offset int, t sqltypes.Type, collation collations.ID, length *int) { + if length != nil { + l := *length asm.emit(func(env *ExpressionEnv) int { arg, err := evalToVarchar(env.vm.stack[env.vm.sp-offset], collation, true) if err != nil { env.vm.stack[env.vm.sp-offset] = nil } else { - arg.truncateInPlace(length) + arg.truncateInPlace(l) arg.tt = int16(t) env.vm.stack[env.vm.sp-offset] = arg } return 1 - }, "CONV (SP-%d), VARCHAR[%d]", offset, length) + }, "CONV (SP-%d), VARCHAR[%d]", offset, l) } else { asm.emit(func(env *ExpressionEnv) int { arg, err := evalToVarchar(env.vm.stack[env.vm.sp-offset], collation, true) @@ -1116,12 +1140,12 @@ func (asm *assembler) Convert_xu(offset int) { }, "CONV (SP-%d), UINT64", offset) } -func (asm *assembler) Convert_xD(offset int) { +func (asm *assembler) Convert_xD(offset int, allowZero bool) { asm.emit(func(env *ExpressionEnv) int { // Need to explicitly check here or we otherwise // store a nil wrapper in an interface vs. a direct // nil. - d := evalToDate(env.vm.stack[env.vm.sp-offset], env.now) + d := evalToDate(env.vm.stack[env.vm.sp-offset], env.now, allowZero) if d == nil { env.vm.stack[env.vm.sp-offset] = nil } else { @@ -1131,27 +1155,12 @@ func (asm *assembler) Convert_xD(offset int) { }, "CONV (SP-%d), DATE", offset) } -func (asm *assembler) Convert_xD_nz(offset int) { - asm.emit(func(env *ExpressionEnv) int { - // Need to explicitly check here or we otherwise - // store a nil wrapper in an interface vs. a direct - // nil. - d := evalToDate(env.vm.stack[env.vm.sp-offset], env.now) - if d == nil || d.isZero() { - env.vm.stack[env.vm.sp-offset] = nil - } else { - env.vm.stack[env.vm.sp-offset] = d - } - return 1 - }, "CONV (SP-%d), DATE(NOZERO)", offset) -} - -func (asm *assembler) Convert_xDT(offset, prec int) { +func (asm *assembler) Convert_xDT(offset, prec int, allowZero bool) { asm.emit(func(env *ExpressionEnv) int { // Need to explicitly check here or we otherwise // store a nil wrapper in an interface vs. a direct // nil. - dt := evalToDateTime(env.vm.stack[env.vm.sp-offset], prec, env.now) + dt := evalToDateTime(env.vm.stack[env.vm.sp-offset], prec, env.now, allowZero) if dt == nil { env.vm.stack[env.vm.sp-offset] = nil } else { @@ -1161,21 +1170,6 @@ func (asm *assembler) Convert_xDT(offset, prec int) { }, "CONV (SP-%d), DATETIME", offset) } -func (asm *assembler) Convert_xDT_nz(offset, prec int) { - asm.emit(func(env *ExpressionEnv) int { - // Need to explicitly check here or we otherwise - // store a nil wrapper in an interface vs. a direct - // nil. - dt := evalToDateTime(env.vm.stack[env.vm.sp-offset], prec, env.now) - if dt == nil || dt.isZero() { - env.vm.stack[env.vm.sp-offset] = nil - } else { - env.vm.stack[env.vm.sp-offset] = dt - } - return 1 - }, "CONV (SP-%d), DATETIME(NOZERO)", offset) -} - func (asm *assembler) Convert_xT(offset, prec int) { asm.emit(func(env *ExpressionEnv) int { t := evalToTime(env.vm.stack[env.vm.sp-offset], prec) @@ -1438,6 +1432,29 @@ func (asm *assembler) Fn_ASCII() { }, "FN ASCII VARCHAR(SP-1)") } +func (asm *assembler) Fn_REVERSE() { + asm.emit(func(env *ExpressionEnv) int { + arg := env.vm.stack[env.vm.sp-1].(*evalBytes) + + arg.tt = int16(sqltypes.VarChar) + arg.bytes = reverse(arg) + return 1 + }, "FN REVERSE VARCHAR(SP-1)") +} + +func (asm *assembler) Fn_SPACE(col collations.TypedCollation) { + asm.emit(func(env *ExpressionEnv) int { + arg := env.vm.stack[env.vm.sp-1].(*evalInt64).i + + if !validMaxLength(1, arg) { + env.vm.stack[env.vm.sp-1] = nil + return 1 + } + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalText(space(arg), col) + return 1 + }, "FN SPACE INT64(SP-1)") +} + func (asm *assembler) Fn_ORD(col collations.ID) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-1].(*evalBytes) @@ -1993,7 +2010,7 @@ func (asm *assembler) Fn_CONV_bu(offset int, baseOffset int) { i, err := fastparse.ParseInt64(arg.string(), int(base.i)) u = uint64(i) if errors.Is(err, fastparse.ErrOverflow) { - u, _ = fastparse.ParseUint64(arg.string(), int(base.i)) + u, _ = fastparse.ParseUint64WithNeg(arg.string(), int(base.i)) } env.vm.stack[env.vm.sp-offset] = env.vm.arena.newEvalUint64(u) return 1 @@ -2034,10 +2051,10 @@ func (asm *assembler) Fn_CONV_uc(t sqltypes.Type, col collations.TypedCollation) }, "FN CONV VARCHAR(SP-3) INT64(SP-2) INT64(SP-1)") } -func (asm *assembler) Fn_COLLATION(col collations.TypedCollation) { +func (asm *assembler) Fn_COLLATION(collationEnv *collations.Environment, col collations.TypedCollation) { asm.emit(func(env *ExpressionEnv) int { v := evalCollation(env.vm.stack[env.vm.sp-1]) - env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalText([]byte(collations.Local().LookupName(v.Collation)), col) + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalText([]byte(collationEnv.LookupName(v.Collation)), col) return 1 }, "FN COLLATION (SP-1)") } @@ -2330,6 +2347,28 @@ func (asm *assembler) Fn_BIT_LENGTH() { }, "FN BIT_LENGTH VARCHAR(SP-1)") } +func (asm *assembler) Fn_INSERT(col collations.TypedCollation) { + asm.adjustStack(-3) + + asm.emit(func(env *ExpressionEnv) int { + str := env.vm.stack[env.vm.sp-4].(*evalBytes) + pos := env.vm.stack[env.vm.sp-3].(*evalInt64).i + l := env.vm.stack[env.vm.sp-2].(*evalInt64).i + newstr := env.vm.stack[env.vm.sp-1].(*evalBytes) + + res := insert(str, newstr, int(pos), int(l)) + if !validMaxLength(int64(len(res)), 1) { + env.vm.stack[env.vm.sp-4] = nil + env.vm.sp -= 3 + return 1 + } + + env.vm.stack[env.vm.sp-4] = env.vm.arena.newEvalText(res, col) + env.vm.sp -= 3 + return 1 + }, "FN INSERT VARCHAR(SP-4) INT64(SP-3) INT64(SP-2) VARCHAR(SP-1)") +} + func (asm *assembler) Fn_LUCASE(upcase bool) { if upcase { asm.emit(func(env *ExpressionEnv) int { @@ -2733,6 +2772,65 @@ func (asm *assembler) Fn_TRIM2(col collations.TypedCollation) { }, "FN TRIM VARCHAR(SP-2) VARCHAR(SP-1)") } +func (asm *assembler) Fn_SUBSTRING2(tt sqltypes.Type, cs types.Charset, col collations.TypedCollation) { + asm.adjustStack(-1) + asm.emit(func(env *ExpressionEnv) int { + str := env.vm.stack[env.vm.sp-2].(*evalBytes) + pos := env.vm.stack[env.vm.sp-1].(*evalInt64) + + end := int64(charset.Length(cs, str.bytes)) + if pos.i < 0 { + pos.i += end + 1 + } + str.tt = int16(tt) + if pos.i < 1 || pos.i > end { + str.bytes = nil + str.col = col + env.vm.sp-- + return 1 + } + + res := charset.Slice(cs, str.bytes, int(pos.i-1), int(end)) + str.bytes = res + str.col = col + env.vm.sp-- + return 1 + }, "FN SUBSTRING VARCHAR(SP-2) INT64(SP-1)") +} + +func (asm *assembler) Fn_SUBSTRING3(tt sqltypes.Type, cs types.Charset, col collations.TypedCollation) { + asm.adjustStack(-2) + asm.emit(func(env *ExpressionEnv) int { + str := env.vm.stack[env.vm.sp-3].(*evalBytes) + pos := env.vm.stack[env.vm.sp-2].(*evalInt64) + ll := env.vm.stack[env.vm.sp-1].(*evalInt64) + + end := int64(charset.Length(cs, str.bytes)) + if pos.i < 0 { + pos.i += end + 1 + } + str.tt = int16(tt) + + if pos.i < 1 || pos.i > end || ll.i < 1 { + str.bytes = nil + str.col = col + env.vm.sp -= 2 + return 1 + } + + if ll.i > end-pos.i+1 { + ll.i = end - pos.i + 1 + } + end = pos.i + ll.i - 1 + res := charset.Slice(cs, str.bytes, int(pos.i-1), int(end)) + str.tt = int16(tt) + str.bytes = res + str.col = col + env.vm.sp -= 2 + return 1 + }, "FN SUBSTRING VARCHAR(SP-3) INT64(SP-2) INT64(SP-1)") +} + func (asm *assembler) Fn_TO_BASE64(t sqltypes.Type, col collations.TypedCollation) { asm.emit(func(env *ExpressionEnv) int { str := env.vm.stack[env.vm.sp-1].(*evalBytes) @@ -2783,7 +2881,7 @@ func (asm *assembler) In_table(not bool, table map[vthash.Hash]struct{}) { } } -func (asm *assembler) In_slow(not bool) { +func (asm *assembler) In_slow(collationsEnv *collations.Environment, not bool) { asm.adjustStack(-1) if not { @@ -2792,7 +2890,7 @@ func (asm *assembler) In_slow(not bool) { rhs := env.vm.stack[env.vm.sp-1].(*evalTuple) var in boolean - in, env.vm.err = evalInExpr(lhs, rhs) + in, env.vm.err = evalInExpr(collationsEnv, lhs, rhs) env.vm.stack[env.vm.sp-2] = in.not().eval() env.vm.sp -= 1 @@ -2804,7 +2902,7 @@ func (asm *assembler) In_slow(not bool) { rhs := env.vm.stack[env.vm.sp-1].(*evalTuple) var in boolean - in, env.vm.err = evalInExpr(lhs, rhs) + in, env.vm.err = evalInExpr(collationsEnv, lhs, rhs) env.vm.stack[env.vm.sp-2] = in.eval() env.vm.sp -= 1 @@ -2890,6 +2988,53 @@ func (asm *assembler) Like_collate(expr *LikeExpr, collation colldata.Collation) }, "LIKE VARCHAR(SP-2), VARCHAR(SP-1) COLLATE '%s'", collation.Name()) } +func (asm *assembler) Locate3(collation colldata.Collation) { + asm.adjustStack(-2) + + asm.emit(func(env *ExpressionEnv) int { + substr := env.vm.stack[env.vm.sp-3].(*evalBytes) + str := env.vm.stack[env.vm.sp-2].(*evalBytes) + pos := env.vm.stack[env.vm.sp-1].(*evalInt64) + env.vm.sp -= 2 + + if pos.i < 1 || pos.i > math.MaxInt { + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(0) + return 1 + } + + found := colldata.Index(collation, str.bytes, substr.bytes, int(pos.i)-1) + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(int64(found) + 1) + return 1 + }, "LOCATE VARCHAR(SP-3), VARCHAR(SP-2) INT64(SP-1) COLLATE '%s'", collation.Name()) +} + +func (asm *assembler) Locate2(collation colldata.Collation) { + asm.adjustStack(-1) + + asm.emit(func(env *ExpressionEnv) int { + substr := env.vm.stack[env.vm.sp-2].(*evalBytes) + str := env.vm.stack[env.vm.sp-1].(*evalBytes) + env.vm.sp-- + + found := colldata.Index(collation, str.bytes, substr.bytes, 0) + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(int64(found) + 1) + return 1 + }, "LOCATE VARCHAR(SP-2), VARCHAR(SP-1) COLLATE '%s'", collation.Name()) +} + +func (asm *assembler) Replace() { + asm.adjustStack(-2) + + asm.emit(func(env *ExpressionEnv) int { + str := env.vm.stack[env.vm.sp-3].(*evalBytes) + from := env.vm.stack[env.vm.sp-2].(*evalBytes) + to := env.vm.stack[env.vm.sp-1].(*evalBytes) + env.vm.sp -= 2 + str.bytes = replace(str.bytes, from.bytes, to.bytes) + return 1 + }, "REPLACE VARCHAR(SP-3), VARCHAR(SP-2) VARCHAR(SP-1)") +} + func (asm *assembler) Strcmp(collation collations.TypedCollation) { asm.adjustStack(-1) @@ -3073,6 +3218,17 @@ func (asm *assembler) NullCheck3(j *jump) { }, "NULLCHECK SP-1, SP-2, SP-3") } +func (asm *assembler) NullCheck4(j *jump) { + asm.emit(func(env *ExpressionEnv) int { + if env.vm.stack[env.vm.sp-4] == nil || env.vm.stack[env.vm.sp-3] == nil || env.vm.stack[env.vm.sp-2] == nil || env.vm.stack[env.vm.sp-1] == nil { + env.vm.stack[env.vm.sp-4] = nil + env.vm.sp -= 3 + return j.offset() + } + return 1 + }, "NULLCHECK SP-1, SP-2, SP-3, SP-4") +} + func (asm *assembler) NullCheckArg(j *jump, offset int) { asm.emit(func(env *ExpressionEnv) int { if env.vm.stack[env.vm.sp-1] == nil { @@ -3235,31 +3391,32 @@ func cmpnum[N interface{ int64 | uint64 | float64 }](a, b N) int { } } -func (asm *assembler) Fn_Now(t querypb.Type, format *datetime.Strftime, prec uint8, utc bool) { +func (asm *assembler) Fn_Now(prec uint8, utc bool) { asm.adjustStack(1) asm.emit(func(env *ExpressionEnv) int { - val := env.vm.arena.newEvalBytesEmpty() - val.tt = int16(t) - val.bytes = format.Format(env.time(utc), prec) - val.col = collationBinary - env.vm.stack[env.vm.sp] = val + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalDateTime(env.time(utc), int(prec)) env.vm.sp++ return 1 - }, "FN NOW") + }, "FN NOW(DATETIME)") +} + +func (asm *assembler) Fn_NowTime(prec uint8, utc bool) { + asm.adjustStack(1) + asm.emit(func(env *ExpressionEnv) int { + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalTime(env.time(utc).Time, int(prec)) + env.vm.sp++ + return 1 + }, "FN NOW(TIME)") } func (asm *assembler) Fn_Sysdate(prec uint8) { asm.adjustStack(1) asm.emit(func(env *ExpressionEnv) int { - val := env.vm.arena.newEvalBytesEmpty() - val.tt = int16(sqltypes.Datetime) now := SystemTime() if tz := env.currentTimezone(); tz != nil { now = now.In(tz) } - val.bytes = datetime.NewDateTimeFromStd(now).Format(prec) - val.col = collationBinary - env.vm.stack[env.vm.sp] = val + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalDateTime(datetime.NewDateTimeFromStd(now), int(prec)) env.vm.sp++ return 1 }, "FN SYSDATE") @@ -3268,11 +3425,7 @@ func (asm *assembler) Fn_Sysdate(prec uint8) { func (asm *assembler) Fn_Curdate() { asm.adjustStack(1) asm.emit(func(env *ExpressionEnv) int { - val := env.vm.arena.newEvalBytesEmpty() - val.tt = int16(sqltypes.Date) - val.bytes = datetime.Date_YYYY_MM_DD.Format(env.time(false), 0) - val.col = collationBinary - env.vm.stack[env.vm.sp] = val + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalDate(env.time(false).Date) env.vm.sp++ return 1 }, "FN CURDATE") @@ -3281,11 +3434,7 @@ func (asm *assembler) Fn_Curdate() { func (asm *assembler) Fn_UtcDate() { asm.adjustStack(1) asm.emit(func(env *ExpressionEnv) int { - val := env.vm.arena.newEvalBytesEmpty() - val.tt = int16(sqltypes.Date) - val.bytes = datetime.Date_YYYY_MM_DD.Format(env.time(true), 0) - val.col = collationBinary - env.vm.stack[env.vm.sp] = val + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalDate(env.time(true).Date) env.vm.sp++ return 1 }, "FN UTC_DATE") @@ -3317,7 +3466,7 @@ func (asm *assembler) Fn_Database() { func (asm *assembler) Fn_Version() { asm.adjustStack(1) asm.emit(func(env *ExpressionEnv) int { - env.vm.stack[env.vm.sp] = env.vm.arena.newEvalText([]byte(servenv.MySQLServerVersion()), collationUtf8mb3) + env.vm.stack[env.vm.sp] = env.vm.arena.newEvalText([]byte(env.currentVersion()), collationUtf8mb3) env.vm.sp++ return 1 }, "FN VERSION") @@ -3587,7 +3736,7 @@ func (asm *assembler) Fn_MAKEDATE() { y := env.vm.stack[env.vm.sp-1].(*evalInt64) yd := env.vm.stack[env.vm.sp-2].(*evalInt64) - t := yearDayToTime(y.i, yd.i) + t := yearDayToTime(env.currentTimezone(), y.i, yd.i) if t.IsZero() { env.vm.stack[env.vm.sp-2] = nil } else { @@ -3727,6 +3876,57 @@ func (asm *assembler) Fn_MONTHNAME(col collations.TypedCollation) { }, "FN MONTHNAME DATE(SP-1)") } +func (asm *assembler) Fn_LAST_DAY() { + asm.emit(func(env *ExpressionEnv) int { + if env.vm.stack[env.vm.sp-1] == nil { + return 1 + } + arg := env.vm.stack[env.vm.sp-1].(*evalTemporal) + d := lastDay(env.currentTimezone(), arg.dt) + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalDate(d) + return 1 + }, "FN LAST_DAY DATETIME(SP-1)") +} + +func (asm *assembler) Fn_TO_DAYS() { + asm.emit(func(env *ExpressionEnv) int { + if env.vm.stack[env.vm.sp-1] == nil { + return 1 + } + arg := env.vm.stack[env.vm.sp-1].(*evalTemporal) + numDays := datetime.MysqlDayNumber(arg.dt.Date.Year(), arg.dt.Date.Month(), arg.dt.Date.Day()) + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(int64(numDays)) + return 1 + }, "FN TO_DAYS DATE(SP-1)") +} + +func (asm *assembler) Fn_FROM_DAYS() { + asm.emit(func(env *ExpressionEnv) int { + arg := env.vm.stack[env.vm.sp-1].(*evalInt64) + d := datetime.DateFromDayNumber(int(arg.i)) + if d.Year() > 9999 { + env.vm.stack[env.vm.sp-1] = nil + return 1 + } + + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalDate(d) + return 1 + }, "FN FROM_DAYS INT64(SP-1)") +} + +func (asm *assembler) Fn_TIME_TO_SEC() { + asm.emit(func(env *ExpressionEnv) int { + if env.vm.stack[env.vm.sp-1] == nil { + return 1 + } + d := env.vm.stack[env.vm.sp-1].(*evalTemporal) + + sec := d.dt.Time.ToSeconds() + env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(sec) + return 1 + }, "FN TIME_TO_SEC TIME(SP-1)") +} + func (asm *assembler) Fn_QUARTER() { asm.emit(func(env *ExpressionEnv) int { if env.vm.stack[env.vm.sp-1] == nil { @@ -3761,9 +3961,6 @@ func (asm *assembler) Fn_UNIX_TIMESTAMP0() { func (asm *assembler) Fn_UNIX_TIMESTAMP1() { asm.emit(func(env *ExpressionEnv) int { res := dateTimeUnixTimestamp(env, env.vm.stack[env.vm.sp-1]) - if _, ok := res.(*evalInt64); !ok { - env.vm.err = errDeoptimize - } env.vm.stack[env.vm.sp-1] = res return 1 }, "FN UNIX_TIMESTAMP (SP-1)") @@ -3859,20 +4056,6 @@ func (asm *assembler) Fn_YEARWEEK() { }, "FN YEARWEEK DATE(SP-1)") } -func (asm *assembler) Interval_i(l int) { - asm.adjustStack(-l) - asm.emit(func(env *ExpressionEnv) int { - if env.vm.stack[env.vm.sp-l] == nil { - env.vm.stack[env.vm.sp-l] = env.vm.arena.newEvalInt64(-1) - env.vm.sp -= l - return 1 - } - - env.vm.sp -= l - return 1 - }, "INTERVAL INT64(SP-1)...INT64(SP-%d)", l) -} - func (asm *assembler) Interval(l int) { asm.adjustStack(-l) asm.emit(func(env *ExpressionEnv) int { @@ -4038,6 +4221,29 @@ func (asm *assembler) Fn_CONCAT_WS(tt querypb.Type, tc collations.TypedCollation }, "FN CONCAT_WS VARCHAR(SP-1) VARCHAR(SP-2)...VARCHAR(SP-N)") } +func (asm *assembler) Fn_CHAR(tt querypb.Type, tc collations.TypedCollation, args int) { + cs := colldata.Lookup(tc.Collation).Charset() + asm.adjustStack(-(args - 1)) + asm.emit(func(env *ExpressionEnv) int { + buf := make([]byte, 0, args) + for i := 0; i < args; i++ { + if env.vm.stack[env.vm.sp-args+i] == nil { + continue + } + arg := env.vm.stack[env.vm.sp-args+i].(*evalInt64) + buf = encodeChar(buf, uint32(arg.i)) + } + + if charset.Validate(cs, buf) { + env.vm.stack[env.vm.sp-args] = env.vm.arena.newEvalRaw(buf, tt, tc) + } else { + env.vm.stack[env.vm.sp-args] = nil + } + env.vm.sp -= args - 1 + return 1 + }, "FN CHAR INT64(SP-1) INT64(SP-2)...INT64(SP-N)") +} + func (asm *assembler) Fn_BIN_TO_UUID0(col collations.TypedCollation) { asm.emit(func(env *ExpressionEnv) int { arg := env.vm.stack[env.vm.sp-1].(*evalBytes) @@ -4182,7 +4388,7 @@ func (asm *assembler) Fn_DATEADD_s(unit datetime.IntervalType, sub bool, col col goto baddate } - tmp = evalToTemporal(env.vm.stack[env.vm.sp-2]) + tmp = evalToTemporal(env.vm.stack[env.vm.sp-2], true) if tmp == nil { goto baddate } diff --git a/go/vt/vtgate/evalengine/compiler_fn.go b/go/vt/vtgate/evalengine/compiler_fn.go index d4157929546..72197f6a492 100644 --- a/go/vt/vtgate/evalengine/compiler_fn.go +++ b/go/vt/vtgate/evalengine/compiler_fn.go @@ -76,7 +76,7 @@ func (c *compiler) compileFn_length(arg IR, asm_ins func()) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, nil) } asm_ins() diff --git a/go/vt/vtgate/evalengine/compiler_test.go b/go/vt/vtgate/evalengine/compiler_test.go index 4fba65aeb75..f101bf61c64 100644 --- a/go/vt/vtgate/evalengine/compiler_test.go +++ b/go/vt/vtgate/evalengine/compiler_test.go @@ -17,6 +17,7 @@ limitations under the License. package evalengine_test import ( + "context" "fmt" "strconv" "strings" @@ -29,6 +30,7 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/evalengine/testcases" ) @@ -97,16 +99,16 @@ func TestCompilerReference(t *testing.T) { defer func() { evalengine.SystemTime = time.Now }() track := NewTracker() - + venv := vtenv.NewTestEnv() for _, tc := range testcases.Cases { t.Run(tc.Name(), func(t *testing.T) { var supported, total int - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(venv) tc.Run(func(query string, row []sqltypes.Value) { env.Row = row - stmt, err := sqlparser.ParseExpr(query) + stmt, err := venv.Parser().ParseExpr(query) if err != nil { // no need to test un-parseable queries return @@ -117,6 +119,7 @@ func TestCompilerReference(t *testing.T) { ResolveColumn: fields.Column, ResolveType: fields.Type, Collation: collations.CollationUtf8mb4ID, + Environment: venv, NoConstantFolding: true, } @@ -496,13 +499,145 @@ func TestCompilerSingle(t *testing.T) { result: `VARCHAR("0")`, collation: collations.CollationUtf8mb4ID, }, + { + expression: `UNIX_TIMESTAMP(0.0) + 1`, + result: `DECIMAL(1.0)`, + }, + { + expression: `UNIX_TIMESTAMP(0.000) + 1`, + result: `DECIMAL(1.000)`, + }, + { + expression: `UNIX_TIMESTAMP(-1.5) + 1`, + result: `DECIMAL(1.0)`, + }, + { + expression: `UNIX_TIMESTAMP(-1.500) + 1`, + result: `DECIMAL(1.000)`, + }, + { + expression: `UNIX_TIMESTAMP(0x0) + 1`, + result: `INT64(1)`, + }, + { + expression: `UNIX_TIMESTAMP(timestamp '2000-01-01 10:34:58.123456') + 1`, + result: `DECIMAL(946719299.123456)`, + }, + { + expression: `UNIX_TIMESTAMP('200001011034581111111') + 1`, + result: `INT64(946719299)`, + }, + { + expression: `CONV(-0x1, 13e0, 13e0)`, + result: `VARCHAR("219505A9511A867B72")`, + }, + { + expression: `UNIX_TIMESTAMP('20000101103458.111111') + 1`, + result: `DECIMAL(946719299.111111)`, + }, + { + expression: `cast(null * 1 as CHAR)`, + result: `NULL`, + }, + { + expression: `cast(null + 1 as CHAR)`, + result: `NULL`, + }, + { + expression: `cast(null - 1 as CHAR)`, + result: `NULL`, + }, + { + expression: `cast(null / 1 as CHAR)`, + result: `NULL`, + }, + { + expression: `cast(null % 1 as CHAR)`, + result: `NULL`, + }, + { + expression: `1 AND NULL * 1`, + result: `NULL`, + }, + { + expression: `case 0 when NULL then 1 else 0 end`, + result: `INT64(0)`, + }, + { + expression: `case when null is null then 23 else null end`, + result: `INT64(23)`, + }, + { + expression: `CAST(0 AS DATE)`, + result: `NULL`, + }, + { + expression: `DAYOFMONTH(0)`, + result: `INT64(0)`, + }, + { + expression: `week('2023-12-31', 4)`, + result: `INT64(53)`, + }, + { + expression: `week('2023-12-31', 2)`, + result: `INT64(53)`, + }, + { + expression: `week('2024-12-31', 1)`, + result: `INT64(53)`, + }, + { + expression: `week('2024-12-31', 5)`, + result: `INT64(53)`, + }, + { + expression: `FROM_UNIXTIME(time '10:04:58.5')`, + result: `DATETIME("1970-01-02 04:54:18.5")`, + }, + { + expression: `0 = time '10:04:58.1'`, + result: `INT64(0)`, + }, + { + expression: `CAST(time '32:34:58.5' AS TIME)`, + result: `TIME("32:34:59")`, + }, + { + expression: `now(6) + interval 1 day`, + result: `DATETIME("2023-10-25 12:00:00.123456")`, + }, + { + expression: `now() + interval 654321 microsecond`, + result: `DATETIME("2023-10-24 12:00:00.654321")`, + }, + { + expression: `time('1111:66:56')`, + result: `NULL`, + }, + { + expression: `locate('Å', 'a')`, + result: `INT64(1)`, + }, + { + expression: `locate('a', 'Å')`, + result: `INT64(1)`, + }, + { + expression: `locate("", "😊😂🤢", 3)`, + result: `INT64(3)`, + }, + { + expression: `REPLACE('www.mysql.com', '', 'Ww')`, + result: `VARCHAR("www.mysql.com")`, + }, } tz, _ := time.LoadLocation("Europe/Madrid") - + venv := vtenv.NewTestEnv() for _, tc := range testCases { t.Run(tc.expression, func(t *testing.T) { - expr, err := sqlparser.ParseExpr(tc.expression) + expr, err := venv.Parser().ParseExpr(tc.expression) if err != nil { t.Fatal(err) } @@ -512,6 +647,7 @@ func TestCompilerSingle(t *testing.T) { ResolveColumn: fields.Column, ResolveType: fields.Type, Collation: collations.CollationUtf8mb4ID, + Environment: venv, NoConstantFolding: true, } @@ -520,8 +656,8 @@ func TestCompilerSingle(t *testing.T) { t.Fatal(err) } - env := evalengine.EmptyExpressionEnv() - env.SetTime(time.Date(2023, 10, 24, 12, 0, 0, 0, tz)) + env := evalengine.NewExpressionEnv(context.Background(), nil, evalengine.NewEmptyVCursor(venv, tz)) + env.SetTime(time.Date(2023, 10, 24, 12, 0, 0, 123456000, tz)) env.Row = tc.values expected, err := env.EvaluateAST(converted) @@ -578,9 +714,10 @@ func TestBindVarLiteral(t *testing.T) { }, } + venv := vtenv.NewTestEnv() for _, tc := range testCases { t.Run(tc.expression, func(t *testing.T) { - expr, err := sqlparser.ParseExpr(tc.expression) + expr, err := venv.Parser().ParseExpr(tc.expression) if err != nil { t.Fatal(err) } @@ -592,6 +729,7 @@ func TestBindVarLiteral(t *testing.T) { ResolveColumn: fields.Column, ResolveType: fields.Type, Collation: collations.CollationUtf8mb4ID, + Environment: venv, NoConstantFolding: true, } @@ -602,7 +740,7 @@ func TestBindVarLiteral(t *testing.T) { result := `VARCHAR("ÿ")` - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(venv) env.BindVars = map[string]*querypb.BindVariable{ "vtg1": tc.bindVar, } @@ -642,15 +780,17 @@ func TestCompilerNonConstant(t *testing.T) { }, } + venv := vtenv.NewTestEnv() for _, tc := range testCases { t.Run(tc.expression, func(t *testing.T) { - expr, err := sqlparser.ParseExpr(tc.expression) + expr, err := venv.Parser().ParseExpr(tc.expression) if err != nil { t.Fatal(err) } cfg := &evalengine.Config{ Collation: collations.CollationUtf8mb4ID, + Environment: venv, NoConstantFolding: true, } @@ -659,7 +799,7 @@ func TestCompilerNonConstant(t *testing.T) { t.Fatal(err) } - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(venv) var prev string for i := 0; i < 1000; i++ { expected, err := env.EvaluateAST(converted) diff --git a/go/vt/vtgate/evalengine/eval.go b/go/vt/vtgate/evalengine/eval.go index e327b9d5651..86d3c949b4d 100644 --- a/go/vt/vtgate/evalengine/eval.go +++ b/go/vt/vtgate/evalengine/eval.go @@ -129,7 +129,7 @@ func evalToSQLValueWithType(e eval, resultType sqltypes.Type) sqltypes.Value { case *evalDecimal: return sqltypes.MakeTrusted(resultType, e.dec.FormatMySQL(e.length)) } - default: + case e != nil: return sqltypes.MakeTrusted(resultType, e.ToRawBytes()) } return sqltypes.NULL @@ -176,7 +176,7 @@ func evalIsTruthy(e eval) boolean { } } -func evalCoerce(e eval, typ sqltypes.Type, col collations.ID, now time.Time) (eval, error) { +func evalCoerce(e eval, typ sqltypes.Type, col collations.ID, now time.Time, allowZero bool) (eval, error) { if e == nil { return nil, nil } @@ -208,9 +208,9 @@ func evalCoerce(e eval, typ sqltypes.Type, col collations.ID, now time.Time) (ev case sqltypes.Uint8, sqltypes.Uint16, sqltypes.Uint32, sqltypes.Uint64: return evalToInt64(e).toUint64(), nil case sqltypes.Date: - return evalToDate(e, now), nil + return evalToDate(e, now, allowZero), nil case sqltypes.Datetime, sqltypes.Timestamp: - return evalToDateTime(e, -1, now), nil + return evalToDateTime(e, -1, now, allowZero), nil case sqltypes.Time: return evalToTime(e, -1), nil default: @@ -218,7 +218,7 @@ func evalCoerce(e eval, typ sqltypes.Type, col collations.ID, now time.Time) (ev } } -func valueToEvalCast(v sqltypes.Value, typ sqltypes.Type, collation collations.ID) (eval, error) { +func valueToEvalCast(v sqltypes.Value, typ sqltypes.Type, collation collations.ID, sqlmode SQLMode) (eval, error) { switch { case typ == sqltypes.Null: return nil, nil @@ -312,7 +312,7 @@ func valueToEvalCast(v sqltypes.Value, typ sqltypes.Type, collation collations.I return newEvalUint64(uint64(i.i)), nil } - case sqltypes.IsText(typ) || sqltypes.IsBinary(typ): + case sqltypes.IsTextOrBinary(typ): switch { case v.IsText() || v.IsBinary(): return newEvalRaw(v.Type(), v.Raw(), typedCoercionCollation(v.Type(), collation)), nil @@ -338,7 +338,7 @@ func valueToEvalCast(v sqltypes.Value, typ sqltypes.Type, collation collations.I return nil, err } // Separate return here to avoid nil wrapped in interface type - d := evalToDate(e, time.Now()) + d := evalToDate(e, time.Now(), sqlmode.AllowZeroDate()) if d == nil { return nil, nil } @@ -349,7 +349,7 @@ func valueToEvalCast(v sqltypes.Value, typ sqltypes.Type, collation collations.I return nil, err } // Separate return here to avoid nil wrapped in interface type - dt := evalToDateTime(e, -1, time.Now()) + dt := evalToDateTime(e, -1, time.Now(), sqlmode.AllowZeroDate()) if dt == nil { return nil, nil } diff --git a/go/vt/vtgate/evalengine/eval_result.go b/go/vt/vtgate/evalengine/eval_result.go index 19a6ea59220..d9916af03be 100644 --- a/go/vt/vtgate/evalengine/eval_result.go +++ b/go/vt/vtgate/evalengine/eval_result.go @@ -28,7 +28,8 @@ import ( ) type EvalResult struct { - v eval + v eval + collationEnv *collations.Environment } // Value allows for retrieval of the value we expose for public consumption. @@ -56,7 +57,7 @@ func (er EvalResult) Collation() collations.ID { } func (er EvalResult) String() string { - return er.Value(collations.Default()).String() + return er.Value(er.collationEnv.DefaultConnectionCharset()).String() } // TupleValues allows for retrieval of the value we expose for public consumption diff --git a/go/vt/vtgate/evalengine/eval_temporal.go b/go/vt/vtgate/evalengine/eval_temporal.go index d44839a6853..7706ec36e64 100644 --- a/go/vt/vtgate/evalengine/eval_temporal.go +++ b/go/vt/vtgate/evalengine/eval_temporal.go @@ -153,7 +153,7 @@ func (e *evalTemporal) addInterval(interval *datetime.Interval, coll collations. tmp.dt.Time, tmp.prec, ok = e.dt.Time.AddInterval(interval, coll != collations.Unknown) case tt == sqltypes.Datetime || tt == sqltypes.Timestamp || (tt == sqltypes.Date && interval.Unit().HasTimeParts()) || (tt == sqltypes.Time && interval.Unit().HasDateParts()): tmp = e.toDateTime(int(e.prec), now) - tmp.dt, tmp.prec, ok = e.dt.AddInterval(interval, coll != collations.Unknown) + tmp.dt, tmp.prec, ok = e.dt.AddInterval(interval, tmp.prec, coll != collations.Unknown) } if !ok { return nil @@ -164,11 +164,17 @@ func (e *evalTemporal) addInterval(interval *datetime.Interval, coll collations. return tmp } -func newEvalDateTime(dt datetime.DateTime, l int) *evalTemporal { +func newEvalDateTime(dt datetime.DateTime, l int, allowZero bool) *evalTemporal { + if !allowZero && dt.IsZero() { + return nil + } return &evalTemporal{t: sqltypes.Datetime, dt: dt.Round(l), prec: uint8(l)} } -func newEvalDate(d datetime.Date) *evalTemporal { +func newEvalDate(d datetime.Date, allowZero bool) *evalTemporal { + if !allowZero && d.IsZero() { + return nil + } return &evalTemporal{t: sqltypes.Date, dt: datetime.DateTime{Date: d}} } @@ -185,7 +191,7 @@ func parseDate(s []byte) (*evalTemporal, error) { if !ok { return nil, errIncorrectTemporal("DATE", s) } - return newEvalDate(t), nil + return newEvalDate(t, true), nil } func parseDateTime(s []byte) (*evalTemporal, error) { @@ -193,12 +199,12 @@ func parseDateTime(s []byte) (*evalTemporal, error) { if !ok { return nil, errIncorrectTemporal("DATETIME", s) } - return newEvalDateTime(t, l), nil + return newEvalDateTime(t, l, true), nil } func parseTime(s []byte) (*evalTemporal, error) { - t, l, ok := datetime.ParseTime(hack.String(s), -1) - if !ok { + t, l, state := datetime.ParseTime(hack.String(s), -1) + if state != datetime.TimeOK { return nil, errIncorrectTemporal("TIME", s) } return newEvalTime(t, l), nil @@ -211,56 +217,56 @@ func precision(req, got int) int { return req } -func evalToTemporal(e eval) *evalTemporal { +func evalToTemporal(e eval, allowZero bool) *evalTemporal { switch e := e.(type) { case *evalTemporal: return e case *evalBytes: if t, l, ok := datetime.ParseDateTime(e.string(), -1); ok { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, ok := datetime.ParseDate(e.string()); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } - if t, l, ok := datetime.ParseTime(e.string(), -1); ok { + if t, l, state := datetime.ParseTime(e.string(), -1); state == datetime.TimeOK { return newEvalTime(t, l) } case *evalInt64: if t, ok := datetime.ParseDateTimeInt64(e.i); ok { - return newEvalDateTime(t, 0) + return newEvalDateTime(t, 0, allowZero) } if d, ok := datetime.ParseDateInt64(e.i); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } if t, ok := datetime.ParseTimeInt64(e.i); ok { return newEvalTime(t, 0) } case *evalUint64: if t, ok := datetime.ParseDateTimeInt64(int64(e.u)); ok { - return newEvalDateTime(t, 0) + return newEvalDateTime(t, 0, allowZero) } if d, ok := datetime.ParseDateInt64(int64(e.u)); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } if t, ok := datetime.ParseTimeInt64(int64(e.u)); ok { return newEvalTime(t, 0) } case *evalFloat: if t, l, ok := datetime.ParseDateTimeFloat(e.f, -1); ok { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, ok := datetime.ParseDateFloat(e.f); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } if t, l, ok := datetime.ParseTimeFloat(e.f, -1); ok { return newEvalTime(t, l) } case *evalDecimal: if t, l, ok := datetime.ParseDateTimeDecimal(e.dec, e.length, -1); ok { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, ok := datetime.ParseDateDecimal(e.dec); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } if d, l, ok := datetime.ParseTimeDecimal(e.dec, e.length, -1); ok { return newEvalTime(d, l) @@ -271,9 +277,9 @@ func evalToTemporal(e eval) *evalTemporal { return newEvalTime(dt.Time, datetime.DefaultPrecision) } if dt.Time.IsZero() { - return newEvalDate(dt.Date) + return newEvalDate(dt.Date, allowZero) } - return newEvalDateTime(dt, datetime.DefaultPrecision) + return newEvalDateTime(dt, datetime.DefaultPrecision, allowZero) } } return nil @@ -287,7 +293,7 @@ func evalToTime(e eval, l int) *evalTemporal { if dt, l, _ := datetime.ParseDateTime(e.string(), l); !dt.IsZero() { return newEvalTime(dt.Time, l) } - if t, l, ok := datetime.ParseTime(e.string(), l); ok || !t.IsZero() { + if t, l, state := datetime.ParseTime(e.string(), l); state != datetime.TimeInvalid { return newEvalTime(t, l) } case *evalInt64: @@ -326,74 +332,74 @@ func evalToTime(e eval, l int) *evalTemporal { return nil } -func evalToDateTime(e eval, l int, now time.Time) *evalTemporal { +func evalToDateTime(e eval, l int, now time.Time, allowZero bool) *evalTemporal { switch e := e.(type) { case *evalTemporal: return e.toDateTime(precision(l, int(e.prec)), now) case *evalBytes: if t, l, _ := datetime.ParseDateTime(e.string(), l); !t.IsZero() { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, _ := datetime.ParseDate(e.string()); !d.IsZero() { - return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0)) + return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0), allowZero) } case *evalInt64: if t, ok := datetime.ParseDateTimeInt64(e.i); ok { - return newEvalDateTime(t, precision(l, 0)) + return newEvalDateTime(t, precision(l, 0), allowZero) } if d, ok := datetime.ParseDateInt64(e.i); ok { - return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0)) + return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0), allowZero) } case *evalUint64: if t, ok := datetime.ParseDateTimeInt64(int64(e.u)); ok { - return newEvalDateTime(t, precision(l, 0)) + return newEvalDateTime(t, precision(l, 0), allowZero) } if d, ok := datetime.ParseDateInt64(int64(e.u)); ok { - return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0)) + return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0), allowZero) } case *evalFloat: if t, l, ok := datetime.ParseDateTimeFloat(e.f, l); ok { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, ok := datetime.ParseDateFloat(e.f); ok { - return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0)) + return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0), allowZero) } case *evalDecimal: if t, l, ok := datetime.ParseDateTimeDecimal(e.dec, e.length, l); ok { - return newEvalDateTime(t, l) + return newEvalDateTime(t, l, allowZero) } if d, ok := datetime.ParseDateDecimal(e.dec); ok { - return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0)) + return newEvalDateTime(datetime.DateTime{Date: d}, precision(l, 0), allowZero) } case *evalJSON: if dt, ok := e.DateTime(); ok { - return newEvalDateTime(dt, precision(l, datetime.DefaultPrecision)) + return newEvalDateTime(dt, precision(l, datetime.DefaultPrecision), allowZero) } } return nil } -func evalToDate(e eval, now time.Time) *evalTemporal { +func evalToDate(e eval, now time.Time, allowZero bool) *evalTemporal { switch e := e.(type) { case *evalTemporal: return e.toDate(now) case *evalBytes: if t, _ := datetime.ParseDate(e.string()); !t.IsZero() { - return newEvalDate(t) + return newEvalDate(t, allowZero) } if dt, _, _ := datetime.ParseDateTime(e.string(), -1); !dt.IsZero() { - return newEvalDate(dt.Date) + return newEvalDate(dt.Date, allowZero) } case evalNumeric: if t, ok := datetime.ParseDateInt64(e.toInt64().i); ok { - return newEvalDate(t) + return newEvalDate(t, allowZero) } if dt, ok := datetime.ParseDateTimeInt64(e.toInt64().i); ok { - return newEvalDate(dt.Date) + return newEvalDate(dt.Date, allowZero) } case *evalJSON: if d, ok := e.Date(); ok { - return newEvalDate(d) + return newEvalDate(d, allowZero) } } return nil diff --git a/go/vt/vtgate/evalengine/expr_arithmetic.go b/go/vt/vtgate/evalengine/expr_arithmetic.go index cb80e57c365..5d82d279d69 100644 --- a/go/vt/vtgate/evalengine/expr_arithmetic.go +++ b/go/vt/vtgate/evalengine/expr_arithmetic.go @@ -66,19 +66,6 @@ func (b *ArithmeticExpr) eval(env *ExpressionEnv) (eval, error) { return b.Op.eval(left, right) } -func makeNumericalType(t sqltypes.Type, f typeFlag) (sqltypes.Type, typeFlag) { - if sqltypes.IsNumber(t) { - return t, f - } - if t == sqltypes.VarBinary && (f&flagHex) != 0 { - return sqltypes.Uint64, f - } - if sqltypes.IsDateOrTime(t) { - return sqltypes.Int64, f | flagAmbiguousType - } - return sqltypes.Float64, f -} - func (b *ArithmeticExpr) compile(c *compiler) (ctype, error) { return b.Op.compile(c, b.Left, b.Right) } @@ -107,12 +94,12 @@ func (op *opArithAdd) compile(c *compiler, left, right IR) (ctype, error) { rt = c.compileToNumeric(rt, 1, sqltypes.Float64, true) lt, rt, swap = c.compileNumericPriority(lt, rt) - var sumtype sqltypes.Type + ct := ctype{Flag: nullableFlags(lt.Flag | rt.Flag), Col: collationNumeric} switch lt.Type { case sqltypes.Int64: c.asm.Add_ii() - sumtype = sqltypes.Int64 + ct.Type = sqltypes.Int64 case sqltypes.Uint64: switch rt.Type { case sqltypes.Int64: @@ -120,7 +107,7 @@ func (op *opArithAdd) compile(c *compiler, left, right IR) (ctype, error) { case sqltypes.Uint64: c.asm.Add_uu() } - sumtype = sqltypes.Uint64 + ct.Type = sqltypes.Uint64 case sqltypes.Decimal: if swap { c.compileToDecimal(rt, 2) @@ -128,7 +115,8 @@ func (op *opArithAdd) compile(c *compiler, left, right IR) (ctype, error) { c.compileToDecimal(rt, 1) } c.asm.Add_dd() - sumtype = sqltypes.Decimal + ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) case sqltypes.Float64: if swap { c.compileToFloat(rt, 2) @@ -136,11 +124,11 @@ func (op *opArithAdd) compile(c *compiler, left, right IR) (ctype, error) { c.compileToFloat(rt, 1) } c.asm.Add_ff() - sumtype = sqltypes.Float64 + ct.Type = sqltypes.Float64 } c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sumtype, Col: collationNumeric}, nil + return ct, nil } func (op *opArithSub) eval(left, right eval) (eval, error) { @@ -164,66 +152,68 @@ func (op *opArithSub) compile(c *compiler, left, right IR) (ctype, error) { lt = c.compileToNumeric(lt, 2, sqltypes.Float64, true) rt = c.compileToNumeric(rt, 1, sqltypes.Float64, true) - var subtype sqltypes.Type - + ct := ctype{Flag: nullableFlags(lt.Flag | rt.Flag), Col: collationNumeric} switch lt.Type { case sqltypes.Int64: switch rt.Type { case sqltypes.Int64: c.asm.Sub_ii() - subtype = sqltypes.Int64 + ct.Type = sqltypes.Int64 case sqltypes.Uint64: c.asm.Sub_iu() - subtype = sqltypes.Uint64 + ct.Type = sqltypes.Uint64 case sqltypes.Float64: c.compileToFloat(lt, 2) c.asm.Sub_ff() - subtype = sqltypes.Float64 + ct.Type = sqltypes.Float64 case sqltypes.Decimal: c.compileToDecimal(lt, 2) c.asm.Sub_dd() - subtype = sqltypes.Decimal + ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) } case sqltypes.Uint64: switch rt.Type { case sqltypes.Int64: c.asm.Sub_ui() - subtype = sqltypes.Uint64 + ct.Type = sqltypes.Uint64 case sqltypes.Uint64: c.asm.Sub_uu() - subtype = sqltypes.Uint64 + ct.Type = sqltypes.Uint64 case sqltypes.Float64: c.compileToFloat(lt, 2) c.asm.Sub_ff() - subtype = sqltypes.Float64 + ct.Type = sqltypes.Float64 case sqltypes.Decimal: c.compileToDecimal(lt, 2) c.asm.Sub_dd() - subtype = sqltypes.Decimal + ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) } case sqltypes.Float64: c.compileToFloat(rt, 1) c.asm.Sub_ff() - subtype = sqltypes.Float64 + ct.Type = sqltypes.Float64 case sqltypes.Decimal: switch rt.Type { case sqltypes.Float64: c.compileToFloat(lt, 2) c.asm.Sub_ff() - subtype = sqltypes.Float64 + ct.Type = sqltypes.Float64 default: c.compileToDecimal(rt, 1) c.asm.Sub_dd() - subtype = sqltypes.Decimal + ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) } } - if subtype == 0 { + if ct.Type == 0 { panic("did not compile?") } c.asm.jumpDestination(skip1, skip2) - return ctype{Type: subtype, Col: collationNumeric}, nil + return ct, nil } func (op *opArithMul) eval(left, right eval) (eval, error) { @@ -250,12 +240,11 @@ func (op *opArithMul) compile(c *compiler, left, right IR) (ctype, error) { rt = c.compileToNumeric(rt, 1, sqltypes.Float64, true) lt, rt, swap = c.compileNumericPriority(lt, rt) - var multype sqltypes.Type - + ct := ctype{Flag: nullableFlags(lt.Flag | rt.Flag), Col: collationNumeric} switch lt.Type { case sqltypes.Int64: c.asm.Mul_ii() - multype = sqltypes.Int64 + ct.Type = sqltypes.Int64 case sqltypes.Uint64: switch rt.Type { case sqltypes.Int64: @@ -263,7 +252,7 @@ func (op *opArithMul) compile(c *compiler, left, right IR) (ctype, error) { case sqltypes.Uint64: c.asm.Mul_uu() } - multype = sqltypes.Uint64 + ct.Type = sqltypes.Uint64 case sqltypes.Float64: if swap { c.compileToFloat(rt, 2) @@ -271,7 +260,7 @@ func (op *opArithMul) compile(c *compiler, left, right IR) (ctype, error) { c.compileToFloat(rt, 1) } c.asm.Mul_ff() - multype = sqltypes.Float64 + ct.Type = sqltypes.Float64 case sqltypes.Decimal: if swap { c.compileToDecimal(rt, 2) @@ -279,11 +268,12 @@ func (op *opArithMul) compile(c *compiler, left, right IR) (ctype, error) { c.compileToDecimal(rt, 1) } c.asm.Mul_dd() - multype = sqltypes.Decimal + ct.Type = sqltypes.Decimal + ct.Scale = lt.Scale + rt.Scale } c.asm.jumpDestination(skip1, skip2) - return ctype{Type: multype, Col: collationNumeric}, nil + return ct, nil } func (op *opArithDiv) eval(left, right eval) (eval, error) { @@ -319,6 +309,7 @@ func (op *opArithDiv) compile(c *compiler, left, right IR) (ctype, error) { c.compileToDecimal(lt, 2) c.compileToDecimal(rt, 1) c.asm.Div_dd() + ct.Scale = lt.Scale + divPrecisionIncrement } c.asm.jumpDestination(skip1, skip2) return ct, nil @@ -432,7 +423,7 @@ func (op *opArithMod) compile(c *compiler, left, right IR) (ctype, error) { lt = c.compileToNumeric(lt, 2, sqltypes.Float64, true) rt = c.compileToNumeric(rt, 1, sqltypes.Float64, true) - ct := ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagNullable} + ct := ctype{Col: collationNumeric, Flag: flagNullable} switch lt.Type { case sqltypes.Int64: ct.Type = sqltypes.Int64 @@ -447,6 +438,7 @@ func (op *opArithMod) compile(c *compiler, left, right IR) (ctype, error) { c.asm.Mod_ff() case sqltypes.Decimal: ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) c.asm.Convert_xd(2, 0, 0) c.asm.Mod_dd() } @@ -463,6 +455,7 @@ func (op *opArithMod) compile(c *compiler, left, right IR) (ctype, error) { c.asm.Mod_ff() case sqltypes.Decimal: ct.Type = sqltypes.Decimal + ct.Scale = max(lt.Scale, rt.Scale) c.asm.Convert_xd(2, 0, 0) c.asm.Mod_dd() } @@ -536,5 +529,15 @@ func (expr *NegateExpr) compile(c *compiler) (ctype, error) { } c.asm.jumpDestination(skip) - return ctype{Type: neg, Col: collationNumeric}, nil + return ctype{ + Type: neg, + Flag: nullableFlags(arg.Flag), + Size: arg.Size, + Scale: arg.Scale, + Col: collationNumeric, + }, nil +} + +func nullableFlags(flag typeFlag) typeFlag { + return flag & (flagNull | flagNullable) } diff --git a/go/vt/vtgate/evalengine/expr_bit.go b/go/vt/vtgate/evalengine/expr_bit.go index e95d54c5b6c..6200875d1fc 100644 --- a/go/vt/vtgate/evalengine/expr_bit.go +++ b/go/vt/vtgate/evalengine/expr_bit.go @@ -104,9 +104,9 @@ func (o opBitShr) numeric(num, shift uint64) uint64 { return num >> shift } func (o opBitShr) binary(num []byte, shift uint64) []byte { var ( - bits = int(shift % 8) - bytes = int(shift / 8) - length = len(num) + bits = int64(shift % 8) + bytes = int64(shift / 8) + length = int64(len(num)) out = make([]byte, length) ) @@ -127,13 +127,13 @@ func (o opBitShl) numeric(num, shift uint64) uint64 { return num << shift } func (o opBitShl) binary(num []byte, shift uint64) []byte { var ( - bits = int(shift % 8) - bytes = int(shift / 8) - length = len(num) + bits = int64(shift % 8) + bytes = int64(shift / 8) + length = int64(len(num)) out = make([]byte, length) ) - for i := 0; i < length; i++ { + for i := int64(0); i < length; i++ { pos := i + bytes + 1 switch { case pos < length: @@ -270,7 +270,7 @@ func (expr *BitwiseExpr) compileBinary(c *compiler, asm_ins_bb, asm_ins_uu func( asm_ins_uu() c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sqltypes.Uint64, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Uint64, Flag: nullableFlags(lt.Flag | rt.Flag), Col: collationNumeric}, nil } func (expr *BitwiseExpr) compileShift(c *compiler, i int) (ctype, error) { @@ -299,8 +299,8 @@ func (expr *BitwiseExpr) compileShift(c *compiler, i int) (ctype, error) { return ctype{Type: sqltypes.VarBinary, Col: collationBinary}, nil } - _ = c.compileToBitwiseUint64(lt, 2) - _ = c.compileToUint64(rt, 1) + lt = c.compileToBitwiseUint64(lt, 2) + rt = c.compileToUint64(rt, 1) if i < 0 { c.asm.BitShiftLeft_uu() @@ -309,7 +309,7 @@ func (expr *BitwiseExpr) compileShift(c *compiler, i int) (ctype, error) { } c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sqltypes.Uint64, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Uint64, Flag: nullableFlags(lt.Flag | rt.Flag), Col: collationNumeric}, nil } func (expr *BitwiseExpr) compile(c *compiler) (ctype, error) { diff --git a/go/vt/vtgate/evalengine/expr_bvar.go b/go/vt/vtgate/evalengine/expr_bvar.go index 6bc49caf660..b21ded90189 100644 --- a/go/vt/vtgate/evalengine/expr_bvar.go +++ b/go/vt/vtgate/evalengine/expr_bvar.go @@ -65,7 +65,7 @@ func (bv *BindVariable) eval(env *ExpressionEnv) (eval, error) { switch bvar.Type { case sqltypes.Tuple: if bv.Type != sqltypes.Tuple { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "query argument '%s' cannot be a tuple", bv.Key) + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "query argument '%s' must be a tuple (is %s)", bv.Key, bvar.Type) } tuple := make([]eval, 0, len(bvar.Values)) @@ -80,7 +80,7 @@ func (bv *BindVariable) eval(env *ExpressionEnv) (eval, error) { default: if bv.Type == sqltypes.Tuple { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "query argument '%s' must be a tuple (is %s)", bv.Key, bvar.Type) + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "query argument '%s' cannot be a tuple", bv.Key) } typ := bvar.Type if bv.typed() { @@ -106,11 +106,11 @@ func (bv *BindVariable) typeof(env *ExpressionEnv) (ctype, error) { case sqltypes.Null: return ctype{Type: sqltypes.Null, Flag: flagNull | flagNullable, Col: collationNull}, nil case sqltypes.HexNum, sqltypes.HexVal: - return ctype{Type: sqltypes.VarBinary, Flag: flagHex, Col: collationNumeric}, nil + return ctype{Type: sqltypes.VarBinary, Flag: flagHex | flagNullable, Col: collationNumeric}, nil case sqltypes.BitNum: - return ctype{Type: sqltypes.VarBinary, Flag: flagBit, Col: collationNumeric}, nil + return ctype{Type: sqltypes.VarBinary, Flag: flagBit | flagNullable, Col: collationNumeric}, nil default: - return ctype{Type: tt, Flag: 0, Col: typedCoercionCollation(tt, collations.CollationForType(tt, bv.Collation))}, nil + return ctype{Type: tt, Flag: flagNullable, Col: typedCoercionCollation(tt, collations.CollationForType(tt, bv.Collation))}, nil } } diff --git a/go/vt/vtgate/evalengine/expr_collate.go b/go/vt/vtgate/evalengine/expr_collate.go index 47e65a0dcc7..bab0e5e52f9 100644 --- a/go/vt/vtgate/evalengine/expr_collate.go +++ b/go/vt/vtgate/evalengine/expr_collate.go @@ -63,11 +63,13 @@ type ( CollateExpr struct { UnaryExpr TypedCollation collations.TypedCollation + CollationEnv *collations.Environment } IntroducerExpr struct { UnaryExpr TypedCollation collations.TypedCollation + CollationEnv *collations.Environment } ) @@ -84,7 +86,7 @@ func (c *CollateExpr) eval(env *ExpressionEnv) (eval, error) { case nil: return nil, nil case *evalBytes: - if err := collations.Local().EnsureCollate(e.col.Collation, c.TypedCollation.Collation); err != nil { + if err := env.collationEnv.EnsureCollate(e.col.Collation, c.TypedCollation.Collation); err != nil { return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, err.Error()) } b = e.withCollation(c.TypedCollation) @@ -109,18 +111,19 @@ func (expr *CollateExpr) compile(c *compiler) (ctype, error) { switch ct.Type { case sqltypes.VarChar: - if err := collations.Local().EnsureCollate(ct.Col.Collation, expr.TypedCollation.Collation); err != nil { + if err := c.env.CollationEnv().EnsureCollate(ct.Col.Collation, expr.TypedCollation.Collation); err != nil { return ctype{}, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, err.Error()) } fallthrough case sqltypes.VarBinary: c.asm.Collate(expr.TypedCollation.Collation) default: - return ctype{}, c.unsupported(expr) + c.asm.Convert_xc(1, sqltypes.VarChar, expr.TypedCollation.Collation, nil) } c.asm.jumpDestination(skip) + ct.Type = sqltypes.VarChar ct.Col = expr.TypedCollation ct.Flag |= flagExplicitCollation | flagNullable return ct, nil diff --git a/go/vt/vtgate/evalengine/expr_column.go b/go/vt/vtgate/evalengine/expr_column.go index 741d04c6a06..06e135c317c 100644 --- a/go/vt/vtgate/evalengine/expr_column.go +++ b/go/vt/vtgate/evalengine/expr_column.go @@ -29,8 +29,11 @@ type ( Column struct { Offset int Type sqltypes.Type + Size int32 + Scale int32 Collation collations.TypedCollation Original sqlparser.Expr + Nullable bool // dynamicTypeOffset is set when the type of this column cannot be calculated // at translation time. Since expressions with dynamic types cannot be compiled ahead of time, @@ -56,7 +59,11 @@ func (c *Column) eval(env *ExpressionEnv) (eval, error) { func (c *Column) typeof(env *ExpressionEnv) (ctype, error) { if c.typed() { - return ctype{Type: c.Type, Flag: flagNullable, Col: c.Collation}, nil + var nullable typeFlag + if c.Nullable { + nullable = flagNullable + } + return ctype{Type: c.Type, Size: c.Size, Scale: c.Scale, Flag: nullable, Col: c.Collation}, nil } if c.Offset < len(env.Fields) { field := env.Fields[c.Offset] @@ -85,7 +92,11 @@ func (column *Column) compile(c *compiler) (ctype, error) { if column.typed() { typ.Type = column.Type typ.Col = column.Collation - typ.Flag = flagNullable + if column.Nullable { + typ.Flag = flagNullable + } + typ.Size = column.Size + typ.Scale = column.Scale } else if c.dynamicTypes != nil { typ = c.dynamicTypes[column.dynamicTypeOffset] } else { diff --git a/go/vt/vtgate/evalengine/expr_column_test.go b/go/vt/vtgate/evalengine/expr_column_test.go index b8bc5b9c640..bd7fd4250fd 100644 --- a/go/vt/vtgate/evalengine/expr_column_test.go +++ b/go/vt/vtgate/evalengine/expr_column_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -28,8 +29,9 @@ func TestTypeOf(t *testing.T) { t.Skipf("TODO: these tests are not green") env := &ExpressionEnv{ - BindVars: make(map[string]*querypb.BindVariable), - now: time.Now(), + BindVars: make(map[string]*querypb.BindVariable), + now: time.Now(), + collationEnv: collations.MySQL8(), } c := &Column{ Type: sqltypes.Unknown, diff --git a/go/vt/vtgate/evalengine/expr_compare.go b/go/vt/vtgate/evalengine/expr_compare.go index 6639bb7f7e2..ca4cdd75f74 100644 --- a/go/vt/vtgate/evalengine/expr_compare.go +++ b/go/vt/vtgate/evalengine/expr_compare.go @@ -52,7 +52,7 @@ type ( ComparisonOp interface { String() string - compare(left, right eval) (boolean, error) + compare(collationEnv *collations.Environment, left, right eval) (boolean, error) } compareEQ struct{} @@ -72,49 +72,49 @@ func (*ComparisonExpr) filterExpr() {} func (*InExpr) filterExpr() {} func (compareEQ) String() string { return "=" } -func (compareEQ) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, true) +func (compareEQ) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, true, collationEnv) return makeboolean2(cmp == 0, isNull), err } func (compareNE) String() string { return "!=" } -func (compareNE) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, true) +func (compareNE) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, true, collationEnv) return makeboolean2(cmp != 0, isNull), err } func (compareLT) String() string { return "<" } -func (compareLT) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, false) +func (compareLT) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, false, collationEnv) return makeboolean2(cmp < 0, isNull), err } func (compareLE) String() string { return "<=" } -func (compareLE) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, false) +func (compareLE) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, false, collationEnv) return makeboolean2(cmp <= 0, isNull), err } func (compareGT) String() string { return ">" } -func (compareGT) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, false) +func (compareGT) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, false, collationEnv) return makeboolean2(cmp > 0, isNull), err } func (compareGE) String() string { return ">=" } -func (compareGE) compare(left, right eval) (boolean, error) { - cmp, isNull, err := evalCompareAll(left, right, false) +func (compareGE) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, isNull, err := evalCompareAll(left, right, false, collationEnv) return makeboolean2(cmp >= 0, isNull), err } func (compareNullSafeEQ) String() string { return "<=>" } -func (compareNullSafeEQ) compare(left, right eval) (boolean, error) { - cmp, err := evalCompareNullSafe(left, right) +func (compareNullSafeEQ) compare(collationEnv *collations.Environment, left, right eval) (boolean, error) { + cmp, err := evalCompareNullSafe(left, right, collationEnv) return makeboolean(cmp == 0), err } func typeIsTextual(tt sqltypes.Type) bool { - return sqltypes.IsText(tt) || sqltypes.IsBinary(tt) || tt == sqltypes.Time + return sqltypes.IsTextOrBinary(tt) || tt == sqltypes.Time } func compareAsStrings(l, r sqltypes.Type) bool { @@ -164,7 +164,7 @@ func compareAsJSON(l, r sqltypes.Type) bool { return l == sqltypes.TypeJSON || r == sqltypes.TypeJSON } -func evalCompareNullSafe(lVal, rVal eval) (int, error) { +func evalCompareNullSafe(lVal, rVal eval, collationEnv *collations.Environment) (int, error) { if lVal == nil { if rVal == nil { return 0, nil @@ -175,18 +175,18 @@ func evalCompareNullSafe(lVal, rVal eval) (int, error) { return 1, nil } if left, right, ok := compareAsTuples(lVal, rVal); ok { - return evalCompareTuplesNullSafe(left.t, right.t) + return evalCompareTuplesNullSafe(left.t, right.t, collationEnv) } - n, err := evalCompare(lVal, rVal) + n, err := evalCompare(lVal, rVal, collationEnv) return n, err } -func evalCompareMany(left, right []eval, fulleq bool) (int, bool, error) { +func evalCompareMany(left, right []eval, fulleq bool, collationEnv *collations.Environment) (int, bool, error) { // For row comparisons, (a, b) = (x, y) is equivalent to: (a = x) AND (b = y) var seenNull bool for idx, lResult := range left { rResult := right[idx] - n, isNull, err := evalCompareAll(lResult, rResult, fulleq) + n, isNull, err := evalCompareAll(lResult, rResult, fulleq, collationEnv) if err != nil { return 0, false, err } @@ -203,20 +203,20 @@ func evalCompareMany(left, right []eval, fulleq bool) (int, bool, error) { return 0, seenNull, nil } -func evalCompareAll(lVal, rVal eval, fulleq bool) (int, bool, error) { +func evalCompareAll(lVal, rVal eval, fulleq bool, collationEnv *collations.Environment) (int, bool, error) { if lVal == nil || rVal == nil { return 0, true, nil } if left, right, ok := compareAsTuples(lVal, rVal); ok { - return evalCompareMany(left.t, right.t, fulleq) + return evalCompareMany(left.t, right.t, fulleq, collationEnv) } - n, err := evalCompare(lVal, rVal) + n, err := evalCompare(lVal, rVal, collationEnv) return n, false, err } // For more details on comparison expression evaluation and type conversion: // - https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html -func evalCompare(left, right eval) (comp int, err error) { +func evalCompare(left, right eval, collationEnv *collations.Environment) (comp int, err error) { lt := left.SQLType() rt := right.SQLType() @@ -224,7 +224,7 @@ func evalCompare(left, right eval) (comp int, err error) { case compareAsDates(lt, rt): return compareDates(left.(*evalTemporal), right.(*evalTemporal)), nil case compareAsStrings(lt, rt): - return compareStrings(left, right) + return compareStrings(left, right, collationEnv) case compareAsSameNumericType(lt, rt) || compareAsDecimal(lt, rt): return compareNumeric(left, right) case compareAsDateAndString(lt, rt): @@ -269,12 +269,12 @@ func fallbackBinary(t sqltypes.Type) bool { return false } -func evalCompareTuplesNullSafe(left, right []eval) (int, error) { +func evalCompareTuplesNullSafe(left, right []eval, collationEnv *collations.Environment) (int, error) { if len(left) != len(right) { panic("did not typecheck cardinality") } for idx, lResult := range left { - res, err := evalCompareNullSafe(lResult, right[idx]) + res, err := evalCompareNullSafe(lResult, right[idx], collationEnv) if err != nil { return 0, err } @@ -302,7 +302,7 @@ func (c *ComparisonExpr) eval(env *ExpressionEnv) (eval, error) { if _, ok := c.Op.(compareNullSafeEQ); !ok && right == nil { return nil, nil } - cmp, err := c.Op.compare(left, right) + cmp, err := c.Op.compare(env.collationEnv, left, right) if err != nil { return nil, err } @@ -312,25 +312,25 @@ func (c *ComparisonExpr) eval(env *ExpressionEnv) (eval, error) { func (expr *ComparisonExpr) compileAsTuple(c *compiler) (ctype, error) { switch expr.Op.(type) { case compareNullSafeEQ: - c.asm.CmpTupleNullsafe() + c.asm.CmpTupleNullsafe(c.env.CollationEnv()) return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean}, nil case compareEQ: - c.asm.CmpTuple(true) + c.asm.CmpTuple(c.env.CollationEnv(), true) c.asm.Cmp_eq_n() case compareNE: - c.asm.CmpTuple(true) + c.asm.CmpTuple(c.env.CollationEnv(), true) c.asm.Cmp_ne_n() case compareLT: - c.asm.CmpTuple(false) + c.asm.CmpTuple(c.env.CollationEnv(), false) c.asm.Cmp_lt_n() case compareLE: - c.asm.CmpTuple(false) + c.asm.CmpTuple(c.env.CollationEnv(), false) c.asm.Cmp_le_n() case compareGT: - c.asm.CmpTuple(false) + c.asm.CmpTuple(c.env.CollationEnv(), false) c.asm.Cmp_gt_n() case compareGE: - c.asm.CmpTuple(false) + c.asm.CmpTuple(c.env.CollationEnv(), false) c.asm.Cmp_ge_n() default: panic("invalid comparison operator") @@ -365,11 +365,13 @@ func (expr *ComparisonExpr) compile(c *compiler) (ctype, error) { swapped := false var skip2 *jump + nullable := true switch expr.Op.(type) { case compareNullSafeEQ: skip2 = c.asm.jumpFrom() c.asm.Cmp_nullsafe(skip2) + nullable = false default: skip2 = c.compileNullCheck1r(rt) } @@ -387,12 +389,22 @@ func (expr *ComparisonExpr) compile(c *compiler) (ctype, error) { c.asm.CmpDateString() case compareAsDateAndNumeric(lt.Type, rt.Type): if sqltypes.IsDateOrTime(lt.Type) { - c.asm.Convert_Ti(2) - lt.Type = sqltypes.Int64 + if lt.Size == 0 { + c.asm.Convert_Ti(2) + lt.Type = sqltypes.Int64 + } else { + c.asm.Convert_Tf(2) + lt.Type = sqltypes.Float64 + } } if sqltypes.IsDateOrTime(rt.Type) { - c.asm.Convert_Ti(1) - rt.Type = sqltypes.Int64 + if rt.Size == 0 { + c.asm.Convert_Ti(1) + rt.Type = sqltypes.Int64 + } else { + c.asm.Convert_Tf(1) + rt.Type = sqltypes.Float64 + } } swapped = c.compareNumericTypes(lt, rt) case compareAsJSON(lt.Type, rt.Type): @@ -407,6 +419,9 @@ func (expr *ComparisonExpr) compile(c *compiler) (ctype, error) { } cmptype := ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean} + if nullable { + cmptype.Flag |= nullableFlags(lt.Flag | rt.Flag) + } switch expr.Op.(type) { case compareEQ: @@ -450,14 +465,14 @@ func (expr *ComparisonExpr) compile(c *compiler) (ctype, error) { return cmptype, nil } -func evalInExpr(lhs eval, rhs *evalTuple) (boolean, error) { +func evalInExpr(collationEnv *collations.Environment, lhs eval, rhs *evalTuple) (boolean, error) { if lhs == nil { return boolNULL, nil } var foundNull, found bool for _, rtuple := range rhs.t { - numeric, isNull, err := evalCompareAll(lhs, rtuple, true) + numeric, isNull, err := evalCompareAll(lhs, rtuple, true, collationEnv) if err != nil { return boolNULL, err } @@ -491,7 +506,7 @@ func (i *InExpr) eval(env *ExpressionEnv) (eval, error) { if !ok { return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "rhs of an In operation should be a tuple") } - in, err := evalInExpr(left, rtuple) + in, err := evalInExpr(env.collationEnv, left, rtuple) if err != nil { return nil, err } @@ -540,18 +555,20 @@ func (expr *InExpr) compile(c *compiler) (ctype, error) { switch rhs := expr.Right.(type) { case TupleExpr: + var rt ctype if table := expr.compileTable(lhs, rhs); table != nil { c.asm.In_table(expr.Negate, table) } else { - _, err := rhs.compile(c) + rt, err = rhs.compile(c) if err != nil { return ctype{}, err } - c.asm.In_slow(expr.Negate) + c.asm.In_slow(c.env.CollationEnv(), expr.Negate) } - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean}, nil + + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean | (nullableFlags(lhs.Flag) | (rt.Flag & flagNullable))}, nil case *BindVariable: - return ctype{}, c.unsupported(expr) + return ctype{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "rhs of an In operation should be a tuple") default: panic("unreachable") } @@ -573,7 +590,7 @@ func (l *LikeExpr) eval(env *ExpressionEnv) (eval, error) { } var col collations.TypedCollation - left, right, col, err = mergeAndCoerceCollations(left, right) + left, right, col, err = mergeAndCoerceCollations(left, right, env.collationEnv) if err != nil { return nil, err } @@ -606,7 +623,7 @@ func (expr *LikeExpr) compile(c *compiler) (ctype, error) { skip := c.compileNullCheck2(lt, rt) if !lt.isTextual() { - c.asm.Convert_xc(2, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(2, sqltypes.VarChar, c.collation, nil) lt.Col = collations.TypedCollation{ Collation: c.collation, Coercibility: collations.CoerceCoercible, @@ -615,7 +632,7 @@ func (expr *LikeExpr) compile(c *compiler) (ctype, error) { } if !rt.isTextual() { - c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, nil) rt.Col = collations.TypedCollation{ Collation: c.collation, Coercibility: collations.CoerceCoercible, @@ -626,10 +643,9 @@ func (expr *LikeExpr) compile(c *compiler) (ctype, error) { var merged collations.TypedCollation var coerceLeft colldata.Coercion var coerceRight colldata.Coercion - var env = collations.Local() if lt.Col.Collation != rt.Col.Collation { - merged, coerceLeft, coerceRight, err = colldata.Merge(env, lt.Col, rt.Col, colldata.CoercionOptions{ + merged, coerceLeft, coerceRight, err = colldata.Merge(c.env.CollationEnv(), lt.Col, rt.Col, colldata.CoercionOptions{ ConvertToSuperset: true, ConvertWithCoercion: true, }) diff --git a/go/vt/vtgate/evalengine/expr_convert.go b/go/vt/vtgate/evalengine/expr_convert.go index 900d4e37f8f..a63b8197d77 100644 --- a/go/vt/vtgate/evalengine/expr_convert.go +++ b/go/vt/vtgate/evalengine/expr_convert.go @@ -19,6 +19,7 @@ package evalengine import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/colldata" + "vitess.io/vitess/go/ptr" "vitess.io/vitess/go/sqltypes" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" @@ -27,15 +28,16 @@ import ( type ( ConvertExpr struct { UnaryExpr - Type string - Length, Scale int - HasLength, HasScale bool - Collation collations.ID + Type string + Length, Scale *int + Collation collations.ID + CollationEnv *collations.Environment } ConvertUsingExpr struct { UnaryExpr - Collation collations.ID + Collation collations.ID + CollationEnv *collations.Environment } ) @@ -45,10 +47,10 @@ var _ IR = (*ConvertUsingExpr)(nil) func (c *ConvertExpr) returnUnsupportedError() error { var err error switch { - case c.HasLength && c.HasScale: - err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s(%d,%d)", c.Type, c.Length, c.Scale) - case c.HasLength: - err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s(%d)", c.Type, c.Length) + case c.Length != nil && c.Scale != nil: + err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s(%d,%d)", c.Type, *c.Length, *c.Scale) + case c.Length != nil: + err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s(%d)", c.Type, *c.Length) default: err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s", c.Type) } @@ -58,11 +60,11 @@ func (c *ConvertExpr) returnUnsupportedError() error { func (c *ConvertExpr) decimalPrecision() (int32, int32) { m := 10 d := 0 - if c.HasLength { - m = c.Length + if c.Length != nil { + m = *c.Length } - if c.HasScale { - d = c.Scale + if c.Scale != nil { + d = *c.Scale } if m == 0 && d == 0 { m = 10 @@ -82,8 +84,8 @@ func (c *ConvertExpr) eval(env *ExpressionEnv) (eval, error) { switch c.Type { case "BINARY": b := evalToBinary(e) - if c.HasLength { - b.truncateInPlace(c.Length) + if c.Length != nil { + b.truncateInPlace(*c.Length) } b.tt = int16(c.convertToBinaryType(e.SQLType())) return b, nil @@ -94,8 +96,8 @@ func (c *ConvertExpr) eval(env *ExpressionEnv) (eval, error) { // return NULL on error return nil, nil } - if c.HasLength { - t.truncateInPlace(c.Length) + if c.Length != nil { + t.truncateInPlace(*c.Length) } t.tt = int16(c.convertToCharType(e.SQLType())) return t, nil @@ -106,8 +108,8 @@ func (c *ConvertExpr) eval(env *ExpressionEnv) (eval, error) { f, _ := evalToFloat(e) return f, nil case "FLOAT": - if c.HasLength { - switch p := c.Length; { + if c.Length != nil { + switch p := *c.Length; { case p > 53: return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Too-big precision %d specified for 'CONVERT'. Maximum is 53.", p) } @@ -120,25 +122,25 @@ func (c *ConvertExpr) eval(env *ExpressionEnv) (eval, error) { case "JSON": return evalToJSON(e) case "DATETIME": - switch p := c.Length; { - case p > 6: + p := ptr.Unwrap(c.Length, 0) + if p > 6 { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Too-big precision %d specified for 'CONVERT'. Maximum is 6.", p) } - if dt := evalToDateTime(e, c.Length, env.now); dt != nil { + if dt := evalToDateTime(e, p, env.now, env.sqlmode.AllowZeroDate()); dt != nil { return dt, nil } return nil, nil case "DATE": - if d := evalToDate(e, env.now); d != nil { + if d := evalToDate(e, env.now, env.sqlmode.AllowZeroDate()); d != nil { return d, nil } return nil, nil case "TIME": - switch p := c.Length; { - case p > 6: + p := ptr.Unwrap(c.Length, 0) + if p > 6 { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Too-big precision %d specified for 'CONVERT'. Maximum is 6.", p) } - if t := evalToTime(e, c.Length); t != nil { + if t := evalToTime(e, p); t != nil { return t, nil } return nil, nil @@ -150,8 +152,8 @@ func (c *ConvertExpr) eval(env *ExpressionEnv) (eval, error) { } func (c *ConvertExpr) convertToBinaryType(tt sqltypes.Type) sqltypes.Type { - if c.HasLength { - if c.Length > 64*1024 { + if c.Length != nil { + if *c.Length > 64*1024 { return sqltypes.Blob } } else if tt == sqltypes.Blob || tt == sqltypes.TypeJSON { @@ -161,9 +163,9 @@ func (c *ConvertExpr) convertToBinaryType(tt sqltypes.Type) sqltypes.Type { } func (c *ConvertExpr) convertToCharType(tt sqltypes.Type) sqltypes.Type { - if c.HasLength { + if c.Length != nil { col := colldata.Lookup(c.Collation) - length := c.Length * col.Charset().MaxWidth() + length := *c.Length * col.Charset().MaxWidth() if length > 64*1024 { return sqltypes.Text } @@ -185,25 +187,25 @@ func (conv *ConvertExpr) compile(c *compiler) (ctype, error) { switch conv.Type { case "BINARY": convt = ctype{Type: conv.convertToBinaryType(arg.Type), Col: collationBinary} - c.asm.Convert_xb(1, convt.Type, conv.Length, conv.HasLength) + c.asm.Convert_xb(1, convt.Type, conv.Length) case "CHAR", "NCHAR": convt = ctype{ Type: conv.convertToCharType(arg.Type), Col: collations.TypedCollation{Collation: conv.Collation}, } - c.asm.Convert_xc(1, convt.Type, convt.Col.Collation, conv.Length, conv.HasLength) + c.asm.Convert_xc(1, convt.Type, convt.Col.Collation, conv.Length) case "DECIMAL": - convt = ctype{Type: sqltypes.Decimal, Col: collationNumeric} m, d := conv.decimalPrecision() + convt = ctype{Type: sqltypes.Decimal, Col: collationNumeric, Size: m, Scale: d} c.asm.Convert_xd(1, m, d) case "DOUBLE", "REAL": convt = c.compileToFloat(arg, 1) case "FLOAT": - return ctype{}, c.unsupported(conv) + return ctype{}, conv.returnUnsupportedError() case "SIGNED", "SIGNED INTEGER": convt = c.compileToInt64(arg, 1) @@ -222,18 +224,18 @@ func (conv *ConvertExpr) compile(c *compiler) (ctype, error) { convt = c.compileToDate(arg, 1) case "DATETIME": - switch p := conv.Length; { - case p > 6: + p := ptr.Unwrap(conv.Length, 0) + if p > 6 { return ctype{}, c.unsupported(conv) } - convt = c.compileToDateTime(arg, 1, conv.Length) + convt = c.compileToDateTime(arg, 1, p) case "TIME": - switch p := conv.Length; { - case p > 6: + p := ptr.Unwrap(conv.Length, 0) + if p > 6 { return ctype{}, c.unsupported(conv) } - convt = c.compileToTime(arg, 1, conv.Length) + convt = c.compileToTime(arg, 1, p) default: return ctype{}, c.unsupported(conv) @@ -267,7 +269,7 @@ func (conv *ConvertUsingExpr) compile(c *compiler) (ctype, error) { } skip := c.compileNullCheck1(ct) - c.asm.Convert_xc(1, sqltypes.VarChar, conv.Collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, conv.Collation, nil) c.asm.jumpDestination(skip) col := collations.TypedCollation{ diff --git a/go/vt/vtgate/evalengine/expr_env.go b/go/vt/vtgate/evalengine/expr_env.go index ffcde05d2a0..6e09b03cffb 100644 --- a/go/vt/vtgate/evalengine/expr_env.go +++ b/go/vt/vtgate/evalengine/expr_env.go @@ -21,15 +21,20 @@ import ( "strings" "time" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/datetime" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vtenv" ) type VCursor interface { TimeZone() *time.Location GetKeyspace() string + SQLMode() string + Environment() *vtenv.Environment } type ( @@ -43,9 +48,11 @@ type ( Fields []*querypb.Field // internal state - now time.Time - vc VCursor - user *querypb.VTGateCallerID + now time.Time + vc VCursor + user *querypb.VTGateCallerID + sqlmode SQLMode + collationEnv *collations.Environment } ) @@ -68,16 +75,14 @@ func (env *ExpressionEnv) currentUser() string { } func (env *ExpressionEnv) currentDatabase() string { - if env.vc == nil { - return "" - } return env.vc.GetKeyspace() } +func (env *ExpressionEnv) currentVersion() string { + return env.vc.Environment().MySQLVersion() +} + func (env *ExpressionEnv) currentTimezone() *time.Location { - if env.vc == nil { - return nil - } return env.vc.TimeZone() } @@ -86,12 +91,12 @@ func (env *ExpressionEnv) Evaluate(expr Expr) (EvalResult, error) { return env.EvaluateVM(p) } e, err := expr.eval(env) - return EvalResult{e}, err + return EvalResult{v: e, collationEnv: env.collationEnv}, err } func (env *ExpressionEnv) EvaluateAST(expr Expr) (EvalResult, error) { e, err := expr.eval(env) - return EvalResult{e}, err + return EvalResult{v: e, collationEnv: env.collationEnv}, err } func (env *ExpressionEnv) TypeOf(expr Expr) (Type, error) { @@ -99,11 +104,7 @@ func (env *ExpressionEnv) TypeOf(expr Expr) (Type, error) { if err != nil { return Type{}, err } - return Type{ - Type: ty.Type, - Coll: ty.Col.Collation, - Nullable: ty.Flag&flagNullable != 0, - }, nil + return NewTypeEx(ty.Type, ty.Col.Collation, ty.Flag&flagNullable != 0, ty.Size, ty.Scale), nil } func (env *ExpressionEnv) SetTime(now time.Time) { @@ -115,9 +116,38 @@ func (env *ExpressionEnv) SetTime(now time.Time) { } } +func (env *ExpressionEnv) VCursor() VCursor { + return env.vc +} + +type emptyVCursor struct { + env *vtenv.Environment + tz *time.Location +} + +func (e *emptyVCursor) Environment() *vtenv.Environment { + return e.env +} + +func (e *emptyVCursor) TimeZone() *time.Location { + return e.tz +} + +func (e *emptyVCursor) GetKeyspace() string { + return "" +} + +func (e *emptyVCursor) SQLMode() string { + return config.DefaultSQLMode +} + +func NewEmptyVCursor(env *vtenv.Environment, tz *time.Location) VCursor { + return &emptyVCursor{env: env, tz: tz} +} + // EmptyExpressionEnv returns a new ExpressionEnv with no bind vars or row -func EmptyExpressionEnv() *ExpressionEnv { - return NewExpressionEnv(context.Background(), nil, nil) +func EmptyExpressionEnv(env *vtenv.Environment) *ExpressionEnv { + return NewExpressionEnv(context.Background(), nil, NewEmptyVCursor(env, time.Local)) } // NewExpressionEnv returns an expression environment with no current row, but with bindvars @@ -125,5 +155,31 @@ func NewExpressionEnv(ctx context.Context, bindVars map[string]*querypb.BindVari env := &ExpressionEnv{BindVars: bindVars, vc: vc} env.user = callerid.ImmediateCallerIDFromContext(ctx) env.SetTime(time.Now()) + env.sqlmode = ParseSQLMode(vc.SQLMode()) + env.collationEnv = vc.Environment().CollationEnv() return env } + +const ( + sqlModeParsed = 1 << iota + sqlModeNoZeroDate +) + +type SQLMode uint32 + +func (mode SQLMode) AllowZeroDate() bool { + if mode == 0 { + // default: do not allow zero-date if the sqlmode is not set + return false + } + return (mode & sqlModeNoZeroDate) == 0 +} + +func ParseSQLMode(sqlmode string) SQLMode { + var mode SQLMode + if strings.Contains(sqlmode, "NO_ZERO_DATE") { + mode |= sqlModeNoZeroDate + } + mode |= sqlModeParsed + return mode +} diff --git a/go/vt/vtgate/evalengine/expr_literal.go b/go/vt/vtgate/evalengine/expr_literal.go index 5058c157229..2291356fcc7 100644 --- a/go/vt/vtgate/evalengine/expr_literal.go +++ b/go/vt/vtgate/evalengine/expr_literal.go @@ -70,6 +70,10 @@ func (l *Literal) typeof(*ExpressionEnv) (ctype, error) { if e.u > math.MaxInt64+1 { f |= flagIntegerOvf } + case *evalTemporal: + return ctype{Type: e.t, Col: collationNumeric, Size: int32(e.prec)}, nil + case *evalDecimal: + return ctype{Type: sqltypes.Decimal, Col: collationNumeric, Size: e.length, Scale: -e.dec.Exponent()}, nil } return ctype{Type: l.inner.SQLType(), Flag: f, Col: evalCollation(l.inner)}, nil } diff --git a/go/vt/vtgate/evalengine/expr_logical.go b/go/vt/vtgate/evalengine/expr_logical.go index 9d2f17becec..ef59616b97c 100644 --- a/go/vt/vtgate/evalengine/expr_logical.go +++ b/go/vt/vtgate/evalengine/expr_logical.go @@ -17,7 +17,6 @@ limitations under the License. package evalengine import ( - "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" ) @@ -379,7 +378,7 @@ func (expr *NotExpr) compile(c *compiler) (ctype, error) { c.asm.Not_i() } c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: flagNullable | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func (l *LogicalExpr) eval(env *ExpressionEnv) (eval, error) { @@ -450,7 +449,7 @@ func (expr *LogicalExpr) compile(c *compiler) (ctype, error) { expr.op.compileRight(c) c.asm.jumpDestination(jump) - return ctype{Type: sqltypes.Int64, Flag: flagNullable | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: ((lt.Flag | rt.Flag) & flagNullable) | flagIsBoolean, Col: collationNumeric}, nil } func intervalCompare(n, val eval) (int, bool, error) { @@ -586,7 +585,6 @@ func (is *IsExpr) compile(c *compiler) (ctype, error) { func (c *CaseExpr) eval(env *ExpressionEnv) (eval, error) { var ta typeAggregation var ca collationAggregation - var local = collations.Local() var result eval var matched = false @@ -606,7 +604,7 @@ func (c *CaseExpr) eval(env *ExpressionEnv) (eval, error) { return nil, err } ta.addEval(then) - if err := ca.add(local, evalCollation(then)); err != nil { + if err := ca.add(evalCollation(then), env.collationEnv); err != nil { return nil, err } @@ -621,7 +619,7 @@ func (c *CaseExpr) eval(env *ExpressionEnv) (eval, error) { return nil, err } ta.addEval(e) - if err := ca.add(local, evalCollation(e)); err != nil { + if err := ca.add(evalCollation(e), env.collationEnv); err != nil { return nil, err } if !matched { @@ -633,7 +631,7 @@ func (c *CaseExpr) eval(env *ExpressionEnv) (eval, error) { if !matched { return nil, nil } - return evalCoerce(result, ta.result(), ca.result().Collation, env.now) + return evalCoerce(result, ta.result(), ca.result().Collation, env.now, env.sqlmode.AllowZeroDate()) } func (c *CaseExpr) constant() bool { @@ -676,7 +674,6 @@ func (c *CaseExpr) simplify(env *ExpressionEnv) error { func (cs *CaseExpr) compile(c *compiler) (ctype, error) { var ca collationAggregation var ta typeAggregation - var local = collations.Local() for _, wt := range cs.cases { when, err := wt.when.compile(c) @@ -694,7 +691,7 @@ func (cs *CaseExpr) compile(c *compiler) (ctype, error) { } ta.add(then.Type, then.Flag) - if err := ca.add(local, then.Col); err != nil { + if err := ca.add(then.Col, c.env.CollationEnv()); err != nil { return ctype{}, err } } @@ -706,13 +703,17 @@ func (cs *CaseExpr) compile(c *compiler) (ctype, error) { } ta.add(els.Type, els.Flag) - if err := ca.add(local, els.Col); err != nil { + if err := ca.add(els.Col, c.env.CollationEnv()); err != nil { return ctype{}, err } } - ct := ctype{Type: ta.result(), Col: ca.result()} - c.asm.CmpCase(len(cs.cases), cs.Else != nil, ct.Type, ct.Col) + var f typeFlag + if ta.nullable { + f |= flagNullable + } + ct := ctype{Type: ta.result(), Flag: f, Col: ca.result()} + c.asm.CmpCase(len(cs.cases), cs.Else != nil, ct.Type, ct.Col, c.sqlmode.AllowZeroDate()) return ct, nil } diff --git a/go/vt/vtgate/evalengine/expr_tuple.go b/go/vt/vtgate/evalengine/expr_tuple.go index 132d38108a0..d4943271ccb 100644 --- a/go/vt/vtgate/evalengine/expr_tuple.go +++ b/go/vt/vtgate/evalengine/expr_tuple.go @@ -66,5 +66,5 @@ func (tuple TupleExpr) FormatFast(buf *sqlparser.TrackedBuffer) { } func (tuple TupleExpr) typeof(*ExpressionEnv) (ctype, error) { - return ctype{Type: sqltypes.Tuple}, nil + return ctype{Type: sqltypes.Tuple, Col: collationBinary}, nil } diff --git a/go/vt/vtgate/evalengine/expr_tuple_bvar.go b/go/vt/vtgate/evalengine/expr_tuple_bvar.go new file mode 100644 index 00000000000..b8e506eaff5 --- /dev/null +++ b/go/vt/vtgate/evalengine/expr_tuple_bvar.go @@ -0,0 +1,110 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package evalengine + +import ( + "errors" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" +) + +type ( + TupleBindVariable struct { + Key string + + Index int + Type sqltypes.Type + Collation collations.ID + + // dynamicTypeOffset is set when the type of this bind variable cannot be calculated + // at translation time. Since expressions with dynamic types cannot be compiled ahead of time, + // compilation will be delayed until the expression is first executed with the bind variables + // sent by the user. See: UntypedExpr + dynamicTypeOffset int + } +) + +var _ IR = (*TupleBindVariable)(nil) +var _ Expr = (*TupleBindVariable)(nil) + +func (bv *TupleBindVariable) IR() IR { + return bv +} + +func (bv *TupleBindVariable) IsExpr() {} + +// eval implements the expression interface +func (bv *TupleBindVariable) eval(env *ExpressionEnv) (eval, error) { + bvar, err := env.lookupBindVar(bv.Key) + if err != nil { + return nil, err + } + + if bvar.Type != sqltypes.Tuple { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "query argument '%s' must be a tuple (is %s)", bv.Key, bvar.Type.String()) + } + + tuple := make([]eval, 0, len(bvar.Values)) + for _, value := range bvar.Values { + if value.Type != sqltypes.Tuple { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "result value must be a tuple (is %s)", value.Type.String()) + } + sValue := sqltypes.ProtoToValue(value) + var evalErr error + idx := 0 + found := false + // looking for a single index on each Tuple Value. + loopErr := sValue.ForEachValue(func(val sqltypes.Value) { + if found || idx != bv.Index { + idx++ + return + } + found = true + e, err := valueToEval(val, typedCoercionCollation(val.Type(), collations.CollationForType(val.Type(), bv.Collation))) + if err != nil { + evalErr = err + return + } + tuple = append(tuple, e) + + }) + if err = errors.Join(loopErr, evalErr); err != nil { + return nil, err + } + if !found { + return nil, vterrors.VT13001("value not found in the bind variable") + } + } + return &evalTuple{t: tuple}, nil +} + +// typeof implements the expression interface +func (bv *TupleBindVariable) typeof(env *ExpressionEnv) (ctype, error) { + _, err := env.lookupBindVar(bv.Key) + if err != nil { + return ctype{}, err + } + + return ctype{Type: sqltypes.Tuple}, nil +} + +func (bv *TupleBindVariable) compile(c *compiler) (ctype, error) { + return ctype{}, c.unsupported(bv) +} diff --git a/go/vt/vtgate/evalengine/expr_tuple_bvar_test.go b/go/vt/vtgate/evalengine/expr_tuple_bvar_test.go new file mode 100644 index 00000000000..cad4d030d1a --- /dev/null +++ b/go/vt/vtgate/evalengine/expr_tuple_bvar_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package evalengine + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" +) + +// TestTupleBindVarEval tests TupleBindVariable eval function. +func TestTupleBindVarEval(t *testing.T) { + key := "vals" + c := &TupleBindVariable{ + Key: key, + Index: 1, + } + collation := collations.TypedCollation{ + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireUnicode, + } + + tcases := []struct { + tName string + bv *querypb.BindVariable + + expEval []eval + expErr string + }{{ + tName: "bind variable not provided", + expErr: "query arguments missing for vals", + }, { + tName: "bind variable provided - wrong type", + bv: sqltypes.Int64BindVariable(1), + expErr: "query argument 'vals' must be a tuple (is INT64)", + }, { + tName: "bind variable provided", + bv: &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: []*querypb.Value{sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a")))}, + }, + expEval: []eval{newEvalText([]byte("a"), collation)}, + }, { + tName: "bind variable provided - multi values", + bv: &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: []*querypb.Value{ + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a"))), + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(2), sqltypes.NewVarChar("b"))), + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(3), sqltypes.NewVarChar("c"))), + }, + }, + expEval: []eval{ + newEvalText([]byte("a"), collation), + newEvalText([]byte("b"), collation), + newEvalText([]byte("c"), collation)}, + }} + + for _, tcase := range tcases { + t.Run(tcase.tName, func(t *testing.T) { + env := &ExpressionEnv{ + BindVars: make(map[string]*querypb.BindVariable), + } + if tcase.bv != nil { + env.BindVars[key] = tcase.bv + } + + res, err := c.eval(env) + if tcase.expErr != "" { + require.ErrorContains(t, err, tcase.expErr) + return + } + require.Equal(t, sqltypes.Tuple, res.SQLType()) + resTuple := res.(*evalTuple) + require.Len(t, resTuple.t, len(tcase.expEval)) + for idx, e := range tcase.expEval { + require.Equal(t, e, resTuple.t[idx]) + } + }) + } +} + +// TestTupleBindVarTypeOf tests TupleBindVariable typeOf function. +func TestTupleBindVarTypeOf(t *testing.T) { + key := "vals" + c := &TupleBindVariable{ + Key: key, + Index: 1, + } + + tcases := []struct { + tName string + bv *querypb.BindVariable + + expErr string + }{{ + tName: "bind variable not provided", + expErr: "query arguments missing for vals", + }, { + // typeOf does not evaluate the bind variable value + tName: "bind variable provided - wrong type", + bv: sqltypes.Int64BindVariable(1), + }, { + tName: "bind variable provided", + bv: &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: []*querypb.Value{sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a")))}, + }, + }, { + tName: "bind variable provided - multi values", + bv: &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: []*querypb.Value{ + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a"))), + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(2), sqltypes.NewVarChar("b"))), + sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(3), sqltypes.NewVarChar("c"))), + }, + }, + }} + + for _, tcase := range tcases { + t.Run(tcase.tName, func(t *testing.T) { + env := &ExpressionEnv{ + BindVars: make(map[string]*querypb.BindVariable), + } + if tcase.bv != nil { + env.BindVars[key] = tcase.bv + } + + res, err := c.typeof(env) + if tcase.expErr != "" { + require.ErrorContains(t, err, tcase.expErr) + return + } + require.Equal(t, sqltypes.Tuple, res.Type) + }) + } +} diff --git a/go/vt/vtgate/evalengine/fn_base64.go b/go/vt/vtgate/evalengine/fn_base64.go index d404d391dd6..77baf060eb9 100644 --- a/go/vt/vtgate/evalengine/fn_base64.go +++ b/go/vt/vtgate/evalengine/fn_base64.go @@ -103,14 +103,14 @@ func (call *builtinToBase64) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, t, 0, false) + c.asm.Convert_xb(1, t, nil) } col := typedCoercionCollation(t, c.collation) c.asm.Fn_TO_BASE64(t, col) c.asm.jumpDestination(skip) - return ctype{Type: t, Col: col}, nil + return ctype{Type: t, Flag: nullableFlags(str.Flag), Col: col}, nil } func (call *builtinFromBase64) eval(env *ExpressionEnv) (eval, error) { @@ -149,11 +149,11 @@ func (call *builtinFromBase64) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, t, 0, false) + c.asm.Convert_xb(1, t, nil) } c.asm.Fn_FROM_BASE64(t) c.asm.jumpDestination(skip) - return ctype{Type: t, Col: collationBinary}, nil + return ctype{Type: t, Flag: nullableFlags(str.Flag), Col: collationBinary}, nil } diff --git a/go/vt/vtgate/evalengine/fn_bit.go b/go/vt/vtgate/evalengine/fn_bit.go index 66edffe268f..9444ee086af 100644 --- a/go/vt/vtgate/evalengine/fn_bit.go +++ b/go/vt/vtgate/evalengine/fn_bit.go @@ -61,11 +61,11 @@ func (expr *builtinBitCount) compile(c *compiler) (ctype, error) { if ct.Type == sqltypes.VarBinary && !ct.isHexOrBitLiteral() { c.asm.BitCount_b() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationBinary}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(ct.Flag), Col: collationBinary}, nil } _ = c.compileToBitwiseUint64(ct, 1) c.asm.BitCount_u() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationBinary}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(ct.Flag), Col: collationBinary}, nil } diff --git a/go/vt/vtgate/evalengine/fn_compare.go b/go/vt/vtgate/evalengine/fn_compare.go index cf40deae94b..1303ac7614d 100644 --- a/go/vt/vtgate/evalengine/fn_compare.go +++ b/go/vt/vtgate/evalengine/fn_compare.go @@ -32,7 +32,7 @@ type ( CallExpr } - multiComparisonFunc func(args []eval, cmp int) (eval, error) + multiComparisonFunc func(collationEnv *collations.Environment, args []eval, cmp int) (eval, error) builtinMultiComparison struct { CallExpr @@ -58,18 +58,21 @@ func (b *builtinCoalesce) eval(env *ExpressionEnv) (eval, error) { func (b *builtinCoalesce) compile(c *compiler) (ctype, error) { var ( - ta typeAggregation - ca collationAggregation - local = collations.Local() + ta typeAggregation + ca collationAggregation ) + f := flagNullable for _, arg := range b.Arguments { tt, err := arg.compile(c) if err != nil { return ctype{}, err } + if !tt.nullable() { + f = 0 + } ta.add(tt.Type, tt.Flag) - if err := ca.add(local, tt.Col); err != nil { + if err := ca.add(tt.Col, c.env.CollationEnv()); err != nil { return ctype{}, err } } @@ -87,7 +90,7 @@ func (b *builtinCoalesce) compile(c *compiler) (ctype, error) { return 1 }, "COALESCE (SP-%d) ... (SP-1)", args) - return ctype{Type: ta.result(), Flag: flagNullable, Col: ca.result()}, nil + return ctype{Type: ta.result(), Flag: f, Col: ca.result()}, nil } func getMultiComparisonFunc(args []eval) multiComparisonFunc { @@ -111,7 +114,7 @@ func getMultiComparisonFunc(args []eval) multiComparisonFunc { for _, arg := range args { if arg == nil { - return func(args []eval, cmp int) (eval, error) { + return func(collationEnv *collations.Environment, args []eval, cmp int) (eval, error) { return nil, nil } } @@ -162,7 +165,7 @@ func getMultiComparisonFunc(args []eval) multiComparisonFunc { panic("unexpected argument type") } -func compareAllInteger_u(args []eval, cmp int) (eval, error) { +func compareAllInteger_u(_ *collations.Environment, args []eval, cmp int) (eval, error) { x := args[0].(*evalUint64) for _, arg := range args[1:] { y := arg.(*evalUint64) @@ -173,7 +176,7 @@ func compareAllInteger_u(args []eval, cmp int) (eval, error) { return x, nil } -func compareAllInteger_i(args []eval, cmp int) (eval, error) { +func compareAllInteger_i(_ *collations.Environment, args []eval, cmp int) (eval, error) { x := args[0].(*evalInt64) for _, arg := range args[1:] { y := arg.(*evalInt64) @@ -184,7 +187,7 @@ func compareAllInteger_i(args []eval, cmp int) (eval, error) { return x, nil } -func compareAllFloat(args []eval, cmp int) (eval, error) { +func compareAllFloat(_ *collations.Environment, args []eval, cmp int) (eval, error) { candidateF, ok := evalToFloat(args[0]) if !ok { return nil, errDecimalOutOfRange @@ -209,7 +212,7 @@ func evalDecimalPrecision(e eval) int32 { return 0 } -func compareAllDecimal(args []eval, cmp int) (eval, error) { +func compareAllDecimal(_ *collations.Environment, args []eval, cmp int) (eval, error) { decExtreme := evalToDecimal(args[0], 0, 0).dec precExtreme := evalDecimalPrecision(args[0]) @@ -226,14 +229,12 @@ func compareAllDecimal(args []eval, cmp int) (eval, error) { return newEvalDecimalWithPrec(decExtreme, precExtreme), nil } -func compareAllText(args []eval, cmp int) (eval, error) { - env := collations.Local() - +func compareAllText(collationEnv *collations.Environment, args []eval, cmp int) (eval, error) { var charsets = make([]charset.Charset, 0, len(args)) var ca collationAggregation for _, arg := range args { col := evalCollation(arg) - if err := ca.add(env, col); err != nil { + if err := ca.add(col, collationEnv); err != nil { return nil, err } charsets = append(charsets, colldata.Lookup(col.Collation).Charset()) @@ -261,7 +262,7 @@ func compareAllText(args []eval, cmp int) (eval, error) { return newEvalText(b1, tc), nil } -func compareAllBinary(args []eval, cmp int) (eval, error) { +func compareAllBinary(_ *collations.Environment, args []eval, cmp int) (eval, error) { candidateB := args[0].ToRawBytes() for _, arg := range args[1:] { @@ -279,30 +280,32 @@ func (call *builtinMultiComparison) eval(env *ExpressionEnv) (eval, error) { if err != nil { return nil, err } - return getMultiComparisonFunc(args)(args, call.cmp) + return getMultiComparisonFunc(args)(env.collationEnv, args, call.cmp) } func (call *builtinMultiComparison) compile_c(c *compiler, args []ctype) (ctype, error) { - env := collations.Local() - var ca collationAggregation + var f typeFlag for _, arg := range args { - if err := ca.add(env, arg.Col); err != nil { + f |= nullableFlags(arg.Flag) + if err := ca.add(arg.Col, c.env.CollationEnv()); err != nil { return ctype{}, err } } tc := ca.result() c.asm.Fn_MULTICMP_c(len(args), call.cmp < 0, tc) - return ctype{Type: sqltypes.VarChar, Col: tc}, nil + return ctype{Type: sqltypes.VarChar, Flag: f, Col: tc}, nil } func (call *builtinMultiComparison) compile_d(c *compiler, args []ctype) (ctype, error) { + var f typeFlag for i, tt := range args { + f |= nullableFlags(tt.Flag) c.compileToDecimal(tt, len(args)-i) } c.asm.Fn_MULTICMP_d(len(args), call.cmp < 0) - return ctype{Type: sqltypes.Decimal, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Decimal, Flag: f, Col: collationNumeric}, nil } func (call *builtinMultiComparison) compile(c *compiler) (ctype, error) { @@ -314,6 +317,7 @@ func (call *builtinMultiComparison) compile(c *compiler) (ctype, error) { text int binary int args []ctype + nullable bool ) /* @@ -333,6 +337,7 @@ func (call *builtinMultiComparison) compile(c *compiler) (ctype, error) { args = append(args, tt) + nullable = nullable || tt.nullable() switch tt.Type { case sqltypes.Int64: signed++ @@ -346,19 +351,25 @@ func (call *builtinMultiComparison) compile(c *compiler) (ctype, error) { text++ case sqltypes.Blob, sqltypes.Binary, sqltypes.VarBinary: binary++ + case sqltypes.Null: + nullable = true default: - return ctype{}, c.unsupported(call) + panic("unexpected argument type") } } + var f typeFlag + if nullable { + f |= flagNullable + } if signed+unsigned == len(args) { if signed == len(args) { c.asm.Fn_MULTICMP_i(len(args), call.cmp < 0) - return ctype{Type: sqltypes.Int64, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: f, Col: collationNumeric}, nil } if unsigned == len(args) { c.asm.Fn_MULTICMP_u(len(args), call.cmp < 0) - return ctype{Type: sqltypes.Uint64, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Uint64, Flag: f, Col: collationNumeric}, nil } return call.compile_d(c, args) } @@ -367,14 +378,14 @@ func (call *builtinMultiComparison) compile(c *compiler) (ctype, error) { return call.compile_c(c, args) } c.asm.Fn_MULTICMP_b(len(args), call.cmp < 0) - return ctype{Type: sqltypes.VarBinary, Col: collationBinary}, nil + return ctype{Type: sqltypes.VarBinary, Flag: f, Col: collationBinary}, nil } else { if floats > 0 { for i, tt := range args { c.compileToFloat(tt, len(args)-i) } c.asm.Fn_MULTICMP_f(len(args), call.cmp < 0) - return ctype{Type: sqltypes.Float64, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Float64, Flag: f, Col: collationNumeric}, nil } if decimals > 0 { return call.compile_d(c, args) diff --git a/go/vt/vtgate/evalengine/fn_crypto.go b/go/vt/vtgate/evalengine/fn_crypto.go index 31783291ce7..8a3765028d9 100644 --- a/go/vt/vtgate/evalengine/fn_crypto.go +++ b/go/vt/vtgate/evalengine/fn_crypto.go @@ -62,13 +62,13 @@ func (call *builtinMD5) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.Binary, 0, false) + c.asm.Convert_xb(1, sqltypes.Binary, nil) } col := typedCoercionCollation(sqltypes.VarChar, c.collation) c.asm.Fn_MD5(col) c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.VarChar, Col: col, Flag: str.Flag}, nil + return ctype{Type: sqltypes.VarChar, Col: col, Flag: nullableFlags(str.Flag)}, nil } type builtinSHA1 struct { @@ -105,12 +105,12 @@ func (call *builtinSHA1) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.Binary, 0, false) + c.asm.Convert_xb(1, sqltypes.Binary, nil) } col := typedCoercionCollation(sqltypes.VarChar, c.collation) c.asm.Fn_SHA1(col) c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.VarChar, Col: col, Flag: str.Flag}, nil + return ctype{Type: sqltypes.VarChar, Col: col, Flag: nullableFlags(str.Flag)}, nil } type builtinSHA2 struct { @@ -174,7 +174,7 @@ func (call *builtinSHA2) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(2, sqltypes.Binary, 0, false) + c.asm.Convert_xb(2, sqltypes.Binary, nil) } switch bits.Type { @@ -189,7 +189,7 @@ func (call *builtinSHA2) compile(c *compiler) (ctype, error) { col := typedCoercionCollation(sqltypes.VarChar, c.collation) c.asm.Fn_SHA2(col) c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sqltypes.VarChar, Col: col, Flag: str.Flag | flagNullable}, nil + return ctype{Type: sqltypes.VarChar, Col: col, Flag: nullableFlags(str.Flag)}, nil } type builtinRandomBytes struct { @@ -244,5 +244,5 @@ func (call *builtinRandomBytes) compile(c *compiler) (ctype, error) { c.asm.Fn_RandomBytes() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.VarBinary, Col: collationBinary, Flag: arg.Flag | flagNullable}, nil + return ctype{Type: sqltypes.VarBinary, Col: collationBinary, Flag: nullableFlags(arg.Flag) | flagNullable}, nil } diff --git a/go/vt/vtgate/evalengine/fn_hex.go b/go/vt/vtgate/evalengine/fn_hex.go index 8552ab888ae..90d3bd1a208 100644 --- a/go/vt/vtgate/evalengine/fn_hex.go +++ b/go/vt/vtgate/evalengine/fn_hex.go @@ -73,13 +73,13 @@ func (call *builtinHex) compile(c *compiler) (ctype, error) { case str.isTextual(): c.asm.Fn_HEX_c(t, col) default: - c.asm.Convert_xc(1, t, c.collation, 0, false) + c.asm.Convert_xc(1, t, c.collation, nil) c.asm.Fn_HEX_c(t, col) } c.asm.jumpDestination(skip) - return ctype{Type: t, Col: col}, nil + return ctype{Type: t, Flag: nullableFlags(str.Flag), Col: col}, nil } type builtinUnhex struct { @@ -191,7 +191,7 @@ func (call *builtinUnhex) compile(c *compiler) (ctype, error) { case str.Type == sqltypes.TypeJSON: c.asm.Fn_UNHEX_j(t) default: - c.asm.Convert_xb(1, t, 0, false) + c.asm.Convert_xb(1, t, nil) c.asm.Fn_UNHEX_b(t) } diff --git a/go/vt/vtgate/evalengine/fn_info.go b/go/vt/vtgate/evalengine/fn_info.go index d8a8aa41947..9380d58fca7 100644 --- a/go/vt/vtgate/evalengine/fn_info.go +++ b/go/vt/vtgate/evalengine/fn_info.go @@ -18,7 +18,6 @@ package evalengine import ( "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/servenv" ) type builtinUser struct { @@ -47,7 +46,7 @@ type builtinVersion struct { var _ IR = (*builtinVersion)(nil) func (call *builtinVersion) eval(env *ExpressionEnv) (eval, error) { - return newEvalText([]byte(servenv.MySQLServerVersion()), collationUtf8mb3), nil + return newEvalText([]byte(env.currentVersion()), collationUtf8mb3), nil } func (*builtinVersion) compile(c *compiler) (ctype, error) { diff --git a/go/vt/vtgate/evalengine/fn_json.go b/go/vt/vtgate/evalengine/fn_json.go index 53930b4678b..54038e28339 100644 --- a/go/vt/vtgate/evalengine/fn_json.go +++ b/go/vt/vtgate/evalengine/fn_json.go @@ -402,7 +402,7 @@ func (call *builtinJSONContainsPath) compile(c *compiler) (ctype, error) { } c.asm.Fn_JSON_CONTAINS_PATH(match, paths) - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean}, nil + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagIsBoolean | flagNullable}, nil } type jsonMatch int8 diff --git a/go/vt/vtgate/evalengine/fn_misc.go b/go/vt/vtgate/evalengine/fn_misc.go index 2f228ff55fa..8813b62f823 100644 --- a/go/vt/vtgate/evalengine/fn_misc.go +++ b/go/vt/vtgate/evalengine/fn_misc.go @@ -120,7 +120,7 @@ func (call *builtinInetAton) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_INET_ATON() @@ -185,7 +185,7 @@ func (call *builtinInet6Aton) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_INET6_ATON() @@ -291,13 +291,13 @@ func (call *builtinIsIPV4) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_IS_IPV4() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: arg.Flag | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func (call *builtinIsIPV4Compat) eval(env *ExpressionEnv) (eval, error) { @@ -328,7 +328,7 @@ func (call *builtinIsIPV4Compat) compile(c *compiler) (ctype, error) { c.asm.SetBool(1, false) } c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: arg.Flag | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func (call *builtinIsIPV4Mapped) eval(env *ExpressionEnv) (eval, error) { @@ -359,7 +359,7 @@ func (call *builtinIsIPV4Mapped) compile(c *compiler) (ctype, error) { c.asm.SetBool(1, false) } c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: arg.Flag | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func (call *builtinIsIPV6) eval(env *ExpressionEnv) (eval, error) { @@ -385,13 +385,13 @@ func (call *builtinIsIPV6) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_IS_IPV6() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: arg.Flag | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func errIncorrectUUID(in []byte, f string) error { @@ -459,11 +459,11 @@ func (call *builtinBinToUUID) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } col := typedCoercionCollation(sqltypes.VarChar, call.collate) - ct := ctype{Type: sqltypes.VarChar, Flag: arg.Flag, Col: col} + ct := ctype{Type: sqltypes.VarChar, Flag: nullableFlags(arg.Flag), Col: col} if len(call.Arguments) == 1 { c.asm.Fn_BIN_TO_UUID0(col) @@ -512,12 +512,12 @@ func (call *builtinIsUUID) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_IS_UUID() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Flag: arg.Flag | flagIsBoolean, Col: collationNumeric}, nil + return ctype{Type: sqltypes.Int64, Flag: nullableFlags(arg.Flag) | flagIsBoolean, Col: collationNumeric}, nil } func (call *builtinUUID) eval(env *ExpressionEnv) (eval, error) { @@ -580,10 +580,10 @@ func (call *builtinUUIDToBin) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } - ct := ctype{Type: sqltypes.VarBinary, Flag: arg.Flag, Col: collationBinary} + ct := ctype{Type: sqltypes.VarBinary, Flag: nullableFlags(arg.Flag), Col: collationBinary} if len(call.Arguments) == 1 { c.asm.Fn_UUID_TO_BIN0() diff --git a/go/vt/vtgate/evalengine/fn_numeric.go b/go/vt/vtgate/evalengine/fn_numeric.go index 7bdd8d8b92e..bd835d88d78 100644 --- a/go/vt/vtgate/evalengine/fn_numeric.go +++ b/go/vt/vtgate/evalengine/fn_numeric.go @@ -149,7 +149,7 @@ func (expr *builtinAbs) compile(c *compiler) (ctype, error) { skip := c.compileNullCheck1(arg) - convt := ctype{Type: arg.Type, Col: collationNumeric, Flag: arg.Flag} + convt := ctype{Type: arg.Type, Col: collationNumeric, Flag: nullableFlags(arg.Flag)} switch arg.Type { case sqltypes.Int64: c.asm.Fn_ABS_i() @@ -302,7 +302,7 @@ func (expr *builtinAtan2) compile(c *compiler) (ctype, error) { c.compileToFloat(arg2, 1) c.asm.Fn_ATAN2() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: arg1.Flag | arg2.Flag}, nil + return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: nullableFlags(arg1.Flag | arg2.Flag)}, nil } type builtinCos struct { @@ -538,7 +538,7 @@ func (expr *builtinLog) compile(c *compiler) (ctype, error) { c.compileToFloat(arg2, 1) c.asm.Fn_LOG() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: arg1.Flag | arg2.Flag}, nil + return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: nullableFlags(arg1.Flag | arg2.Flag)}, nil } type builtinLog10 struct { @@ -638,7 +638,7 @@ func (expr *builtinPow) compile(c *compiler) (ctype, error) { c.compileToFloat(arg2, 1) c.asm.Fn_POW() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: arg1.Flag | arg2.Flag | flagNullable}, nil + return ctype{Type: sqltypes.Float64, Col: collationNumeric, Flag: nullableFlags(arg1.Flag | arg2.Flag)}, nil } type builtinSign struct { @@ -718,7 +718,7 @@ func (expr *builtinSign) compile(c *compiler) (ctype, error) { } c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag}, nil + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: nullableFlags(arg.Flag)}, nil } type builtinSqrt struct { @@ -1267,7 +1267,7 @@ func (expr *builtinCrc32) compile(c *compiler) (ctype, error) { switch { case arg.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.Binary, 0, false) + c.asm.Convert_xb(1, sqltypes.Binary, nil) } c.asm.Fn_CRC32() @@ -1332,7 +1332,7 @@ func (call *builtinConv) eval(env *ExpressionEnv) (eval, error) { i, err := fastparse.ParseInt64(nStr.string(), int(fromBase)) u = uint64(i) if errors.Is(err, fastparse.ErrOverflow) { - u, _ = fastparse.ParseUint64(nStr.string(), int(fromBase)) + u, _ = fastparse.ParseUint64WithNeg(nStr.string(), int(fromBase)) } } @@ -1374,7 +1374,7 @@ func (expr *builtinConv) compile(c *compiler) (ctype, error) { switch { case n.isTextual(): default: - c.asm.Convert_xb(3, t, 0, false) + c.asm.Convert_xb(3, t, nil) } if n.isHexOrBitLiteral() { diff --git a/go/vt/vtgate/evalengine/fn_regexp.go b/go/vt/vtgate/evalengine/fn_regexp.go index 4897ba63f6a..a94b9a83aee 100644 --- a/go/vt/vtgate/evalengine/fn_regexp.go +++ b/go/vt/vtgate/evalengine/fn_regexp.go @@ -91,7 +91,7 @@ func position(val *evalInt64, limit int64, f string) (int64, error) { return pos, nil } -func evalRegexpCollation(input, pat eval, f string) (eval, eval, collations.TypedCollation, icuregex.RegexpFlag, error) { +func evalRegexpCollation(env *collations.Environment, input, pat eval, f string) (eval, eval, collations.TypedCollation, icuregex.RegexpFlag, error) { var typedCol collations.TypedCollation var err error @@ -101,7 +101,6 @@ func evalRegexpCollation(input, pat eval, f string) (eval, eval, collations.Type patCol := patBytes.col.Collation if (inputCol == collations.CollationBinaryID && patCol != collations.CollationBinaryID) || (inputCol != collations.CollationBinaryID && patCol == collations.CollationBinaryID) { - env := collations.Local() inputColName := env.LookupName(inputCol) patColName := env.LookupName(patCol) return nil, nil, typedCol, 0, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.CharacterSetMismatch, "Character set '%s' cannot be used in conjunction with '%s' in call to %s.", inputColName, patColName, f) @@ -109,13 +108,13 @@ func evalRegexpCollation(input, pat eval, f string) (eval, eval, collations.Type } } - input, pat, typedCol, err = mergeAndCoerceCollations(input, pat) + input, pat, typedCol, err = mergeAndCoerceCollations(input, pat, env) if err != nil { return nil, nil, collations.TypedCollation{}, 0, err } var flags icuregex.RegexpFlag - var collation = collations.Local().LookupName(typedCol.Collation) + collation := env.LookupName(typedCol.Collation) if strings.Contains(collation, "_ci") { flags |= icuregex.CaseInsensitive } @@ -123,11 +122,10 @@ func evalRegexpCollation(input, pat eval, f string) (eval, eval, collations.Type return input, pat, typedCol, flags, nil } -func compileRegexpCollation(input, pat ctype, f string) (collations.TypedCollation, icuregex.RegexpFlag, error) { +func compileRegexpCollation(env *collations.Environment, input, pat ctype, f string) (collations.TypedCollation, icuregex.RegexpFlag, error) { var merged collations.TypedCollation var err error - env := collations.Local() if input.isTextual() && pat.isTextual() { inputCol := input.Col.Collation patCol := pat.Col.Collation @@ -140,7 +138,7 @@ func compileRegexpCollation(input, pat ctype, f string) (collations.TypedCollati } if input.Col.Collation != pat.Col.Collation { - merged, _, _, err = mergeCollations(input.Col, pat.Col, input.Type, pat.Type) + merged, _, _, err = mergeCollations(input.Col, pat.Col, input.Type, pat.Type, env) } else { merged = input.Col } @@ -212,13 +210,15 @@ func compileRegex(pat eval, c colldata.Charset, flags icuregex.RegexpFlag) (*icu return nil, err } +var errNonConstantRegexp = errors.New("non-constant regexp") + func compileConstantRegex(c *compiler, args TupleExpr, pat, mt int, cs collations.TypedCollation, flags icuregex.RegexpFlag, f string) (*icuregex.Pattern, error) { pattern := args[pat] if !pattern.constant() { - return nil, c.unsupported(pattern) + return nil, errNonConstantRegexp } var err error - staticEnv := EmptyExpressionEnv() + staticEnv := EmptyExpressionEnv(c.env) pattern, err = simplifyExpr(staticEnv, pattern) if err != nil { return nil, err @@ -227,7 +227,7 @@ func compileConstantRegex(c *compiler, args TupleExpr, pat, mt int, cs collation if len(args) > mt { fl := args[mt] if !fl.constant() { - return nil, c.unsupported(fl) + return nil, errNonConstantRegexp } fl, err = simplifyExpr(staticEnv, fl) if err != nil { @@ -240,7 +240,7 @@ func compileConstantRegex(c *compiler, args TupleExpr, pat, mt int, cs collation } if pattern.(*Literal).inner == nil { - return nil, c.unsupported(pattern) + return nil, errNonConstantRegexp } innerPat, err := evalToVarchar(pattern.(*Literal).inner, cs.Collation, true) @@ -278,7 +278,7 @@ func (r *builtinRegexpLike) eval(env *ExpressionEnv) (eval, error) { return nil, err } - input, pat, typedCol, flags, err := evalRegexpCollation(input, pat, "regexp_like") + input, pat, typedCol, flags, err := evalRegexpCollation(env.collationEnv, input, pat, "regexp_like") if err != nil { return nil, err } @@ -348,7 +348,7 @@ func (r *builtinRegexpLike) compile(c *compiler) (ctype, error) { skips = append(skips, c.compileNullCheckArg(f, 2)) } - merged, flags, err := compileRegexpCollation(input, pat, "regexp_like") + merged, flags, err := compileRegexpCollation(c.env.CollationEnv(), input, pat, "regexp_like") if err != nil { return ctype{}, err } @@ -387,7 +387,7 @@ func (r *builtinRegexpInstr) eval(env *ExpressionEnv) (eval, error) { return nil, err } - input, pat, typedCol, flags, err := evalRegexpCollation(input, pat, "regexp_instr") + input, pat, typedCol, flags, err := evalRegexpCollation(env.collationEnv, input, pat, "regexp_instr") if err != nil { return nil, err } @@ -551,11 +551,11 @@ func (r *builtinRegexpInstr) compile(c *compiler) (ctype, error) { switch { case matchType.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } } - merged, flags, err := compileRegexpCollation(input, pat, "regexp_instr") + merged, flags, err := compileRegexpCollation(c.env.CollationEnv(), input, pat, "regexp_instr") if err != nil { return ctype{}, err } @@ -594,7 +594,7 @@ func (r *builtinRegexpSubstr) eval(env *ExpressionEnv) (eval, error) { return nil, err } - input, pat, typedCol, flags, err := evalRegexpCollation(input, pat, "regexp_substr") + input, pat, typedCol, flags, err := evalRegexpCollation(env.collationEnv, input, pat, "regexp_substr") if err != nil { return nil, err } @@ -728,11 +728,11 @@ func (r *builtinRegexpSubstr) compile(c *compiler) (ctype, error) { switch { case matchType.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } } - merged, flags, err := compileRegexpCollation(input, pat, "regexp_substr") + merged, flags, err := compileRegexpCollation(c.env.CollationEnv(), input, pat, "regexp_substr") if err != nil { return ctype{}, err } @@ -828,7 +828,7 @@ func (r *builtinRegexpReplace) eval(env *ExpressionEnv) (eval, error) { return nil, err } - input, pat, typedCol, flags, err := evalRegexpCollation(input, pat, "regexp_replace") + input, pat, typedCol, flags, err := evalRegexpCollation(env.collationEnv, input, pat, "regexp_replace") if err != nil { return nil, err } @@ -968,11 +968,11 @@ func (r *builtinRegexpReplace) compile(c *compiler) (ctype, error) { switch { case matchType.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } } - merged, flags, err := compileRegexpCollation(input, pat, "regexp_replace") + merged, flags, err := compileRegexpCollation(c.env.CollationEnv(), input, pat, "regexp_replace") if err != nil { return ctype{}, err } diff --git a/go/vt/vtgate/evalengine/fn_string.go b/go/vt/vtgate/evalengine/fn_string.go index 8d61905d237..e0887037c0a 100644 --- a/go/vt/vtgate/evalengine/fn_string.go +++ b/go/vt/vtgate/evalengine/fn_string.go @@ -18,6 +18,7 @@ package evalengine import ( "bytes" + "math" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/collations/charset" @@ -29,6 +30,11 @@ import ( ) type ( + builtinInsert struct { + CallExpr + collate collations.ID + } + builtinChangeCase struct { CallExpr upcase bool @@ -47,6 +53,16 @@ type ( CallExpr } + builtinReverse struct { + CallExpr + collate collations.ID + } + + builtinSpace struct { + CallExpr + collate collations.ID + } + builtinOrd struct { CallExpr collate collations.ID @@ -62,9 +78,8 @@ type ( builtinWeightString struct { CallExpr - Cast string - Len int - HasLen bool + Cast string + Len *int } builtinLeftRight struct { @@ -89,19 +104,181 @@ type ( collate collations.ID trim sqlparser.TrimType } + + builtinSubstring struct { + CallExpr + collate collations.ID + } + + builtinLocate struct { + CallExpr + collate collations.ID + } + + builtinChar struct { + CallExpr + collate collations.ID + } + + builtinRepeat struct { + CallExpr + collate collations.ID + } + + builtinConcat struct { + CallExpr + collate collations.ID + } + + builtinConcatWs struct { + CallExpr + collate collations.ID + } + + builtinReplace struct { + CallExpr + collate collations.ID + } ) +var _ IR = (*builtinInsert)(nil) var _ IR = (*builtinChangeCase)(nil) var _ IR = (*builtinCharLength)(nil) var _ IR = (*builtinLength)(nil) var _ IR = (*builtinASCII)(nil) +var _ IR = (*builtinReverse)(nil) +var _ IR = (*builtinSpace)(nil) var _ IR = (*builtinOrd)(nil) var _ IR = (*builtinBitLength)(nil) var _ IR = (*builtinCollation)(nil) var _ IR = (*builtinWeightString)(nil) var _ IR = (*builtinLeftRight)(nil) var _ IR = (*builtinPad)(nil) +var _ IR = (*builtinStrcmp)(nil) var _ IR = (*builtinTrim)(nil) +var _ IR = (*builtinSubstring)(nil) +var _ IR = (*builtinLocate)(nil) +var _ IR = (*builtinChar)(nil) +var _ IR = (*builtinRepeat)(nil) +var _ IR = (*builtinConcat)(nil) +var _ IR = (*builtinConcatWs)(nil) +var _ IR = (*builtinReplace)(nil) + +func insert(str, newstr *evalBytes, pos, l int) []byte { + pos-- + + cs := colldata.Lookup(str.col.Collation).Charset() + strLen := charset.Length(cs, str.bytes) + + if pos < 0 || strLen <= pos { + return str.bytes + } + if l < 0 { + l = strLen + } + + front := charset.Slice(cs, str.bytes, 0, pos) + var back []byte + if pos <= math.MaxInt-l && pos+l < strLen { + back = charset.Slice(cs, str.bytes, pos+l, strLen) + } + + res := make([]byte, len(front)+len(newstr.bytes)+len(back)) + + copy(res[:len(front)], front) + copy(res[len(front):], newstr.bytes) + copy(res[len(front)+len(newstr.bytes):], back) + + return res +} + +func (call *builtinInsert) eval(env *ExpressionEnv) (eval, error) { + args, err := call.args(env) + if err != nil { + return nil, err + } + if args[0] == nil || args[1] == nil || args[2] == nil || args[3] == nil { + return nil, nil + } + + str, ok := args[0].(*evalBytes) + if !ok { + str, err = evalToVarchar(args[0], call.collate, true) + if err != nil { + return nil, err + } + } + + pos := evalToInt64(args[1]).i + l := evalToInt64(args[2]).i + + newstr, err := evalToVarchar(args[3], str.col.Collation, true) + if err != nil { + return nil, err + } + + res := insert(str, newstr, int(pos), int(l)) + if !validMaxLength(int64(len(res)), 1) { + return nil, nil + } + return newEvalText(res, str.col), nil +} + +func (call *builtinInsert) compile(c *compiler) (ctype, error) { + str, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + pos, err := call.Arguments[1].compile(c) + if err != nil { + return ctype{}, err + } + + l, err := call.Arguments[2].compile(c) + if err != nil { + return ctype{}, err + } + + newstr, err := call.Arguments[3].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck4(str, pos, l, newstr) + + _ = c.compileToInt64(pos, 3) + _ = c.compileToInt64(l, 2) + + if err != nil { + return ctype{}, nil + } + + col := str.Col + + switch { + case str.isTextual(): + default: + c.asm.Convert_xce(4, sqltypes.VarChar, c.collation) + col = typedCoercionCollation(sqltypes.VarChar, c.collation) + } + + switch { + case newstr.isTextual(): + fromCharset := colldata.Lookup(newstr.Col.Collation).Charset() + toCharset := colldata.Lookup(col.Collation).Charset() + if fromCharset != toCharset && !toCharset.IsSuperset(fromCharset) { + c.asm.Convert_xce(1, sqltypes.VarChar, col.Collation) + } + default: + c.asm.Convert_xce(1, sqltypes.VarChar, col.Collation) + } + + c.asm.Fn_INSERT(col) + c.asm.jumpDestination(skip) + + return ctype{Type: sqltypes.VarChar, Col: col, Flag: flagNullable}, nil +} func (call *builtinChangeCase) eval(env *ExpressionEnv) (eval, error) { arg, err := call.arg1(env) @@ -146,7 +323,7 @@ func (call *builtinChangeCase) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, nil) } c.asm.Fn_LUCASE(call.upcase) @@ -239,13 +416,107 @@ func (call *builtinASCII) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } c.asm.Fn_ASCII() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: str.Flag}, nil + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: nullableFlags(str.Flag)}, nil +} + +func reverse(in *evalBytes) []byte { + cs := colldata.Lookup(in.col.Collation).Charset() + b := in.bytes + + out, end := make([]byte, len(b)), len(b) + for len(b) > 0 { + _, size := cs.DecodeRune(b) + copy(out[end-size:end], b[:size]) + b = b[size:] + end -= size + } + return out +} + +func (call *builtinReverse) eval(env *ExpressionEnv) (eval, error) { + arg, err := call.arg1(env) + if err != nil { + return nil, err + } + if arg == nil { + return nil, nil + } + + b, ok := arg.(*evalBytes) + if !ok { + b, err = evalToVarchar(arg, call.collate, true) + if err != nil { + return nil, err + } + } + + return newEvalText(reverse(b), b.col), nil +} + +func (call *builtinReverse) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + switch { + case arg.isTextual(): + default: + c.asm.Convert_xc(1, sqltypes.VarChar, c.collation, nil) + } + + c.asm.Fn_REVERSE() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.VarChar, Col: arg.Col, Flag: flagNullable}, nil +} + +func space(num int64) []byte { + num = max(num, 0) + + spaces := bytes.Repeat([]byte{0x20}, int(num)) + return spaces +} + +func (call *builtinSpace) eval(env *ExpressionEnv) (eval, error) { + arg, err := call.arg1(env) + if err != nil { + return nil, err + } + if arg == nil { + return nil, nil + } + + num := evalToInt64(arg).i + + if !validMaxLength(1, num) { + return nil, nil + } + col := typedCoercionCollation(sqltypes.VarChar, call.collate) + return newEvalText(space(num), col), nil +} + +func (call *builtinSpace) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + _ = c.compileToInt64(arg, 1) + + col := typedCoercionCollation(sqltypes.VarChar, call.collate) + c.asm.Fn_SPACE(col) + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.VarChar, Col: col, Flag: flagNullable}, nil } func charOrd(b []byte, coll collations.ID) int64 { @@ -294,13 +565,13 @@ func (call *builtinOrd) compile(c *compiler) (ctype, error) { case str.isTextual(): col = str.Col.Collation default: - c.asm.Convert_xc(1, sqltypes.VarChar, call.collate, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, call.collate, nil) } c.asm.Fn_ORD(col) c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: str.Flag}, nil + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: nullableFlags(str.Flag)}, nil } // maxRepeatLength is the maximum number of times a string can be repeated. @@ -317,11 +588,6 @@ func (call *builtinOrd) compile(c *compiler) (ctype, error) { // - `> max_allowed_packet`, no error and returns `NULL`. const maxRepeatLength = 1073741824 -type builtinRepeat struct { - CallExpr - collate collations.ID -} - func (call *builtinRepeat) eval(env *ExpressionEnv) (eval, error) { arg1, arg2, err := call.arg2(env) if err != nil { @@ -377,7 +643,7 @@ func (expr *builtinRepeat) compile(c *compiler) (ctype, error) { switch { case str.isTextual(): default: - c.asm.Convert_xc(2, sqltypes.VarChar, c.collation, 0, false) + c.asm.Convert_xc(2, sqltypes.VarChar, c.collation, nil) } _ = c.compileToInt64(repeat, 1) @@ -396,7 +662,7 @@ func (c *builtinCollation) eval(env *ExpressionEnv) (eval, error) { // the collation of a `COLLATION` expr is hardcoded to `utf8mb3_general_ci`, // not to the default collation of our connection. this is probably a bug in MySQL, but we match it - return newEvalText([]byte(collations.Local().LookupName(col.Collation)), collationUtf8mb3), nil + return newEvalText([]byte(env.collationEnv.LookupName(col.Collation)), collationUtf8mb3), nil } func (expr *builtinCollation) compile(c *compiler) (ctype, error) { @@ -407,7 +673,7 @@ func (expr *builtinCollation) compile(c *compiler) (ctype, error) { skip := c.asm.jumpFrom() - c.asm.Fn_COLLATION(collationUtf8mb3) + c.asm.Fn_COLLATION(c.env.CollationEnv(), collationUtf8mb3) c.asm.jumpDestination(skip) return ctype{Type: sqltypes.VarChar, Col: collationUtf8mb3}, nil @@ -429,7 +695,7 @@ func (c *builtinWeightString) eval(env *ExpressionEnv) (eval, error) { typ = sqltypes.Blob } - weights, _, err = evalWeightString(weights, evalToBinary(input), c.Len, 0) + weights, _, err = evalWeightString(weights, evalToBinary(input), *c.Len, 0) if err != nil { return nil, err } @@ -464,7 +730,7 @@ func (c *builtinWeightString) eval(env *ExpressionEnv) (eval, error) { } else { var strLen int if c.Cast == "char" { - strLen = c.Len + strLen = *c.Len } weights, _, err = evalWeightString(weights, val, strLen, 0) } @@ -494,14 +760,14 @@ func (call *builtinWeightString) compile(c *compiler) (ctype, error) { skip := c.compileNullCheck1(str) if call.Cast == "binary" { if !sqltypes.IsBinary(str.Type) { - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } switch str.Type { case sqltypes.Blob, sqltypes.Text, sqltypes.TypeJSON: typ = sqltypes.Blob } - c.asm.Fn_WEIGHT_STRING(typ, call.Len) + c.asm.Fn_WEIGHT_STRING(typ, *call.Len) c.asm.jumpDestination(skip) return ctype{Type: sqltypes.VarBinary, Flag: flagNullable | flagNull, Col: collationBinary}, nil } @@ -522,7 +788,7 @@ func (call *builtinWeightString) compile(c *compiler) (ctype, error) { } var strLen int if call.Cast == "char" { - strLen = call.Len + strLen = *call.Len } c.asm.Fn_WEIGHT_STRING(typ, strLen) @@ -592,7 +858,7 @@ func (call *builtinLeftRight) compile(c *compiler) (ctype, error) { case str.isTextual(): col = str.Col default: - c.asm.Convert_xc(2, sqltypes.VarChar, col.Collation, 0, false) + c.asm.Convert_xc(2, sqltypes.VarChar, col.Collation, nil) } _ = c.compileToInt64(l, 1) @@ -719,7 +985,7 @@ func (call *builtinPad) compile(c *compiler) (ctype, error) { c.asm.Fn_RPAD(col) } c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.VarChar, Col: col}, nil + return ctype{Type: sqltypes.VarChar, Flag: flagNullable, Col: col}, nil } func strcmpCollate(left, right []byte, col collations.ID) int64 { @@ -755,7 +1021,7 @@ func (l *builtinStrcmp) eval(env *ExpressionEnv) (eval, error) { col1 := evalCollation(left) col2 := evalCollation(right) - mcol, _, _, err := colldata.Merge(collations.Local(), col1, col2, colldata.CoercionOptions{ + mcol, _, _, err := colldata.Merge(env.collationEnv, col1, col2, colldata.CoercionOptions{ ConvertToSuperset: true, ConvertWithCoercion: true, }) @@ -795,7 +1061,7 @@ func (expr *builtinStrcmp) compile(c *compiler) (ctype, error) { if sqltypes.IsNumber(lt.Type) || sqltypes.IsNumber(rt.Type) { mcol = collationNumeric } else { - mcol, _, _, err = colldata.Merge(collations.Local(), lt.Col, rt.Col, colldata.CoercionOptions{ + mcol, _, _, err = colldata.Merge(c.env.CollationEnv(), lt.Col, rt.Col, colldata.CoercionOptions{ ConvertToSuperset: true, ConvertWithCoercion: true, }) @@ -814,10 +1080,10 @@ func (expr *builtinStrcmp) compile(c *compiler) (ctype, error) { c.asm.Strcmp(mcol) c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagNullable}, nil + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: nullableFlags(lt.Flag | rt.Flag)}, nil } -func (call builtinTrim) eval(env *ExpressionEnv) (eval, error) { +func (call *builtinTrim) eval(env *ExpressionEnv) (eval, error) { str, err := call.arg1(env) if err != nil { return nil, err @@ -872,7 +1138,7 @@ func (call builtinTrim) eval(env *ExpressionEnv) (eval, error) { } } -func (call builtinTrim) compile(c *compiler) (ctype, error) { +func (call *builtinTrim) compile(c *compiler) (ctype, error) { str, err := call.Arguments[0].compile(c) if err != nil { return ctype{}, err @@ -885,7 +1151,7 @@ func (call builtinTrim) compile(c *compiler) (ctype, error) { case str.isTextual(): col = str.Col default: - c.asm.Convert_xc(1, sqltypes.VarChar, col.Collation, 0, false) + c.asm.Convert_xc(1, sqltypes.VarChar, col.Collation, nil) } if len(call.Arguments) == 1 { @@ -898,7 +1164,7 @@ func (call builtinTrim) compile(c *compiler) (ctype, error) { c.asm.Fn_TRIM1(col) } c.asm.jumpDestination(skip1) - return ctype{Type: sqltypes.VarChar, Col: col}, nil + return ctype{Type: sqltypes.VarChar, Flag: nullableFlags(str.Flag), Col: col}, nil } pat, err := call.Arguments[1].compile(c) @@ -929,12 +1195,211 @@ func (call builtinTrim) compile(c *compiler) (ctype, error) { } c.asm.jumpDestination(skip1, skip2) - return ctype{Type: sqltypes.VarChar, Col: col}, nil + return ctype{Type: sqltypes.VarChar, Flag: flagNullable, Col: col}, nil +} + +func (call *builtinSubstring) eval(env *ExpressionEnv) (eval, error) { + str, err := call.Arguments[0].eval(env) + if err != nil || str == nil { + return nil, err + } + + tt := str.SQLType() + text, ok := str.(*evalBytes) + if !ok { + text, err = evalToVarchar(str, call.collate, true) + if err != nil { + return nil, err + } + tt = sqltypes.VarChar + } + + p, err := call.Arguments[1].eval(env) + if err != nil || p == nil { + return nil, err + } + + var l eval + if len(call.Arguments) > 2 { + l, err = call.Arguments[2].eval(env) + if err != nil || l == nil { + return nil, err + } + } + + pos := evalToInt64(p).i + if pos == 0 { + return newEvalRaw(tt, nil, text.col), nil + } + cs := colldata.Lookup(text.col.Collation).Charset() + end := int64(charset.Length(cs, text.bytes)) + + if pos < 0 { + pos += end + 1 + } + if pos < 1 || pos > end { + return newEvalRaw(tt, nil, text.col), nil + } + + if len(call.Arguments) > 2 { + ll := evalToInt64(l).i + if ll < 1 { + return newEvalRaw(tt, nil, text.col), nil + } + if ll > end-pos+1 { + ll = end - pos + 1 + } + end = pos + ll - 1 + } + res := charset.Slice(cs, text.bytes, int(pos-1), int(end)) + return newEvalRaw(tt, res, text.col), nil +} + +func (call *builtinSubstring) compile(c *compiler) (ctype, error) { + str, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + p, err := call.Arguments[1].compile(c) + if err != nil { + return ctype{}, err + } + + tt := str.Type + skip1 := c.compileNullCheck2(str, p) + + col := typedCoercionCollation(sqltypes.VarChar, c.collation) + switch { + case str.isTextual(): + col = str.Col + default: + tt = sqltypes.VarChar + c.asm.Convert_xc(2, tt, col.Collation, nil) + } + _ = c.compileToInt64(p, 1) + + cs := colldata.Lookup(str.Col.Collation).Charset() + var skip2 *jump + if len(call.Arguments) > 2 { + l, err := call.Arguments[2].compile(c) + if err != nil { + return ctype{}, err + } + skip2 = c.compileNullCheck2(str, l) + _ = c.compileToInt64(l, 1) + c.asm.Fn_SUBSTRING3(tt, cs, col) + } else { + c.asm.Fn_SUBSTRING2(tt, cs, col) + } + + c.asm.jumpDestination(skip1, skip2) + return ctype{Type: tt, Col: col, Flag: flagNullable}, nil } -type builtinConcat struct { - CallExpr - collate collations.ID +func (call *builtinLocate) eval(env *ExpressionEnv) (eval, error) { + substr, err := call.Arguments[0].eval(env) + if err != nil || substr == nil { + return nil, err + } + + str, err := call.Arguments[1].eval(env) + if err != nil || str == nil { + return nil, err + } + + if _, ok := str.(*evalBytes); !ok { + str, err = evalToVarchar(str, call.collate, true) + if err != nil { + return nil, err + } + } + + col := str.(*evalBytes).col.Collation + substr, err = evalToVarchar(substr, col, true) + if err != nil { + return nil, err + } + + pos := int64(1) + if len(call.Arguments) > 2 { + p, err := call.Arguments[2].eval(env) + if err != nil || p == nil { + return nil, err + } + pos = evalToInt64(p).i + if pos < 1 || pos > math.MaxInt { + return newEvalInt64(0), nil + } + } + + var coll colldata.Collation + if typeIsTextual(substr.SQLType()) && typeIsTextual(str.SQLType()) { + coll = colldata.Lookup(col) + } else { + coll = colldata.Lookup(collations.CollationBinaryID) + } + found := colldata.Index(coll, str.ToRawBytes(), substr.ToRawBytes(), int(pos)-1) + return newEvalInt64(int64(found) + 1), nil +} + +func (call *builtinLocate) compile(c *compiler) (ctype, error) { + substr, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + str, err := call.Arguments[1].compile(c) + if err != nil { + return ctype{}, err + } + + skip1 := c.compileNullCheck2(substr, str) + var skip2 *jump + if len(call.Arguments) > 2 { + l, err := call.Arguments[2].compile(c) + if err != nil { + return ctype{}, err + } + skip2 = c.compileNullCheck2(str, l) + _ = c.compileToInt64(l, 1) + } + + if !str.isTextual() { + c.asm.Convert_xce(len(call.Arguments)-1, sqltypes.VarChar, c.collation) + str.Col = collations.TypedCollation{ + Collation: c.collation, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + } + + fromCharset := colldata.Lookup(substr.Col.Collation).Charset() + toCharset := colldata.Lookup(str.Col.Collation).Charset() + if !substr.isTextual() || (fromCharset != toCharset && !toCharset.IsSuperset(fromCharset)) { + c.asm.Convert_xce(len(call.Arguments), sqltypes.VarChar, str.Col.Collation) + substr.Col = collations.TypedCollation{ + Collation: str.Col.Collation, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + } + + var coll colldata.Collation + if typeIsTextual(substr.Type) && typeIsTextual(str.Type) { + coll = colldata.Lookup(str.Col.Collation) + } else { + coll = colldata.Lookup(collations.CollationBinaryID) + } + + if len(call.Arguments) > 2 { + c.asm.Locate3(coll) + } else { + c.asm.Locate2(coll) + } + + c.asm.jumpDestination(skip1, skip2) + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: flagNullable}, nil } func concatSQLType(arg sqltypes.Type, tt sqltypes.Type) sqltypes.Type { @@ -966,7 +1431,6 @@ func concatConvert(buf []byte, str *evalBytes, tc collations.TypedCollation) ([] } func (call *builtinConcat) eval(env *ExpressionEnv) (eval, error) { - local := collations.Local() var ca collationAggregation tt := sqltypes.VarChar @@ -979,7 +1443,7 @@ func (call *builtinConcat) eval(env *ExpressionEnv) (eval, error) { args = append(args, a) tt = concatSQLType(a.SQLType(), tt) - err = ca.add(local, evalCollation(a)) + err = ca.add(evalCollation(a), env.collationEnv) if err != nil { return nil, err } @@ -1014,7 +1478,6 @@ func (call *builtinConcat) eval(env *ExpressionEnv) (eval, error) { } func (call *builtinConcat) compile(c *compiler) (ctype, error) { - local := collations.Local() var ca collationAggregation tt := sqltypes.VarChar var f typeFlag @@ -1031,7 +1494,7 @@ func (call *builtinConcat) compile(c *compiler) (ctype, error) { args = append(args, a) tt = concatSQLType(a.Type, tt) - err = ca.add(local, a.Col) + err = ca.add(a.Col, c.env.CollationEnv()) if err != nil { return ctype{}, err } @@ -1067,13 +1530,7 @@ func (call *builtinConcat) compile(c *compiler) (ctype, error) { return ctype{Type: tt, Flag: f, Col: tc}, nil } -type builtinConcatWs struct { - CallExpr - collate collations.ID -} - func (call *builtinConcatWs) eval(env *ExpressionEnv) (eval, error) { - local := collations.Local() var ca collationAggregation tt := sqltypes.VarChar @@ -1093,7 +1550,7 @@ func (call *builtinConcatWs) eval(env *ExpressionEnv) (eval, error) { args = append(args, a) tt = concatSQLType(a.SQLType(), tt) - err = ca.add(local, evalCollation(a)) + err = ca.add(evalCollation(a), env.collationEnv) if err != nil { return nil, err } @@ -1143,7 +1600,6 @@ func (call *builtinConcatWs) eval(env *ExpressionEnv) (eval, error) { } func (call *builtinConcatWs) compile(c *compiler) (ctype, error) { - local := collations.Local() var ca collationAggregation tt := sqltypes.VarChar @@ -1156,7 +1612,7 @@ func (call *builtinConcatWs) compile(c *compiler) (ctype, error) { } tt = concatSQLType(a.Type, tt) - err = ca.add(local, a.Col) + err = ca.add(a.Col, c.env.CollationEnv()) if err != nil { return ctype{}, err } @@ -1204,3 +1660,196 @@ func (call *builtinConcatWs) compile(c *compiler) (ctype, error) { return ctype{Type: tt, Flag: args[0].Flag, Col: tc}, nil } + +func (call *builtinChar) eval(env *ExpressionEnv) (eval, error) { + vals := make([]eval, 0, len(call.Arguments)) + for _, arg := range call.Arguments { + a, err := arg.eval(env) + if err != nil { + return nil, err + } + if a == nil { + continue + } + vals = append(vals, a) + } + + buf := make([]byte, 0, len(vals)) + for _, v := range vals { + buf = encodeChar(buf, uint32(evalToInt64(v).i)) + } + if call.collate == collations.CollationBinaryID { + return newEvalBinary(buf), nil + } + + cs := colldata.Lookup(call.collate).Charset() + if !charset.Validate(cs, buf) { + return nil, nil + } + + return newEvalText(buf, collations.TypedCollation{ + Collation: call.collate, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + }), nil +} + +func (call *builtinChar) compile(c *compiler) (ctype, error) { + for _, arg := range call.Arguments { + a, err := arg.compile(c) + if err != nil { + return ctype{}, err + } + j := c.compileNullCheck1(a) + switch a.Type { + case sqltypes.Int64: + // No-op, already correct type + case sqltypes.Uint64: + c.asm.Convert_ui(1) + default: + c.asm.Convert_xi(1) + } + c.asm.jumpDestination(j) + } + tt := sqltypes.VarBinary + if call.collate != collations.CollationBinaryID { + tt = sqltypes.VarChar + } + col := collations.TypedCollation{ + Collation: call.collate, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + c.asm.Fn_CHAR(tt, col, len(call.Arguments)) + return ctype{Type: tt, Flag: flagNullable, Col: col}, nil +} + +func encodeChar(buf []byte, i uint32) []byte { + switch { + case i < 0x100: + buf = append(buf, byte(i)) + case i < 0x10000: + buf = append(buf, byte(i>>8), byte(i)) + case i < 0x1000000: + buf = append(buf, byte(i>>16), byte(i>>8), byte(i)) + default: + buf = append(buf, byte(i>>24), byte(i>>16), byte(i>>8), byte(i)) + } + return buf +} + +func (call *builtinReplace) eval(env *ExpressionEnv) (eval, error) { + str, err := call.Arguments[0].eval(env) + if err != nil || str == nil { + return nil, err + } + + fromStr, err := call.Arguments[1].eval(env) + if err != nil || fromStr == nil { + return nil, err + } + + toStr, err := call.Arguments[2].eval(env) + if err != nil || toStr == nil { + return nil, err + } + + if _, ok := str.(*evalBytes); !ok { + str, err = evalToVarchar(str, call.collate, true) + if err != nil { + return nil, err + } + } + + col := str.(*evalBytes).col + fromStr, err = evalToVarchar(fromStr, col.Collation, true) + if err != nil { + return nil, err + } + + toStr, err = evalToVarchar(toStr, col.Collation, true) + if err != nil { + return nil, err + } + + strBytes := str.(*evalBytes).bytes + fromBytes := fromStr.(*evalBytes).bytes + toBytes := toStr.(*evalBytes).bytes + + out := replace(strBytes, fromBytes, toBytes) + return newEvalRaw(str.SQLType(), out, col), nil +} + +func (call *builtinReplace) compile(c *compiler) (ctype, error) { + str, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + fromStr, err := call.Arguments[1].compile(c) + if err != nil { + return ctype{}, err + } + + toStr, err := call.Arguments[2].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck3(str, fromStr, toStr) + if !str.isTextual() { + c.asm.Convert_xce(3, sqltypes.VarChar, c.collation) + str.Col = collations.TypedCollation{ + Collation: c.collation, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + } + + fromCharset := colldata.Lookup(fromStr.Col.Collation).Charset() + toCharset := colldata.Lookup(toStr.Col.Collation).Charset() + strCharset := colldata.Lookup(str.Col.Collation).Charset() + if !fromStr.isTextual() || (fromCharset != strCharset && !strCharset.IsSuperset(fromCharset)) { + c.asm.Convert_xce(2, sqltypes.VarChar, str.Col.Collation) + fromStr.Col = collations.TypedCollation{ + Collation: str.Col.Collation, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + } + + if !toStr.isTextual() || (toCharset != strCharset && !strCharset.IsSuperset(toCharset)) { + c.asm.Convert_xce(1, sqltypes.VarChar, str.Col.Collation) + toStr.Col = collations.TypedCollation{ + Collation: str.Col.Collation, + Coercibility: collations.CoerceCoercible, + Repertoire: collations.RepertoireASCII, + } + } + + c.asm.Replace() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.VarChar, Col: str.Col, Flag: flagNullable}, nil +} + +func replace(str, from, to []byte) []byte { + if len(from) == 0 { + return str + } + n := bytes.Count(str, from) + if n == 0 { + return str + } + + out := make([]byte, len(str)+n*(len(to)-len(from))) + end := 0 + start := 0 + for i := 0; i < n; i++ { + pos := start + bytes.Index(str[start:], from) + end += copy(out[end:], str[start:pos]) + end += copy(out[end:], to) + start = pos + len(from) + } + end += copy(out[end:], str[start:]) + return out[0:end] +} diff --git a/go/vt/vtgate/evalengine/fn_time.go b/go/vt/vtgate/evalengine/fn_time.go index 430b975974b..57edf29e2a4 100644 --- a/go/vt/vtgate/evalengine/fn_time.go +++ b/go/vt/vtgate/evalengine/fn_time.go @@ -29,6 +29,8 @@ import ( var SystemTime = time.Now +const maxTimePrec = 6 + type ( builtinNow struct { CallExpr @@ -105,6 +107,22 @@ type ( collate collations.ID } + builtinLastDay struct { + CallExpr + } + + builtinToDays struct { + CallExpr + } + + builtinFromDays struct { + CallExpr + } + + builtinTimeToSec struct { + CallExpr + } + builtinQuarter struct { CallExpr } @@ -166,6 +184,10 @@ var _ IR = (*builtinMicrosecond)(nil) var _ IR = (*builtinMinute)(nil) var _ IR = (*builtinMonth)(nil) var _ IR = (*builtinMonthName)(nil) +var _ IR = (*builtinLastDay)(nil) +var _ IR = (*builtinToDays)(nil) +var _ IR = (*builtinFromDays)(nil) +var _ IR = (*builtinTimeToSec)(nil) var _ IR = (*builtinQuarter)(nil) var _ IR = (*builtinSecond)(nil) var _ IR = (*builtinTime)(nil) @@ -179,26 +201,22 @@ var _ IR = (*builtinYearWeek)(nil) func (call *builtinNow) eval(env *ExpressionEnv) (eval, error) { now := env.time(call.utc) if call.onlyTime { - buf := datetime.Time_hh_mm_ss.Format(now, call.prec) - return newEvalRaw(sqltypes.Time, buf, collationBinary), nil + return newEvalTime(now.Time, int(call.prec)), nil } else { - buf := datetime.DateTime_YYYY_MM_DD_hh_mm_ss.Format(now, call.prec) - return newEvalRaw(sqltypes.Datetime, buf, collationBinary), nil + return newEvalDateTime(now, int(call.prec), false), nil } } func (call *builtinNow) compile(c *compiler) (ctype, error) { - var format *datetime.Strftime var t sqltypes.Type if call.onlyTime { - format = datetime.Time_hh_mm_ss t = sqltypes.Time + c.asm.Fn_NowTime(call.prec, call.utc) } else { - format = datetime.DateTime_YYYY_MM_DD_hh_mm_ss t = sqltypes.Datetime + c.asm.Fn_Now(call.prec, call.utc) } - c.asm.Fn_Now(t, format, call.prec, call.utc) return ctype{Type: t, Col: collationBinary}, nil } @@ -211,7 +229,7 @@ func (call *builtinSysdate) eval(env *ExpressionEnv) (eval, error) { if tz := env.currentTimezone(); tz != nil { now = now.In(tz) } - return newEvalRaw(sqltypes.Datetime, datetime.NewDateTimeFromStd(now).Format(call.prec), collationBinary), nil + return newEvalDateTime(datetime.NewDateTimeFromStd(now), int(call.prec), false), nil } func (call *builtinSysdate) compile(c *compiler) (ctype, error) { @@ -225,7 +243,7 @@ func (call *builtinSysdate) constant() bool { func (call *builtinCurdate) eval(env *ExpressionEnv) (eval, error) { now := env.time(false) - return newEvalRaw(sqltypes.Date, datetime.Date_YYYY_MM_DD.Format(now, 0), collationBinary), nil + return newEvalDate(now.Date, false), nil } func (*builtinCurdate) compile(c *compiler) (ctype, error) { @@ -239,7 +257,7 @@ func (call *builtinCurdate) constant() bool { func (call *builtinUtcDate) eval(env *ExpressionEnv) (eval, error) { now := env.time(true) - return newEvalRaw(sqltypes.Date, datetime.Date_YYYY_MM_DD.Format(now, 0), collationBinary), nil + return newEvalDate(now.Date, false), nil } func (*builtinUtcDate) compile(c *compiler) (ctype, error) { @@ -264,8 +282,8 @@ func (b *builtinDateFormat) eval(env *ExpressionEnv) (eval, error) { case *evalTemporal: t = e.toDateTime(datetime.DefaultPrecision, env.now) default: - t = evalToDateTime(date, datetime.DefaultPrecision, env.now) - if t == nil || t.isZero() { + t = evalToDateTime(date, datetime.DefaultPrecision, env.now, false) + if t == nil { return nil, nil } } @@ -289,7 +307,7 @@ func (call *builtinDateFormat) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Datetime, sqltypes.Date: default: - c.asm.Convert_xDT_nz(1, datetime.DefaultPrecision) + c.asm.Convert_xDT(1, datetime.DefaultPrecision, false) } format, err := call.Arguments[1].compile(c) @@ -302,7 +320,7 @@ func (call *builtinDateFormat) compile(c *compiler) (ctype, error) { switch format.Type { case sqltypes.VarChar, sqltypes.VarBinary: default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } col := typedCoercionCollation(sqltypes.VarChar, c.collation) @@ -323,6 +341,10 @@ func convertTz(dt datetime.DateTime, from, to *time.Location) (datetime.DateTime if err != nil { return datetime.DateTime{}, false } + + if ts.Unix() < 0 || ts.Unix() >= maxUnixtime { + return dt, true + } return datetime.NewDateTimeFromStd(ts.In(to)), true } @@ -357,8 +379,8 @@ func (call *builtinConvertTz) eval(env *ExpressionEnv) (eval, error) { return nil, nil } - dt := evalToDateTime(n, -1, env.now) - if dt == nil || dt.isZero() { + dt := evalToDateTime(n, -1, env.now, false) + if dt == nil { return nil, nil } @@ -366,7 +388,7 @@ func (call *builtinConvertTz) eval(env *ExpressionEnv) (eval, error) { if !ok { return nil, nil } - return newEvalDateTime(out, int(dt.prec)), nil + return newEvalDateTime(out, int(dt.prec), false), nil } func (call *builtinConvertTz) compile(c *compiler) (ctype, error) { @@ -388,19 +410,19 @@ func (call *builtinConvertTz) compile(c *compiler) (ctype, error) { switch { case from.isTextual(): default: - c.asm.Convert_xb(2, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(2, sqltypes.VarBinary, nil) } switch { case to.isTextual(): default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } switch n.Type { case sqltypes.Datetime, sqltypes.Date: default: - c.asm.Convert_xDT_nz(3, -1) + c.asm.Convert_xDT(3, -1, false) } c.asm.Fn_CONVERT_TZ() @@ -416,7 +438,7 @@ func (b *builtinDate) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, env.sqlmode.AllowZeroDate()) if d == nil { return nil, nil } @@ -434,7 +456,7 @@ func (call *builtinDate) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, c.sqlmode.AllowZeroDate()) } c.asm.jumpDestination(skip) @@ -449,7 +471,7 @@ func (b *builtinDayOfMonth) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, true) if d == nil { return nil, nil } @@ -467,7 +489,7 @@ func (call *builtinDayOfMonth) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, true) } c.asm.Fn_DAYOFMONTH() c.asm.jumpDestination(skip) @@ -482,11 +504,11 @@ func (b *builtinDayOfWeek) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } - return newEvalInt64(int64(d.dt.Date.ToStdTime(time.Local).Weekday() + 1)), nil + return newEvalInt64(int64(d.dt.Date.Weekday() + 1)), nil } func (call *builtinDayOfWeek) compile(c *compiler) (ctype, error) { @@ -500,7 +522,7 @@ func (call *builtinDayOfWeek) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } c.asm.Fn_DAYOFWEEK() c.asm.jumpDestination(skip) @@ -515,11 +537,11 @@ func (b *builtinDayOfYear) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } - return newEvalInt64(int64(d.dt.Date.ToStdTime(time.Local).YearDay())), nil + return newEvalInt64(int64(d.dt.Date.ToStdTime(env.currentTimezone()).YearDay())), nil } func (call *builtinDayOfYear) compile(c *compiler) (ctype, error) { @@ -533,7 +555,7 @@ func (call *builtinDayOfYear) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } c.asm.Fn_DAYOFYEAR() c.asm.jumpDestination(skip) @@ -557,50 +579,79 @@ func (b *builtinFromUnixtime) eval(env *ExpressionEnv) (eval, error) { switch ts := ts.(type) { case *evalInt64: + if ts.i < 0 || ts.i >= maxUnixtime { + return nil, nil + } sec = ts.i case *evalUint64: + if ts.u >= maxUnixtime { + return nil, nil + } sec = int64(ts.u) case *evalFloat: + if ts.f < 0 || ts.f >= maxUnixtime { + return nil, nil + } sf, ff := math.Modf(ts.f) sec = int64(sf) frac = int64(ff * 1e9) - prec = 6 + prec = maxTimePrec case *evalDecimal: + if ts.dec.Sign() < 0 { + return nil, nil + } sd, fd := ts.dec.QuoRem(decimal.New(1, 0), 0) sec, _ = sd.Int64() + if sec >= maxUnixtime { + return nil, nil + } frac, _ = fd.Mul(decimal.New(1, 9)).Int64() prec = int(ts.length) case *evalTemporal: if ts.prec == 0 { sec = ts.toInt64() + if sec < 0 || sec >= maxUnixtime { + return nil, nil + } } else { dec := ts.toDecimal() + if dec.Sign() < 0 { + return nil, nil + } sd, fd := dec.QuoRem(decimal.New(1, 0), 0) sec, _ = sd.Int64() + if sec >= maxUnixtime { + return nil, nil + } frac, _ = fd.Mul(decimal.New(1, 9)).Int64() prec = int(ts.prec) } case *evalBytes: if ts.isHexOrBitLiteral() { u, _ := ts.toNumericHex() + if u.u >= maxUnixtime { + return nil, nil + } sec = int64(u.u) } else { f, _ := evalToFloat(ts) + if f.f < 0 || f.f >= maxUnixtime { + return nil, nil + } sf, ff := math.Modf(f.f) sec = int64(sf) frac = int64(ff * 1e9) - prec = 6 + prec = maxTimePrec } default: f, _ := evalToFloat(ts) + if f.f < 0 || f.f >= maxUnixtime { + return nil, nil + } sf, ff := math.Modf(f.f) sec = int64(sf) frac = int64(ff * 1e9) - prec = 6 - } - - if sec < 0 || sec >= maxUnixtime { - return nil, nil + prec = maxTimePrec } t := time.Unix(sec, frac) @@ -608,7 +659,7 @@ func (b *builtinFromUnixtime) eval(env *ExpressionEnv) (eval, error) { t = t.In(tz) } - dt := newEvalDateTime(datetime.NewDateTimeFromStd(t), prec) + dt := newEvalDateTime(datetime.NewDateTimeFromStd(t), prec, env.sqlmode.AllowZeroDate()) if len(b.Arguments) == 1 { return dt, nil @@ -643,8 +694,13 @@ func (call *builtinFromUnixtime) compile(c *compiler) (ctype, error) { case sqltypes.Decimal: c.asm.Fn_FROM_UNIXTIME_d() case sqltypes.Datetime, sqltypes.Date, sqltypes.Time: - c.asm.Convert_Ti(1) - c.asm.Fn_FROM_UNIXTIME_i() + if arg.Size == 0 { + c.asm.Convert_Ti(1) + c.asm.Fn_FROM_UNIXTIME_i() + } else { + c.asm.Convert_Td(1) + c.asm.Fn_FROM_UNIXTIME_d() + } case sqltypes.VarChar, sqltypes.VarBinary: if arg.isHexOrBitLiteral() { c.asm.Convert_xu(1) @@ -673,7 +729,7 @@ func (call *builtinFromUnixtime) compile(c *compiler) (ctype, error) { switch format.Type { case sqltypes.VarChar, sqltypes.VarBinary: default: - c.asm.Convert_xb(1, sqltypes.VarBinary, 0, false) + c.asm.Convert_xb(1, sqltypes.VarBinary, nil) } col := typedCoercionCollation(sqltypes.VarChar, c.collation) @@ -715,7 +771,7 @@ func (call *builtinHour) compile(c *compiler) (ctype, error) { return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag | flagNullable}, nil } -func yearDayToTime(y, yd int64) time.Time { +func yearDayToTime(loc *time.Location, y, yd int64) time.Time { if y >= 0 && y < 100 { if y < 70 { y += 2000 @@ -727,7 +783,7 @@ func yearDayToTime(y, yd int64) time.Time { if y < 0 || y > 9999 || yd < 1 || yd > math.MaxInt32 { return time.Time{} } - t := time.Date(int(y), time.January, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, int(yd-1)) + t := time.Date(int(y), time.January, 1, 0, 0, 0, 0, loc).AddDate(0, 0, int(yd-1)) if t.Year() > 9999 { return time.Time{} } @@ -755,11 +811,11 @@ func (b *builtinMakedate) eval(env *ExpressionEnv) (eval, error) { y := evalToInt64(year).i yd := evalToInt64(yearDay).i - t := yearDayToTime(y, yd) + t := yearDayToTime(env.currentTimezone(), y, yd) if t.IsZero() { return nil, nil } - return newEvalDate(datetime.NewDateTimeFromStd(t).Date), nil + return newEvalDate(datetime.NewDateTimeFromStd(t).Date, false), nil } func (call *builtinMakedate) compile(c *compiler) (ctype, error) { @@ -1100,7 +1156,7 @@ func (b *builtinMonth) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, true) if d == nil { return nil, nil } @@ -1118,7 +1174,7 @@ func (call *builtinMonth) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, true) } c.asm.Fn_MONTH() c.asm.jumpDestination(skip) @@ -1133,7 +1189,7 @@ func (b *builtinMonthName) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, false) if d == nil { return nil, nil } @@ -1156,7 +1212,7 @@ func (call *builtinMonthName) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, false) } col := typedCoercionCollation(sqltypes.VarChar, call.collate) c.asm.Fn_MONTHNAME(col) @@ -1164,6 +1220,159 @@ func (call *builtinMonthName) compile(c *compiler) (ctype, error) { return ctype{Type: sqltypes.VarChar, Col: col, Flag: arg.Flag | flagNullable}, nil } +func lastDay(loc *time.Location, dt datetime.DateTime) datetime.Date { + ts := dt.Date.ToStdTime(loc) + firstDayOfMonth := time.Date(ts.Year(), ts.Month(), 1, 0, 0, 0, 0, loc) + lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1) + + date := datetime.NewDateFromStd(lastDayOfMonth) + return date +} + +func (b *builtinLastDay) eval(env *ExpressionEnv) (eval, error) { + date, err := b.arg1(env) + if err != nil { + return nil, err + } + if date == nil { + return nil, nil + } + dt := evalToDateTime(date, -1, env.now, env.sqlmode.AllowZeroDate()) + if dt == nil || dt.isZero() { + return nil, nil + } + + d := lastDay(env.currentTimezone(), dt.dt) + return newEvalDate(d, env.sqlmode.AllowZeroDate()), nil +} + +func (call *builtinLastDay) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + switch arg.Type { + case sqltypes.Date, sqltypes.Datetime: + default: + c.asm.Convert_xD(1, c.sqlmode.AllowZeroDate()) + } + c.asm.Fn_LAST_DAY() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.Date, Flag: arg.Flag | flagNullable}, nil +} + +func (b *builtinToDays) eval(env *ExpressionEnv) (eval, error) { + date, err := b.arg1(env) + if err != nil { + return nil, err + } + if date == nil { + return nil, nil + } + dt := evalToDate(date, env.now, false) + if dt == nil { + return nil, nil + } + + numDays := datetime.MysqlDayNumber(dt.dt.Date.Year(), dt.dt.Date.Month(), dt.dt.Date.Day()) + return newEvalInt64(int64(numDays)), nil +} + +func (call *builtinToDays) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + switch arg.Type { + case sqltypes.Date, sqltypes.Datetime: + default: + c.asm.Convert_xD(1, false) + } + c.asm.Fn_TO_DAYS() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag | flagNullable}, nil +} + +func (b *builtinFromDays) eval(env *ExpressionEnv) (eval, error) { + arg, err := b.arg1(env) + if arg == nil { + return nil, nil + } + if err != nil { + return nil, err + } + + d := datetime.DateFromDayNumber(int(evalToInt64(arg).i)) + + // mysql returns NULL if year is greater than 9999 + if d.Year() > 9999 { + return nil, nil + } + return newEvalDate(d, true), nil +} + +func (call *builtinFromDays) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + switch arg.Type { + case sqltypes.Int64: + default: + c.asm.Convert_xi(1) + } + + c.asm.Fn_FROM_DAYS() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.Date, Flag: arg.Flag | flagNullable}, nil +} + +func (b *builtinTimeToSec) eval(env *ExpressionEnv) (eval, error) { + arg, err := b.arg1(env) + if arg == nil { + return nil, nil + } + if err != nil { + return nil, err + } + + d := evalToTime(arg, -1) + if d == nil { + return nil, nil + } + + sec := d.dt.Time.ToSeconds() + return newEvalInt64(sec), nil +} + +func (call *builtinTimeToSec) compile(c *compiler) (ctype, error) { + arg, err := call.Arguments[0].compile(c) + if err != nil { + return ctype{}, err + } + + skip := c.compileNullCheck1(arg) + + switch arg.Type { + case sqltypes.Date, sqltypes.Datetime, sqltypes.Time: + default: + c.asm.Convert_xT(1, -1) + } + + c.asm.Fn_TIME_TO_SEC() + c.asm.jumpDestination(skip) + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag | flagNullable}, nil +} + func (b *builtinQuarter) eval(env *ExpressionEnv) (eval, error) { date, err := b.arg1(env) if err != nil { @@ -1172,7 +1381,7 @@ func (b *builtinQuarter) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, true) if d == nil { return nil, nil } @@ -1190,7 +1399,7 @@ func (call *builtinQuarter) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, true) } c.asm.Fn_QUARTER() c.asm.jumpDestination(skip) @@ -1268,8 +1477,8 @@ func dateTimeUnixTimestamp(env *ExpressionEnv, date eval) evalNumeric { case *evalTemporal: dt = e.toDateTime(int(e.prec), env.now) default: - dt = evalToDateTime(date, -1, env.now) - if dt == nil || dt.isZero() { + dt = evalToDateTime(date, -1, env.now, false) + if dt == nil { var prec int32 switch d := date.(type) { case *evalInt64, *evalUint64: @@ -1280,15 +1489,19 @@ func dateTimeUnixTimestamp(env *ExpressionEnv, date eval) evalNumeric { if d.isHexOrBitLiteral() { return newEvalInt64(0) } - prec = 6 + prec = maxTimePrec default: - prec = 6 + prec = maxTimePrec } return newEvalDecimalWithPrec(decimal.Zero, prec) } } ts := dt.dt.ToStdTime(env.now) + if ts.Unix() < 0 || ts.Unix() >= maxUnixtime { + return newEvalInt64(0) + } + if dt.prec == 0 { return newEvalInt64(ts.Unix()) } @@ -1336,7 +1549,30 @@ func (call *builtinUnixTimestamp) compile(c *compiler) (ctype, error) { c.asm.Fn_UNIX_TIMESTAMP1() c.asm.jumpDestination(skip) - return ctype{Type: sqltypes.Int64, Col: collationBinary, Flag: arg.Flag | flagAmbiguousType}, nil + switch arg.Type { + case sqltypes.Datetime, sqltypes.Time, sqltypes.Decimal: + if arg.Size == 0 { + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag}, nil + } + return ctype{Type: sqltypes.Decimal, Size: arg.Size, Col: collationNumeric, Flag: arg.Flag}, nil + case sqltypes.Date, sqltypes.Int64, sqltypes.Uint64: + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag}, nil + case sqltypes.VarChar, sqltypes.VarBinary: + if arg.isHexOrBitLiteral() { + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag}, nil + } + if lit, ok := call.Arguments[0].(*Literal); ok { + if dt := evalToDateTime(lit.inner, -1, time.Now(), c.sqlmode.AllowZeroDate()); dt != nil { + if dt.prec == 0 { + return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag}, nil + } + return ctype{Type: sqltypes.Decimal, Size: int32(dt.prec), Col: collationNumeric, Flag: arg.Flag}, nil + } + } + fallthrough + default: + return ctype{Type: sqltypes.Decimal, Size: maxTimePrec, Col: collationNumeric, Flag: arg.Flag}, nil + } } func (b *builtinWeek) eval(env *ExpressionEnv) (eval, error) { @@ -1348,8 +1584,8 @@ func (b *builtinWeek) eval(env *ExpressionEnv) (eval, error) { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } @@ -1381,7 +1617,7 @@ func (call *builtinWeek) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } if len(call.Arguments) == 1 { @@ -1408,8 +1644,8 @@ func (b *builtinWeekDay) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } return newEvalInt64(int64(d.dt.Date.Weekday()+6) % 7), nil @@ -1426,7 +1662,7 @@ func (call *builtinWeekDay) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } c.asm.Fn_WEEKDAY() @@ -1442,8 +1678,8 @@ func (b *builtinWeekOfYear) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } @@ -1462,7 +1698,7 @@ func (call *builtinWeekOfYear) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } c.asm.Fn_WEEKOFYEAR() @@ -1478,7 +1714,7 @@ func (b *builtinYear) eval(env *ExpressionEnv) (eval, error) { if date == nil { return nil, nil } - d := evalToDate(date, env.now) + d := evalToDate(date, env.now, true) if d == nil { return nil, nil } @@ -1497,7 +1733,7 @@ func (call *builtinYear) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD(1) + c.asm.Convert_xD(1, true) } c.asm.Fn_YEAR() @@ -1514,8 +1750,8 @@ func (b *builtinYearWeek) eval(env *ExpressionEnv) (eval, error) { return nil, nil } - d := evalToDate(date, env.now) - if d == nil || d.isZero() { + d := evalToDate(date, env.now, false) + if d == nil { return nil, nil } @@ -1547,7 +1783,7 @@ func (call *builtinYearWeek) compile(c *compiler) (ctype, error) { switch arg.Type { case sqltypes.Date, sqltypes.Datetime: default: - c.asm.Convert_xD_nz(1) + c.asm.Convert_xD(1, false) } if len(call.Arguments) == 1 { @@ -1599,7 +1835,7 @@ func (call *builtinDateMath) eval(env *ExpressionEnv) (eval, error) { return tmp.addInterval(interval, collations.Unknown, env.now), nil } - if tmp := evalToTemporal(date); tmp != nil { + if tmp := evalToTemporal(date, env.sqlmode.AllowZeroDate()); tmp != nil { return tmp.addInterval(interval, call.collate, env.now), nil } diff --git a/go/vt/vtgate/evalengine/format.go b/go/vt/vtgate/evalengine/format.go index db473fd418e..7576a8b0cda 100644 --- a/go/vt/vtgate/evalengine/format.go +++ b/go/vt/vtgate/evalengine/format.go @@ -152,6 +152,18 @@ func (bv *BindVariable) format(buf *sqlparser.TrackedBuffer) { } } +func (bv *TupleBindVariable) Format(buf *sqlparser.TrackedBuffer) { + bv.format(buf) +} + +func (bv *TupleBindVariable) FormatFast(buf *sqlparser.TrackedBuffer) { + bv.format(buf) +} + +func (bv *TupleBindVariable) format(buf *sqlparser.TrackedBuffer) { + buf.WriteString(fmt.Sprintf("%s:%d", bv.Key, bv.Index)) +} + func (c *Column) Format(buf *sqlparser.TrackedBuffer) { c.format(buf) } @@ -206,12 +218,12 @@ func (tuple TupleExpr) format(buf *sqlparser.TrackedBuffer) { func (c *CollateExpr) format(buf *sqlparser.TrackedBuffer) { formatExpr(buf, c, c.Inner, true) buf.WriteLiteral(" COLLATE ") - buf.WriteString(collations.Local().LookupName(c.TypedCollation.Collation)) + buf.WriteString(c.CollationEnv.LookupName(c.TypedCollation.Collation)) } func (i *IntroducerExpr) format(buf *sqlparser.TrackedBuffer) { buf.WriteString("_") - buf.WriteString(collations.Local().LookupName(i.TypedCollation.Collation)) + buf.WriteString(i.CollationEnv.LookupName(i.TypedCollation.Collation)) formatExpr(buf, i, i.Inner, true) } @@ -261,7 +273,7 @@ func (c *builtinWeightString) format(buf *sqlparser.TrackedBuffer) { if c.Cast != "" { buf.WriteLiteral(" as ") buf.WriteLiteral(c.Cast) - _, _ = fmt.Fprintf(buf, "(%d)", c.Len) + _, _ = fmt.Fprintf(buf, "(%d)", *c.Len) } buf.WriteByte(')') } @@ -285,16 +297,16 @@ func (c *ConvertExpr) format(buf *sqlparser.TrackedBuffer) { formatExpr(buf, c, c.Inner, true) switch { - case c.HasLength && c.HasScale: - _, _ = fmt.Fprintf(buf, ", %s(%d,%d)", c.Type, c.Length, c.Scale) - case c.HasLength: - _, _ = fmt.Fprintf(buf, ", %s(%d)", c.Type, c.Length) + case c.Length != nil && c.Scale != nil: + _, _ = fmt.Fprintf(buf, ", %s(%d,%d)", c.Type, *c.Length, *c.Scale) + case c.Length != nil: + _, _ = fmt.Fprintf(buf, ", %s(%d)", c.Type, *c.Length) default: _, _ = fmt.Fprintf(buf, ", %s", c.Type) } if c.Collation != collations.Unknown { buf.WriteLiteral(" character set ") - buf.WriteString(collations.Local().LookupName(c.Collation)) + buf.WriteString(c.CollationEnv.LookupName(c.Collation)) } buf.WriteByte(')') } @@ -303,7 +315,7 @@ func (c *ConvertUsingExpr) format(buf *sqlparser.TrackedBuffer) { buf.WriteLiteral("convert(") formatExpr(buf, c, c.Inner, true) buf.WriteLiteral(" using ") - buf.WriteString(collations.Local().LookupName(c.Collation)) + buf.WriteString(c.CollationEnv.LookupName(c.Collation)) buf.WriteByte(')') } diff --git a/go/vt/vtgate/evalengine/integration/comparison_test.go b/go/vt/vtgate/evalengine/integration/comparison_test.go index 649dc7b5583..ea327601975 100644 --- a/go/vt/vtgate/evalengine/integration/comparison_test.go +++ b/go/vt/vtgate/evalengine/integration/comparison_test.go @@ -28,9 +28,11 @@ import ( "time" "github.com/spf13/pflag" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/format" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" @@ -38,13 +40,12 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/evalengine/testcases" ) var ( - collationEnv *collations.Environment - debugGolden = false debugNormalize = true debugSimplify = time.Now().UnixNano()&1 != 0 @@ -81,7 +82,7 @@ func normalizeValue(v sqltypes.Value, coll collations.ID) sqltypes.Value { return v } -func compareRemoteExprEnv(t *testing.T, env *evalengine.ExpressionEnv, conn *mysql.Conn, expr string, fields []*querypb.Field, cmp *testcases.Comparison) { +func compareRemoteExprEnv(t *testing.T, collationEnv *collations.Environment, env *evalengine.ExpressionEnv, conn *mysql.Conn, expr string, fields []*querypb.Field, cmp *testcases.Comparison) { t.Helper() localQuery := "SELECT " + expr @@ -144,7 +145,7 @@ func compareRemoteExprEnv(t *testing.T, env *evalengine.ExpressionEnv, conn *mys var localVal, remoteVal sqltypes.Value var localCollation, remoteCollation collations.ID if localErr == nil { - v := local.Value(collations.Default()) + v := local.Value(collations.MySQL8().DefaultConnectionCharset()) if debugCheckCollations { if v.IsNull() { localCollation = collations.CollationBinaryID @@ -205,6 +206,7 @@ func compareRemoteExprEnv(t *testing.T, env *evalengine.ExpressionEnv, conn *mys var seenGoldenTests []GoldenTest type vcursor struct { + env *vtenv.Environment } func (vc *vcursor) GetKeyspace() string { @@ -215,6 +217,14 @@ func (vc *vcursor) TimeZone() *time.Location { return time.Local } +func (vc *vcursor) SQLMode() string { + return config.DefaultSQLMode +} + +func (vc *vcursor) Environment() *vtenv.Environment { + return vc.env +} + func initTimezoneData(t *testing.T, conn *mysql.Conn) { // We load the timezone information into MySQL. The evalengine assumes // our backend MySQL is configured with the timezone information as well @@ -247,20 +257,23 @@ func TestMySQL(t *testing.T) { // We require MySQL 8.0 collations for the comparisons in the tests - servenv.SetMySQLServerVersionForTest(conn.ServerVersion) - collationEnv = collations.NewEnvironment(conn.ServerVersion) + collationEnv := collations.NewEnvironment(conn.ServerVersion) servenv.OnParse(registerFlags) initTimezoneData(t, conn) + venv, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: conn.ServerVersion, + }) + require.NoError(t, err) for _, tc := range testcases.Cases { t.Run(tc.Name(), func(t *testing.T) { ctx := callerid.NewContext(context.Background(), &vtrpc.CallerID{Principal: "testuser"}, &querypb.VTGateCallerID{ Username: "vt_dba", }) - env := evalengine.NewExpressionEnv(ctx, nil, &vcursor{}) + env := evalengine.NewExpressionEnv(ctx, nil, &vcursor{env: venv}) tc.Run(func(query string, row []sqltypes.Value) { env.Row = row - compareRemoteExprEnv(t, env, conn, query, tc.Schema, tc.Compare) + compareRemoteExprEnv(t, collationEnv, env, conn, query, tc.Schema, tc.Compare) }) }) } diff --git a/go/vt/vtgate/evalengine/integration/fuzz_test.go b/go/vt/vtgate/evalengine/integration/fuzz_test.go index 657fbcb7c68..cb29b9c9c32 100644 --- a/go/vt/vtgate/evalengine/integration/fuzz_test.go +++ b/go/vt/vtgate/evalengine/integration/fuzz_test.go @@ -19,7 +19,6 @@ limitations under the License. package integration import ( - "context" "encoding/json" "fmt" "math/rand" @@ -35,6 +34,7 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/evalengine/testcases" "vitess.io/vitess/go/vt/vtgate/simplifier" @@ -132,7 +132,7 @@ func errorsMatch(remote, local error) bool { } func evaluateLocalEvalengine(env *evalengine.ExpressionEnv, query string, fields []*querypb.Field) (evalengine.EvalResult, error) { - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) if err != nil { return evalengine.EvalResult{}, err } @@ -147,6 +147,7 @@ func evaluateLocalEvalengine(env *evalengine.ExpressionEnv, query string, fields cfg := &evalengine.Config{ ResolveColumn: evalengine.FieldResolver(fields).Column, Collation: collations.CollationUtf8mb4ID, + Environment: env.VCursor().Environment(), NoConstantFolding: !debugSimplify, } expr, err = evalengine.Translate(astExpr, cfg) @@ -197,10 +198,11 @@ func TestGenerateFuzzCases(t *testing.T) { var conn = mysqlconn(t) defer conn.Close() + venv := vtenv.NewTestEnv() compareWithMySQL := func(expr sqlparser.Expr) *mismatch { query := "SELECT " + sqlparser.String(expr) - env := evalengine.NewExpressionEnv(context.Background(), nil, nil) + env := evalengine.EmptyExpressionEnv(venv) eval, localErr := evaluateLocalEvalengine(env, query, nil) remote, remoteErr := conn.ExecuteFetch(query, 1, false) @@ -218,7 +220,7 @@ func TestGenerateFuzzCases(t *testing.T) { remoteErr: remoteErr, } if localErr == nil { - res.localVal = eval.Value(collations.Default()) + res.localVal = eval.Value(collations.MySQL8().DefaultConnectionCharset()) } if remoteErr == nil { res.remoteVal = remote.Rows[0][0] @@ -233,7 +235,7 @@ func TestGenerateFuzzCases(t *testing.T) { var start = time.Now() for len(failures) < fuzzMaxFailures { query := "SELECT " + gen.expr() - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) if err != nil { t.Fatal(err) } @@ -333,7 +335,7 @@ func compareResult(local, remote Result, cmp *testcases.Comparison) error { var localCollationName string var remoteCollationName string - env := collations.Local() + env := collations.MySQL8() if coll := local.Collation; coll != collations.Unknown { localCollationName = env.LookupName(coll) } diff --git a/go/vt/vtgate/evalengine/mysql_test.go b/go/vt/vtgate/evalengine/mysql_test.go index bfa503d82dd..6434d7dcfbf 100644 --- a/go/vt/vtgate/evalengine/mysql_test.go +++ b/go/vt/vtgate/evalengine/mysql_test.go @@ -17,7 +17,6 @@ limitations under the License. package evalengine import ( - "context" "encoding/json" "errors" "os" @@ -28,6 +27,7 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" ) func internalExpression(e Expr) IR { @@ -63,13 +63,14 @@ func knownBadQuery(e Expr) bool { var errKnownBadQuery = errors.New("this query is known to give bad results in MySQL") func convert(t *testing.T, query string, simplify bool) (Expr, error) { - stmt, err := sqlparser.Parse(query) + stmt, err := sqlparser.NewTestParser().Parse(query) if err != nil { t.Fatalf("failed to parse '%s': %v", query, err) } cfg := &Config{ Collation: collations.CollationUtf8mb4ID, + Environment: vtenv.NewTestEnv(), NoConstantFolding: !simplify, } @@ -89,7 +90,7 @@ func testSingle(t *testing.T, query string) (EvalResult, error) { if err != nil { return EvalResult{}, err } - return NewExpressionEnv(context.Background(), nil, nil).Evaluate(converted) + return EmptyExpressionEnv(vtenv.NewTestEnv()).Evaluate(converted) } func TestMySQLGolden(t *testing.T) { @@ -141,11 +142,11 @@ func TestMySQLGolden(t *testing.T) { continue } if tc.Error != "" { - t.Errorf("query %d: %s\nmysql err: %s\nvitess val: %s", testcount, tc.Query, tc.Error, eval.Value(collations.Default())) + t.Errorf("query %d: %s\nmysql err: %s\nvitess val: %s", testcount, tc.Query, tc.Error, eval.Value(collations.MySQL8().DefaultConnectionCharset())) continue } if eval.String() != tc.Value { - t.Errorf("query %d: %s\nmysql val: %s\nvitess val: %s", testcount, tc.Query, tc.Value, eval.Value(collations.Default())) + t.Errorf("query %d: %s\nmysql val: %s\nvitess val: %s", testcount, tc.Query, tc.Value, eval.Value(collations.MySQL8().DefaultConnectionCharset())) continue } ok++ @@ -159,5 +160,5 @@ func TestMySQLGolden(t *testing.T) { func TestDebug1(t *testing.T) { // Debug eval, err := testSingle(t, `SELECT _latin1 0xFF regexp _latin1 '[[:lower:]]' COLLATE latin1_bin`) - t.Logf("eval=%s err=%v coll=%s", eval.String(), err, collations.Local().LookupName(eval.Collation())) + t.Logf("eval=%s err=%v coll=%s", eval.String(), err, collations.MySQL8().LookupName(eval.Collation())) } diff --git a/go/vt/vtgate/evalengine/perf_test.go b/go/vt/vtgate/evalengine/perf_test.go index 10974cd313d..f3e0b32de08 100644 --- a/go/vt/vtgate/evalengine/perf_test.go +++ b/go/vt/vtgate/evalengine/perf_test.go @@ -5,7 +5,7 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -22,8 +22,9 @@ func BenchmarkCompilerExpressions(b *testing.B) { {"comparison_f", "column0 = 12", []sqltypes.Value{sqltypes.NewFloat64(420.0)}}, } + venv := vtenv.NewTestEnv() for _, tc := range testCases { - expr, err := sqlparser.ParseExpr(tc.expression) + expr, err := venv.Parser().ParseExpr(tc.expression) if err != nil { b.Fatal(err) } @@ -33,6 +34,7 @@ func BenchmarkCompilerExpressions(b *testing.B) { ResolveColumn: fields.Column, ResolveType: fields.Type, Collation: collations.CollationUtf8mb4ID, + Environment: venv, } translated, err := evalengine.Translate(expr, cfg) diff --git a/go/vt/vtgate/evalengine/testcases/cases.go b/go/vt/vtgate/evalengine/testcases/cases.go index cd52631c00c..b55f6c2c18d 100644 --- a/go/vt/vtgate/evalengine/testcases/cases.go +++ b/go/vt/vtgate/evalengine/testcases/cases.go @@ -63,12 +63,15 @@ var Cases = []TestCase{ {Run: TupleComparisons}, {Run: Comparisons}, {Run: InStatement}, + {Run: FnInsert}, {Run: FnLower}, {Run: FnUpper}, {Run: FnCharLength}, {Run: FnLength}, {Run: FnBitLength}, {Run: FnAscii}, + {Run: FnReverse}, + {Run: FnSpace}, {Run: FnOrd}, {Run: FnRepeat}, {Run: FnLeft}, @@ -78,8 +81,12 @@ var Cases = []TestCase{ {Run: FnLTrim}, {Run: FnRTrim}, {Run: FnTrim}, + {Run: FnSubstr}, + {Run: FnLocate}, + {Run: FnReplace}, {Run: FnConcat}, {Run: FnConcatWs}, + {Run: FnChar}, {Run: FnHex}, {Run: FnUnhex}, {Run: FnCeil}, @@ -111,6 +118,8 @@ var Cases = []TestCase{ {Run: FnTruncate}, {Run: FnCrc32}, {Run: FnConv}, + {Run: FnBin}, + {Run: FnOct}, {Run: FnMD5}, {Run: FnSHA1}, {Run: FnSHA2}, @@ -129,6 +138,10 @@ var Cases = []TestCase{ {Run: FnMinute}, {Run: FnMonth}, {Run: FnMonthName}, + {Run: FnLastDay}, + {Run: FnToDays}, + {Run: FnFromDays}, + {Run: FnTimeToSec}, {Run: FnQuarter}, {Run: FnSecond}, {Run: FnTime}, @@ -655,6 +668,24 @@ func FnConv(yield Query) { } } +func FnBin(yield Query) { + for _, num := range radianInputs { + yield(fmt.Sprintf("BIN(%s)", num), nil) + } + for _, num := range inputBitwise { + yield(fmt.Sprintf("BIN(%s)", num), nil) + } +} + +func FnOct(yield Query) { + for _, num := range radianInputs { + yield(fmt.Sprintf("OCT(%s)", num), nil) + } + for _, num := range inputBitwise { + yield(fmt.Sprintf("OCT(%s)", num), nil) + } +} + func FnMD5(yield Query) { for _, num := range radianInputs { yield(fmt.Sprintf("MD5(%s)", num), nil) @@ -1307,6 +1338,28 @@ var JSONExtract_Schema = []*querypb.Field{ }, } +func FnInsert(yield Query) { + for _, s := range insertStrings { + for _, ns := range insertStrings { + for _, l := range inputBitwise { + for _, p := range inputBitwise { + yield(fmt.Sprintf("INSERT(%s, %s, %s, %s)", s, p, l, ns), nil) + } + } + } + } + + mysqlDocSamples := []string{ + "INSERT('Quadratic', 3, 4, 'What')", + "INSERT('Quadratic', -1, 4, 'What')", + "INSERT('Quadratic', 3, 100, 'What')", + } + + for _, q := range mysqlDocSamples { + yield(q, nil) + } +} + func FnLower(yield Query) { for _, str := range inputStrings { yield(fmt.Sprintf("LOWER(%s)", str), nil) @@ -1347,6 +1400,34 @@ func FnAscii(yield Query) { } } +func FnReverse(yield Query) { + for _, str := range inputStrings { + yield(fmt.Sprintf("REVERSE(%s)", str), nil) + } +} + +func FnSpace(yield Query) { + counts := []string{ + "0", + "12", + "23", + "-1", + "-12393128120", + "-432766734237843674326423876243876234786", + "'-432766734237843674326423876243876234786'", + "432766734237843674326423876243876234786", + "1073741825", + "1.5", + "-3.2", + "'jhgjhg'", + "6", + } + + for _, c := range counts { + yield(fmt.Sprintf("SPACE(%s)", c), nil) + } +} + func FnOrd(yield Query) { for _, str := range inputStrings { yield(fmt.Sprintf("ORD(%s)", str), nil) @@ -1436,6 +1517,94 @@ func FnTrim(yield Query) { } } +func FnSubstr(yield Query) { + mysqlDocSamples := []string{ + `SUBSTRING('Quadratically',5)`, + `SUBSTRING('foobarbar' FROM 4)`, + `SUBSTRING('Quadratically',5,6)`, + `SUBSTRING('Sakila', -3)`, + `SUBSTRING('Sakila', -5, 3)`, + `SUBSTRING('Sakila' FROM -4 FOR 2)`, + `SUBSTR('Quadratically',5)`, + `SUBSTR('foobarbar' FROM 4)`, + `SUBSTR('Quadratically',5,6)`, + `SUBSTR('Sakila', -3)`, + `SUBSTR('Sakila', -5, 3)`, + `SUBSTR('Sakila' FROM -4 FOR 2)`, + `MID('Quadratically',5,6)`, + `MID('Sakila', -5, 3)`, + } + + for _, q := range mysqlDocSamples { + yield(q, nil) + } + + for _, str := range inputStrings { + for _, i := range radianInputs { + yield(fmt.Sprintf("SUBSTRING(%s, %s)", str, i), nil) + + for _, j := range radianInputs { + yield(fmt.Sprintf("SUBSTRING(%s, %s, %s)", str, i, j), nil) + } + } + } +} + +func FnLocate(yield Query) { + mysqlDocSamples := []string{ + `LOCATE('bar', 'foobarbar')`, + `LOCATE('xbar', 'foobar')`, + `LOCATE('bar', 'foobarbar', 5)`, + `INSTR('foobarbar', 'bar')`, + `INSTR('xbar', 'foobar')`, + `POSITION('bar' IN 'foobarbar')`, + `POSITION('xbar' IN 'foobar')`, + } + + for _, q := range mysqlDocSamples { + yield(q, nil) + } + + for _, substr := range locateStrings { + for _, str := range locateStrings { + yield(fmt.Sprintf("LOCATE(%s, %s)", substr, str), nil) + yield(fmt.Sprintf("INSTR(%s, %s)", str, substr), nil) + yield(fmt.Sprintf("POSITION(%s IN %s)", str, substr), nil) + + for _, i := range radianInputs { + yield(fmt.Sprintf("LOCATE(%s, %s, %s)", substr, str, i), nil) + } + } + } +} + +func FnReplace(yield Query) { + cases := []string{ + `REPLACE('www.mysql.com', 'w', 'Ww')`, + // MySQL doesn't do collation matching for replace, only + // byte equivalence, but make sure to check. + `REPLACE('straße', 'ss', 'b')`, + `REPLACE('straße', 'ß', 'b')`, + // From / to strings are converted into the collation of + // the input string. + `REPLACE('fooÿbar', _latin1 0xFF, _latin1 0xFE)`, + // First occurence is replaced + `replace('fff', 'ff', 'gg')`, + } + + for _, q := range cases { + yield(q, nil) + } + + for _, substr := range inputStrings { + for _, str := range inputStrings { + for _, i := range inputStrings { + yield(fmt.Sprintf("REPLACE(%s, %s, %s)", substr, str, i), nil) + } + } + } +} + func FnConcat(yield Query) { for _, str := range inputStrings { yield(fmt.Sprintf("CONCAT(%s)", str), nil) @@ -1486,6 +1655,31 @@ func FnConcatWs(yield Query) { } } +func FnChar(yield Query) { + mysqlDocSamples := []string{ + `CHAR(77,121,83,81,'76')`, + `CHAR(77,77.3,'77.3')`, + `CHAR(77,121,83,81,'76' USING utf8mb4)`, + `CHAR(77,77.3,'77.3' USING utf8mb4)`, + `HEX(CHAR(1,0))`, + `HEX(CHAR(256))`, + `HEX(CHAR(1,0,0))`, + `HEX(CHAR(256*256)`, + } + + for _, q := range mysqlDocSamples { + yield(q, nil) + } + + for _, i1 := range radianInputs { + for _, i2 := range inputBitwise { + for _, i3 := range inputConversions { + yield(fmt.Sprintf("CHAR(%s, %s, %s)", i1, i2, i3), nil) + } + } + } +} + func FnHex(yield Query) { for _, str := range inputStrings { yield(fmt.Sprintf("hex(%s)", str), nil) @@ -1710,6 +1904,99 @@ func FnMonthName(yield Query) { } } +func FnLastDay(yield Query) { + for _, d := range inputConversions { + yield(fmt.Sprintf("LAST_DAY(%s)", d), nil) + } + + dates := []string{ + `DATE'2024-02-18'`, + `DATE'2023-02-01'`, + `DATE'2100-02-01'`, + `TIMESTAMP'2020-12-31 23:59:59'`, + `TIMESTAMP'2025-01-01 00:00:00'`, + `'2000-02-01'`, + `'2020-12-31 23:59:59'`, + `'2025-01-01 00:00:00'`, + `20250101`, + `'20250101'`, + } + + for _, d := range dates { + yield(fmt.Sprintf("LAST_DAY(%s)", d), nil) + } +} + +func FnToDays(yield Query) { + for _, d := range inputConversions { + yield(fmt.Sprintf("TO_DAYS(%s)", d), nil) + } + + dates := []string{ + `DATE'0000-00-00'`, + `0`, + `'0000-00-00'`, + `DATE'2023-09-03 00:00:00'`, + `DATE'2023-09-03 07:00:00'`, + `DATE'0000-00-00 00:00:00'`, + `950501`, + `'2007-10-07'`, + `'2008-10-07'`, + `'08-10-07'`, + `'0000-01-01'`, + } + + for _, d := range dates { + yield(fmt.Sprintf("TO_DAYS(%s)", d), nil) + } +} + +func FnFromDays(yield Query) { + for _, d := range inputConversions { + yield(fmt.Sprintf("FROM_DAYS(%s)", d), nil) + } + + days := []string{ + "0", + "1", + "366", + "365242", + "3652424", + "3652425", + "3652500", + "3652499", + "730669", + } + + for _, d := range days { + yield(fmt.Sprintf("FROM_DAYS(%s)", d), nil) + } +} + +func FnTimeToSec(yield Query) { + for _, d := range inputConversions { + yield(fmt.Sprintf("TIME_TO_SEC(%s)", d), nil) + } + + time := []string{ + `0`, + `'00:00:00'`, + `'22:23:00'`, + `'00:39:38'`, + `TIME'00:39:38'`, + `TIME'102:39:38'`, + `TIME'838:59:59'`, + `TIME'-838:59:59'`, + `'000220`, + `'2003-09-03 00:39:38'`, + `'2003-09-03'`, + } + + for _, t := range time { + yield(fmt.Sprintf("TIME_TO_SEC(%s)", t), nil) + } +} + func FnQuarter(yield Query) { for _, d := range inputConversions { yield(fmt.Sprintf("QUARTER(%s)", d), nil) @@ -1726,6 +2013,25 @@ func FnTime(yield Query) { for _, d := range inputConversions { yield(fmt.Sprintf("TIME(%s)", d), nil) } + times := []string{ + "'00:00:00'", + "'asdadsasd'", + "'312sadd'", + "'11-12-23'", + "'0000-11-23'", + "'0-0-0'", + "00:00", + "00:00-00", + "00:00:0:0:0:0", + "00::00", + "12::00", + "'00000001'", + "'11116656'", + } + + for _, d := range times { + yield(fmt.Sprintf("TIME(%s)", d), nil) + } } func FnUnixTimestamp(yield Query) { @@ -1767,7 +2073,7 @@ func FnYear(yield Query) { } func FnYearWeek(yield Query) { - for i := 0; i < 4; i++ { + for i := 0; i < 8; i++ { for _, d := range inputConversions { yield(fmt.Sprintf("YEARWEEK(%s, %d)", d, i), nil) } diff --git a/go/vt/vtgate/evalengine/testcases/helpers.go b/go/vt/vtgate/evalengine/testcases/helpers.go index 245d59992aa..a908b8196c8 100644 --- a/go/vt/vtgate/evalengine/testcases/helpers.go +++ b/go/vt/vtgate/evalengine/testcases/helpers.go @@ -187,12 +187,12 @@ func (cmp *Comparison) Equals(local, remote sqltypes.Value, now time.Time) (bool } return cmp.closeDatetime(localDatetime.ToStdTime(now), remoteDatetime.ToStdTime(now), 1*time.Second), nil case cmp.LooseTime && local.IsTime() && remote.IsTime(): - localTime, _, ok := datetime.ParseTime(local.ToString(), -1) - if !ok { + localTime, _, state := datetime.ParseTime(local.ToString(), -1) + if state != datetime.TimeOK { return false, fmt.Errorf("error converting local value '%s' to time", local) } - remoteTime, _, ok := datetime.ParseTime(remote.ToString(), -1) - if !ok { + remoteTime, _, state := datetime.ParseTime(remote.ToString(), -1) + if state != datetime.TimeOK { return false, fmt.Errorf("error converting remote value '%s' to time", remote) } return cmp.closeDatetime(localTime.ToStdTime(now), remoteTime.ToStdTime(now), 1*time.Second), nil diff --git a/go/vt/vtgate/evalengine/testcases/inputs.go b/go/vt/vtgate/evalengine/testcases/inputs.go index c6796ba5d32..c4ab2fdb92d 100644 --- a/go/vt/vtgate/evalengine/testcases/inputs.go +++ b/go/vt/vtgate/evalengine/testcases/inputs.go @@ -93,19 +93,22 @@ var inputConversions = []string{ `0x0`, `0x1`, `0xff`, `X'00'`, `X'01'`, `X'ff'`, `0b1001`, `b'1001'`, `0x9`, `x'09'`, "NULL", "true", "false", + "NULL * 1", "1 * NULL", "NULL * NULL", "NULL / 1", "1 / NULL", "NULL / NULL", + "NULL + 1", "1 + NULL", "NULL + NULL", "NULL - 1", "1 - NULL", "NULL - NULL", "0xFF666F6F626172FF", "0x666F6F626172FF", "0xFF666F6F626172", "9223372036854775807", "-9223372036854775808", "18446744073709551615", "18446744073709540000e0", "-18446744073709540000e0", "JSON_OBJECT()", "JSON_ARRAY()", - "time '10:04:58'", "time '31:34:58'", "time '32:34:58'", "time '130:34:58'", "time '5 10:34:58'", "date '2000-01-01'", + "time '10:04:58'", "time '31:34:58'", "time '32:34:58'", "time '130:34:58'", "time '5 10:34:58'", + "time '10:04:58.1'", "time '31:34:58.4'", "time '32:34:58.5'", "time '130:34:58.6'", "time '5 10:34:58.9'", "date '2000-01-01'", "timestamp '2000-01-01 10:34:58'", "timestamp '2000-01-01 10:34:58.123456'", "timestamp '2000-01-01 10:34:58.978654'", "20000101103458", "20000101103458.1234", "20000101103458.123456", "20000101", "103458", "103458.123456", "'20000101103458'", "'20000101103458.1234'", "'20000101103458.123456'", "'20000101'", "'103458'", "'103458.123456'", "'20000101103458foo'", "'20000101103458.1234foo'", "'20000101103458.123456foo'", "'20000101foo'", "'103458foo'", "'103458.123456foo'", "time '-10:04:58'", "time '-31:34:58'", "time '-32:34:58'", "time '-101:34:58'", "time '-5 10:34:58'", - "'10:04:58'", "'101:34:58'", "'5 10:34:58'", "'2000-01-01'", "'2000-01-01 12:34:58'", + "'10:04:58'", "'101:34:58'", "'5 10:34:58'", "'2000-01-01'", "'2000-01-01 12:34:58'", "'0000-02-29'", "'0000-01-03'", "'1969-02-18'", "'1970-01-01 00:00:01'", "'3001-02-19 00:00:00'", "'3001-02-18 23:59:59'", "cast(0 as json)", "cast(1 as json)", "cast(true as json)", "cast(false as json)", "cast('{}' as json)", "cast('[]' as json)", @@ -196,6 +199,77 @@ var inputStrings = []string{ // "_ucs2 'AabcÅå'", } +var insertStrings = []string{ + "NULL", + "\"\"", + "\"a\"", + "\"abc\"", + "1", + "-1", + "0123", + "0xAACC", + "3.1415926", + // MySQL has broken behavior for these inputs, + // see https://github.com/mysql/mysql-server/pull/517 + // "\"Å å\"", + // "\"中文测试\"", + // "\"日本語テスト\"", + // "\"한국어 시험\"", + // "\"😊😂🤢\"", + // "_utf8mb4 'abcABCÅå'", + "DATE '2022-10-11'", + "TIME '11:02:23'", + "'123'", + "9223372036854775807", + "-9223372036854775808", + "999999999999999999999999", + "-999999999999999999999999", + "_binary 'Müller' ", + "_latin1 0xFF", + // TODO: support other multibyte encodings + // "_dec8 'ÒòÅå'", + // "_utf8mb3 'abcABCÅå'", + // "_utf16 'AabcÅå'", + // "_utf32 'AabcÅå'", + // "_ucs2 'AabcÅå'", +} + +var locateStrings = []string{ + "NULL", + "\"\"", + "\"a\"", + "\"abc\"", + "1", + "-1", + "0123", + "0xAACC", + "3.1415926", + // MySQL has broken behavior for these inputs, + // see https://bugs.mysql.com/bug.php?id=113933 + // "\"Å å\"", + // "\"中文测试\"", + // "\"日本語テスト\"", + // "\"한국어 시험\"", + // "\"😊😂🤢\"", + // "_utf8mb4 'abcABCÅå'", + "DATE '2022-10-11'", + "TIME '11:02:23'", + "'123'", + "9223372036854775807", + "-9223372036854775808", + "999999999999999999999999", + "-999999999999999999999999", + "_binary 'Müller' ", + "_utf8mb4 'abcABCÅå'", + "_latin1 0xFF", + // TODO: support other multibyte encodings + // "_dec8 'ÒòÅå'", + // "_utf8mb3 'abcABCÅå'", + // "_utf16 'AabcÅå'", + // "_utf32 'AabcÅå'", + // "_ucs2 'AabcÅå'", +} + var inputConversionTypes = []string{ "BINARY", "BINARY(1)", "BINARY(0)", "BINARY(16)", "BINARY(-1)", "CHAR", "CHAR(1)", "CHAR(0)", "CHAR(16)", "CHAR(-1)", diff --git a/go/vt/vtgate/evalengine/translate.go b/go/vt/vtgate/evalengine/translate.go index c8f6f7d1337..d1c32b113c2 100644 --- a/go/vt/vtgate/evalengine/translate.go +++ b/go/vt/vtgate/evalengine/translate.go @@ -27,6 +27,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" ) @@ -193,7 +194,7 @@ func (ast *astCompiler) translateIsExpr(left sqlparser.Expr, op sqlparser.IsExpr } func (ast *astCompiler) translateBindVar(arg *sqlparser.Argument) (IR, error) { - bvar := NewBindVar(arg.Name, Type{Type: arg.Type, Coll: ast.cfg.Collation}) + bvar := NewBindVar(arg.Name, NewType(arg.Type, ast.cfg.Collation)) if !bvar.typed() { bvar.dynamicTypeOffset = len(ast.untyped) @@ -203,12 +204,12 @@ func (ast *astCompiler) translateBindVar(arg *sqlparser.Argument) (IR, error) { } func (ast *astCompiler) translateColOffset(col *sqlparser.Offset) (IR, error) { - typ := UnknownType() + var typ Type if ast.cfg.ResolveType != nil { typ, _ = ast.cfg.ResolveType(col.Original) } - if typ.Coll == collations.Unknown { - typ.Coll = ast.cfg.Collation + if typ.Valid() && typ.collation == collations.Unknown { + typ.collation = ast.cfg.Collation } column := NewColumn(col.V, typ, col.Original) @@ -227,12 +228,12 @@ func (ast *astCompiler) translateColName(colname *sqlparser.ColName) (IR, error) if err != nil { return nil, err } - typ := UnknownType() + var typ Type if ast.cfg.ResolveType != nil { typ, _ = ast.cfg.ResolveType(colname) } - if typ.Coll == collations.Unknown { - typ.Coll = ast.cfg.Collation + if typ.Valid() && typ.collation == collations.Unknown { + typ.collation = ast.cfg.Collation } column := NewColumn(idx, typ, colname) @@ -334,7 +335,7 @@ func (ast *astCompiler) translateCollateExpr(collate *sqlparser.CollateExpr) (IR if err != nil { return nil, err } - coll := collations.Local().LookupByName(collate.Collation) + coll := ast.cfg.Environment.CollationEnv().LookupByName(collate.Collation) if coll == collations.Unknown { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unknown collation: '%s'", collate.Collation) } @@ -345,6 +346,7 @@ func (ast *astCompiler) translateCollateExpr(collate *sqlparser.CollateExpr) (IR Coercibility: collations.CoerceExplicit, Repertoire: collations.RepertoireUnicode, }, + CollationEnv: ast.cfg.Environment.CollationEnv(), }, nil } @@ -358,7 +360,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer if strings.ToLower(introduced.CharacterSet) == "_binary" { collation = collations.CollationBinaryID } else { - defaultCollation := collations.Local().DefaultCollationForCharset(introduced.CharacterSet[1:]) + defaultCollation := ast.cfg.Environment.CollationEnv().DefaultCollationForCharset(introduced.CharacterSet[1:]) if defaultCollation == collations.Unknown { panic(fmt.Sprintf("unknown character set: %s", introduced.CharacterSet)) } @@ -389,6 +391,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer Coercibility: collations.CoerceExplicit, Repertoire: collations.RepertoireUnicode, }, + CollationEnv: ast.cfg.Environment.CollationEnv(), }, nil default: panic("character set introducers are only supported for literals and arguments") @@ -420,7 +423,7 @@ func (ast *astCompiler) translateUnaryExpr(unary *sqlparser.UnaryExpr) (IR, erro case sqlparser.TildaOp: return &BitwiseNotExpr{UnaryExpr: UnaryExpr{expr}}, nil case sqlparser.NStringOp: - return &ConvertExpr{UnaryExpr: UnaryExpr{expr}, Type: "NCHAR", Collation: collations.CollationUtf8mb3ID}, nil + return &ConvertExpr{UnaryExpr: UnaryExpr{expr}, Type: "NCHAR", Collation: collations.CollationUtf8mb3ID, CollationEnv: ast.cfg.Environment.CollationEnv()}, nil default: return nil, translateExprNotSupported(unary) } @@ -569,16 +572,11 @@ type Config struct { Collation collations.ID NoConstantFolding bool NoCompilation bool + SQLMode SQLMode + Environment *vtenv.Environment } func Translate(e sqlparser.Expr, cfg *Config) (Expr, error) { - if cfg == nil { - cfg = &Config{} - } - if cfg.Collation == collations.Unknown { - cfg.Collation = collations.Default() - } - ast := astCompiler{cfg: cfg} expr, err := ast.translateExpr(e) @@ -591,7 +589,7 @@ func Translate(e sqlparser.Expr, cfg *Config) (Expr, error) { } if !cfg.NoConstantFolding { - staticEnv := EmptyExpressionEnv() + staticEnv := EmptyExpressionEnv(cfg.Environment) expr, err = simplifyExpr(staticEnv, expr) if err != nil { return nil, err @@ -603,11 +601,12 @@ func Translate(e sqlparser.Expr, cfg *Config) (Expr, error) { } if len(ast.untyped) == 0 && !cfg.NoCompilation { - comp := compiler{collation: cfg.Collation} + comp := compiler{collation: cfg.Collation, env: cfg.Environment, sqlmode: cfg.SQLMode} return comp.compile(expr) } return &UntypedExpr{ + env: cfg.Environment, ir: expr, collation: cfg.Collation, needTypes: ast.untyped, @@ -626,9 +625,14 @@ type typedExpr struct { err error } -func (typed *typedExpr) compile(expr IR, collation collations.ID) (*CompiledExpr, error) { +func (typed *typedExpr) compile(env *vtenv.Environment, expr IR, collation collations.ID, sqlmode SQLMode) (*CompiledExpr, error) { typed.once.Do(func() { - comp := compiler{collation: collation, dynamicTypes: typed.types} + comp := compiler{ + env: env, + collation: collation, + dynamicTypes: typed.types, + sqlmode: sqlmode, + } typed.compiled, typed.err = comp.compile(expr) }) return typed.compiled, typed.err @@ -642,6 +646,7 @@ type typedIR interface { // UntypedExpr is a translated expression that cannot be compiled ahead of time because it // contains dynamic types. type UntypedExpr struct { + env *vtenv.Environment // ir is the translated IR for the expression ir IR // collation is the default collation for the translated expression @@ -695,7 +700,7 @@ func (u *UntypedExpr) Compile(env *ExpressionEnv) (*CompiledExpr, error) { if err != nil { return nil, err } - return typed.compile(u.ir, u.collation) + return typed.compile(u.env, u.ir, u.collation, env.sqlmode) } func (u *UntypedExpr) typeof(env *ExpressionEnv) (ctype, error) { @@ -734,9 +739,9 @@ func (fields FieldResolver) Type(expr sqlparser.Expr) (Type, bool) { name := expr.CompliantName() for _, f := range fields { if f.Name == name { - return Type{Type: f.Type, Coll: collations.ID(f.Charset)}, true + return NewType(f.Type, collations.ID(f.Charset)), true } } } - return UnknownType(), false + return Type{}, false } diff --git a/go/vt/vtgate/evalengine/translate_builtin.go b/go/vt/vtgate/evalengine/translate_builtin.go index 04bd2ca3428..cabe406bfb6 100644 --- a/go/vt/vtgate/evalengine/translate_builtin.go +++ b/go/vt/vtgate/evalengine/translate_builtin.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + "vitess.io/vitess/go/mysql/collations" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" @@ -255,6 +256,22 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) { return nil, argError(method) } return &builtinConv{CallExpr: call, collate: ast.cfg.Collation}, nil + case "bin": + if len(args) != 1 { + return nil, argError(method) + } + args = append(args, NewLiteralInt(10)) + args = append(args, NewLiteralInt(2)) + var cexpr = CallExpr{Arguments: args, Method: "BIN"} + return &builtinConv{CallExpr: cexpr, collate: ast.cfg.Collation}, nil + case "oct": + if len(args) != 1 { + return nil, argError(method) + } + args = append(args, NewLiteralInt(10)) + args = append(args, NewLiteralInt(8)) + var cexpr = CallExpr{Arguments: args, Method: "OCT"} + return &builtinConv{CallExpr: cexpr, collate: ast.cfg.Collation}, nil case "left", "right": if len(args) != 2 { return nil, argError(method) @@ -295,6 +312,16 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) { return nil, argError(method) } return &builtinASCII{CallExpr: call}, nil + case "reverse": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinReverse{CallExpr: call, collate: ast.cfg.Collation}, nil + case "space": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinSpace{CallExpr: call, collate: ast.cfg.Collation}, nil case "ord": if len(args) != 1 { return nil, argError(method) @@ -414,6 +441,26 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) { return nil, argError(method) } return &builtinMonthName{CallExpr: call, collate: ast.cfg.Collation}, nil + case "last_day": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinLastDay{CallExpr: call}, nil + case "to_days": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinToDays{CallExpr: call}, nil + case "from_days": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinFromDays{CallExpr: call}, nil + case "time_to_sec": + if len(args) != 1 { + return nil, argError(method) + } + return &builtinTimeToSec{CallExpr: call}, nil case "quarter": if len(args) != 1 { return nil, argError(method) @@ -574,6 +621,17 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) { return nil, argError(method) } return &builtinStrcmp{CallExpr: call, collate: ast.cfg.Collation}, nil + case "instr": + if len(args) != 2 { + return nil, argError(method) + } + call = CallExpr{Arguments: []IR{call.Arguments[1], call.Arguments[0]}, Method: method} + return &builtinLocate{CallExpr: call, collate: ast.cfg.Collation}, nil + case "replace": + if len(args) != 3 { + return nil, argError(method) + } + return &builtinReplace{CallExpr: call, collate: ast.cfg.Collation}, nil default: return nil, translateExprNotSupported(fn) } @@ -603,10 +661,7 @@ func (ast *astCompiler) translateCallable(call sqlparser.Callable) (IR, error) { } if call.As != nil { ws.Cast = strings.ToLower(call.As.Type) - ws.Len, ws.HasLen, err = ast.translateIntegral(call.As.Length) - if err != nil { - return nil, err - } + ws.Len = call.As.Length } return ws, nil @@ -699,7 +754,7 @@ func (ast *astCompiler) translateCallable(call sqlparser.Callable) (IR, error) { case *sqlparser.CurTimeFuncExpr: if call.Fsp > 6 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Too-big precision 12 specified for '%s'. Maximum is 6.", call.Name.String()) + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Too-big precision %d specified for '%s'. Maximum is 6.", call.Fsp, call.Name.String()) } var cexpr = CallExpr{Arguments: nil, Method: call.Name.String()} @@ -747,6 +802,56 @@ func (ast *astCompiler) translateCallable(call sqlparser.Callable) (IR, error) { trim: call.Type, }, nil + case *sqlparser.SubstrExpr: + var args []IR + str, err := ast.translateExpr(call.Name) + if err != nil { + return nil, err + } + args = append(args, str) + pos, err := ast.translateExpr(call.From) + if err != nil { + return nil, err + } + args = append(args, pos) + + if call.To != nil { + to, err := ast.translateExpr(call.To) + if err != nil { + return nil, err + } + args = append(args, to) + } + var cexpr = CallExpr{Arguments: args, Method: "SUBSTRING"} + return &builtinSubstring{ + CallExpr: cexpr, + collate: ast.cfg.Collation, + }, nil + case *sqlparser.LocateExpr: + var args []IR + substr, err := ast.translateExpr(call.SubStr) + if err != nil { + return nil, err + } + args = append(args, substr) + str, err := ast.translateExpr(call.Str) + if err != nil { + return nil, err + } + args = append(args, str) + + if call.Pos != nil { + to, err := ast.translateExpr(call.Pos) + if err != nil { + return nil, err + } + args = append(args, to) + } + var cexpr = CallExpr{Arguments: args, Method: "LOCATE"} + return &builtinLocate{ + CallExpr: cexpr, + collate: ast.cfg.Collation, + }, nil case *sqlparser.IntervalDateExpr: var err error args := make([]IR, 2) @@ -929,6 +1034,61 @@ func (ast *astCompiler) translateCallable(call sqlparser.Callable) (IR, error) { return &builtinRegexpReplace{ CallExpr: CallExpr{Arguments: args, Method: "REGEXP_REPLACE"}, }, nil + + case *sqlparser.InsertExpr: + str, err := ast.translateExpr(call.Str) + if err != nil { + return nil, err + } + + pos, err := ast.translateExpr(call.Pos) + if err != nil { + return nil, err + } + + len, err := ast.translateExpr(call.Len) + if err != nil { + return nil, err + } + + newstr, err := ast.translateExpr(call.NewStr) + if err != nil { + return nil, err + } + + args := []IR{str, pos, len, newstr} + + var cexpr = CallExpr{Arguments: args, Method: "INSERT"} + return &builtinInsert{ + CallExpr: cexpr, + collate: ast.cfg.Collation, + }, nil + case *sqlparser.CharExpr: + args := make([]IR, 0, len(call.Exprs)) + for _, expr := range call.Exprs { + arg, err := ast.translateExpr(expr) + if err != nil { + return nil, err + } + args = append(args, arg) + } + + var cexpr = CallExpr{Arguments: args, Method: "CHAR"} + var coll collations.ID + if call.Charset == "" { + coll = collations.CollationBinaryID + } else { + var err error + coll, err = ast.translateConvertCharset(call.Charset, false) + if err != nil { + return nil, err + } + } + + return &builtinChar{ + CallExpr: cexpr, + collate: coll, + }, nil default: return nil, translateExprNotSupported(call) } diff --git a/go/vt/vtgate/evalengine/translate_convert.go b/go/vt/vtgate/evalengine/translate_convert.go index 29216716b2b..b47aa6f1fd9 100644 --- a/go/vt/vtgate/evalengine/translate_convert.go +++ b/go/vt/vtgate/evalengine/translate_convert.go @@ -32,7 +32,7 @@ func (ast *astCompiler) binaryCollationForCollation(collation collations.ID) col if binary == nil { return collations.Unknown } - return collations.Local().BinaryCollationForCharset(binary.Charset().Name()) + return ast.cfg.Environment.CollationEnv().BinaryCollationForCharset(binary.Charset().Name()) } func (ast *astCompiler) translateConvertCharset(charset string, binary bool) (collations.ID, error) { @@ -47,7 +47,7 @@ func (ast *astCompiler) translateConvertCharset(charset string, binary bool) (co return collation, nil } charset = strings.ToLower(charset) - collationID := collations.Local().DefaultCollationForCharset(charset) + collationID := ast.cfg.Environment.CollationEnv().DefaultCollationForCharset(charset) if collationID == collations.Unknown { return collations.Unknown, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unknown character set: '%s'", charset) } @@ -66,39 +66,36 @@ func (ast *astCompiler) translateConvertExpr(expr sqlparser.Expr, convertType *s err error ) + convert.CollationEnv = ast.cfg.Environment.CollationEnv() convert.Inner, err = ast.translateExpr(expr) if err != nil { return nil, err } - convert.Length, convert.HasLength, err = ast.translateIntegral(convertType.Length) - if err != nil { - return nil, err - } - - convert.Scale, convert.HasScale, err = ast.translateIntegral(convertType.Scale) - if err != nil { - return nil, err - } - + convert.Length = convertType.Length + convert.Scale = convertType.Scale convert.Type = strings.ToUpper(convertType.Type) switch convert.Type { case "DECIMAL": - if convert.Length < convert.Scale { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%s').", - "", // TODO: column name - ) - } - if convert.Length > decimal.MyMaxPrecision { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "Too-big precision %d specified for '%s'. Maximum is %d.", - convert.Length, sqlparser.String(expr), decimal.MyMaxPrecision) - } - if convert.Scale > decimal.MyMaxScale { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "Too big scale %d specified for column '%s'. Maximum is %d.", - convert.Scale, sqlparser.String(expr), decimal.MyMaxScale) + if convert.Length != nil { + if *convert.Length > decimal.MyMaxPrecision { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, + "Too-big precision %d specified for '%s'. Maximum is %d.", + *convert.Length, sqlparser.String(expr), decimal.MyMaxPrecision) + } + if convert.Scale != nil { + if *convert.Scale > decimal.MyMaxScale { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, + "Too big scale %d specified for column '%s'. Maximum is %d.", + *convert.Scale, sqlparser.String(expr), decimal.MyMaxScale) + } + if *convert.Length < *convert.Scale { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, + "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%s').", + "", // TODO: column name + ) + } + } } case "NCHAR": convert.Collation = collations.CollationUtf8mb3ID @@ -123,6 +120,7 @@ func (ast *astCompiler) translateConvertUsingExpr(expr *sqlparser.ConvertUsingEx err error ) + using.CollationEnv = ast.cfg.Environment.CollationEnv() using.Inner, err = ast.translateExpr(expr.Expr) if err != nil { return nil, err diff --git a/go/vt/vtgate/evalengine/translate_simplify.go b/go/vt/vtgate/evalengine/translate_simplify.go index 2537ad20b2b..6af7f7646a0 100644 --- a/go/vt/vtgate/evalengine/translate_simplify.go +++ b/go/vt/vtgate/evalengine/translate_simplify.go @@ -26,6 +26,10 @@ func (expr *BindVariable) constant() bool { return false } +func (expr *TupleBindVariable) constant() bool { + return false +} + func (expr *Column) constant() bool { return false } @@ -55,6 +59,10 @@ func (expr *BindVariable) simplify(_ *ExpressionEnv) error { return nil } +func (expr *TupleBindVariable) simplify(_ *ExpressionEnv) error { + return nil +} + func (expr *Column) simplify(_ *ExpressionEnv) error { return nil } diff --git a/go/vt/vtgate/evalengine/translate_test.go b/go/vt/vtgate/evalengine/translate_test.go index 377f34db8f2..3702230e22e 100644 --- a/go/vt/vtgate/evalengine/translate_test.go +++ b/go/vt/vtgate/evalengine/translate_test.go @@ -20,10 +20,12 @@ import ( "context" "strings" "testing" + "time" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -112,9 +114,10 @@ func TestTranslateSimplification(t *testing.T) { {"json->>\"$.c\"", ok("JSON_UNQUOTE(JSON_EXTRACT(`json`, '$.c'))"), ok("JSON_UNQUOTE(JSON_EXTRACT(`json`, '$.c'))")}, } + venv := vtenv.NewTestEnv() for _, tc := range testCases { t.Run(tc.expression, func(t *testing.T) { - stmt, err := sqlparser.Parse("select " + tc.expression) + stmt, err := venv.Parser().Parse("select " + tc.expression) if err != nil { t.Fatal(err) } @@ -125,7 +128,8 @@ func TestTranslateSimplification(t *testing.T) { cfg := &Config{ ResolveColumn: fields.Column, - Collation: collations.Default(), + Collation: venv.CollationEnv().DefaultConnectionCharset(), + Environment: venv, NoConstantFolding: true, NoCompilation: true, } @@ -295,13 +299,17 @@ func TestEvaluate(t *testing.T) { expected: False, }} + venv := vtenv.NewTestEnv() for _, test := range tests { t.Run(test.expression, func(t *testing.T) { // Given - stmt, err := sqlparser.Parse("select " + test.expression) + stmt, err := sqlparser.NewTestParser().Parse("select " + test.expression) require.NoError(t, err) astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr - sqltypesExpr, err := Translate(astExpr, &Config{Collation: collations.Default()}) + sqltypesExpr, err := Translate(astExpr, &Config{ + Collation: venv.CollationEnv().DefaultConnectionCharset(), + Environment: venv, + }) require.Nil(t, err) require.NotNil(t, sqltypesExpr) env := NewExpressionEnv(context.Background(), map[string]*querypb.BindVariable{ @@ -311,14 +319,14 @@ func TestEvaluate(t *testing.T) { "uint32_bind_variable": sqltypes.Uint32BindVariable(21), "uint64_bind_variable": sqltypes.Uint64BindVariable(22), "float_bind_variable": sqltypes.Float64BindVariable(2.2), - }, nil) + }, NewEmptyVCursor(venv, time.Local)) // When r, err := env.Evaluate(sqltypesExpr) // Then require.NoError(t, err) - assert.Equal(t, test.expected, r.Value(collations.Default()), "expected %s", test.expected.String()) + assert.Equal(t, test.expected, r.Value(collations.MySQL8().DefaultConnectionCharset()), "expected %s", test.expected.String()) }) } } @@ -340,18 +348,22 @@ func TestEvaluateTuple(t *testing.T) { expected: []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewVarChar("2"), sqltypes.NewDecimal("4.0")}, }} + venv := vtenv.NewTestEnv() for _, test := range tests { t.Run(test.expression, func(t *testing.T) { // Given - stmt, err := sqlparser.Parse("select " + test.expression) + stmt, err := sqlparser.NewTestParser().Parse("select " + test.expression) require.NoError(t, err) astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr - sqltypesExpr, err := Translate(astExpr, &Config{Collation: collations.Default()}) + sqltypesExpr, err := Translate(astExpr, &Config{ + Collation: venv.CollationEnv().DefaultConnectionCharset(), + Environment: venv, + }) require.Nil(t, err) require.NotNil(t, sqltypesExpr) // When - r, err := EmptyExpressionEnv().Evaluate(sqltypesExpr) + r, err := EmptyExpressionEnv(venv).Evaluate(sqltypesExpr) // Then require.NoError(t, err) @@ -377,13 +389,17 @@ func TestTranslationFailures(t *testing.T) { }, } + venv := vtenv.NewTestEnv() for _, testcase := range testcases { t.Run(testcase.expression, func(t *testing.T) { // Given - stmt, err := sqlparser.Parse("select " + testcase.expression) + stmt, err := sqlparser.NewTestParser().Parse("select " + testcase.expression) require.NoError(t, err) astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr - _, err = Translate(astExpr, &Config{Collation: collations.Default()}) + _, err = Translate(astExpr, &Config{ + Collation: venv.CollationEnv().DefaultConnectionCharset(), + Environment: venv, + }) require.EqualError(t, err, testcase.expectedErr) }) } @@ -410,16 +426,21 @@ func TestCardinalityWithBindVariables(t *testing.T) { {expr: `1 IN ::bar`}, } + venv := vtenv.NewTestEnv() for _, testcase := range testcases { t.Run(testcase.expr, func(t *testing.T) { err := func() error { - stmt, err := sqlparser.Parse("select " + testcase.expr) + stmt, err := sqlparser.NewTestParser().Parse("select " + testcase.expr) if err != nil { return err } astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr - _, err = Translate(astExpr, &Config{Collation: collations.Default(), NoCompilation: true}) + _, err = Translate(astExpr, &Config{ + Collation: venv.CollationEnv().DefaultConnectionCharset(), + Environment: venv, + NoCompilation: true, + }) return err }() diff --git a/go/vt/vtgate/evalengine/vm.go b/go/vt/vtgate/evalengine/vm.go index df1d6aa6405..28c3af70e0e 100644 --- a/go/vt/vtgate/evalengine/vm.go +++ b/go/vt/vtgate/evalengine/vm.go @@ -87,12 +87,12 @@ func (env *ExpressionEnv) EvaluateVM(p *CompiledExpr) (EvalResult, error) { goto err } } - return EvalResult{env.vm.stack[env.vm.sp-1]}, nil + return EvalResult{v: env.vm.stack[env.vm.sp-1], collationEnv: env.collationEnv}, nil err: if env.vm.err == errDeoptimize { e, err := p.ir.eval(env) - return EvalResult{e}, err + return EvalResult{v: e, collationEnv: env.collationEnv}, err } - return EvalResult{}, env.vm.err + return EvalResult{collationEnv: env.collationEnv}, env.vm.err } diff --git a/go/vt/vtgate/evalengine/weights.go b/go/vt/vtgate/evalengine/weights.go index fa7fa7e11a6..2a9d6c9f93e 100644 --- a/go/vt/vtgate/evalengine/weights.go +++ b/go/vt/vtgate/evalengine/weights.go @@ -41,11 +41,11 @@ import ( // externally communicates with the `WEIGHT_STRING` function, so that we // can also use this to order / sort other types like Float and Decimal // as well. -func WeightString(dst []byte, v sqltypes.Value, coerceTo sqltypes.Type, col collations.ID, length, precision int) ([]byte, bool, error) { +func WeightString(dst []byte, v sqltypes.Value, coerceTo sqltypes.Type, col collations.ID, length, precision int, sqlmode SQLMode) ([]byte, bool, error) { // We optimize here for the case where we already have the desired type. // Otherwise, we fall back to the general evalengine conversion logic. if v.Type() != coerceTo { - return fallbackWeightString(dst, v, coerceTo, col, length, precision) + return fallbackWeightString(dst, v, coerceTo, col, length, precision, sqlmode) } switch { @@ -117,12 +117,12 @@ func WeightString(dst []byte, v sqltypes.Value, coerceTo sqltypes.Type, col coll } return j.WeightString(dst), false, nil default: - return fallbackWeightString(dst, v, coerceTo, col, length, precision) + return fallbackWeightString(dst, v, coerceTo, col, length, precision, sqlmode) } } -func fallbackWeightString(dst []byte, v sqltypes.Value, coerceTo sqltypes.Type, col collations.ID, length, precision int) ([]byte, bool, error) { - e, err := valueToEvalCast(v, coerceTo, col) +func fallbackWeightString(dst []byte, v sqltypes.Value, coerceTo sqltypes.Type, col collations.ID, length, precision int, sqlmode SQLMode) ([]byte, bool, error) { + e, err := valueToEvalCast(v, coerceTo, col, sqlmode) if err != nil { return dst, false, err } diff --git a/go/vt/vtgate/evalengine/weights_test.go b/go/vt/vtgate/evalengine/weights_test.go index 0dee4c72d03..9a34e6e9e81 100644 --- a/go/vt/vtgate/evalengine/weights_test.go +++ b/go/vt/vtgate/evalengine/weights_test.go @@ -77,7 +77,7 @@ func TestTinyWeightStrings(t *testing.T) { return cmp } - cmp, err := NullsafeCompare(a, b, tc.col) + cmp, err := NullsafeCompare(a, b, collations.MySQL8(), tc.col) require.NoError(t, err) fullComparisons++ @@ -88,7 +88,7 @@ func TestTinyWeightStrings(t *testing.T) { a := items[i] b := items[i+1] - cmp, err := NullsafeCompare(a, b, tc.col) + cmp, err := NullsafeCompare(a, b, collations.MySQL8(), tc.col) require.NoError(t, err) if cmp > 0 { @@ -136,7 +136,7 @@ func TestWeightStrings(t *testing.T) { items := make([]item, 0, Length) for i := 0; i < Length; i++ { v := tc.gen() - w, _, err := WeightString(nil, v, typ, tc.col, tc.len, tc.prec) + w, _, err := WeightString(nil, v, typ, tc.col, tc.len, tc.prec, 0) require.NoError(t, err) items = append(items, item{value: v, weight: string(w)}) @@ -156,12 +156,12 @@ func TestWeightStrings(t *testing.T) { a := items[i] b := items[i+1] - v1, err := valueToEvalCast(a.value, typ, tc.col) + v1, err := valueToEvalCast(a.value, typ, tc.col, 0) require.NoError(t, err) - v2, err := valueToEvalCast(b.value, typ, tc.col) + v2, err := valueToEvalCast(b.value, typ, tc.col, 0) require.NoError(t, err) - cmp, err := evalCompareNullSafe(v1, v2) + cmp, err := evalCompareNullSafe(v1, v2, collations.MySQL8()) require.NoError(t, err) if cmp > 0 { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 152533f2c0d..520214d65fd 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -30,14 +30,12 @@ import ( "github.com/spf13/pflag" - "vitess.io/vitess/go/cache/theine" - "vitess.io/vitess/go/streamlog" - "vitess.io/vitess/go/vt/vthash" - "vitess.io/vitess/go/acl" + "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/key" @@ -52,6 +50,7 @@ import ( "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -61,6 +60,7 @@ import ( "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vtgate/vschemaacl" "vitess.io/vitess/go/vt/vtgate/vtgateservice" + "vitess.io/vitess/go/vt/vthash" ) var ( @@ -93,6 +93,7 @@ func init() { // Executor is the engine that executes queries by utilizing // the abilities of the underlying vttablets. type Executor struct { + env *vtenv.Environment serv srvtopo.Server cell string resolver *Resolver @@ -142,6 +143,7 @@ func DefaultPlanCache() *PlanCache { // NewExecutor creates a new Executor. func NewExecutor( ctx context.Context, + env *vtenv.Environment, serv srvtopo.Server, cell string, resolver *Resolver, @@ -154,6 +156,7 @@ func NewExecutor( warmingReadsPercent int, ) *Executor { e := &Executor{ + env: env, serv: serv, cell: cell, resolver: resolver, @@ -177,6 +180,7 @@ func NewExecutor( serv: serv, cell: cell, schema: e.schemaTracker, + parser: env.Parser(), } serv.WatchSrvVSchema(ctx, cell, e.vm.VSchemaUpdate) @@ -223,7 +227,7 @@ func (e *Executor) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn } if result != nil && len(result.Rows) > warnMemoryRows { warnings.Add("ResultsExceeded", 1) - piiSafeSQL, err := sqlparser.RedactSQLQuery(sql) + piiSafeSQL, err := e.env.Parser().RedactSQLQuery(sql) if err != nil { piiSafeSQL = logStats.StmtType } @@ -357,7 +361,7 @@ func (e *Executor) StreamExecute( saveSessionStats(safeSession, srr.stmtType, srr.rowsAffected, srr.insertID, srr.rowsReturned, err) if srr.rowsReturned > warnMemoryRows { warnings.Add("ResultsExceeded", 1) - piiSafeSQL, err := sqlparser.RedactSQLQuery(sql) + piiSafeSQL, err := e.env.Parser().RedactSQLQuery(sql) if err != nil { piiSafeSQL = logStats.StmtType } @@ -456,7 +460,11 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars }) bindVars[key] = sqltypes.Int64BindVariable(v) case sysvars.TransactionMode.Name: - bindVars[key] = sqltypes.StringBindVariable(session.TransactionMode.String()) + txMode := session.TransactionMode + if txMode == vtgatepb.TransactionMode_UNSPECIFIED { + txMode = getTxMode() + } + bindVars[key] = sqltypes.StringBindVariable(txMode.String()) case sysvars.Workload.Name: var v string ifOptionsExist(session, func(options *querypb.ExecuteOptions) { @@ -499,16 +507,20 @@ func (e *Executor) addNeededBindVars(vcursor *vcursorImpl, bindVarNeeds *sqlpars bindVars[key] = sqltypes.StringBindVariable(mysqlSocketPath()) default: if value, hasSysVar := session.SystemVariables[sysVar]; hasSysVar { - expr, err := sqlparser.ParseExpr(value) + expr, err := e.env.Parser().ParseExpr(value) if err != nil { return err } - evalExpr, err := evalengine.Translate(expr, nil) + evalExpr, err := evalengine.Translate(expr, &evalengine.Config{ + Collation: vcursor.collation, + Environment: e.env, + SQLMode: evalengine.ParseSQLMode(vcursor.SQLMode()), + }) if err != nil { return err } - evaluated, err := evalengine.EmptyExpressionEnv().Evaluate(evalExpr) + evaluated, err := evalengine.NewExpressionEnv(context.Background(), nil, vcursor).Evaluate(evalExpr) if err != nil { return err } @@ -1042,6 +1054,7 @@ func (e *Executor) getPlan( vcursor.SetIgnoreMaxMemoryRows(sqlparser.IgnoreMaxMaxMemoryRowsDirective(stmt)) vcursor.SetConsolidator(sqlparser.Consolidator(stmt)) vcursor.SetWorkloadName(sqlparser.GetWorkloadNameFromStatement(stmt)) + vcursor.UpdateForeignKeyChecksState(sqlparser.ForeignKeyChecksState(stmt)) priority, err := sqlparser.GetPriorityFromStatement(stmt) if err != nil { return nil, err @@ -1066,6 +1079,7 @@ func (e *Executor) getPlan( vcursor.safeSession.getSelectLimit(), setVarComment, vcursor.safeSession.SystemVariables, + vcursor.GetForeignKeyChecksState(), vcursor, ) if err != nil { @@ -1335,7 +1349,7 @@ func (e *Executor) handlePrepare(ctx context.Context, safeSession *SafeSession, query, comments := sqlparser.SplitMarginComments(sql) vcursor, _ := newVCursorImpl(safeSession, comments, e, logStats, e.vm, e.VSchema(), e.resolver.resolver, e.serv, e.warnShardedOnly, e.pv) - stmt, reservedVars, err := parseAndValidateQuery(query) + stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { return nil, err } @@ -1370,8 +1384,8 @@ func (e *Executor) handlePrepare(ctx context.Context, safeSession *SafeSession, return qr.Fields, err } -func parseAndValidateQuery(query string) (sqlparser.Statement, *sqlparser.ReservedVars, error) { - stmt, reserved, err := sqlparser.Parse2(query) +func parseAndValidateQuery(query string, parser *sqlparser.Parser) (sqlparser.Statement, *sqlparser.ReservedVars, error) { + stmt, reserved, err := parser.Parse2(query) if err != nil { return nil, nil, err } @@ -1506,7 +1520,7 @@ func (e *Executor) ReleaseLock(ctx context.Context, session *SafeSession) error // planPrepareStmt implements the IExecutor interface func (e *Executor) planPrepareStmt(ctx context.Context, vcursor *vcursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) { - stmt, reservedVars, err := parseAndValidateQuery(query) + stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { return nil, nil, err } @@ -1539,3 +1553,7 @@ func (e *Executor) Close() { topo.Close() e.plans.Close() } + +func (e *Executor) environment() *vtenv.Environment { + return e.env +} diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 961e6e32eca..d0efcfe765a 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -25,8 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" querypb "vitess.io/vitess/go/vt/proto/query" @@ -532,7 +532,7 @@ func TestUpdateMultiOwned(t *testing.T) { } } ` - executor, sbc1, sbc2, sbclookup, ctx := createCustomExecutor(t, vschema) + executor, sbc1, sbc2, sbclookup, ctx := createCustomExecutor(t, vschema, config.DefaultMySQLVersion) sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult( @@ -1469,7 +1469,7 @@ func TestInsertShardedAutocommitLookup(t *testing.T) { } } ` - executor, sbc1, sbc2, sbclookup, ctx := createCustomExecutor(t, vschema) + executor, sbc1, sbc2, sbclookup, ctx := createCustomExecutor(t, vschema, config.DefaultMySQLVersion) _, err := executorExecSession(ctx, executor, "insert into user(id, v, name, music) values (1, 2, 'myname', 'star')", nil, &vtgatepb.Session{}) require.NoError(t, err) @@ -2268,7 +2268,7 @@ func TestInsertBadAutoInc(t *testing.T) { } } ` - executor, _, _, _, ctx := createCustomExecutor(t, vschema) + executor, _, _, _, ctx := createCustomExecutor(t, vschema, config.DefaultMySQLVersion) // If auto inc table cannot be found, the table should not be added to vschema. session := &vtgatepb.Session{ @@ -3017,3 +3017,57 @@ func TestInsertReference(t *testing.T) { _, err = executorExec(ctx, executor, session, "insert into TestExecutor.zip_detail(id, status) values (1, 'CLOSED')", nil) require.NoError(t, err) // Gen4 planner can redirect the query to correct source for update when reference table is involved. } + +func TestDeleteMultiTable(t *testing.T) { + executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) + executor.vschema.Keyspaces["TestExecutor"].Tables["user"].PrimaryKey = sqlparser.Columns{sqlparser.NewIdentifierCI("id")} + + logChan := executor.queryLogger.Subscribe("TestDeleteMultiTable") + defer executor.queryLogger.Unsubscribe(logChan) + + session := &vtgatepb.Session{TargetString: "@primary"} + _, err := executorExec(ctx, executor, session, "delete user from user join music on user.col = music.col where music.user_id = 1", nil) + require.NoError(t, err) + + var dmlVals []*querypb.Value + for i := 0; i < 8; i++ { + dmlVals = append(dmlVals, sqltypes.ValueToProto(sqltypes.NewInt32(1))) + } + + bq := &querypb.BoundQuery{ + Sql: "select 1 from music where music.user_id = 1 and music.col = :user_col", + BindVariables: map[string]*querypb.BindVariable{"user_col": sqltypes.StringBindVariable("foo")}, + } + wantQueries := []*querypb.BoundQuery{ + {Sql: "select `user`.id, `user`.col from `user`", BindVariables: map[string]*querypb.BindVariable{}}, + bq, bq, bq, bq, bq, bq, bq, bq, + {Sql: "select `user`.Id, `user`.`name` from `user` where `user`.id in ::dml_vals for update", BindVariables: map[string]*querypb.BindVariable{"dml_vals": {Type: querypb.Type_TUPLE, Values: dmlVals}}}, + {Sql: "delete from `user` where `user`.id in ::dml_vals", BindVariables: map[string]*querypb.BindVariable{"dml_vals": {Type: querypb.Type_TUPLE, Values: dmlVals}}}} + assertQueries(t, sbc1, wantQueries) + + wantQueries = []*querypb.BoundQuery{ + {Sql: "select `user`.id, `user`.col from `user`", BindVariables: map[string]*querypb.BindVariable{}}, + {Sql: "select `user`.Id, `user`.`name` from `user` where `user`.id in ::dml_vals for update", BindVariables: map[string]*querypb.BindVariable{"dml_vals": {Type: querypb.Type_TUPLE, Values: dmlVals}}}, + {Sql: "delete from `user` where `user`.id in ::dml_vals", BindVariables: map[string]*querypb.BindVariable{"dml_vals": {Type: querypb.Type_TUPLE, Values: dmlVals}}}, + } + assertQueries(t, sbc2, wantQueries) + + bq = &querypb.BoundQuery{ + Sql: "delete from name_user_map where `name` = :name and user_id = :user_id", + BindVariables: map[string]*querypb.BindVariable{ + "name": sqltypes.StringBindVariable("foo"), + "user_id": sqltypes.Uint64BindVariable(1), + }} + wantQueries = []*querypb.BoundQuery{ + bq, bq, bq, bq, bq, bq, bq, bq, + } + assertQueries(t, sbclookup, wantQueries) + + testQueryLog(t, executor, logChan, "MarkSavepoint", "SAVEPOINT", "savepoint s1", 8) + testQueryLog(t, executor, logChan, "VindexDelete", "DELETE", "delete from name_user_map where `name` = :name and user_id = :user_id", 1) + // select `user`.id, `user`.col from `user` - 8 shard + // select 1 from music where music.user_id = 1 and music.col = :user_col - 8 shards + // select Id, `name` from `user` where (`user`.id) in ::dml_vals for update - 1 shard + // delete from `user` where (`user`.id) in ::dml_vals - 1 shard + testQueryLog(t, executor, logChan, "TestExecute", "DELETE", "delete `user` from `user` join music on `user`.col = music.col where music.user_id = 1", 18) +} diff --git a/go/vt/vtgate/executor_framework_test.go b/go/vt/vtgate/executor_framework_test.go index 8baffdfde09..332139c4a78 100644 --- a/go/vt/vtgate/executor_framework_test.go +++ b/go/vt/vtgate/executor_framework_test.go @@ -26,27 +26,24 @@ import ( "testing" "github.com/stretchr/testify/assert" - - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sidecardb" - "vitess.io/vitess/go/vt/vtgate/logstats" - - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" - "github.com/stretchr/testify/require" "vitess.io/vitess/go/cache/theine" - "vitess.io/vitess/go/test/utils" - "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/streamlog" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/sandboxconn" ) @@ -184,7 +181,7 @@ func createExecutorEnvCallback(t testing.TB, eachShard func(shard, ks string, ta // one-off queries from thrashing the cache. Disable the doorkeeper in the tests to prevent flakiness. plans := theine.NewStore[PlanCacheKey, *engine.Plan](queryPlanCacheMemory, false) - executor = NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + executor = NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) executor.SetQueryLogger(queryLogger) key.AnyShardPicker = DestinationAnyShardPickerFirstShard{} @@ -212,7 +209,7 @@ func createExecutorEnv(t testing.TB) (executor *Executor, sbc1, sbc2, sbclookup return } -func createCustomExecutor(t testing.TB, vschema string) (executor *Executor, sbc1, sbc2, sbclookup *sandboxconn.SandboxConn, ctx context.Context) { +func createCustomExecutor(t testing.TB, vschema string, mysqlVersion string) (executor *Executor, sbc1, sbc2, sbclookup *sandboxconn.SandboxConn, ctx context.Context) { var cancel context.CancelFunc ctx, cancel = context.WithCancel(context.Background()) cell := "aa" @@ -231,7 +228,9 @@ func createCustomExecutor(t testing.TB, vschema string) (executor *Executor, sbc queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) plans := DefaultPlanCache() - executor = NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + env, err := vtenv.New(vtenv.Options{MySQLServerVersion: mysqlVersion}) + require.NoError(t, err) + executor = NewExecutor(ctx, env, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) executor.SetQueryLogger(queryLogger) t.Cleanup(func() { @@ -268,7 +267,7 @@ func createCustomExecutorSetValues(t testing.TB, vschema string, values []*sqlty sbclookup = hc.AddTestTablet(cell, "0", 1, KsTestUnsharded, "0", topodatapb.TabletType_PRIMARY, true, 1, nil) queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) plans := DefaultPlanCache() - executor = NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + executor = NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) executor.SetQueryLogger(queryLogger) t.Cleanup(func() { @@ -293,7 +292,7 @@ func createExecutorEnvWithPrimaryReplicaConn(t testing.TB, ctx context.Context, replica = hc.AddTestTablet(cell, "0-replica", 1, KsTestUnsharded, "0", topodatapb.TabletType_REPLICA, true, 1, nil) queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) - executor = NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, DefaultPlanCache(), nil, false, querypb.ExecuteOptions_Gen4, warmingReadsPercent) + executor = NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, false, false, testBufferSize, DefaultPlanCache(), nil, false, querypb.ExecuteOptions_Gen4, warmingReadsPercent) executor.SetQueryLogger(queryLogger) t.Cleanup(func() { @@ -367,8 +366,8 @@ func assertQueries(t *testing.T, sbc *sandboxconn.SandboxConn, wantQueries []*qu } got := query.Sql expected := wantQueries[idx].Sql - utils.MustMatch(t, expected, got) - utils.MustMatch(t, wantQueries[idx].BindVariables, query.BindVariables) + utils.MustMatch(t, expected, got, fmt.Sprintf("query did not match on index: %d", idx)) + utils.MustMatch(t, wantQueries[idx].BindVariables, query.BindVariables, fmt.Sprintf("bind variables did not match on index: %d", idx)) idx++ } } diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index f3544c1362e..d73445e4160 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -26,29 +26,26 @@ import ( "testing" "time" - _flag "vitess.io/vitess/go/internal/flag" - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/streamlog" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vtgate/logstats" - - "vitess.io/vitess/go/vt/sqlparser" - "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + _flag "vitess.io/vitess/go/internal/flag" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/vterrors" - _ "vitess.io/vitess/go/vt/vtgate/vindexes" - "vitess.io/vitess/go/vt/vttablet/sandboxconn" - querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/logstats" + _ "vitess.io/vitess/go/vt/vtgate/vindexes" + "vitess.io/vitess/go/vt/vttablet/sandboxconn" ) func TestSelectNext(t *testing.T) { @@ -160,18 +157,16 @@ func TestSelectDBA(t *testing.T) { } func TestSystemVariablesMySQLBelow80(t *testing.T) { - executor, sbc1, _, _, _ := createExecutorEnv(t) + executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "5.7.0") executor.normalize = true - - sqlparser.SetParserVersion("57000") setVarEnabled = true session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, - {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar(""), @@ -196,10 +191,9 @@ func TestSystemVariablesMySQLBelow80(t *testing.T) { } func TestSystemVariablesWithSetVarDisabled(t *testing.T) { - executor, sbc1, _, _, _ := createExecutorEnv(t) + executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - sqlparser.SetParserVersion("80000") setVarEnabled = false defer func() { setVarEnabled = true @@ -208,8 +202,8 @@ func TestSystemVariablesWithSetVarDisabled(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, - {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar(""), @@ -234,11 +228,9 @@ func TestSystemVariablesWithSetVarDisabled(t *testing.T) { } func TestSetSystemVariablesTx(t *testing.T) { - executor, sbc1, _, _, _ := createExecutorEnv(t) + executor, sbc1, _, _, _ := createCustomExecutor(t, "{}", "8.0.1") executor.normalize = true - sqlparser.SetParserVersion("80001") - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: "TestExecutor"}) _, err := executor.Execute(context.Background(), nil, "TestBegin", session, "begin", map[string]*querypb.BindVariable{}) @@ -250,8 +242,8 @@ func TestSetSystemVariablesTx(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, - {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar(""), @@ -285,16 +277,14 @@ func TestSetSystemVariables(t *testing.T) { executor, _, _, lookup, _ := createExecutorEnv(t) executor.normalize = true - sqlparser.SetParserVersion("80001") - session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded, SystemVariables: map[string]string{}}) // Set @@sql_mode and execute a select statement. We should have SET_VAR in the select statement lookup.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, - {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar(""), @@ -327,7 +317,7 @@ func TestSetSystemVariables(t *testing.T) { lookup.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "sql_safe_updates", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "sql_safe_updates", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("0"), @@ -350,7 +340,7 @@ func TestSetSystemVariables(t *testing.T) { lookup.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "max_tmp_tables", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "max_tmp_tables", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("4"), @@ -373,7 +363,7 @@ func TestSetSystemVariables(t *testing.T) { lookup.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "max_tmp_tables", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "max_tmp_tables", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("1"), @@ -402,8 +392,8 @@ func TestSetSystemVariablesWithReservedConnection(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, - {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "orig", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "new", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("only_full_group_by"), @@ -614,7 +604,7 @@ func TestStreamBuffering(t *testing.T) { sbclookup.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "col", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "col", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewInt32(1), @@ -646,7 +636,7 @@ func TestStreamBuffering(t *testing.T) { wantResults := []*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "col", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "col", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, }, { Rows: [][]sqltypes.Value{{ @@ -690,7 +680,7 @@ func TestStreamLimitOffset(t *testing.T) { conn.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, {Name: "weight_string(id)", Type: sqltypes.VarBinary, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_BINARY_FLAG)}, }, Rows: returnRows[shard], @@ -719,7 +709,7 @@ func TestStreamLimitOffset(t *testing.T) { wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ @@ -755,7 +745,7 @@ func TestSelectLastInsertId(t *testing.T) { result, err := executorExec(ctx, executor, session, sql, map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "last_insert_id()", Type: sqltypes.Uint64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG | querypb.MySqlFlag_UNSIGNED_FLAG)}, + {Name: "last_insert_id()", Type: sqltypes.Uint64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG | querypb.MySqlFlag_UNSIGNED_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewUint64(52), @@ -787,20 +777,20 @@ func TestSelectSystemVariables(t *testing.T) { result, err := executorExec(ctx, executor, session, sql, map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "@@autocommit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@client_found_rows", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@skip_query_plan_cache", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@enable_system_settings", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@sql_select_limit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@transaction_mode", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@workload", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@read_after_write_gtid", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@read_after_write_timeout", Type: sqltypes.Float64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@session_track_gtids", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@ddl_strategy", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@migration_context", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@socket", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, - {Name: "@@query_timeout", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@autocommit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@client_found_rows", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@skip_query_plan_cache", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@enable_system_settings", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@sql_select_limit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@transaction_mode", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@workload", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@read_after_write_gtid", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@read_after_write_timeout", Type: sqltypes.Float64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@session_track_gtids", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@ddl_strategy", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@migration_context", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@socket", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "@@query_timeout", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, Rows: [][]sqltypes.Value{{ // the following are the uninitialised session values @@ -809,7 +799,7 @@ func TestSelectSystemVariables(t *testing.T) { sqltypes.NewInt64(0), sqltypes.NewInt64(0), sqltypes.NewInt64(0), - sqltypes.NewVarChar("UNSPECIFIED"), + sqltypes.NewVarChar("MULTI"), sqltypes.NewVarChar(""), // these have been set at the beginning of the test sqltypes.NewVarChar("a fine gtid"), @@ -843,9 +833,9 @@ func TestSelectInitializedVitessAwareVariable(t *testing.T) { result, err := executorExec(ctx, executor, session, sql, nil) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "@@autocommit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@enable_system_settings", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, - {Name: "@@query_timeout", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@autocommit", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@enable_system_settings", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, + {Name: "@@query_timeout", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(1), @@ -884,7 +874,7 @@ func TestSelectUserDefinedVariable(t *testing.T) { require.NoError(t, err) wantResult = &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "@foo", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, + {Name: "@foo", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("bar"), @@ -910,7 +900,7 @@ func TestFoundRows(t *testing.T) { result, err := executorExec(ctx, executor, session, sql, map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "found_rows()", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "found_rows()", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(1), @@ -943,7 +933,7 @@ func testRowCount(t *testing.T, ctx context.Context, executor *Executor, session result, err := executorExec(ctx, executor, session, "select row_count()", map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "row_count()", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "row_count()", Type: sqltypes.Int64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(wantRowCount), @@ -964,7 +954,7 @@ func TestSelectLastInsertIdInUnion(t *testing.T) { result1 := []*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, InsertID: 0, Rows: [][]sqltypes.Value{{ @@ -978,7 +968,7 @@ func TestSelectLastInsertIdInUnion(t *testing.T) { require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG)}, + {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewInt32(52), @@ -1046,7 +1036,7 @@ func TestLastInsertIDInSubQueryExpression(t *testing.T) { require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "x", Type: sqltypes.Uint64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG | querypb.MySqlFlag_NUM_FLAG | querypb.MySqlFlag_UNSIGNED_FLAG)}, + {Name: "x", Type: sqltypes.Uint64, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG | querypb.MySqlFlag_UNSIGNED_FLAG)}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewUint64(12345), @@ -1077,7 +1067,7 @@ func TestSelectDatabase(t *testing.T) { map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ - {Name: "database()", Type: sqltypes.VarChar, Charset: uint32(collations.Default()), Flags: uint32(querypb.MySqlFlag_NOT_NULL_FLAG)}, + {Name: "database()", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{{ sqltypes.NewVarChar("TestExecutor@primary"), @@ -1275,7 +1265,7 @@ func TestSelectINFromOR(t *testing.T) { _, err := executorExec(ctx, executor, session, "select 1 from user where id = 1 and name = 'apa' or id = 2 and name = 'toto'", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select 1 from `user` where id = 1 and `name` = 'apa' or id = 2 and `name` = 'toto'", + Sql: "select 1 from `user` where id in ::__vals and (id = 1 or `name` = 'toto') and (`name` = 'apa' or id = 2) and `name` in ('apa', 'toto')", BindVariables: map[string]*querypb.BindVariable{ "__vals": sqltypes.TestBindVariable([]any{int64(1), int64(2)}), }, @@ -1560,10 +1550,79 @@ func TestStreamSelectIN(t *testing.T) { utils.MustMatch(t, wantQueries, sbclookup.Queries) } +// TestSelectListArg tests list arg filter with select query +func TestSelectListArg(t *testing.T) { + executor, sbc1, sbc2, _, ctx := createExecutorEnv(t) + session := &vtgatepb.Session{ + TargetString: "@primary", + } + + tupleBV := &querypb.BindVariable{ + Type: querypb.Type_TUPLE, + Values: []*querypb.Value{sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewVarChar("a")))}, + } + bvMap := map[string]*querypb.BindVariable{"vals": tupleBV} + _, err := executorExec(ctx, executor, session, "select id from user where (id, col) in ::vals", bvMap) + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{{ + Sql: "select id from `user` where (id, col) in ::vals", + BindVariables: bvMap, + }} + utils.MustMatch(t, wantQueries, sbc1.Queries) + assert.Nil(t, sbc2.Queries, "sbc2.Queries: %+v, want nil", sbc2.Queries) + + sbc1.Queries = nil + // get c0-e0 sandbox connection. + tbh, err := executor.scatterConn.gateway.hc.GetTabletHealthByAlias(&topodatapb.TabletAlias{ + Cell: "aa", + Uid: 7, + }) + require.NoError(t, err) + sbc := tbh.Conn.(*sandboxconn.SandboxConn) + sbc.Queries = nil + + _, err = executorExec(ctx, executor, session, "select id from multicol_tbl where (cola, colb) in ::vals", bvMap) + require.NoError(t, err) + + wantQueries = []*querypb.BoundQuery{{ + Sql: "select id from multicol_tbl where (cola, colb) in ::vals", + BindVariables: bvMap, + }} + utils.MustMatch(t, wantQueries, sbc.Queries) + assert.Nil(t, sbc1.Queries, "sbc1.Queries: %+v, want nil", sbc2.Queries) + assert.Nil(t, sbc2.Queries, "sbc2.Queries: %+v, want nil", sbc2.Queries) + + tupleBV.Values[0] = sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewInt64(1), sqltypes.NewInt64(42), sqltypes.NewVarChar("a"))) + sbc.Queries = nil + _, err = executorExec(ctx, executor, session, "select id from multicol_tbl where (cola, colx, colb) in ::vals", bvMap) + require.NoError(t, err) + + wantQueries = []*querypb.BoundQuery{{ + Sql: "select id from multicol_tbl where (cola, colx, colb) in ::vals", + BindVariables: bvMap, + }} + utils.MustMatch(t, wantQueries, sbc.Queries) + assert.Nil(t, sbc1.Queries, "sbc1.Queries: %+v, want nil", sbc2.Queries) + assert.Nil(t, sbc2.Queries, "sbc2.Queries: %+v, want nil", sbc2.Queries) + + tupleBV.Values[0] = sqltypes.ValueToProto(sqltypes.TestTuple(sqltypes.NewVarChar("a"), sqltypes.NewInt64(42), sqltypes.NewInt64(1))) + sbc.Queries = nil + _, err = executorExec(ctx, executor, session, "select id from multicol_tbl where (colb, colx, cola) in ::vals", bvMap) + require.NoError(t, err) + + wantQueries = []*querypb.BoundQuery{{ + Sql: "select id from multicol_tbl where (colb, colx, cola) in ::vals", + BindVariables: bvMap, + }} + utils.MustMatch(t, wantQueries, sbc.Queries) + assert.Nil(t, sbc1.Queries, "sbc1.Queries: %+v, want nil", sbc2.Queries) + assert.Nil(t, sbc2.Queries, "sbc2.Queries: %+v, want nil", sbc2.Queries) +} + func createExecutor(ctx context.Context, serv *sandboxTopo, cell string, resolver *Resolver) *Executor { queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) plans := DefaultPlanCache() - ex := NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + ex := NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) ex.SetQueryLogger(queryLogger) return ex } @@ -1920,7 +1979,8 @@ func TestSelectScatterOrderByVarChar(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "col1", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "weight_string(textcol)", Type: sqltypes.VarBinary, Charset: collations.CollationBinaryID}, }, InsertID: 0, Rows: [][]sqltypes.Value{{ @@ -1929,6 +1989,7 @@ func TestSelectScatterOrderByVarChar(t *testing.T) { // This will allow us to test that cross-shard ordering // still works correctly. sqltypes.NewVarChar(fmt.Sprintf("%d", i%4)), + sqltypes.NewVarBinary(fmt.Sprintf("%d", i%4)), }}, }}) conns = append(conns, sbc) @@ -1944,7 +2005,7 @@ func TestSelectScatterOrderByVarChar(t *testing.T) { require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select col1, textcol from `user` order by textcol desc", + Sql: "select col1, textcol, weight_string(textcol) from `user` order by textcol desc", BindVariables: map[string]*querypb.BindVariable{}, }} for _, conn := range conns { @@ -1954,7 +2015,7 @@ func TestSelectScatterOrderByVarChar(t *testing.T) { wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "col1", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, InsertID: 0, } @@ -2052,12 +2113,14 @@ func TestStreamSelectScatterOrderByVarChar(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, + {Name: "weight_string(textcol)", Type: sqltypes.VarBinary, Charset: collations.CollationBinaryID}, }, InsertID: 0, Rows: [][]sqltypes.Value{{ sqltypes.NewInt32(1), sqltypes.NewVarChar(fmt.Sprintf("%d", i%4)), + sqltypes.NewVarBinary(fmt.Sprintf("%d", i%4)), }}, }}) conns = append(conns, sbc) @@ -2070,7 +2133,7 @@ func TestStreamSelectScatterOrderByVarChar(t *testing.T) { require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select id, textcol from `user` order by textcol desc", + Sql: "select id, textcol, weight_string(textcol) from `user` order by textcol desc", BindVariables: map[string]*querypb.BindVariable{}, }} for _, conn := range conns { @@ -2080,7 +2143,7 @@ func TestStreamSelectScatterOrderByVarChar(t *testing.T) { wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "textcol", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, } for i := 0; i < 4; i++ { @@ -3189,7 +3252,7 @@ func TestStreamOrderByLimitWithMultipleResults(t *testing.T) { } queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) plans := DefaultPlanCache() - executor := NewExecutor(ctx, serv, cell, resolver, true, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + executor := NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, true, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) executor.SetQueryLogger(queryLogger) defer executor.Close() // some sleep for all goroutines to start @@ -3851,14 +3914,14 @@ func TestSelectAggregationNoData(t *testing.T) { { sql: `select count(*) from (select col1, col2 from user limit 2) x`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|1", "int64|int64|int64")), - expSandboxQ: "select col1, col2, 1 from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, 1 from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"count(*)" type:INT64]`, expRow: `[[INT64(0)]]`, }, { sql: `select col2, count(*) from (select col1, col2 from user limit 2) x group by col2`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|1|weight_string(col2)", "int64|int64|int64|varbinary")), - expSandboxQ: "select col1, col2, 1, weight_string(col2) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, 1, weight_string(x.col2) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col2" type:INT64 name:"count(*)" type:INT64]`, expRow: `[]`, }, @@ -3943,70 +4006,70 @@ func TestSelectAggregationData(t *testing.T) { { sql: `select count(*) from (select col1, col2 from user limit 2) x`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|1", "int64|int64|int64"), "100|200|1", "200|300|1"), - expSandboxQ: "select col1, col2, 1 from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, 1 from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"count(*)" type:INT64]`, expRow: `[[INT64(2)]]`, }, { sql: `select col2, count(*) from (select col1, col2 from user limit 9) x group by col2`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|1|weight_string(col2)", "int64|int64|int64|varbinary"), "100|3|1|NULL", "200|2|1|NULL"), - expSandboxQ: "select col1, col2, 1, weight_string(col2) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, 1, weight_string(x.col2) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col2" type:INT64 name:"count(*)" type:INT64]`, expRow: `[[INT64(2) INT64(4)] [INT64(3) INT64(5)]]`, }, { sql: `select count(col1) from (select id, col1 from user limit 2) x`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("id|col1", "int64|varchar"), "1|a", "2|b"), - expSandboxQ: "select id, col1 from (select id, col1 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.id, x.col1 from (select id, col1 from `user`) as x limit :__upper_limit", expField: `[name:"count(col1)" type:INT64]`, expRow: `[[INT64(2)]]`, }, { sql: `select count(col1), col2 from (select col2, col1 from user limit 9) x group by col2`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col2|col1|weight_string(col2)", "int64|varchar|varbinary"), "3|a|NULL", "2|b|NULL"), - expSandboxQ: "select col2, col1, weight_string(col2) from (select col2, col1 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col2, x.col1, weight_string(x.col2) from (select col2, col1 from `user`) as x limit :__upper_limit", expField: `[name:"count(col1)" type:INT64 name:"col2" type:INT64]`, expRow: `[[INT64(4) INT64(2)] [INT64(5) INT64(3)]]`, }, { sql: `select col1, count(col2) from (select col1, col2 from user limit 9) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "a|1|a", "b|null|b"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"count(col2)" type:INT64]`, expRow: `[[VARCHAR("a") INT64(5)] [VARCHAR("b") INT64(0)]]`, }, { sql: `select col1, count(col2) from (select col1, col2 from user limit 32) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "null|1|null", "null|null|null", "a|1|a", "b|null|b"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"count(col2)" type:INT64]`, expRow: `[[NULL INT64(8)] [VARCHAR("a") INT64(8)] [VARCHAR("b") INT64(0)]]`, }, { sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|int64|varbinary"), "a|3|a"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:DECIMAL]`, expRow: `[[VARCHAR("a") DECIMAL(12)]]`, }, { sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|2|a"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:FLOAT64]`, expRow: `[[VARCHAR("a") FLOAT64(8)]]`, }, { sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|x|a"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:FLOAT64]`, expRow: `[[VARCHAR("a") FLOAT64(0)]]`, }, { sql: `select col1, sum(col2) from (select col1, col2 from user limit 4) x group by col1`, sandboxRes: sqltypes.MakeTestResult(sqltypes.MakeTestFields("col1|col2|weight_string(col1)", "varchar|varchar|varbinary"), "a|null|a"), - expSandboxQ: "select col1, col2, weight_string(col1) from (select col1, col2 from `user`) as x limit :__upper_limit", + expSandboxQ: "select x.col1, x.col2, weight_string(x.col1) from (select col1, col2 from `user`) as x limit :__upper_limit", expField: `[name:"col1" type:VARCHAR name:"sum(col2)" type:FLOAT64]`, expRow: `[[VARCHAR("a") NULL]]`, }, @@ -4124,7 +4187,7 @@ func TestSelectCFC(t *testing.T) { func TestSelectView(t *testing.T) { executor, sbc, _, _, _ := createExecutorEnv(t) // add the view to local vschema - err := executor.vschema.AddView(KsTestSharded, "user_details_view", "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id") + err := executor.vschema.AddView(KsTestSharded, "user_details_view", "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id", executor.vm.parser) require.NoError(t, err) executor.normalize = true @@ -4170,7 +4233,7 @@ func TestWarmingReads(t *testing.T) { executor.normalize = true session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) - // Since queries on the replica will run in a separate go-routine, we need sycnronization for the Queries field in the sandboxconn. + // Since queries on the replica will run in a separate go-routine, we need synchronization for the Queries field in the sandboxconn. replica.RequireQueriesLocking() _, err := executor.Execute(ctx, nil, "TestWarmingReads", session, "select age, city from user", map[string]*querypb.BindVariable{}) diff --git a/go/vt/vtgate/executor_set_test.go b/go/vt/vtgate/executor_set_test.go index e71a41eeb7f..5e66899db44 100644 --- a/go/vt/vtgate/executor_set_test.go +++ b/go/vt/vtgate/executor_set_test.go @@ -21,8 +21,6 @@ import ( "testing" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/sqlparser" - querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/test/utils" @@ -321,6 +319,10 @@ func TestExecutorSetOp(t *testing.T) { }, { in: "set foreign_key_checks = 1", result: returnNoResult("foreign_key_checks", "int64"), + }, { + in: "set foreign_key_checks = 0", + sysVars: map[string]string{"foreign_key_checks": "0"}, + result: returnResult("foreign_key_checks", "int64", "0"), }, { in: "set unique_checks = 0", sysVars: map[string]string{"unique_checks": "0"}, @@ -503,14 +505,9 @@ func createMap(keys []string, values []any) map[string]*querypb.BindVariable { } func TestSetVar(t *testing.T) { - executor, _, _, sbc, ctx := createExecutorEnv(t) + executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - oldVersion := sqlparser.GetParserVersion() - sqlparser.SetParserVersion("80000") - defer func() { - sqlparser.SetParserVersion(oldVersion) - }() session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( @@ -547,14 +544,9 @@ func TestSetVar(t *testing.T) { } func TestSetVarShowVariables(t *testing.T) { - executor, _, _, sbc, ctx := createExecutorEnv(t) + executor, _, _, sbc, ctx := createCustomExecutor(t, "{}", "8.0.0") executor.normalize = true - oldVersion := sqlparser.GetParserVersion() - sqlparser.SetParserVersion("80000") - defer func() { - sqlparser.SetParserVersion(oldVersion) - }() session := NewAutocommitSession(&vtgatepb.Session{EnableSystemSettings: true, TargetString: KsTestUnsharded}) sbc.SetResults([]*sqltypes.Result{ diff --git a/go/vt/vtgate/executor_stream_test.go b/go/vt/vtgate/executor_stream_test.go index 5ef00fd0691..b8cfeaf3cd5 100644 --- a/go/vt/vtgate/executor_stream_test.go +++ b/go/vt/vtgate/executor_stream_test.go @@ -21,18 +21,17 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/discovery" querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/logstats" - - "vitess.io/vitess/go/vt/discovery" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/sqltypes" _ "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/sandboxconn" ) @@ -68,7 +67,7 @@ func TestStreamSQLSharded(t *testing.T) { queryLogger := streamlog.New[*logstats.LogStats]("VTGate", queryLogBufferSize) plans := DefaultPlanCache() - executor := NewExecutor(ctx, serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) + executor := NewExecutor(ctx, vtenv.NewTestEnv(), serv, cell, resolver, false, false, testBufferSize, plans, nil, false, querypb.ExecuteOptions_Gen4, 0) executor.SetQueryLogger(queryLogger) defer executor.Close() diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 2a44d0a8b00..5df4c7887f6 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -103,7 +103,7 @@ func TestExecutorMaxMemoryRowsExceeded(t *testing.T) { for _, test := range testCases { sbclookup.SetResults([]*sqltypes.Result{result}) - stmt, err := sqlparser.Parse(test.query) + stmt, err := sqlparser.NewTestParser().Parse(test.query) require.NoError(t, err) _, err = executor.Execute(ctx, nil, "TestExecutorMaxMemoryRowsExceeded", session, test.query, nil) @@ -640,7 +640,7 @@ func TestExecutorShow(t *testing.T) { lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql assert.Equal(t, wantQuery, lastQuery, "Got: %v. Want: %v", lastQuery, wantQuery) - // Set desitation keyspace in session + // Set destination keyspace in session session.TargetString = KsTestUnsharded _, err = executor.Execute(ctx, nil, "TestExecute", session, "show create table unknown", nil) require.NoError(t, err) @@ -667,7 +667,7 @@ func TestExecutorShow(t *testing.T) { append(buildVarCharRow( "utf8mb4", "UTF-8 Unicode", - collations.Local().LookupName(collations.Default())), + collations.MySQL8().LookupName(collations.MySQL8().DefaultConnectionCharset())), sqltypes.NewUint32(4)), }, } @@ -712,7 +712,7 @@ func TestExecutorShow(t *testing.T) { append(buildVarCharRow( "utf8mb4", "UTF-8 Unicode", - collations.Local().LookupName(collations.Default())), + collations.MySQL8().LookupName(collations.MySQL8().DefaultConnectionCharset())), sqltypes.NewUint32(4)), }, } @@ -763,7 +763,7 @@ func TestExecutorShow(t *testing.T) { wantqr = &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32, Charset: collations.CollationBinaryID, Flags: uint32(querypb.MySqlFlag_NUM_FLAG)}, - {Name: "value", Type: sqltypes.VarChar, Charset: uint32(collations.Default())}, + {Name: "value", Type: sqltypes.VarChar, Charset: uint32(collations.MySQL8().DefaultConnectionCharset())}, }, Rows: [][]sqltypes.Value{ {sqltypes.NewInt32(1), sqltypes.NewVarChar("foo")}, @@ -889,6 +889,7 @@ func TestExecutorShow(t *testing.T) { buildVarCharRow("TestExecutor", "keyspace_id", "numeric", "", ""), buildVarCharRow("TestExecutor", "krcol_unique_vdx", "keyrange_lookuper_unique", "", ""), buildVarCharRow("TestExecutor", "krcol_vdx", "keyrange_lookuper", "", ""), + buildVarCharRow("TestExecutor", "multicol_vdx", "multicol", "column_count=2; column_vindex=xxhash,binary", ""), buildVarCharRow("TestExecutor", "music_user_map", "lookup_hash_unique", "from=music_id; table=music_user_map; to=user_id", "music"), buildVarCharRow("TestExecutor", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"), buildVarCharRow("TestExecutor", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"), @@ -1155,97 +1156,6 @@ func TestExecutorComment(t *testing.T) { } } -func TestExecutorOther(t *testing.T) { - executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) - - type cnts struct { - Sbc1Cnt int64 - Sbc2Cnt int64 - SbcLookupCnt int64 - } - - tcs := []struct { - targetStr string - - hasNoKeyspaceErr bool - hasDestinationShardErr bool - wantCnts cnts - }{ - { - targetStr: "", - hasNoKeyspaceErr: true, - }, - { - targetStr: "TestExecutor[-]", - hasDestinationShardErr: true, - }, - { - targetStr: KsTestUnsharded, - wantCnts: cnts{ - Sbc1Cnt: 0, - Sbc2Cnt: 0, - SbcLookupCnt: 1, - }, - }, - { - targetStr: "TestExecutor", - wantCnts: cnts{ - Sbc1Cnt: 1, - Sbc2Cnt: 0, - SbcLookupCnt: 0, - }, - }, - { - targetStr: "TestExecutor/-20", - wantCnts: cnts{ - Sbc1Cnt: 1, - Sbc2Cnt: 0, - SbcLookupCnt: 0, - }, - }, - { - targetStr: "TestExecutor[00]", - wantCnts: cnts{ - Sbc1Cnt: 1, - Sbc2Cnt: 0, - SbcLookupCnt: 0, - }, - }, - } - - stmts := []string{ - "describe select * from t1", - "explain select * from t1", - "repair table t1", - "optimize table t1", - } - - for _, stmt := range stmts { - for _, tc := range tcs { - t.Run(fmt.Sprintf("%s-%s", stmt, tc.targetStr), func(t *testing.T) { - sbc1.ExecCount.Store(0) - sbc2.ExecCount.Store(0) - sbclookup.ExecCount.Store(0) - - _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) - if tc.hasNoKeyspaceErr { - assert.Error(t, err, errNoKeyspace) - } else if tc.hasDestinationShardErr { - assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt) - } else { - assert.NoError(t, err) - } - - utils.MustMatch(t, tc.wantCnts, cnts{ - Sbc1Cnt: sbc1.ExecCount.Load(), - Sbc2Cnt: sbc2.ExecCount.Load(), - SbcLookupCnt: sbclookup.ExecCount.Load(), - }) - }) - } - } -} - func TestExecutorDDL(t *testing.T) { executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) @@ -1306,7 +1216,7 @@ func TestExecutorDDL(t *testing.T) { "drop table t2", `create table test_partitioned ( id bigint, - date_create int, + date_create int, primary key(id) ) Engine=InnoDB /*!50100 PARTITION BY RANGE (date_create) (PARTITION p2018_06_14 VALUES LESS THAN (1528959600) ENGINE = InnoDB, @@ -1697,7 +1607,7 @@ func getPlanCached(t *testing.T, ctx context.Context, e *Executor, vcursor *vcur Options: &querypb.ExecuteOptions{SkipQueryPlanCache: skipQueryPlanCache}}, } - stmt, reservedVars, err := parseAndValidateQuery(sql) + stmt, reservedVars, err := parseAndValidateQuery(sql, sqlparser.NewTestParser()) require.NoError(t, err) plan, err := e.getPlan(context.Background(), vcursor, sql, stmt, comments, bindVars, reservedVars /* normalize */, e.normalize, logStats) require.NoError(t, err) @@ -1865,7 +1775,7 @@ func TestGetPlanPriority(t *testing.T) { vCursor, err := newVCursorImpl(session, makeComments(""), r, nil, r.vm, r.VSchema(), r.resolver.resolver, nil, false, pv) assert.NoError(t, err) - stmt, err := sqlparser.Parse(testCase.sql) + stmt, err := sqlparser.NewTestParser().Parse(testCase.sql) assert.NoError(t, err) crticalityFromStatement, _ := sqlparser.GetPriorityFromStatement(stmt) @@ -2145,8 +2055,8 @@ func TestServingKeyspaces(t *testing.T) { require.Equal(t, `[[VARCHAR("TestUnsharded")]]`, fmt.Sprintf("%v", result.Rows)) } -func TestExecutorOtherRead(t *testing.T) { - executor, sbc1, sbc2, sbclookup, _ := createExecutorEnv(t) +func TestExecutorOther(t *testing.T) { + executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) type cnts struct { Sbc1Cnt int64 @@ -2185,26 +2095,42 @@ func TestExecutorOtherRead(t *testing.T) { SbcLookupCnt: 0, }, }, + { + targetStr: "TestExecutor/-20", + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 0, + SbcLookupCnt: 0, + }, + }, + { + targetStr: "TestExecutor[00]", + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 0, + SbcLookupCnt: 0, + }, + }, } stmts := []string{ - "describe select * from t1", - "explain select * from t1", + "repair table t1", + "optimize table t1", "do 1", } for _, stmt := range stmts { for _, tc := range tcs { - t.Run(stmt+tc.targetStr, func(t *testing.T) { + t.Run(fmt.Sprintf("%s-%s", stmt, tc.targetStr), func(t *testing.T) { sbc1.ExecCount.Store(0) sbc2.ExecCount.Store(0) sbclookup.ExecCount.Store(0) - _, err := executor.Execute(context.Background(), nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) if tc.hasNoKeyspaceErr { - assert.EqualError(t, err, errNoKeyspace.Error()) + assert.Error(t, err, errNoKeyspace) } else if tc.hasDestinationShardErr { - assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) + assert.Errorf(t, err, "Destination can only be a single shard for statement: %s", stmt) } else { assert.NoError(t, err) } @@ -2213,7 +2139,7 @@ func TestExecutorOtherRead(t *testing.T) { Sbc1Cnt: sbc1.ExecCount.Load(), Sbc2Cnt: sbc2.ExecCount.Load(), SbcLookupCnt: sbclookup.ExecCount.Load(), - }, "count did not match") + }) }) } } @@ -2268,6 +2194,71 @@ func TestExecutorAnalyze(t *testing.T) { } } +func TestExecutorExplainStmt(t *testing.T) { + executor, sbc1, sbc2, sbclookup, ctx := createExecutorEnv(t) + + type cnts struct { + Sbc1Cnt int64 + Sbc2Cnt int64 + SbcLookupCnt int64 + } + + tcs := []struct { + targetStr string + + wantCnts cnts + }{ + { + targetStr: "", + wantCnts: cnts{Sbc1Cnt: 1}, + }, + { + targetStr: "TestExecutor[-]", + wantCnts: cnts{Sbc1Cnt: 1, Sbc2Cnt: 1}, + }, + { + targetStr: KsTestUnsharded, + wantCnts: cnts{SbcLookupCnt: 1}, + }, + { + targetStr: "TestExecutor", + wantCnts: cnts{Sbc1Cnt: 1}, + }, + { + targetStr: "TestExecutor/-20", + wantCnts: cnts{Sbc1Cnt: 1}, + }, + { + targetStr: "TestExecutor[00]", + wantCnts: cnts{Sbc1Cnt: 1}, + }, + } + + stmts := []string{ + "describe select * from t1", + "explain select * from t1", + } + + for _, stmt := range stmts { + for _, tc := range tcs { + t.Run(fmt.Sprintf("%s-%s", stmt, tc.targetStr), func(t *testing.T) { + sbc1.ExecCount.Store(0) + sbc2.ExecCount.Store(0) + sbclookup.ExecCount.Store(0) + + _, err := executor.Execute(ctx, nil, "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + assert.NoError(t, err) + + utils.MustMatch(t, tc.wantCnts, cnts{ + Sbc1Cnt: sbc1.ExecCount.Load(), + Sbc2Cnt: sbc2.ExecCount.Load(), + SbcLookupCnt: sbclookup.ExecCount.Load(), + }) + }) + } + } +} + func TestExecutorVExplain(t *testing.T) { executor, _, _, _, ctx := createExecutorEnv(t) @@ -2628,6 +2619,7 @@ func TestExecutorCallProc(t *testing.T) { func TestExecutorTempTable(t *testing.T) { executor, _, _, sbcUnsharded, ctx := createExecutorEnv(t) + initialWarningsCount := warnings.Counts()["WarnUnshardedOnly"] executor.warnShardedOnly = true creatQuery := "create temporary table temp_t(id bigint primary key)" session := NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) @@ -2635,6 +2627,7 @@ func TestExecutorTempTable(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, 1, sbcUnsharded.ExecCount.Load()) assert.NotEmpty(t, session.Warnings) + assert.Equal(t, initialWarningsCount+1, warnings.Counts()["WarnUnshardedOnly"], "warnings count") before := executor.plans.Len() diff --git a/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go b/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go index cf272fe3606..55a067807bd 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go +++ b/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go @@ -108,6 +108,7 @@ func TestGRPCVTGateConnAuth(t *testing.T) { fs := pflag.NewFlagSet("", pflag.ContinueOnError) grpcclient.RegisterFlags(fs) + grpcclient.ResetStaticAuth() err = fs.Parse([]string{ "--grpc_auth_static_client_creds", f.Name(), @@ -148,6 +149,7 @@ func TestGRPCVTGateConnAuth(t *testing.T) { fs = pflag.NewFlagSet("", pflag.ContinueOnError) grpcclient.RegisterFlags(fs) + grpcclient.ResetStaticAuth() err = fs.Parse([]string{ "--grpc_auth_static_client_creds", f.Name(), diff --git a/go/vt/vtgate/plan_execute.go b/go/vt/vtgate/plan_execute.go index 5d2414ac275..4e2c3bfea4c 100644 --- a/go/vt/vtgate/plan_execute.go +++ b/go/vt/vtgate/plan_execute.go @@ -80,7 +80,7 @@ func (e *Executor) newExecute( query, comments := sqlparser.SplitMarginComments(sql) // 2: Parse and Validate query - stmt, reservedVars, err := parseAndValidateQuery(query) + stmt, reservedVars, err := parseAndValidateQuery(query, e.env.Parser()) if err != nil { return err } diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index a67878d7119..e79e19ee96b 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -22,8 +22,8 @@ import ( "sort" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/vschemawrapper" "vitess.io/vitess/go/vt/key" - "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" @@ -70,11 +70,24 @@ func singleTable(ks, tbl string) string { // TestBuilder builds a plan for a query based on the specified vschema. // This method is only used from tests func TestBuilder(query string, vschema plancontext.VSchema, keyspace string) (*engine.Plan, error) { - stmt, reserved, err := sqlparser.Parse2(query) + stmt, reserved, err := vschema.Environment().Parser().Parse2(query) if err != nil { return nil, err } - result, err := sqlparser.RewriteAST(stmt, keyspace, sqlparser.SQLSelectLimitUnset, "", nil, vschema) + // Store the foreign key mode like we do for vcursor. + vw, isVw := vschema.(*vschemawrapper.VSchemaWrapper) + if isVw { + fkState := sqlparser.ForeignKeyChecksState(stmt) + if fkState != nil { + // Restore the old volue of ForeignKeyChecksState to not interfere with the next test cases. + oldVal := vw.ForeignKeyChecksState + vw.ForeignKeyChecksState = fkState + defer func() { + vw.ForeignKeyChecksState = oldVal + }() + } + } + result, err := sqlparser.RewriteAST(stmt, keyspace, sqlparser.SQLSelectLimitUnset, "", nil, vschema.GetForeignKeyChecksState(), vschema) if err != nil { return nil, err } @@ -116,7 +129,6 @@ func getConfiguredPlanner(vschema plancontext.VSchema, stmt sqlparser.Statement, case Gen4Left2Right, Gen4GreedyOnly, Gen4: default: // default is gen4 plan - log.Infof("Using Gen4 planner instead of %s", planner.String()) planner = Gen4 } return gen4Planner(query, planner), nil @@ -145,25 +157,7 @@ func buildRoutePlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVa func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { switch stmt := stmt.(type) { - case *sqlparser.Select: - configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) - if err != nil { - return nil, err - } - return buildRoutePlan(stmt, reservedVars, vschema, configuredPlanner) - case *sqlparser.Insert: - configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) - if err != nil { - return nil, err - } - return buildRoutePlan(stmt, reservedVars, vschema, configuredPlanner) - case *sqlparser.Update: - configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) - if err != nil { - return nil, err - } - return buildRoutePlan(stmt, reservedVars, vschema, configuredPlanner) - case *sqlparser.Delete: + case *sqlparser.Select, *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: configuredPlanner, err := getConfiguredPlanner(vschema, stmt, query) if err != nil { return nil, err @@ -191,8 +185,10 @@ func createInstructionFor(ctx context.Context, query string, stmt sqlparser.Stat return buildVSchemaDDLPlan(stmt, vschema) case *sqlparser.Use: return buildUsePlan(stmt) - case sqlparser.Explain: - return buildExplainPlan(ctx, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) + case *sqlparser.ExplainTab: + return explainTabPlan(stmt, vschema) + case *sqlparser.ExplainStmt: + return buildRoutePlan(stmt, reservedVars, vschema, buildExplainStmtPlan) case *sqlparser.VExplainStmt: return buildVExplainPlan(ctx, stmt, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) case *sqlparser.OtherAdmin: @@ -246,7 +242,7 @@ func buildAnalyzePlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, vsche var err error dest := key.Destination(key.DestinationAllShards{}) - if !analyzeStmt.Table.Qualifier.IsEmpty() && sqlparser.SystemSchema(analyzeStmt.Table.Qualifier.String()) { + if analyzeStmt.Table.Qualifier.NotEmpty() && sqlparser.SystemSchema(analyzeStmt.Table.Qualifier.String()) { ks, err = vschema.AnyKeyspace() if err != nil { return nil, err @@ -370,18 +366,12 @@ func buildFlushOptions(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*pla dest = key.DestinationAllShards{} } - tc := &tableCollector{} - for _, tbl := range stmt.TableNames { - tc.addASTTable(keyspace.Name, tbl) - } - return newPlanResult(&engine.Send{ - Keyspace: keyspace, - TargetDestination: dest, - Query: sqlparser.String(stmt), - IsDML: false, - SingleShardOnly: false, - }, tc.getTables()...), nil + Keyspace: keyspace, + TargetDestination: dest, + Query: sqlparser.String(stmt), + ReservedConnectionNeeded: stmt.WithLock, + }), nil } func buildFlushTables(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*planResult, error) { @@ -429,9 +419,10 @@ func buildFlushTables(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*plan if len(tablesMap) == 1 { for sendDest, tables := range tablesMap { return newPlanResult(&engine.Send{ - Keyspace: sendDest.ks, - TargetDestination: sendDest.dest, - Query: sqlparser.String(newFlushStmt(stmt, tables)), + Keyspace: sendDest.ks, + TargetDestination: sendDest.dest, + Query: sqlparser.String(newFlushStmt(stmt, tables)), + ReservedConnectionNeeded: stmt.WithLock, }, tc.getTables()...), nil } } @@ -443,9 +434,10 @@ func buildFlushTables(stmt *sqlparser.Flush, vschema plancontext.VSchema) (*plan var sources []engine.Primitive for _, sendDest := range keys { plan := &engine.Send{ - Keyspace: sendDest.ks, - TargetDestination: sendDest.dest, - Query: sqlparser.String(newFlushStmt(stmt, tablesMap[sendDest])), + Keyspace: sendDest.ks, + TargetDestination: sendDest.dest, + Query: sqlparser.String(newFlushStmt(stmt, tablesMap[sendDest])), + ReservedConnectionNeeded: stmt.WithLock, } sources = append(sources, plan) } diff --git a/go/vt/vtgate/planbuilder/call_proc.go b/go/vt/vtgate/planbuilder/call_proc.go index 13fe5cc60e4..34f475689aa 100644 --- a/go/vt/vtgate/planbuilder/call_proc.go +++ b/go/vt/vtgate/planbuilder/call_proc.go @@ -25,7 +25,7 @@ import ( func buildCallProcPlan(stmt *sqlparser.CallProc, vschema plancontext.VSchema) (*planResult, error) { var ks string - if !stmt.Name.Qualifier.IsEmpty() { + if stmt.Name.Qualifier.NotEmpty() { ks = stmt.Name.Qualifier.String() } diff --git a/go/vt/vtgate/planbuilder/collations_test.go b/go/vt/vtgate/planbuilder/collations_test.go index 7eaf3968f74..b393e186679 100644 --- a/go/vt/vtgate/planbuilder/collations_test.go +++ b/go/vt/vtgate/planbuilder/collations_test.go @@ -20,11 +20,11 @@ import ( "fmt" "testing" - "vitess.io/vitess/go/test/vschemawrapper" - "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/test/vschemawrapper" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/engine" ) @@ -45,6 +45,7 @@ func (tc *collationTestCase) run(t *testing.T) { V: loadSchema(t, "vschemas/schema.json", false), SysVarEnabled: true, Version: Gen4, + Env: vtenv.NewTestEnv(), } tc.addCollationsToSchema(vschemaWrapper) @@ -59,7 +60,6 @@ func (tc *collationTestCase) addCollationsToSchema(vschema *vschemawrapper.VSche for i, c := range tbl.Columns { if c.Name.EqualString(collation.colName) { tbl.Columns[i].CollationName = collation.collationName - break } } } @@ -67,7 +67,7 @@ func (tc *collationTestCase) addCollationsToSchema(vschema *vschemawrapper.VSche func TestOrderedAggregateCollations(t *testing.T) { collid := func(collname string) collations.ID { - return collations.Local().LookupByName(collname) + return collations.MySQL8().LookupByName(collname) } testCases := []collationTestCase{ { @@ -76,7 +76,7 @@ func TestOrderedAggregateCollations(t *testing.T) { check: func(t *testing.T, colls []collationInTable, primitive engine.Primitive) { oa, isOA := primitive.(*engine.OrderedAggregate) require.True(t, isOA, "should be an OrderedAggregate") - require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Coll) + require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Collation()) }, }, { @@ -85,7 +85,7 @@ func TestOrderedAggregateCollations(t *testing.T) { check: func(t *testing.T, colls []collationInTable, primitive engine.Primitive) { distinct, isDistinct := primitive.(*engine.Distinct) require.True(t, isDistinct, "should be a distinct") - require.Equal(t, collid(colls[0].collationName), distinct.CheckCols[0].Type.Coll) + require.Equal(t, collid(colls[0].collationName), distinct.CheckCols[0].Type.Collation()) }, }, { @@ -97,8 +97,8 @@ func TestOrderedAggregateCollations(t *testing.T) { check: func(t *testing.T, colls []collationInTable, primitive engine.Primitive) { oa, isOA := primitive.(*engine.OrderedAggregate) require.True(t, isOA, "should be an OrderedAggregate") - require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Coll) - require.Equal(t, collid(colls[1].collationName), oa.GroupByKeys[1].Type.Coll) + require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Collation()) + require.Equal(t, collid(colls[1].collationName), oa.GroupByKeys[1].Type.Collation()) }, }, { @@ -109,7 +109,7 @@ func TestOrderedAggregateCollations(t *testing.T) { check: func(t *testing.T, colls []collationInTable, primitive engine.Primitive) { oa, isOA := primitive.(*engine.OrderedAggregate) require.True(t, isOA, "should be an OrderedAggregate") - require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Coll) + require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Collation()) }, }, { @@ -122,7 +122,7 @@ func TestOrderedAggregateCollations(t *testing.T) { require.True(t, isMemSort, "should be a MemorySort") oa, isOA := memSort.Input.(*engine.OrderedAggregate) require.True(t, isOA, "should be an OrderedAggregate") - require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Coll) + require.Equal(t, collid(colls[0].collationName), oa.GroupByKeys[0].Type.Collation()) }, }, } diff --git a/go/vt/vtgate/planbuilder/ddl.go b/go/vt/vtgate/planbuilder/ddl.go index 41e5d64346e..fe5ebeb0889 100644 --- a/go/vt/vtgate/planbuilder/ddl.go +++ b/go/vt/vtgate/planbuilder/ddl.go @@ -2,6 +2,7 @@ package planbuilder import ( "context" + "errors" "fmt" "vitess.io/vitess/go/vt/key" @@ -172,7 +173,8 @@ func findTableDestinationAndKeyspace(vschema plancontext.VSchema, ddlStatement s var err error table, _, _, _, destination, err = vschema.FindTableOrVindex(ddlStatement.GetTable()) if err != nil { - _, isNotFound := err.(vindexes.NotFoundError) + var notFoundError vindexes.NotFoundError + isNotFound := errors.As(err, ¬FoundError) if !isNotFound { return nil, nil, err } @@ -312,7 +314,8 @@ func buildDropTable(vschema plancontext.VSchema, ddlStatement sqlparser.DDLState table, _, _, _, destinationTab, err = vschema.FindTableOrVindex(tab) if err != nil { - _, isNotFound := err.(vindexes.NotFoundError) + var notFoundError vindexes.NotFoundError + isNotFound := errors.As(err, ¬FoundError) if !isNotFound { return nil, nil, err } @@ -355,7 +358,8 @@ func buildRenameTable(vschema plancontext.VSchema, renameTable *sqlparser.Rename table, _, _, _, destinationFrom, err = vschema.FindTableOrVindex(tabPair.FromTable) if err != nil { - _, isNotFound := err.(vindexes.NotFoundError) + var notFoundError vindexes.NotFoundError + isNotFound := errors.As(err, ¬FoundError) if !isNotFound { return nil, nil, err } diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 188c1485d1d..a613c158ab7 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -50,7 +50,7 @@ func gen4DeleteStmtPlanner( return nil, err } - err = rewriteRoutedTables(deleteStmt, vschema) + err = queryRewrite(ctx.SemTable, reservedVars, deleteStmt) if err != nil { return nil, err } @@ -72,11 +72,6 @@ func gen4DeleteStmtPlanner( return nil, err } - err = queryRewrite(ctx.SemTable, reservedVars, deleteStmt) - if err != nil { - return nil, err - } - op, err := operators.PlanQuery(ctx, deleteStmt) if err != nil { return nil, err @@ -95,7 +90,7 @@ func rewriteSingleTbl(del *sqlparser.Delete) (*sqlparser.Delete, error) { if !ok { return del, nil } - if !atExpr.As.IsEmpty() && !sqlparser.Equals.IdentifierCS(del.Targets[0].Name, atExpr.As) { + if atExpr.As.NotEmpty() && !sqlparser.Equals.IdentifierCS(del.Targets[0].Name, atExpr.As) { // Unknown table in MULTI DELETE return nil, vterrors.VT03003(del.Targets[0].Name.String()) } @@ -144,32 +139,9 @@ func checkIfDeleteSupported(del *sqlparser.Delete, semTable *semantics.SemTable) return semTable.NotUnshardedErr } - // Delete is only supported for a single TableExpr which is supposed to be an aliased expression - multiShardErr := vterrors.VT12001("multi-shard or vindex write statement") - if len(del.TableExprs) != 1 { - return multiShardErr - } - _, isAliasedExpr := del.TableExprs[0].(*sqlparser.AliasedTableExpr) - if !isAliasedExpr { - return multiShardErr - } - + // Delete is only supported for single Target. if len(del.Targets) > 1 { - return vterrors.VT12001("multi-table DELETE statement in a sharded keyspace") - } - - err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - switch node.(type) { - case *sqlparser.Subquery, *sqlparser.DerivedTable: - // We have a subquery, so we must fail the planning. - // If this subquery and the table expression were all belonging to the same unsharded keyspace, - // we would have already created a plan for them before doing these checks. - return false, vterrors.VT12001("subqueries in DML") - } - return true, nil - }, del) - if err != nil { - return err + return vterrors.VT12001("multi-table DELETE statement with multi-target") } return nil diff --git a/go/vt/vtgate/planbuilder/dml_planner.go b/go/vt/vtgate/planbuilder/dml_planner.go deleted file mode 100644 index 7ec616f7f36..00000000000 --- a/go/vt/vtgate/planbuilder/dml_planner.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package planbuilder - -import ( - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" -) - -func rewriteRoutedTables(stmt sqlparser.Statement, vschema plancontext.VSchema) error { - // Rewrite routed tables - return sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { - aliasTbl, isAlias := node.(*sqlparser.AliasedTableExpr) - if !isAlias { - return true, nil - } - tableName, ok := aliasTbl.Expr.(sqlparser.TableName) - if !ok { - return true, nil - } - vschemaTable, vindexTbl, _, _, _, err := vschema.FindTableOrVindex(tableName) - if err != nil { - return false, err - } - if vindexTbl != nil { - // vindex cannot be present in a dml statement. - return false, vterrors.VT09014() - } - - if vschemaTable.Name.String() != tableName.Name.String() { - name := tableName.Name - if aliasTbl.As.IsEmpty() { - // if the user hasn't specified an alias, we'll insert one here so the old table name still works - aliasTbl.As = sqlparser.NewIdentifierCS(name.String()) - } - tableName.Name = sqlparser.NewIdentifierCS(vschemaTable.Name.String()) - aliasTbl.Expr = tableName - } - - return true, nil - }, stmt) -} - -func generateQuery(statement sqlparser.Statement) string { - buf := sqlparser.NewTrackedBuffer(dmlFormatter) - statement.Format(buf) - return buf.String() -} diff --git a/go/maps2/maps.go b/go/vt/vtgate/planbuilder/dml_with_input.go similarity index 55% rename from go/maps2/maps.go rename to go/vt/vtgate/planbuilder/dml_with_input.go index 56191bea1a7..1b6d1c0835a 100644 --- a/go/maps2/maps.go +++ b/go/vt/vtgate/planbuilder/dml_with_input.go @@ -14,24 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -package maps2 - -// Keys returns the keys of the map m. -// The keys will be in an indeterminate order. -func Keys[M ~map[K]V, K comparable, V any](m M) []K { - r := make([]K, 0, len(m)) - for k := range m { - r = append(r, k) - } - return r +package planbuilder + +import ( + "vitess.io/vitess/go/vt/vtgate/engine" +) + +type dmlWithInput struct { + input logicalPlan + dml logicalPlan + + outputCols []int } -// Values returns the values of the map m. -// The values will be in an indeterminate order. -func Values[M ~map[K]V, K comparable, V any](m M) []V { - r := make([]V, 0, len(m)) - for _, v := range m { - r = append(r, v) +var _ logicalPlan = (*dmlWithInput)(nil) + +// Primitive implements the logicalPlan interface +func (d *dmlWithInput) Primitive() engine.Primitive { + inp := d.input.Primitive() + del := d.dml.Primitive() + return &engine.DMLWithInput{ + DML: del, + Input: inp, + OutputCols: d.outputCols, } - return r } diff --git a/go/vt/vtgate/planbuilder/expression_converter.go b/go/vt/vtgate/planbuilder/expression_converter.go index 61eebbe7f99..961c4f1fb4b 100644 --- a/go/vt/vtgate/planbuilder/expression_converter.go +++ b/go/vt/vtgate/planbuilder/expression_converter.go @@ -20,16 +20,18 @@ import ( "fmt" "strings" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type expressionConverter struct { tabletExpressions []sqlparser.Expr + env *vtenv.Environment + collation collations.ID } func booleanValues(astExpr sqlparser.Expr) evalengine.Expr { @@ -81,12 +83,15 @@ func (ec *expressionConverter) convert(astExpr sqlparser.Expr, boolean, identifi return evalExpr, nil } } - evalExpr, err := evalengine.Translate(astExpr, nil) + evalExpr, err := evalengine.Translate(astExpr, &evalengine.Config{ + Collation: ec.collation, + Environment: ec.env, + }) if err != nil { if !strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) { return nil, err } - evalExpr = evalengine.NewColumn(len(ec.tabletExpressions), evalengine.UnknownType(), nil) + evalExpr = evalengine.NewColumn(len(ec.tabletExpressions), evalengine.Type{}, nil) ec.tabletExpressions = append(ec.tabletExpressions, astExpr) } return evalExpr, nil diff --git a/go/vt/vtgate/planbuilder/expression_converter_test.go b/go/vt/vtgate/planbuilder/expression_converter_test.go index 798ed1e1635..c92cc184262 100644 --- a/go/vt/vtgate/planbuilder/expression_converter_test.go +++ b/go/vt/vtgate/planbuilder/expression_converter_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -40,16 +41,20 @@ func TestConversion(t *testing.T) { expressionsOut: e(evalengine.NewLiteralInt(1)), }, { expressionsIn: "@@foo", - expressionsOut: e(evalengine.NewColumn(0, evalengine.UnknownType(), nil)), + expressionsOut: e(evalengine.NewColumn(0, evalengine.Type{}, nil)), }} + venv := vtenv.NewTestEnv() for _, tc := range queries { t.Run(tc.expressionsIn, func(t *testing.T) { - statement, err := sqlparser.Parse("select " + tc.expressionsIn) + statement, err := sqlparser.NewTestParser().Parse("select " + tc.expressionsIn) require.NoError(t, err) slct := statement.(*sqlparser.Select) exprs := extract(slct.SelectExprs) - ec := &expressionConverter{} + ec := &expressionConverter{ + env: venv, + collation: venv.CollationEnv().DefaultConnectionCharset(), + } var result []evalengine.Expr for _, expr := range exprs { evalExpr, err := ec.convert(expr, false, false) diff --git a/go/vt/vtgate/planbuilder/insert.go b/go/vt/vtgate/planbuilder/insert.go index c187cd7efdc..b08330f060d 100644 --- a/go/vt/vtgate/planbuilder/insert.go +++ b/go/vt/vtgate/planbuilder/insert.go @@ -33,7 +33,7 @@ func gen4InsertStmtPlanner(version querypb.ExecuteOptions_PlannerVersion, insStm return nil, err } - err = rewriteRoutedTables(insStmt, vschema) + err = queryRewrite(ctx.SemTable, reservedVars, insStmt) if err != nil { return nil, err } @@ -62,12 +62,11 @@ func gen4InsertStmtPlanner(version querypb.ExecuteOptions_PlannerVersion, insStm return nil, err } - if err = errOutIfPlanCannotBeConstructed(ctx, tblInfo.GetVindexTable(), insStmt, ctx.SemTable.ForeignKeysPresent()); err != nil { - return nil, err + if _, isVindex := tblInfo.(*semantics.VindexTable); isVindex { + return nil, vterrors.VT09014() } - err = queryRewrite(ctx.SemTable, reservedVars, insStmt) - if err != nil { + if err = errOutIfPlanCannotBeConstructed(ctx, tblInfo.GetVindexTable()); err != nil { return nil, err } @@ -84,42 +83,38 @@ func gen4InsertStmtPlanner(version querypb.ExecuteOptions_PlannerVersion, insStm return newPlanResult(plan.Primitive(), operators.TablesUsed(op)...), nil } -func errOutIfPlanCannotBeConstructed(ctx *plancontext.PlanningContext, vTbl *vindexes.Table, insStmt *sqlparser.Insert, fkPlanNeeded bool) error { - if vTbl.Keyspace.Sharded && ctx.SemTable.NotUnshardedErr != nil { - return ctx.SemTable.NotUnshardedErr - } - if insStmt.Action != sqlparser.ReplaceAct { +func errOutIfPlanCannotBeConstructed(ctx *plancontext.PlanningContext, vTbl *vindexes.Table) error { + if !vTbl.Keyspace.Sharded { return nil } - if fkPlanNeeded { - return vterrors.VT12001("REPLACE INTO with foreign keys") - } - return nil + return ctx.SemTable.NotUnshardedErr } func insertUnshardedShortcut(stmt *sqlparser.Insert, ks *vindexes.Keyspace, tables []*vindexes.Table) logicalPlan { - eIns := &engine.Insert{} - eIns.Keyspace = ks - eIns.TableName = tables[0].Name.String() - eIns.Opcode = engine.InsertUnsharded + eIns := &engine.Insert{ + InsertCommon: engine.InsertCommon{ + Opcode: engine.InsertUnsharded, + Keyspace: ks, + TableName: tables[0].Name.String(), + }, + } eIns.Query = generateQuery(stmt) return &insert{eInsert: eIns} } type insert struct { - eInsert *engine.Insert - source logicalPlan + eInsert *engine.Insert + eInsertSelect *engine.InsertSelect + source logicalPlan } var _ logicalPlan = (*insert)(nil) func (i *insert) Primitive() engine.Primitive { - if i.source != nil { - i.eInsert.Input = i.source.Primitive() + if i.source == nil { + return i.eInsert } - return i.eInsert -} - -func (i *insert) ContainsTables() semantics.TableSet { - panic("does not expect insert to get contains tables call") + input := i.source.Primitive() + i.eInsertSelect.Input = input + return i.eInsertSelect } diff --git a/go/vt/vtgate/planbuilder/join.go b/go/vt/vtgate/planbuilder/join.go index 02027a8b49e..462b45fa00a 100644 --- a/go/vt/vtgate/planbuilder/join.go +++ b/go/vt/vtgate/planbuilder/join.go @@ -55,3 +55,16 @@ func (j *join) Primitive() engine.Primitive { Opcode: j.Opcode, } } + +type hashJoin struct { + lhs, rhs logicalPlan + inner *engine.HashJoin +} + +func (hj *hashJoin) Primitive() engine.Primitive { + lhs := hj.lhs.Primitive() + rhs := hj.rhs.Primitive() + hj.inner.Left = lhs + hj.inner.Right = rhs + return hj.inner +} diff --git a/go/vt/vtgate/planbuilder/locktables.go b/go/vt/vtgate/planbuilder/locktables.go index 9c3a5fa44e9..e8776d13e65 100644 --- a/go/vt/vtgate/planbuilder/locktables.go +++ b/go/vt/vtgate/planbuilder/locktables.go @@ -33,6 +33,5 @@ func buildLockPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, _ planco // buildUnlockPlan plans lock tables statement. func buildUnlockPlan(stmt sqlparser.Statement, _ *sqlparser.ReservedVars, _ plancontext.VSchema) (*planResult, error) { - log.Warningf("Unlock Tables statement is ignored: %v", stmt) - return newPlanResult(engine.NewRowsPrimitive(make([][]sqltypes.Value, 0), make([]*querypb.Field, 0))), nil + return newPlanResult(&engine.Unlock{}), nil } diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index a04c4b00c2c..577e585351d 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -22,21 +22,21 @@ import ( "strconv" "strings" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/slice" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/engine/opcode" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" ) -func transformToLogicalPlan(ctx *plancontext.PlanningContext, op ops.Operator) (logicalPlan, error) { +func transformToLogicalPlan(ctx *plancontext.PlanningContext, op operators.Operator) (logicalPlan, error) { switch op := op.(type) { case *operators.Route: return transformRoutePlan(ctx, op) @@ -68,11 +68,78 @@ func transformToLogicalPlan(ctx *plancontext.PlanningContext, op ops.Operator) ( return transformFkVerify(ctx, op) case *operators.InsertSelection: return transformInsertionSelection(ctx, op) + case *operators.Upsert: + return transformUpsert(ctx, op) + case *operators.HashJoin: + return transformHashJoin(ctx, op) + case *operators.Sequential: + return transformSequential(ctx, op) + case *operators.DMLWithInput: + return transformDMLWithInput(ctx, op) } return nil, vterrors.VT13001(fmt.Sprintf("unknown type encountered: %T (transformToLogicalPlan)", op)) } +func transformDMLWithInput(ctx *plancontext.PlanningContext, op *operators.DMLWithInput) (logicalPlan, error) { + input, err := transformToLogicalPlan(ctx, op.Source) + if err != nil { + return nil, err + } + + del, err := transformToLogicalPlan(ctx, op.DML) + if err != nil { + return nil, err + } + return &dmlWithInput{ + input: input, + dml: del, + outputCols: op.Offsets, + }, nil +} + +func transformUpsert(ctx *plancontext.PlanningContext, op *operators.Upsert) (logicalPlan, error) { + u := &upsert{} + for _, source := range op.Sources { + iLp, uLp, err := transformOneUpsert(ctx, source) + if err != nil { + return nil, err + } + u.insert = append(u.insert, iLp) + u.update = append(u.update, uLp) + } + return u, nil +} + +func transformOneUpsert(ctx *plancontext.PlanningContext, source operators.UpsertSource) (iLp, uLp logicalPlan, err error) { + iLp, err = transformToLogicalPlan(ctx, source.Insert) + if err != nil { + return + } + if ins, ok := iLp.(*insert); ok { + ins.eInsert.PreventAutoCommit = true + } + uLp, err = transformToLogicalPlan(ctx, source.Update) + return +} + +func transformSequential(ctx *plancontext.PlanningContext, op *operators.Sequential) (logicalPlan, error) { + var lps []logicalPlan + for _, source := range op.Sources { + lp, err := transformToLogicalPlan(ctx, source) + if err != nil { + return nil, err + } + if ins, ok := lp.(*insert); ok { + ins.eInsert.PreventAutoCommit = true + } + lps = append(lps, lp) + } + return &sequential{ + sources: lps, + }, nil +} + func transformInsertionSelection(ctx *plancontext.PlanningContext, op *operators.InsertSelection) (logicalPlan, error) { rb, isRoute := op.Insert.(*operators.Route) if !isRoute { @@ -89,20 +156,20 @@ func transformInsertionSelection(ctx *plancontext.PlanningContext, op *operators } ins := dmlOp.(*operators.Insert) - eins := &engine.Insert{ - Opcode: mapToInsertOpCode(rb.Routing.OpCode(), true), - Keyspace: rb.Routing.Keyspace(), - TableName: ins.VTable.Name.String(), - Ignore: ins.Ignore, - ForceNonStreaming: op.ForceNonStreaming, - Generate: autoIncGenerate(ins.AutoIncrement), - ColVindexes: ins.ColVindexes, - VindexValues: ins.VindexValues, + eins := &engine.InsertSelect{ + InsertCommon: engine.InsertCommon{ + Keyspace: rb.Routing.Keyspace(), + TableName: ins.VTable.Name.String(), + Ignore: ins.Ignore, + ForceNonStreaming: op.ForceNonStreaming, + Generate: autoIncGenerate(ins.AutoIncrement), + ColVindexes: ins.ColVindexes, + }, VindexValueOffset: ins.VindexValueOffset, } - lp := &insert{eInsert: eins} + lp := &insert{eInsertSelect: eins} - eins.Prefix, eins.Mid, eins.Suffix = generateInsertShardedQuery(ins.AST) + eins.Prefix, _, eins.Suffix = generateInsertShardedQuery(ins.AST) selectionPlan, err := transformToLogicalPlan(ctx, op.Select) if err != nil { @@ -139,9 +206,10 @@ func transformFkCascade(ctx *plancontext.PlanningContext, fkc *operators.FkCasca childEngine := childLP.Primitive() children = append(children, &engine.FkChild{ - BVName: child.BVName, - Cols: child.Cols, - Exec: childEngine, + BVName: child.BVName, + Cols: child.Cols, + NonLiteralInfo: child.NonLiteralInfo, + Exec: childEngine, }) } @@ -168,10 +236,7 @@ func transformSubQuery(ctx *plancontext.PlanningContext, op *operators.SubQuery) return newUncorrelatedSubquery(op.FilterType, op.SubqueryValueName, op.HasValuesName, inner, outer), nil } - lhsCols, err := op.OuterExpressionsNeeded(ctx, op.Outer) - if err != nil { - return nil, err - } + lhsCols := op.OuterExpressionsNeeded(ctx, op.Outer) return newSemiJoin(outer, inner, op.Vars, lhsCols), nil } @@ -210,13 +275,14 @@ func transformAggregator(ctx *plancontext.PlanningContext, op *operators.Aggrega oa := &orderedAggregate{ resultsBuilder: newResultsBuilder(plan, nil), + collationEnv: ctx.VSchema.Environment().CollationEnv(), } for _, aggr := range op.Aggregations { if aggr.OpCode == opcode.AggregateUnassigned { return nil, vterrors.VT12001(fmt.Sprintf("in scatter query: aggregation function '%s'", sqlparser.String(aggr.Original))) } - aggrParam := engine.NewAggregateParam(aggr.OpCode, aggr.ColOffset, aggr.Alias) + aggrParam := engine.NewAggregateParam(aggr.OpCode, aggr.ColOffset, aggr.Alias, ctx.VSchema.Environment().CollationEnv()) aggrParam.Expr = aggr.Func aggrParam.Original = aggr.Original aggrParam.OrigOpcode = aggr.OriginalOpCode @@ -225,18 +291,16 @@ func transformAggregator(ctx *plancontext.PlanningContext, op *operators.Aggrega oa.aggregates = append(oa.aggregates, aggrParam) } for _, groupBy := range op.Grouping { - typ, _ := ctx.SemTable.TypeForExpr(groupBy.SimplifiedExpr) + typ, _ := ctx.SemTable.TypeForExpr(groupBy.Inner) oa.groupByKeys = append(oa.groupByKeys, &engine.GroupByParams{ KeyCol: groupBy.ColOffset, WeightStringCol: groupBy.WSOffset, - Expr: groupBy.AsAliasedExpr().Expr, + Expr: groupBy.Inner, Type: typ, + CollationEnv: ctx.VSchema.Environment().CollationEnv(), }) } - if err != nil { - return nil, err - } oa.truncateColumnCount = op.ResultColumns return oa, nil } @@ -274,6 +338,7 @@ func createMemorySort(ctx *plancontext.PlanningContext, src logicalPlan, orderin WeightStringCol: ordering.WOffset[idx], Desc: order.Inner.Direction == sqlparser.DescOrder, Type: typ, + CollationEnv: ctx.VSchema.Environment().CollationEnv(), }) } @@ -413,10 +478,7 @@ func routeToEngineRoute(ctx *plancontext.PlanningContext, op *operators.Route, h } rp := newRoutingParams(ctx, op.Routing.OpCode()) - err = op.Routing.UpdateRoutingParams(ctx, rp) - if err != nil { - return nil, err - } + op.Routing.UpdateRoutingParams(ctx, rp) e := &engine.Route{ TableName: strings.Join(tableNames, ", "), @@ -485,7 +547,7 @@ func transformRoutePlan(ctx *plancontext.PlanningContext, op *operators.Route) ( case *sqlparser.Update: return buildUpdateLogicalPlan(ctx, op, dmlOp, stmt, hints) case *sqlparser.Delete: - return buildDeleteLogicalPlan(ctx, op, dmlOp, hints) + return buildDeleteLogicalPlan(ctx, op, dmlOp, stmt, hints) case *sqlparser.Insert: return buildInsertLogicalPlan(op, dmlOp, stmt, hints) default: @@ -504,6 +566,7 @@ func buildRouteLogicalPlan(ctx *plancontext.PlanningContext, op *operators.Route WeightStringCol: order.WOffset, Desc: order.Direction == sqlparser.DescOrder, Type: typ, + CollationEnv: ctx.VSchema.Environment().CollationEnv(), }) } if err != nil { @@ -522,19 +585,27 @@ func buildRouteLogicalPlan(ctx *plancontext.PlanningContext, op *operators.Route } func buildInsertLogicalPlan( - rb *operators.Route, op ops.Operator, stmt *sqlparser.Insert, + rb *operators.Route, op operators.Operator, stmt *sqlparser.Insert, hints *queryHints, ) (logicalPlan, error) { ins := op.(*operators.Insert) + + ic := engine.InsertCommon{ + Opcode: mapToInsertOpCode(rb.Routing.OpCode()), + Keyspace: rb.Routing.Keyspace(), + TableName: ins.VTable.Name.String(), + Ignore: ins.Ignore, + Generate: autoIncGenerate(ins.AutoIncrement), + ColVindexes: ins.ColVindexes, + } + if hints != nil { + ic.MultiShardAutocommit = hints.multiShardAutocommit + ic.QueryTimeout = hints.queryTimeout + } + eins := &engine.Insert{ - Opcode: mapToInsertOpCode(rb.Routing.OpCode(), false), - Keyspace: rb.Routing.Keyspace(), - TableName: ins.VTable.Name.String(), - Ignore: ins.Ignore, - Generate: autoIncGenerate(ins.AutoIncrement), - ColVindexes: ins.ColVindexes, - VindexValues: ins.VindexValues, - VindexValueOffset: ins.VindexValueOffset, + InsertCommon: ic, + VindexValues: ins.VindexValues, } lp := &insert{eInsert: eins} @@ -544,22 +615,14 @@ func buildInsertLogicalPlan( eins.Prefix, eins.Mid, eins.Suffix = generateInsertShardedQuery(ins.AST) } - if hints != nil { - eins.MultiShardAutocommit = hints.multiShardAutocommit - eins.QueryTimeout = hints.queryTimeout - } - eins.Query = generateQuery(stmt) return lp, nil } -func mapToInsertOpCode(code engine.Opcode, insertSelect bool) engine.InsertOpcode { +func mapToInsertOpCode(code engine.Opcode) engine.InsertOpcode { if code == engine.Unsharded { return engine.InsertUnsharded } - if insertSelect { - return engine.InsertSelect - } return engine.InsertSharded } @@ -579,7 +642,7 @@ func autoIncGenerate(gen *operators.Generate) *engine.Generate { } } -func generateInsertShardedQuery(ins *sqlparser.Insert) (prefix string, mids sqlparser.Values, suffix string) { +func generateInsertShardedQuery(ins *sqlparser.Insert) (prefix string, mids sqlparser.Values, suffix sqlparser.OnDup) { mids, isValues := ins.Rows.(sqlparser.Values) prefixFormat := "insert %v%sinto %v%v " if isValues { @@ -594,9 +657,13 @@ func generateInsertShardedQuery(ins *sqlparser.Insert) (prefix string, mids sqlp ins.Table, ins.Columns) prefix = prefixBuf.String() - suffixBuf := sqlparser.NewTrackedBuffer(dmlFormatter) - suffixBuf.Myprintf("%v", ins.OnDup) - suffix = suffixBuf.String() + suffix = sqlparser.CopyOnRewrite(ins.OnDup, nil, func(cursor *sqlparser.CopyOnWriteCursor) { + if tblName, ok := cursor.Node().(sqlparser.TableName); ok { + if tblName.Qualifier != sqlparser.NewIdentifierCS("") { + cursor.Replace(sqlparser.NewTableName(tblName.Name.String())) + } + } + }, nil).(sqlparser.OnDup) return } @@ -613,77 +680,70 @@ func dmlFormatter(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { func buildUpdateLogicalPlan( ctx *plancontext.PlanningContext, rb *operators.Route, - dmlOp ops.Operator, + dmlOp operators.Operator, stmt *sqlparser.Update, hints *queryHints, ) (logicalPlan, error) { upd := dmlOp.(*operators.Update) - rp := newRoutingParams(ctx, rb.Routing.OpCode()) - err := rb.Routing.UpdateRoutingParams(ctx, rp) - if err != nil { - return nil, err - } - edml := &engine.DML{ - Query: generateQuery(stmt), - TableNames: []string{upd.VTable.Name.String()}, - Vindexes: upd.VTable.ColumnVindexes, - OwnedVindexQuery: upd.OwnedVindexQuery, - RoutingParameters: rp, + var vindexes []*vindexes.ColumnVindex + vQuery := "" + if len(upd.ChangedVindexValues) > 0 { + upd.OwnedVindexQuery.From = stmt.GetFrom() + upd.OwnedVindexQuery.Where = stmt.Where + vQuery = sqlparser.String(upd.OwnedVindexQuery) + vindexes = upd.Target.VTable.ColumnVindexes + if upd.OwnedVindexQuery.Limit != nil && len(upd.OwnedVindexQuery.OrderBy) == 0 { + return nil, vterrors.VT12001("Vindex update should have ORDER BY clause when using LIMIT") + } } - transformDMLPlan(upd.VTable, edml, rb.Routing, len(upd.ChangedVindexValues) > 0) + edml := createDMLPrimitive(ctx, rb, hints, upd.Target.VTable, generateQuery(stmt), vindexes, vQuery) - e := &engine.Update{ - ChangedVindexValues: upd.ChangedVindexValues, + return &primitiveWrapper{prim: &engine.Update{ DML: edml, + ChangedVindexValues: upd.ChangedVindexValues, + }}, nil +} + +func buildDeleteLogicalPlan(ctx *plancontext.PlanningContext, rb *operators.Route, dmlOp operators.Operator, stmt *sqlparser.Delete, hints *queryHints) (logicalPlan, error) { + del := dmlOp.(*operators.Delete) + + var vindexes []*vindexes.ColumnVindex + vQuery := "" + if del.OwnedVindexQuery != nil { + del.OwnedVindexQuery.From = stmt.GetFrom() + del.OwnedVindexQuery.Where = stmt.Where + vQuery = sqlparser.String(del.OwnedVindexQuery) + vindexes = del.Target.VTable.Owned } - if hints != nil { - e.MultiShardAutocommit = hints.multiShardAutocommit - e.QueryTimeout = hints.queryTimeout - } - return &primitiveWrapper{prim: e}, nil + edml := createDMLPrimitive(ctx, rb, hints, del.Target.VTable, generateQuery(stmt), vindexes, vQuery) + + return &primitiveWrapper{prim: &engine.Delete{DML: edml}}, nil } -func buildDeleteLogicalPlan( - ctx *plancontext.PlanningContext, - rb *operators.Route, - dmlOp ops.Operator, - hints *queryHints, -) (logicalPlan, error) { - del := dmlOp.(*operators.Delete) +func createDMLPrimitive(ctx *plancontext.PlanningContext, rb *operators.Route, hints *queryHints, vTbl *vindexes.Table, query string, colVindexes []*vindexes.ColumnVindex, vindexQuery string) *engine.DML { rp := newRoutingParams(ctx, rb.Routing.OpCode()) - err := rb.Routing.UpdateRoutingParams(ctx, rp) - if err != nil { - return nil, err - } + rb.Routing.UpdateRoutingParams(ctx, rp) edml := &engine.DML{ - Query: generateQuery(del.AST), - TableNames: []string{del.VTable.Name.String()}, - Vindexes: del.VTable.Owned, - OwnedVindexQuery: del.OwnedVindexQuery, + Query: query, + TableNames: []string{vTbl.Name.String()}, + Vindexes: colVindexes, + OwnedVindexQuery: vindexQuery, RoutingParameters: rp, } - transformDMLPlan(del.VTable, edml, rb.Routing, del.OwnedVindexQuery != "") - - e := &engine.Delete{ - DML: edml, - } - if hints != nil { - e.MultiShardAutocommit = hints.multiShardAutocommit - e.QueryTimeout = hints.queryTimeout - } - - return &primitiveWrapper{prim: e}, nil -} - -func transformDMLPlan(vtable *vindexes.Table, edml *engine.DML, routing operators.Routing, setVindex bool) { - if routing.OpCode() != engine.Unsharded && setVindex { - primary := vtable.ColumnVindexes[0] + if rb.Routing.OpCode() != engine.Unsharded && vindexQuery != "" { + primary := vTbl.ColumnVindexes[0] edml.KsidVindex = primary.Vindex edml.KsidLength = len(primary.Columns) } + + if hints != nil { + edml.MultiShardAutocommit = hints.multiShardAutocommit + edml.QueryTimeout = hints.queryTimeout + } + return edml } func updateSelectedVindexPredicate(op *operators.Route) sqlparser.Expr { @@ -717,7 +777,7 @@ func updateSelectedVindexPredicate(op *operators.Route) sqlparser.Expr { func getAllTableNames(op *operators.Route) ([]string, error) { tableNameMap := map[string]any{} - err := rewrite.Visit(op, func(op ops.Operator) error { + err := operators.Visit(op, func(op operators.Operator) error { tbl, isTbl := op.(*operators.Table) var name string if isTbl { @@ -742,7 +802,7 @@ func getAllTableNames(op *operators.Route) ([]string, error) { } func transformUnionPlan(ctx *plancontext.PlanningContext, op *operators.Union) (logicalPlan, error) { - sources, err := slice.MapWithError(op.Sources, func(src ops.Operator) (logicalPlan, error) { + sources, err := slice.MapWithError(op.Sources, func(src operators.Operator) (logicalPlan, error) { plan, err := transformToLogicalPlan(ctx, src) if err != nil { return nil, err @@ -769,19 +829,23 @@ func transformLimit(ctx *plancontext.PlanningContext, op *operators.Limit) (logi return nil, err } - return createLimit(plan, op.AST) + return createLimit(plan, op.AST, ctx.VSchema.Environment(), ctx.VSchema.ConnCollation()) } -func createLimit(input logicalPlan, limit *sqlparser.Limit) (logicalPlan, error) { +func createLimit(input logicalPlan, limit *sqlparser.Limit, env *vtenv.Environment, coll collations.ID) (logicalPlan, error) { plan := newLimit(input) - pv, err := evalengine.Translate(limit.Rowcount, nil) + cfg := &evalengine.Config{ + Collation: coll, + Environment: env, + } + pv, err := evalengine.Translate(limit.Rowcount, cfg) if err != nil { return nil, vterrors.Wrap(err, "unexpected expression in LIMIT") } plan.elimit.Count = pv if limit.Offset != nil { - pv, err = evalengine.Translate(limit.Offset, nil) + pv, err = evalengine.Translate(limit.Offset, cfg) if err != nil { return nil, vterrors.Wrap(err, "unexpected expression in OFFSET") } @@ -790,3 +854,65 @@ func createLimit(input logicalPlan, limit *sqlparser.Limit) (logicalPlan, error) return plan, nil } + +func transformHashJoin(ctx *plancontext.PlanningContext, op *operators.HashJoin) (logicalPlan, error) { + lhs, err := transformToLogicalPlan(ctx, op.LHS) + if err != nil { + return nil, err + } + rhs, err := transformToLogicalPlan(ctx, op.RHS) + if err != nil { + return nil, err + } + + if len(op.LHSKeys) != 1 { + return nil, vterrors.VT12001("hash joins must have exactly one join predicate") + } + + joinOp := engine.InnerJoin + if op.LeftJoin { + joinOp = engine.LeftJoin + } + + var missingTypes []string + + ltyp, found := ctx.SemTable.TypeForExpr(op.JoinComparisons[0].LHS) + if !found { + missingTypes = append(missingTypes, sqlparser.String(op.JoinComparisons[0].LHS)) + } + rtyp, found := ctx.SemTable.TypeForExpr(op.JoinComparisons[0].RHS) + if !found { + missingTypes = append(missingTypes, sqlparser.String(op.JoinComparisons[0].RHS)) + } + + if len(missingTypes) > 0 { + return nil, vterrors.VT12001( + fmt.Sprintf("missing type information for [%s]", strings.Join(missingTypes, ", "))) + } + + comparisonType, err := evalengine.CoerceTypes(ltyp, rtyp, ctx.VSchema.Environment().CollationEnv()) + if err != nil { + return nil, err + } + + return &hashJoin{ + lhs: lhs, + rhs: rhs, + inner: &engine.HashJoin{ + Opcode: joinOp, + Cols: op.ColumnOffsets, + LHSKey: op.LHSKeys[0], + RHSKey: op.RHSKeys[0], + ASTPred: op.JoinPredicate(), + Collation: comparisonType.Collation(), + ComparisonType: comparisonType.Type(), + CollationEnv: ctx.VSchema.Environment().CollationEnv(), + }, + }, nil +} + +func generateQuery(statement sqlparser.Statement) string { + buf := sqlparser.NewTrackedBuffer(dmlFormatter) + statement.Format(buf) + return buf.String() +} diff --git a/go/vt/vtgate/planbuilder/operators/SQL_builder.go b/go/vt/vtgate/planbuilder/operators/SQL_builder.go index 5201818951d..88f9d985d81 100644 --- a/go/vt/vtgate/planbuilder/operators/SQL_builder.go +++ b/go/vt/vtgate/planbuilder/operators/SQL_builder.go @@ -21,9 +21,9 @@ import ( "slices" "sort" + "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -33,15 +33,18 @@ type ( ctx *plancontext.PlanningContext stmt sqlparser.Statement tableNames []string - dmlOperator ops.Operator + dmlOperator Operator } ) func (qb *queryBuilder) asSelectStatement() sqlparser.SelectStatement { return qb.stmt.(sqlparser.SelectStatement) } +func (qb *queryBuilder) asOrderAndLimit() sqlparser.OrderAndLimit { + return qb.stmt.(sqlparser.OrderAndLimit) +} -func ToSQL(ctx *plancontext.PlanningContext, op ops.Operator) (_ sqlparser.Statement, _ ops.Operator, err error) { +func ToSQL(ctx *plancontext.PlanningContext, op Operator) (_ sqlparser.Statement, _ Operator, err error) { defer PanicHandler(&err) q := &queryBuilder{ctx: ctx} @@ -70,22 +73,20 @@ func (qb *queryBuilder) addTableExpr( if qb.stmt == nil { qb.stmt = &sqlparser.Select{} } - sel := qb.stmt.(*sqlparser.Select) - elems := &sqlparser.AliasedTableExpr{ + tbl := &sqlparser.AliasedTableExpr{ Expr: tblExpr, Partitions: nil, As: sqlparser.NewIdentifierCS(alias), Hints: hints, Columns: columnAliases, } - qb.ctx.SemTable.ReplaceTableSetFor(tableID, elems) - sel.From = append(sel.From, elems) - qb.stmt = sel + qb.ctx.SemTable.ReplaceTableSetFor(tableID, tbl) + qb.stmt.(FromStatement).SetFrom(append(qb.stmt.(FromStatement).GetFrom(), tbl)) qb.tableNames = append(qb.tableNames, tableName) } func (qb *queryBuilder) addPredicate(expr sqlparser.Expr) { - if _, toBeSkipped := qb.ctx.SkipPredicates[expr]; toBeSkipped { + if qb.ctx.ShouldSkip(expr) { // This is a predicate that was added to the RHS of an ApplyJoin. // The original predicate will be added, so we don't have to add this here return @@ -201,62 +202,81 @@ func (qb *queryBuilder) unionWith(other *queryBuilder, distinct bool) { } } +type FromStatement interface { + GetFrom() []sqlparser.TableExpr + SetFrom([]sqlparser.TableExpr) + GetWherePredicate() sqlparser.Expr + SetWherePredicate(sqlparser.Expr) +} + +var _ FromStatement = (*sqlparser.Select)(nil) +var _ FromStatement = (*sqlparser.Update)(nil) +var _ FromStatement = (*sqlparser.Delete)(nil) + func (qb *queryBuilder) joinInnerWith(other *queryBuilder, onCondition sqlparser.Expr) { - sel := qb.stmt.(*sqlparser.Select) - otherSel := other.stmt.(*sqlparser.Select) - sel.From = append(sel.From, otherSel.From...) - sel.SelectExprs = append(sel.SelectExprs, otherSel.SelectExprs...) + stmt := qb.stmt.(FromStatement) + otherStmt := other.stmt.(FromStatement) + + if sel, isSel := stmt.(*sqlparser.Select); isSel { + otherSel := otherStmt.(*sqlparser.Select) + sel.SelectExprs = append(sel.SelectExprs, otherSel.SelectExprs...) + } + + newFromClause := append(stmt.GetFrom(), otherStmt.GetFrom()...) + stmt.SetFrom(newFromClause) + qb.mergeWhereClauses(stmt, otherStmt) + qb.addPredicate(onCondition) +} + +func (qb *queryBuilder) joinOuterWith(other *queryBuilder, onCondition sqlparser.Expr) { + stmt := qb.stmt.(FromStatement) + otherStmt := other.stmt.(FromStatement) - var predicate sqlparser.Expr - if sel.Where != nil { - predicate = sel.Where.Expr + if sel, isSel := stmt.(*sqlparser.Select); isSel { + otherSel := otherStmt.(*sqlparser.Select) + sel.SelectExprs = append(sel.SelectExprs, otherSel.SelectExprs...) } - if otherSel.Where != nil { + + newFromClause := []sqlparser.TableExpr{buildOuterJoin(stmt, otherStmt, onCondition)} + stmt.SetFrom(newFromClause) + qb.mergeWhereClauses(stmt, otherStmt) +} + +func (qb *queryBuilder) mergeWhereClauses(stmt, otherStmt FromStatement) { + predicate := stmt.GetWherePredicate() + if otherPredicate := otherStmt.GetWherePredicate(); otherPredicate != nil { predExprs := sqlparser.SplitAndExpression(nil, predicate) - otherExprs := sqlparser.SplitAndExpression(nil, otherSel.Where.Expr) + otherExprs := sqlparser.SplitAndExpression(nil, otherPredicate) predicate = qb.ctx.SemTable.AndExpressions(append(predExprs, otherExprs...)...) } if predicate != nil { - sel.Where = &sqlparser.Where{Type: sqlparser.WhereClause, Expr: predicate} + stmt.SetWherePredicate(predicate) } - - qb.addPredicate(onCondition) } -func (qb *queryBuilder) joinOuterWith(other *queryBuilder, onCondition sqlparser.Expr) { - sel := qb.stmt.(*sqlparser.Select) - otherSel := other.stmt.(*sqlparser.Select) +func buildOuterJoin(stmt FromStatement, otherStmt FromStatement, onCondition sqlparser.Expr) *sqlparser.JoinTableExpr { var lhs sqlparser.TableExpr - if len(sel.From) == 1 { - lhs = sel.From[0] + fromClause := stmt.GetFrom() + if len(fromClause) == 1 { + lhs = fromClause[0] } else { - lhs = &sqlparser.ParenTableExpr{Exprs: sel.From} + lhs = &sqlparser.ParenTableExpr{Exprs: fromClause} } var rhs sqlparser.TableExpr - if len(otherSel.From) == 1 { - rhs = otherSel.From[0] + otherFromClause := otherStmt.GetFrom() + if len(otherFromClause) == 1 { + rhs = otherFromClause[0] } else { - rhs = &sqlparser.ParenTableExpr{Exprs: otherSel.From} + rhs = &sqlparser.ParenTableExpr{Exprs: otherFromClause} } - sel.From = []sqlparser.TableExpr{&sqlparser.JoinTableExpr{ + + return &sqlparser.JoinTableExpr{ LeftExpr: lhs, RightExpr: rhs, Join: sqlparser.LeftJoinType, Condition: &sqlparser.JoinCondition{ On: onCondition, }, - }} - - sel.SelectExprs = append(sel.SelectExprs, otherSel.SelectExprs...) - var predicate sqlparser.Expr - if sel.Where != nil { - predicate = sel.Where.Expr - } - if otherSel.Where != nil { - predicate = qb.ctx.SemTable.AndExpressions(predicate, otherSel.Where.Expr) - } - if predicate != nil { - sel.Where = &sqlparser.Where{Type: sqlparser.WhereClause, Expr: predicate} } } @@ -310,7 +330,7 @@ func (ts *tableSorter) Swap(i, j int) { func removeKeyspaceFromSelectExpr(expr sqlparser.SelectExpr) { switch expr := expr.(type) { case *sqlparser.AliasedExpr: - sqlparser.RemoveKeyspaceFromColName(expr.Expr) + sqlparser.RemoveKeyspaceInCol(expr.Expr) case *sqlparser.StarExpr: expr.TableName.Qualifier = sqlparser.NewIdentifierCS("") } @@ -347,7 +367,7 @@ func stripDownQuery(from, to sqlparser.SelectStatement) { } // buildQuery recursively builds the query into an AST, from an operator tree -func buildQuery(op ops.Operator, qb *queryBuilder) { +func buildQuery(op Operator, qb *queryBuilder) { switch op := op.(type) { case *Table: buildTable(op, qb) @@ -377,7 +397,7 @@ func buildQuery(op ops.Operator, qb *queryBuilder) { case *Update: buildUpdate(op, qb) case *Delete: - buildDML(op, qb) + buildDelete(op, qb) case *Insert: buildDML(op, qb) default: @@ -385,12 +405,28 @@ func buildQuery(op ops.Operator, qb *queryBuilder) { } } +func buildDelete(op *Delete, qb *queryBuilder) { + qb.stmt = &sqlparser.Delete{ + Ignore: op.Ignore, + Targets: sqlparser.TableNames{op.Target.Name}, + } + buildQuery(op.Source, qb) + + qb.dmlOperator = op +} + func buildUpdate(op *Update, qb *queryBuilder) { - tblName := sqlparser.NewTableName(op.QTable.Table.Name.String()) - aTblExpr := &sqlparser.AliasedTableExpr{ - Expr: tblName, - As: op.QTable.Alias.As, + updExprs := getUpdateExprs(op) + upd := &sqlparser.Update{ + Ignore: op.Ignore, + Exprs: updExprs, } + qb.stmt = upd + qb.dmlOperator = op + buildQuery(op.Source, qb) +} + +func getUpdateExprs(op *Update) sqlparser.UpdateExprs { updExprs := make(sqlparser.UpdateExprs, 0, len(op.Assignments)) for _, se := range op.Assignments { updExprs = append(updExprs, &sqlparser.UpdateExpr{ @@ -398,24 +434,11 @@ func buildUpdate(op *Update, qb *queryBuilder) { Expr: se.Expr.EvalExpr, }) } - - qb.stmt = &sqlparser.Update{ - Ignore: op.Ignore, - TableExprs: sqlparser.TableExprs{aTblExpr}, - Exprs: updExprs, - OrderBy: op.OrderBy, - Limit: op.Limit, - } - - for _, pred := range op.QTable.Predicates { - qb.addPredicate(pred) - } - - qb.dmlOperator = op + return updExprs } type OpWithAST interface { - ops.Operator + Operator Statement() sqlparser.Statement } @@ -436,7 +459,7 @@ func buildAggregation(op *Aggregator, qb *queryBuilder) { for _, by := range op.Grouping { qb.addGroupBy(by.Inner) - simplified := by.SimplifiedExpr + simplified := by.Inner if by.WSOffset != -1 { qb.addGroupBy(weightStringFor(simplified)) } @@ -447,13 +470,13 @@ func buildOrdering(op *Ordering, qb *queryBuilder) { buildQuery(op.Source, qb) for _, order := range op.Order { - qb.asSelectStatement().AddOrder(order.Inner) + qb.asOrderAndLimit().AddOrder(order.Inner) } } func buildLimit(op *Limit, qb *queryBuilder) { buildQuery(op.Source, qb) - qb.asSelectStatement().SetLimit(op.AST) + qb.asOrderAndLimit().SetLimit(op.AST) } func buildTable(op *Table, qb *queryBuilder) { @@ -502,20 +525,24 @@ func buildProjection(op *Projection, qb *queryBuilder) { } func buildApplyJoin(op *ApplyJoin, qb *queryBuilder) { + predicates := slice.Map(op.JoinPredicates.columns, func(jc applyJoinColumn) sqlparser.Expr { + // since we are adding these join predicates, we need to mark to broken up version (RHSExpr) of it as done + err := qb.ctx.SkipJoinPredicates(jc.Original) + if err != nil { + panic(err) + } + return jc.Original + }) + pred := sqlparser.AndExpressions(predicates...) + buildQuery(op.LHS, qb) - // If we are going to add the predicate used in join here - // We should not add the predicate's copy of when it was split into - // two parts. To avoid this, we use the SkipPredicates map. - for _, expr := range qb.ctx.JoinPredicates[op.Predicate] { - qb.ctx.SkipPredicates[expr] = nil - } + qbR := &queryBuilder{ctx: qb.ctx} buildQuery(op.RHS, qbR) - if op.LeftJoin { - qb.joinOuterWith(qbR, op.Predicate) + qb.joinOuterWith(qbR, pred) } else { - qb.joinInnerWith(qbR, op.Predicate) + qb.joinInnerWith(qbR, pred) } } @@ -546,7 +573,7 @@ func buildFilter(op *Filter, qb *queryBuilder) { func buildDerived(op *Horizon, qb *queryBuilder) { buildQuery(op.Source, qb) - sqlparser.RemoveKeyspace(op.Query) + sqlparser.RemoveKeyspaceInCol(op.Query) stmt := qb.stmt qb.stmt = nil diff --git a/go/vt/vtgate/planbuilder/operators/aggregation_pushing.go b/go/vt/vtgate/planbuilder/operators/aggregation_pushing.go index 85c50427364..a0963929eaa 100644 --- a/go/vt/vtgate/planbuilder/operators/aggregation_pushing.go +++ b/go/vt/vtgate/planbuilder/operators/aggregation_pushing.go @@ -24,42 +24,55 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine/opcode" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/vt/vtgate/semantics" ) -func tryPushAggregator(ctx *plancontext.PlanningContext, aggregator *Aggregator) (output ops.Operator, applyResult *rewrite.ApplyResult, err error) { +func errDistinctAggrWithMultiExpr(f sqlparser.AggrFunc) { + if f == nil { + panic(vterrors.VT12001("distinct aggregation function with multiple expressions")) + } + panic(vterrors.VT12001(fmt.Sprintf("distinct aggregation function with multiple expressions '%s'", sqlparser.String(f)))) +} + +func tryPushAggregator(ctx *plancontext.PlanningContext, aggregator *Aggregator) (output Operator, applyResult *ApplyResult) { if aggregator.Pushed { - return aggregator, rewrite.SameTree, nil + return aggregator, NoRewrite + } + + // this rewrite is always valid, and we should do it whenever possible + if route, ok := aggregator.Source.(*Route); ok && (route.IsSingleShard() || overlappingUniqueVindex(ctx, aggregator.Grouping)) { + return Swap(aggregator, route, "push down aggregation under route - remove original") + } + + // other rewrites require us to have reached this phase before we can consider them + if !reachedPhase(ctx, delegateAggregation) { + return aggregator, NoRewrite + } + + // if we have not yet been able to push this aggregation down, + // we need to turn AVG into SUM/COUNT to support this over a sharded keyspace + if needAvgBreaking(aggregator.Aggregations) { + return splitAvgAggregations(ctx, aggregator) } + switch src := aggregator.Source.(type) { case *Route: // if we have a single sharded route, we can push it down - output, applyResult, err = pushAggregationThroughRoute(ctx, aggregator, src) + output, applyResult = pushAggregationThroughRoute(ctx, aggregator, src) case *ApplyJoin: - if reachedPhase(ctx, delegateAggregation) { - output, applyResult, err = pushAggregationThroughJoin(ctx, aggregator, src) - } + output, applyResult = pushAggregationThroughApplyJoin(ctx, aggregator, src) + case *HashJoin: + output, applyResult = pushAggregationThroughHashJoin(ctx, aggregator, src) case *Filter: - if reachedPhase(ctx, delegateAggregation) { - output, applyResult, err = pushAggregationThroughFilter(ctx, aggregator, src) - } + output, applyResult = pushAggregationThroughFilter(ctx, aggregator, src) case *SubQueryContainer: - if reachedPhase(ctx, delegateAggregation) { - output, applyResult, err = pushAggregationThroughSubquery(ctx, aggregator, src) - } + output, applyResult = pushAggregationThroughSubquery(ctx, aggregator, src) default: - return aggregator, rewrite.SameTree, nil - } - - if err != nil { - return nil, nil, err + return aggregator, NoRewrite } if output == nil { - return aggregator, rewrite.SameTree, nil + return aggregator, NoRewrite } aggregator.Pushed = true @@ -81,16 +94,13 @@ func pushAggregationThroughSubquery( ctx *plancontext.PlanningContext, rootAggr *Aggregator, src *SubQueryContainer, -) (ops.Operator, *rewrite.ApplyResult, error) { - pushedAggr := rootAggr.Clone([]ops.Operator{src.Outer}).(*Aggregator) +) (Operator, *ApplyResult) { + pushedAggr := rootAggr.Clone([]Operator{src.Outer}).(*Aggregator) pushedAggr.Original = false pushedAggr.Pushed = false for _, subQuery := range src.Inner { - lhsCols, err := subQuery.OuterExpressionsNeeded(ctx, src.Outer) - if err != nil { - return nil, nil, err - } + lhsCols := subQuery.OuterExpressionsNeeded(ctx, src.Outer) for _, colName := range lhsCols { idx := slices.IndexFunc(pushedAggr.Columns, func(ae *sqlparser.AliasedExpr) bool { return ctx.SemTable.EqualsExpr(ae.Expr, colName) @@ -104,13 +114,17 @@ func pushAggregationThroughSubquery( src.Outer = pushedAggr + for _, aggregation := range pushedAggr.Aggregations { + aggregation.Original.Expr = rewriteColNameToArgument(ctx, aggregation.Original.Expr, aggregation.SubQueryExpression, src.Inner...) + } + if !rootAggr.Original { - return src, rewrite.NewTree("push Aggregation under subquery - keep original", rootAggr), nil + return src, Rewrote("push Aggregation under subquery - keep original") } rootAggr.aggregateTheAggregates() - return rootAggr, rewrite.NewTree("push Aggregation under subquery", rootAggr), nil + return rootAggr, Rewrote("push Aggregation under subquery") } func (a *Aggregator) aggregateTheAggregates() { @@ -134,24 +148,12 @@ func pushAggregationThroughRoute( ctx *plancontext.PlanningContext, aggregator *Aggregator, route *Route, -) (ops.Operator, *rewrite.ApplyResult, error) { - // If the route is single-shard, or we are grouping by sharding keys, we can just push down the aggregation - if route.IsSingleShard() || overlappingUniqueVindex(ctx, aggregator.Grouping) { - return rewrite.Swap(aggregator, route, "push down aggregation under route - remove original") - } - - if !reachedPhase(ctx, delegateAggregation) { - return nil, nil, nil - } - +) (Operator, *ApplyResult) { // Create a new aggregator to be placed below the route. aggrBelowRoute := aggregator.SplitAggregatorBelowRoute(route.Inputs()) aggrBelowRoute.Aggregations = nil - err := pushAggregations(ctx, aggregator, aggrBelowRoute) - if err != nil { - return nil, nil, err - } + pushAggregations(ctx, aggregator, aggrBelowRoute) // Set the source of the route to the new aggregator placed below the route. route.Source = aggrBelowRoute @@ -159,18 +161,15 @@ func pushAggregationThroughRoute( if !aggregator.Original { // we only keep the root aggregation, if this aggregator was created // by splitting one and pushing under a join, we can get rid of this one - return aggregator.Source, rewrite.NewTree("push aggregation under route - remove original", aggregator), nil + return aggregator.Source, Rewrote("push aggregation under route - remove original") } - return aggregator, rewrite.NewTree("push aggregation under route - keep original", aggregator), nil + return aggregator, Rewrote("push aggregation under route - keep original") } // pushAggregations splits aggregations between the original aggregator and the one we are pushing down -func pushAggregations(ctx *plancontext.PlanningContext, aggregator *Aggregator, aggrBelowRoute *Aggregator) error { - canPushDistinctAggr, distinctExpr, err := checkIfWeCanPush(ctx, aggregator) - if err != nil { - return err - } +func pushAggregations(ctx *plancontext.PlanningContext, aggregator *Aggregator, aggrBelowRoute *Aggregator) { + canPushDistinctAggr, distinctExprs := checkIfWeCanPush(ctx, aggregator) distinctAggrGroupByAdded := false @@ -181,16 +180,20 @@ func pushAggregations(ctx *plancontext.PlanningContext, aggregator *Aggregator, continue } + if len(distinctExprs) != 1 { + errDistinctAggrWithMultiExpr(aggr.Func) + } + // We handle a distinct aggregation by turning it into a group by and // doing the aggregating on the vtgate level instead - aeDistinctExpr := aeWrap(distinctExpr) + aeDistinctExpr := aeWrap(distinctExprs[0]) aggrBelowRoute.Columns[aggr.ColOffset] = aeDistinctExpr // We handle a distinct aggregation by turning it into a group by and // doing the aggregating on the vtgate level instead // Adding to group by can be done only once even though there are multiple distinct aggregation with same expression. if !distinctAggrGroupByAdded { - groupBy := NewGroupBy(distinctExpr, distinctExpr, aeDistinctExpr) + groupBy := NewGroupBy(distinctExprs[0]) groupBy.ColOffset = aggr.ColOffset aggrBelowRoute.Grouping = append(aggrBelowRoute.Grouping, groupBy) distinctAggrGroupByAdded = true @@ -198,15 +201,13 @@ func pushAggregations(ctx *plancontext.PlanningContext, aggregator *Aggregator, } if !canPushDistinctAggr { - aggregator.DistinctExpr = distinctExpr + aggregator.DistinctExpr = distinctExprs[0] } - - return nil } -func checkIfWeCanPush(ctx *plancontext.PlanningContext, aggregator *Aggregator) (bool, sqlparser.Expr, error) { +func checkIfWeCanPush(ctx *plancontext.PlanningContext, aggregator *Aggregator) (bool, sqlparser.Exprs) { canPush := true - var distinctExpr sqlparser.Expr + var distinctExprs sqlparser.Exprs var differentExpr *sqlparser.AliasedExpr for _, aggr := range aggregator.Aggregations { @@ -214,42 +215,52 @@ func checkIfWeCanPush(ctx *plancontext.PlanningContext, aggregator *Aggregator) continue } - innerExpr := aggr.Func.GetArg() - if !exprHasUniqueVindex(ctx, innerExpr) { + args := aggr.Func.GetArgs() + hasUniqVindex := false + for _, arg := range args { + if exprHasUniqueVindex(ctx, arg) { + hasUniqVindex = true + break + } + } + if !hasUniqVindex { canPush = false } - if distinctExpr == nil { - distinctExpr = innerExpr + if len(distinctExprs) == 0 { + distinctExprs = args } - if !ctx.SemTable.EqualsExpr(distinctExpr, innerExpr) { - differentExpr = aggr.Original + for idx, expr := range distinctExprs { + if !ctx.SemTable.EqualsExpr(expr, args[idx]) { + differentExpr = aggr.Original + break + } } } if !canPush && differentExpr != nil { - return false, nil, vterrors.VT12001(fmt.Sprintf("only one DISTINCT aggregation is allowed in a SELECT: %s", sqlparser.String(differentExpr))) + panic(vterrors.VT12001(fmt.Sprintf("only one DISTINCT aggregation is allowed in a SELECT: %s", sqlparser.String(differentExpr)))) } - return canPush, distinctExpr, nil + return canPush, distinctExprs } func pushAggregationThroughFilter( ctx *plancontext.PlanningContext, aggregator *Aggregator, filter *Filter, -) (ops.Operator, *rewrite.ApplyResult, error) { +) (Operator, *ApplyResult) { columnsNeeded := collectColNamesNeeded(ctx, filter) // Create a new aggregator to be placed below the route. - pushedAggr := aggregator.Clone([]ops.Operator{filter.Source}).(*Aggregator) + pushedAggr := aggregator.Clone([]Operator{filter.Source}).(*Aggregator) pushedAggr.Pushed = false pushedAggr.Original = false withNextColumn: for _, col := range columnsNeeded { for _, gb := range pushedAggr.Grouping { - if ctx.SemTable.EqualsExpr(col, gb.SimplifiedExpr) { + if ctx.SemTable.EqualsExpr(col, gb.Inner) { continue withNextColumn } } @@ -262,10 +273,10 @@ withNextColumn: if !aggregator.Original { // we only keep the root aggregation, if this aggregator was created // by splitting one and pushing under a join, we can get rid of this one - return aggregator.Source, rewrite.NewTree("push aggregation under filter - remove original", aggregator), nil + return aggregator.Source, Rewrote("push aggregation under filter - remove original") } aggregator.aggregateTheAggregates() - return aggregator, rewrite.NewTree("push aggregation under filter - keep original", aggregator), nil + return aggregator, Rewrote("push aggregation under filter - keep original") } func collectColNamesNeeded(ctx *plancontext.PlanningContext, f *Filter) (columnsNeeded []*sqlparser.ColName) { @@ -289,7 +300,7 @@ func collectColNamesNeeded(ctx *plancontext.PlanningContext, f *Filter) (columns func overlappingUniqueVindex(ctx *plancontext.PlanningContext, groupByExprs []GroupBy) bool { for _, groupByExpr := range groupByExprs { - if exprHasUniqueVindex(ctx, groupByExpr.SimplifiedExpr) { + if exprHasUniqueVindex(ctx, groupByExpr.Inner) { return true } } @@ -324,116 +335,153 @@ func exprHasVindex(ctx *plancontext.PlanningContext, expr sqlparser.Expr, hasToB } /* -We push down aggregations using the logic from the paper Orthogonal Optimization of Subqueries and Aggregation, by -Cesar A. Galindo-Legaria and Milind M. Joshi from Microsoft Corp. +Using techniques from "Orthogonal Optimization of Subqueries and Aggregation" by Cesar A. Galindo-Legaria +and Milind M. Joshi (Microsoft Corp), we push down aggregations. It splits an aggregation +into local aggregates depending on one side of a join, and pushes these into the inputs of the join. +These then combine to form the final group by/aggregate query. -It explains how one can split an aggregation into local aggregates that depend on only one side of the join. -The local aggregates can then be gathered together to produce the global -group by/aggregate query that the user asked for. +In Vitess, this technique is extremely useful. It enables pushing aggregations to routes, +even with joins at the vtgate level. Thus, rather than handling all grouping and +aggregation at vtgate, most work is offloaded to MySQL, with vtgate summarizing results. -In Vitess, this is particularly useful because it allows us to push aggregation down to the routes, even when -we have to join the results at the vtgate level. Instead of doing all the grouping and aggregation at the -vtgate level, we can offload most of the work to MySQL, and at the vtgate just summarize the results. +# For a query like: -# For a query, such as - -select count(*) from R1 JOIN R2 on R1.id = R2.id +select count(*) from L JOIN R on L.id = R.id Original: - GB <- This is the original grouping, doing count(*) - | - JOIN + Aggr <- Original grouping, doing count(*) + | + Join / \ - R1 R2 + L R Transformed: - rootAggr <- This grouping is now SUMing together the distributed `count(*)` we got back - | - Proj <- This projection makes sure that the columns are lined up as expected + rootAggr <- New grouping SUMs distributed `count(*)` | - Sort <- Here we are sorting the input so that the OrderedAggregate can do its thing + Proj <- Projection multiplying `count(*)` from each side of the join | - JOIN + Join / \ - lAggr rAggr + lhsAggr rhsAggr <- `count(*)` aggregation can now be pushed under a route / \ - R1 R2 + L R */ -func pushAggregationThroughJoin(ctx *plancontext.PlanningContext, rootAggr *Aggregator, join *ApplyJoin) (ops.Operator, *rewrite.ApplyResult, error) { - lhs := &joinPusher{ - orig: rootAggr, - pushed: &Aggregator{ - Source: join.LHS, - QP: rootAggr.QP, - }, - columns: initColReUse(len(rootAggr.Columns)), - tableID: TableID(join.LHS), - } - rhs := &joinPusher{ - orig: rootAggr, - pushed: &Aggregator{ - Source: join.RHS, - QP: rootAggr.QP, - }, - columns: initColReUse(len(rootAggr.Columns)), - tableID: TableID(join.RHS), - } +func pushAggregationThroughApplyJoin(ctx *plancontext.PlanningContext, rootAggr *Aggregator, join *ApplyJoin) (Operator, *ApplyResult) { + lhs := createJoinPusher(rootAggr, join.LHS) + rhs := createJoinPusher(rootAggr, join.RHS) - joinColumns, output, err := splitAggrColumnsToLeftAndRight(ctx, rootAggr, join, lhs, rhs) + columns := &applyJoinColumns{} + output, err := splitAggrColumnsToLeftAndRight(ctx, rootAggr, join, join.LeftJoin, columns, lhs, rhs) + join.JoinColumns = columns if err != nil { // if we get this error, we just abort the splitting and fall back on simpler ways of solving the same query if errors.Is(err, errAbortAggrPushing) { - return nil, nil, nil + return nil, nil } - return nil, nil, err + panic(err) } - groupingJCs, err := splitGroupingToLeftAndRight(ctx, rootAggr, lhs, rhs) - if err != nil { - return nil, nil, err - } - joinColumns = append(joinColumns, groupingJCs...) + splitGroupingToLeftAndRight(ctx, rootAggr, lhs, rhs, join.JoinColumns) // We need to add any columns coming from the lhs of the join to the group by on that side // If we don't, the LHS will not be able to return the column, and it can't be used to send down to the RHS - err = addColumnsFromLHSInJoinPredicates(ctx, rootAggr, join, lhs) + addColumnsFromLHSInJoinPredicates(ctx, join, lhs) + + join.LHS, join.RHS = lhs.pushed, rhs.pushed + + if !rootAggr.Original { + // we only keep the root aggregation, if this aggregator was created + // by splitting one and pushing under a join, we can get rid of this one + return output, Rewrote("push Aggregation under join - keep original") + } + + rootAggr.aggregateTheAggregates() + rootAggr.Source = output + return rootAggr, Rewrote("push Aggregation under join") +} + +// pushAggregationThroughHashJoin pushes aggregation through a hash-join in a similar way to pushAggregationThroughApplyJoin +func pushAggregationThroughHashJoin(ctx *plancontext.PlanningContext, rootAggr *Aggregator, join *HashJoin) (Operator, *ApplyResult) { + lhs := createJoinPusher(rootAggr, join.LHS) + rhs := createJoinPusher(rootAggr, join.RHS) + + columns := &hashJoinColumns{} + output, err := splitAggrColumnsToLeftAndRight(ctx, rootAggr, join, join.LeftJoin, columns, lhs, rhs) if err != nil { - return nil, nil, err + // if we get this error, we just abort the splitting and fall back on simpler ways of solving the same query + if errors.Is(err, errAbortAggrPushing) { + return nil, nil + } + panic(err) + } + + // The two sides of the hash comparisons are added as grouping expressions + for _, cmp := range join.JoinComparisons { + lhs.addGrouping(ctx, NewGroupBy(cmp.LHS)) + columns.addLeft(cmp.LHS) + + rhs.addGrouping(ctx, NewGroupBy(cmp.RHS)) + columns.addRight(cmp.RHS) + } + + // The grouping columns need to be pushed down as grouping columns on the respective sides + for _, groupBy := range rootAggr.Grouping { + deps := ctx.SemTable.RecursiveDeps(groupBy.Inner) + switch { + case deps.IsSolvedBy(lhs.tableID): + lhs.addGrouping(ctx, groupBy) + columns.addLeft(groupBy.Inner) + case deps.IsSolvedBy(rhs.tableID): + rhs.addGrouping(ctx, groupBy) + columns.addRight(groupBy.Inner) + case deps.IsSolvedBy(lhs.tableID.Merge(rhs.tableID)): + // TODO: Support this as well + return nil, nil + default: + panic(vterrors.VT13001(fmt.Sprintf("grouping with bad dependencies %s", groupBy.Inner))) + } } join.LHS, join.RHS = lhs.pushed, rhs.pushed - join.JoinColumns = joinColumns + join.columns = columns if !rootAggr.Original { // we only keep the root aggregation, if this aggregator was created // by splitting one and pushing under a join, we can get rid of this one - return output, rewrite.NewTree("push Aggregation under join - keep original", rootAggr), nil + return output, Rewrote("push Aggregation under hash join - keep original") } rootAggr.aggregateTheAggregates() rootAggr.Source = output - return rootAggr, rewrite.NewTree("push Aggregation under join", rootAggr), nil + return rootAggr, Rewrote("push Aggregation under hash join") } var errAbortAggrPushing = fmt.Errorf("abort aggregation pushing") -func addColumnsFromLHSInJoinPredicates(ctx *plancontext.PlanningContext, rootAggr *Aggregator, join *ApplyJoin, lhs *joinPusher) error { - for _, pred := range join.JoinPredicates { +func createJoinPusher(rootAggr *Aggregator, operator Operator) *joinPusher { + return &joinPusher{ + orig: rootAggr, + pushed: &Aggregator{ + Source: operator, + QP: rootAggr.QP, + }, + columns: initColReUse(len(rootAggr.Columns)), + tableID: TableID(operator), + } +} + +func addColumnsFromLHSInJoinPredicates(ctx *plancontext.PlanningContext, join *ApplyJoin, lhs *joinPusher) { + for _, pred := range join.JoinPredicates.columns { for _, bve := range pred.LHSExprs { - expr := bve.Expr - wexpr, err := rootAggr.QP.GetSimplifiedExpr(ctx, expr) - if err != nil { - return err - } - idx, found := canReuseColumn(ctx, lhs.pushed.Columns, expr, extractExpr) + idx, found := canReuseColumn(ctx, lhs.pushed.Columns, bve.Expr, extractExpr) if !found { idx = len(lhs.pushed.Columns) - lhs.pushed.Columns = append(lhs.pushed.Columns, aeWrap(expr)) + lhs.pushed.Columns = append(lhs.pushed.Columns, aeWrap(bve.Expr)) } - _, found = canReuseColumn(ctx, lhs.pushed.Grouping, wexpr, func(by GroupBy) sqlparser.Expr { - return by.SimplifiedExpr + _, found = canReuseColumn(ctx, lhs.pushed.Grouping, bve.Expr, func(by GroupBy) sqlparser.Expr { + return by.Inner }) if found { @@ -441,50 +489,40 @@ func addColumnsFromLHSInJoinPredicates(ctx *plancontext.PlanningContext, rootAgg } lhs.pushed.Grouping = append(lhs.pushed.Grouping, GroupBy{ - Inner: expr, - SimplifiedExpr: wexpr, - ColOffset: idx, - WSOffset: -1, + Inner: bve.Expr, + ColOffset: idx, + WSOffset: -1, }) } } - return nil } -func splitGroupingToLeftAndRight(ctx *plancontext.PlanningContext, rootAggr *Aggregator, lhs, rhs *joinPusher) ([]JoinColumn, error) { - var groupingJCs []JoinColumn - +func splitGroupingToLeftAndRight( + ctx *plancontext.PlanningContext, + rootAggr *Aggregator, + lhs, rhs *joinPusher, + columns joinColumns, +) { for _, groupBy := range rootAggr.Grouping { deps := ctx.SemTable.RecursiveDeps(groupBy.Inner) - expr := groupBy.Inner switch { case deps.IsSolvedBy(lhs.tableID): lhs.addGrouping(ctx, groupBy) - groupingJCs = append(groupingJCs, JoinColumn{ - Original: aeWrap(groupBy.Inner), - LHSExprs: []BindVarExpr{{Expr: expr}}, - }) + columns.addLeft(groupBy.Inner) case deps.IsSolvedBy(rhs.tableID): rhs.addGrouping(ctx, groupBy) - groupingJCs = append(groupingJCs, JoinColumn{ - Original: aeWrap(groupBy.Inner), - RHSExpr: expr, - }) + columns.addRight(groupBy.Inner) case deps.IsSolvedBy(lhs.tableID.Merge(rhs.tableID)): - jc, err := BreakExpressionInLHSandRHS(ctx, groupBy.SimplifiedExpr, lhs.tableID) - if err != nil { - return nil, err - } + jc := breakExpressionInLHSandRHSForApplyJoin(ctx, groupBy.Inner, lhs.tableID) for _, lhsExpr := range jc.LHSExprs { e := lhsExpr.Expr - lhs.addGrouping(ctx, NewGroupBy(e, e, aeWrap(e))) + lhs.addGrouping(ctx, NewGroupBy(e)) } - rhs.addGrouping(ctx, NewGroupBy(jc.RHSExpr, jc.RHSExpr, aeWrap(jc.RHSExpr))) + rhs.addGrouping(ctx, NewGroupBy(jc.RHSExpr)) default: - return nil, vterrors.VT13001(fmt.Sprintf("grouping with bad dependencies %s", groupBy.SimplifiedExpr)) + panic(vterrors.VT13001(fmt.Sprintf("grouping with bad dependencies %s", groupBy.Inner))) } } - return groupingJCs, nil } // splitAggrColumnsToLeftAndRight pushes all aggregations on the aggregator above a join and @@ -493,28 +531,31 @@ func splitGroupingToLeftAndRight(ctx *plancontext.PlanningContext, rootAggr *Agg func splitAggrColumnsToLeftAndRight( ctx *plancontext.PlanningContext, aggregator *Aggregator, - join *ApplyJoin, + join Operator, + leftJoin bool, + columns joinColumns, lhs, rhs *joinPusher, -) ([]JoinColumn, ops.Operator, error) { +) (Operator, error) { proj := newAliasedProjection(join) proj.FromAggr = true builder := &aggBuilder{ - lhs: lhs, - rhs: rhs, - proj: proj, - outerJoin: join.LeftJoin, + lhs: lhs, + rhs: rhs, + joinColumns: columns, + proj: proj, + outerJoin: leftJoin, } - canPushDistinctAggr, distinctExpr, err := checkIfWeCanPush(ctx, aggregator) - if err != nil { - return nil, nil, err - } + canPushDistinctAggr, distinctExprs := checkIfWeCanPush(ctx, aggregator) // Distinct aggregation cannot be pushed down in the join. // We keep node of the distinct aggregation expression to be used later for ordering. if !canPushDistinctAggr { - aggregator.DistinctExpr = distinctExpr - return nil, nil, errAbortAggrPushing + if len(distinctExprs) != 1 { + errDistinctAggrWithMultiExpr(nil) + } + aggregator.DistinctExpr = distinctExprs[0] + return nil, errAbortAggrPushing } outer: @@ -524,208 +565,15 @@ outer: if aggr.ColOffset == colIdx { err := builder.handleAggr(ctx, aggr) if err != nil { - return nil, nil, err + return nil, err } continue outer } } - _, err := builder.proj.addUnexploredExpr(col, col.Expr) - if err != nil { - return nil, nil, err - } - } - return builder.joinColumns, builder.proj, nil -} - -type ( - // aggBuilder is a helper struct that aids in pushing down an Aggregator through a join - // it accumulates the projections (if any) that need to be evaluated on top of the join - aggBuilder struct { - lhs, rhs *joinPusher - joinColumns []JoinColumn - proj *Projection - outerJoin bool - } - // joinPusher is a helper struct that aids in pushing down an Aggregator into one side of a Join. - // It creates a new Aggregator that is pushed down and keeps track of the column dependencies that the new Aggregator has. - joinPusher struct { - orig *Aggregator // The original Aggregator before pushing. - pushed *Aggregator // The new Aggregator created for push-down. - columns []int // List of column offsets used in the new Aggregator. - tableID semantics.TableSet // The TableSet denoting the side of the Join where the new Aggregator is pushed. - - // csAE keeps the copy of the countStar expression that has already been added to split an aggregation. - // No need to have multiple countStars, so we cache it here - csAE *sqlparser.AliasedExpr - } -) - -func (ab *aggBuilder) leftCountStar(ctx *plancontext.PlanningContext) *sqlparser.AliasedExpr { - ae, created := ab.lhs.countStar(ctx) - if created { - ab.joinColumns = append(ab.joinColumns, JoinColumn{ - Original: ae, - LHSExprs: []BindVarExpr{{Expr: ae.Expr}}, - }) - } - return ae -} - -func (ab *aggBuilder) rightCountStar(ctx *plancontext.PlanningContext) *sqlparser.AliasedExpr { - ae, created := ab.rhs.countStar(ctx) - if created { - ab.joinColumns = append(ab.joinColumns, JoinColumn{ - Original: ae, - RHSExpr: ae.Expr, - }) - } - return ae -} - -func (p *joinPusher) countStar(ctx *plancontext.PlanningContext) (*sqlparser.AliasedExpr, bool) { - if p.csAE != nil { - return p.csAE, false - } - cs := &sqlparser.CountStar{} - ae := aeWrap(cs) - csAggr := NewAggr(opcode.AggregateCountStar, cs, ae, "") - expr := p.addAggr(ctx, csAggr) - p.csAE = aeWrap(expr) - return p.csAE, true -} - -func (ab *aggBuilder) handleAggr(ctx *plancontext.PlanningContext, aggr Aggr) error { - switch aggr.OpCode { - case opcode.AggregateCountStar: - return ab.handleCountStar(ctx, aggr) - case opcode.AggregateCount, opcode.AggregateSum: - return ab.handleAggrWithCountStarMultiplier(ctx, aggr) - case opcode.AggregateMax, opcode.AggregateMin, opcode.AggregateAnyValue: - return ab.handlePushThroughAggregation(ctx, aggr) - case opcode.AggregateGroupConcat: - f := aggr.Func.(*sqlparser.GroupConcatExpr) - if f.Distinct || len(f.OrderBy) > 0 || f.Separator != "" { - panic("fail here") - } - // this needs special handling, currently aborting the push of function - // and later will try pushing the column instead. - // TODO: this should be handled better by pushing the function down. - return errAbortAggrPushing - case opcode.AggregateUnassigned: - return vterrors.VT12001(fmt.Sprintf("in scatter query: aggregation function '%s'", sqlparser.String(aggr.Original))) - case opcode.AggregateGtid: - // this is only used for SHOW GTID queries that will never contain joins - return vterrors.VT13001("cannot do join with vgtid") - case opcode.AggregateSumDistinct, opcode.AggregateCountDistinct: - // we are not going to see values multiple times, so we don't need to multiply with the count(*) from the other side - return ab.handlePushThroughAggregation(ctx, aggr) - default: - return vterrors.VT12001(fmt.Sprintf("aggregation not planned: %s", aggr.OpCode.String())) + builder.proj.addUnexploredExpr(col, col.Expr) } -} - -// pushThroughLeft and Right are used for extremums and random, -// which are not split and then arithmetics is used to aggregate the per-shard aggregations. -// For these, we just copy the aggregation to one side of the join and then pick the max of the max:es returned -func (ab *aggBuilder) pushThroughLeft(aggr Aggr) { - ab.lhs.pushThroughAggr(aggr) - ab.joinColumns = append(ab.joinColumns, JoinColumn{ - Original: aggr.Original, - LHSExprs: []BindVarExpr{{Expr: aggr.Original.Expr}}, - }) -} -func (ab *aggBuilder) pushThroughRight(aggr Aggr) { - ab.rhs.pushThroughAggr(aggr) - ab.joinColumns = append(ab.joinColumns, JoinColumn{ - Original: aggr.Original, - RHSExpr: aggr.Original.Expr, - }) -} - -func (ab *aggBuilder) handlePushThroughAggregation(ctx *plancontext.PlanningContext, aggr Aggr) error { - _, err := ab.proj.addUnexploredExpr(aggr.Original, aggr.Original.Expr) - if err != nil { - return err - } - - deps := ctx.SemTable.RecursiveDeps(aggr.Original.Expr) - switch { - case deps.IsSolvedBy(ab.lhs.tableID): - ab.pushThroughLeft(aggr) - case deps.IsSolvedBy(ab.rhs.tableID): - ab.pushThroughRight(aggr) - default: - return errAbortAggrPushing - } - return nil -} - -func (ab *aggBuilder) handleCountStar(ctx *plancontext.PlanningContext, aggr Aggr) error { - // Add the aggregate to both sides of the join. - lhsAE := ab.leftCountStar(ctx) - rhsAE := ab.rightCountStar(ctx) - - return ab.buildProjectionForAggr(lhsAE, rhsAE, aggr, true) -} - -func (ab *aggBuilder) handleAggrWithCountStarMultiplier(ctx *plancontext.PlanningContext, aggr Aggr) error { - var lhsAE, rhsAE *sqlparser.AliasedExpr - var addCoalesce bool - - deps := ctx.SemTable.RecursiveDeps(aggr.Original.Expr) - switch { - case deps.IsSolvedBy(ab.lhs.tableID): - ab.pushThroughLeft(aggr) - lhsAE = aggr.Original - rhsAE = ab.rightCountStar(ctx) - if ab.outerJoin { - addCoalesce = true - } - - case deps.IsSolvedBy(ab.rhs.tableID): - ab.pushThroughRight(aggr) - lhsAE = ab.leftCountStar(ctx) - rhsAE = aggr.Original - - default: - return errAbortAggrPushing - } - - return ab.buildProjectionForAggr(lhsAE, rhsAE, aggr, addCoalesce) -} - -func (ab *aggBuilder) buildProjectionForAggr(lhsAE *sqlparser.AliasedExpr, rhsAE *sqlparser.AliasedExpr, aggr Aggr, coalesce bool) error { - // We expect the expressions to be different on each side of the join, otherwise it's an error. - if lhsAE.Expr == rhsAE.Expr { - panic(fmt.Sprintf("Need the two produced expressions to be different. %T %T", lhsAE, rhsAE)) - } - - rhsExpr := rhsAE.Expr - - // When dealing with outer joins, we don't want null values from the RHS to ruin the calculations we are doing, - // so we use the MySQL `coalesce` after the join is applied to multiply the count from LHS with 1. - if ab.outerJoin && coalesce { - rhsExpr = coalesceFunc(rhsExpr) - } - - // The final COUNT is obtained by multiplying the counts from both sides. - // This is equivalent to transforming a "select count(*) from t1 join t2" into - // "select count_t1*count_t2 from - // (select count(*) as count_t1 from t1) as x, - // (select count(*) as count_t2 from t2) as y". - projExpr := &sqlparser.BinaryExpr{ - Operator: sqlparser.MultOp, - Left: lhsAE.Expr, - Right: rhsExpr, - } - projAE := &sqlparser.AliasedExpr{ - Expr: aggr.Original.Expr, - As: sqlparser.NewIdentifierCI(aggr.Original.ColumnName()), - } - - _, err := ab.proj.addUnexploredExpr(projAE, projExpr) - return err + return builder.proj, nil } func coalesceFunc(e sqlparser.Expr) sqlparser.Expr { @@ -739,64 +587,6 @@ func coalesceFunc(e sqlparser.Expr) sqlparser.Expr { } } -// addAggr creates a copy of the given aggregation, updates its column offset to point to the correct location in the new Aggregator, -// and adds it to the list of Aggregations of the new Aggregator. It also updates the semantic analysis information to reflect the new structure. -// It returns the expression of the aggregation as it should be used in the parent Aggregator. -func (p *joinPusher) addAggr(ctx *plancontext.PlanningContext, aggr Aggr) sqlparser.Expr { - copyAggr := aggr - expr := sqlparser.CloneExpr(aggr.Original.Expr) - copyAggr.Original = aeWrap(expr) - // copy dependencies so we can keep track of which side expressions need to be pushed to - ctx.SemTable.Direct[expr] = p.tableID - ctx.SemTable.Recursive[expr] = p.tableID - copyAggr.ColOffset = len(p.pushed.Columns) - p.pushed.Columns = append(p.pushed.Columns, copyAggr.Original) - p.pushed.Aggregations = append(p.pushed.Aggregations, copyAggr) - return expr -} - -// pushThroughAggr pushes through an aggregation without changing dependencies. -// Can be used for aggregations we can push in one piece -func (p *joinPusher) pushThroughAggr(aggr Aggr) { - newAggr := NewAggr(aggr.OpCode, aggr.Func, aggr.Original, aggr.Alias) - newAggr.ColOffset = len(p.pushed.Columns) - p.pushed.Columns = append(p.pushed.Columns, newAggr.Original) - p.pushed.Aggregations = append(p.pushed.Aggregations, newAggr) -} - -// addGrouping creates a copy of the given GroupBy, updates its column offset to point to the correct location in the new Aggregator, -// and adds it to the list of GroupBy expressions of the new Aggregator. It also updates the semantic analysis information to reflect the new structure. -// It returns the expression of the GroupBy as it should be used in the parent Aggregator. -func (p *joinPusher) addGrouping(ctx *plancontext.PlanningContext, gb GroupBy) sqlparser.Expr { - copyGB := gb - expr := sqlparser.CloneExpr(gb.Inner) - // copy dependencies so we can keep track of which side expressions need to be pushed to - ctx.SemTable.CopyDependencies(gb.Inner, expr) - // if the column exists in the selection then copy it down to the pushed aggregator operator. - if copyGB.ColOffset != -1 { - offset := p.useColumn(copyGB.ColOffset) - copyGB.ColOffset = offset - } else { - copyGB.ColOffset = len(p.pushed.Columns) - p.pushed.Columns = append(p.pushed.Columns, aeWrap(copyGB.Inner)) - } - p.pushed.Grouping = append(p.pushed.Grouping, copyGB) - return expr -} - -// useColumn checks whether the column corresponding to the given offset has been used in the new Aggregator. -// If it has not been used before, it adds the column to the new Aggregator -// and updates the columns mapping to reflect the new location of the column. -// It returns the offset of the column in the new Aggregator. -func (p *joinPusher) useColumn(offset int) int { - if p.columns[offset] == -1 { - p.columns[offset] = len(p.pushed.Columns) - // still haven't used this expression on this side - p.pushed.Columns = append(p.pushed.Columns, p.orig.Columns[offset]) - } - return p.columns[offset] -} - func initColReUse(size int) []int { cols := make([]int, size) for i := 0; i < size; i++ { @@ -806,3 +596,71 @@ func initColReUse(size int) []int { } func extractExpr(expr *sqlparser.AliasedExpr) sqlparser.Expr { return expr.Expr } + +func needAvgBreaking(aggrs []Aggr) bool { + for _, aggr := range aggrs { + if aggr.OpCode == opcode.AggregateAvg { + return true + } + } + return false +} + +// splitAvgAggregations takes an aggregator that has AVG aggregations in it and splits +// these into sum/count expressions that can be spread out to shards +func splitAvgAggregations(ctx *plancontext.PlanningContext, aggr *Aggregator) (Operator, *ApplyResult) { + proj := newAliasedProjection(aggr) + + var columns []*sqlparser.AliasedExpr + var aggregations []Aggr + + for offset, col := range aggr.Columns { + avg, ok := col.Expr.(*sqlparser.Avg) + if !ok { + proj.addColumnWithoutPushing(ctx, col, false /* addToGroupBy */) + continue + } + + if avg.Distinct { + panic(vterrors.VT12001("AVG(distinct <>)")) + } + + // We have an AVG that we need to split + sumExpr := &sqlparser.Sum{Arg: avg.Arg} + countExpr := &sqlparser.Count{Args: []sqlparser.Expr{avg.Arg}} + calcExpr := &sqlparser.BinaryExpr{ + Operator: sqlparser.DivOp, + Left: sumExpr, + Right: countExpr, + } + + outputColumn := aeWrap(col.Expr) + outputColumn.As = sqlparser.NewIdentifierCI(col.ColumnName()) + proj.addUnexploredExpr(sqlparser.CloneRefOfAliasedExpr(col), calcExpr) + col.Expr = sumExpr + found := false + for aggrOffset, aggregation := range aggr.Aggregations { + if offset == aggregation.ColOffset { + // We have found the AVG column. We'll change it to SUM, and then we add a COUNT as well + aggr.Aggregations[aggrOffset].OpCode = opcode.AggregateSum + + countExprAlias := aeWrap(countExpr) + countAggr := NewAggr(opcode.AggregateCount, countExpr, countExprAlias, sqlparser.String(countExpr)) + countAggr.ColOffset = len(aggr.Columns) + len(columns) + aggregations = append(aggregations, countAggr) + columns = append(columns, countExprAlias) + found = true + break // no need to search the remaining aggregations + } + } + if !found { + // if we get here, it's because we didn't find the aggregation. Something is wrong + panic(vterrors.VT13001("no aggregation pointing to this column was found")) + } + } + + aggr.Columns = append(aggr.Columns, columns...) + aggr.Aggregations = append(aggr.Aggregations, aggregations...) + + return proj, Rewrote("split avg aggregation") +} diff --git a/go/vt/vtgate/planbuilder/operators/aggregation_pushing_helper.go b/go/vt/vtgate/planbuilder/operators/aggregation_pushing_helper.go new file mode 100644 index 00000000000..eb14f83b7df --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/aggregation_pushing_helper.go @@ -0,0 +1,320 @@ +/* +Copyright 2023 The Vitess Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "fmt" + "slices" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/engine/opcode" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" +) + +type ( + // aggBuilder is a helper struct that aids in pushing down an Aggregator through a join + // it accumulates the projections (if any) that need to be evaluated on top of the join + aggBuilder struct { + lhs, rhs *joinPusher + joinColumns joinColumns + proj *Projection + outerJoin bool + } + + // joinPusher is a helper struct that aids in pushing down an Aggregator into one side of a Join. + // It creates a new Aggregator that is pushed down and keeps track of the column dependencies that the new Aggregator has. + joinPusher struct { + orig *Aggregator // The original Aggregator before pushing. + pushed *Aggregator // The new Aggregator created for push-down. + columns []int // List of column offsets used in the new Aggregator. + tableID semantics.TableSet // The TableSet denoting the side of the Join where the new Aggregator is pushed. + + // csAE keeps the copy of the countStar expression that has already been added to split an aggregation. + // No need to have multiple countStars, so we cache it here + csAE *sqlparser.AliasedExpr + } + + joinColumns interface { + addLeft(expr sqlparser.Expr) + addRight(expr sqlparser.Expr) + } + + applyJoinColumns struct { + columns []applyJoinColumn + } + + hashJoinColumns struct { + columns []hashJoinColumn + } +) + +func (jc *applyJoinColumns) addLeft(expr sqlparser.Expr) { + jc.columns = append(jc.columns, applyJoinColumn{ + Original: expr, + LHSExprs: []BindVarExpr{{Expr: expr}}, + }) +} + +func (jc *applyJoinColumns) addRight(expr sqlparser.Expr) { + jc.columns = append(jc.columns, applyJoinColumn{ + Original: expr, + RHSExpr: expr, + }) +} + +func (jc *applyJoinColumns) clone() *applyJoinColumns { + return &applyJoinColumns{columns: slices.Clone(jc.columns)} +} + +func (jc *applyJoinColumns) add(col applyJoinColumn) { + jc.columns = append(jc.columns, col) +} + +func (hj *hashJoinColumns) addLeft(expr sqlparser.Expr) { + hj.columns = append(hj.columns, hashJoinColumn{ + expr: expr, + side: Left, + }) +} + +func (hj *hashJoinColumns) add(expr sqlparser.Expr) { + hj.columns = append(hj.columns, hashJoinColumn{ + expr: expr, + }) +} + +func (hj *hashJoinColumns) addRight(expr sqlparser.Expr) { + hj.columns = append(hj.columns, hashJoinColumn{ + expr: expr, + side: Right, + }) +} + +func (hj *hashJoinColumns) clone() *hashJoinColumns { + return &hashJoinColumns{columns: slices.Clone(hj.columns)} +} + +func (ab *aggBuilder) leftCountStar(ctx *plancontext.PlanningContext) *sqlparser.AliasedExpr { + ae, created := ab.lhs.countStar(ctx) + if created { + ab.joinColumns.addLeft(ae.Expr) + } + return ae +} + +func (ab *aggBuilder) rightCountStar(ctx *plancontext.PlanningContext) *sqlparser.AliasedExpr { + ae, created := ab.rhs.countStar(ctx) + if created { + ab.joinColumns.addRight(ae.Expr) + } + return ae +} + +func (ab *aggBuilder) handleAggr(ctx *plancontext.PlanningContext, aggr Aggr) error { + switch aggr.OpCode { + case opcode.AggregateCountStar: + ab.handleCountStar(ctx, aggr) + return nil + case opcode.AggregateCount, opcode.AggregateSum: + return ab.handleAggrWithCountStarMultiplier(ctx, aggr) + case opcode.AggregateMax, opcode.AggregateMin, opcode.AggregateAnyValue: + return ab.handlePushThroughAggregation(ctx, aggr) + case opcode.AggregateGroupConcat: + f := aggr.Func.(*sqlparser.GroupConcatExpr) + if f.Distinct || len(f.OrderBy) > 0 || f.Separator != "" { + panic("fail here") + } + // this needs special handling, currently aborting the push of function + // and later will try pushing the column instead. + // TODO: this should be handled better by pushing the function down. + return errAbortAggrPushing + case opcode.AggregateUnassigned: + panic(vterrors.VT12001(fmt.Sprintf("in scatter query: aggregation function '%s'", sqlparser.String(aggr.Original)))) + case opcode.AggregateGtid: + // this is only used for SHOW GTID queries that will never contain joins + panic(vterrors.VT13001("cannot do join with vgtid")) + case opcode.AggregateSumDistinct, opcode.AggregateCountDistinct: + // we are not going to see values multiple times, so we don't need to multiply with the count(*) from the other side + return ab.handlePushThroughAggregation(ctx, aggr) + default: + panic(vterrors.VT12001(fmt.Sprintf("aggregation not planned: %s", aggr.OpCode.String()))) + } +} + +// pushThroughLeft and Right are used for extremums and random, +// which are not split and then arithmetics is used to aggregate the per-shard aggregations. +// For these, we just copy the aggregation to one side of the join and then pick the max of the max:es returned +func (ab *aggBuilder) pushThroughLeft(aggr Aggr) { + ab.lhs.pushThroughAggr(aggr) + ab.joinColumns.addLeft(aggr.Original.Expr) +} + +func (ab *aggBuilder) pushThroughRight(aggr Aggr) { + ab.rhs.pushThroughAggr(aggr) + ab.joinColumns.addRight(aggr.Original.Expr) +} + +func (ab *aggBuilder) handlePushThroughAggregation(ctx *plancontext.PlanningContext, aggr Aggr) error { + ab.proj.addUnexploredExpr(aggr.Original, aggr.Original.Expr) + + deps := ctx.SemTable.RecursiveDeps(aggr.Original.Expr) + switch { + case deps.IsSolvedBy(ab.lhs.tableID): + ab.pushThroughLeft(aggr) + case deps.IsSolvedBy(ab.rhs.tableID): + ab.pushThroughRight(aggr) + default: + return errAbortAggrPushing + } + return nil +} + +func (ab *aggBuilder) handleCountStar(ctx *plancontext.PlanningContext, aggr Aggr) { + // Add the aggregate to both sides of the join. + lhsAE := ab.leftCountStar(ctx) + rhsAE := ab.rightCountStar(ctx) + + ab.buildProjectionForAggr(lhsAE, rhsAE, aggr, true) +} + +func (ab *aggBuilder) handleAggrWithCountStarMultiplier(ctx *plancontext.PlanningContext, aggr Aggr) error { + var lhsAE, rhsAE *sqlparser.AliasedExpr + var addCoalesce bool + + deps := ctx.SemTable.RecursiveDeps(aggr.Original.Expr) + switch { + case deps.IsSolvedBy(ab.lhs.tableID): + ab.pushThroughLeft(aggr) + lhsAE = aggr.Original + rhsAE = ab.rightCountStar(ctx) + if ab.outerJoin { + addCoalesce = true + } + + case deps.IsSolvedBy(ab.rhs.tableID): + ab.pushThroughRight(aggr) + lhsAE = ab.leftCountStar(ctx) + rhsAE = aggr.Original + + default: + return errAbortAggrPushing + } + + ab.buildProjectionForAggr(lhsAE, rhsAE, aggr, addCoalesce) + return nil +} + +func (ab *aggBuilder) buildProjectionForAggr(lhsAE *sqlparser.AliasedExpr, rhsAE *sqlparser.AliasedExpr, aggr Aggr, coalesce bool) { + // We expect the expressions to be different on each side of the join, otherwise it's an error. + if lhsAE.Expr == rhsAE.Expr { + panic(fmt.Sprintf("Need the two produced expressions to be different. %T %T", lhsAE, rhsAE)) + } + + rhsExpr := rhsAE.Expr + + // When dealing with outer joins, we don't want null values from the RHS to ruin the calculations we are doing, + // so we use the MySQL `coalesce` after the join is applied to multiply the count from LHS with 1. + if ab.outerJoin && coalesce { + rhsExpr = coalesceFunc(rhsExpr) + } + + // The final COUNT is obtained by multiplying the counts from both sides. + // This is equivalent to transforming a "select count(*) from t1 join t2" into + // "select count_t1*count_t2 from + // (select count(*) as count_t1 from t1) as x, + // (select count(*) as count_t2 from t2) as y". + projExpr := &sqlparser.BinaryExpr{ + Operator: sqlparser.MultOp, + Left: lhsAE.Expr, + Right: rhsExpr, + } + projAE := &sqlparser.AliasedExpr{ + Expr: aggr.Original.Expr, + As: sqlparser.NewIdentifierCI(aggr.Original.ColumnName()), + } + + ab.proj.addUnexploredExpr(projAE, projExpr) +} + +func (p *joinPusher) countStar(ctx *plancontext.PlanningContext) (*sqlparser.AliasedExpr, bool) { + if p.csAE != nil { + return p.csAE, false + } + cs := &sqlparser.CountStar{} + ae := aeWrap(cs) + csAggr := NewAggr(opcode.AggregateCountStar, cs, ae, "") + expr := p.addAggr(ctx, csAggr) + p.csAE = aeWrap(expr) + return p.csAE, true +} + +// addAggr creates a copy of the given aggregation, updates its column offset to point to the correct location in the new Aggregator, +// and adds it to the list of Aggregations of the new Aggregator. It also updates the semantic analysis information to reflect the new structure. +// It returns the expression of the aggregation as it should be used in the parent Aggregator. +func (p *joinPusher) addAggr(ctx *plancontext.PlanningContext, aggr Aggr) sqlparser.Expr { + copyAggr := aggr + expr := sqlparser.CloneExpr(aggr.Original.Expr) + copyAggr.Original = aeWrap(expr) + // copy dependencies so we can keep track of which side expressions need to be pushed to + ctx.SemTable.Direct[expr] = p.tableID + ctx.SemTable.Recursive[expr] = p.tableID + copyAggr.ColOffset = len(p.pushed.Columns) + p.pushed.Columns = append(p.pushed.Columns, copyAggr.Original) + p.pushed.Aggregations = append(p.pushed.Aggregations, copyAggr) + return expr +} + +// pushThroughAggr pushes through an aggregation without changing dependencies. +// Can be used for aggregations we can push in one piece +func (p *joinPusher) pushThroughAggr(aggr Aggr) { + newAggr := NewAggr(aggr.OpCode, aggr.Func, aggr.Original, aggr.Alias) + newAggr.ColOffset = len(p.pushed.Columns) + p.pushed.Columns = append(p.pushed.Columns, newAggr.Original) + p.pushed.Aggregations = append(p.pushed.Aggregations, newAggr) +} + +// addGrouping creates a copy of the given GroupBy, updates its column offset to point to the correct location in the new Aggregator, +// and adds it to the list of GroupBy expressions of the new Aggregator. It also updates the semantic analysis information to reflect the new structure. +// It returns the expression of the GroupBy as it should be used in the parent Aggregator. +func (p *joinPusher) addGrouping(ctx *plancontext.PlanningContext, gb GroupBy) sqlparser.Expr { + copyGB := gb + expr := sqlparser.CloneExpr(gb.Inner) + // copy dependencies so we can keep track of which side expressions need to be pushed to + ctx.SemTable.CopyDependencies(gb.Inner, expr) + // if the column exists in the selection then copy it down to the pushed aggregator operator. + if copyGB.ColOffset != -1 { + offset := p.useColumn(copyGB.ColOffset) + copyGB.ColOffset = offset + } else { + copyGB.ColOffset = len(p.pushed.Columns) + p.pushed.Columns = append(p.pushed.Columns, aeWrap(copyGB.Inner)) + } + p.pushed.Grouping = append(p.pushed.Grouping, copyGB) + return expr +} + +// useColumn checks whether the column corresponding to the given offset has been used in the new Aggregator. +// If it has not been used before, it adds the column to the new Aggregator +// and updates the columns mapping to reflect the new location of the column. +// It returns the offset of the column in the new Aggregator. +func (p *joinPusher) useColumn(offset int) int { + if p.columns[offset] == -1 { + p.columns[offset] = len(p.pushed.Columns) + // still haven't used this expression on this side + p.pushed.Columns = append(p.pushed.Columns, p.orig.Columns[offset]) + } + return p.columns[offset] +} diff --git a/go/vt/vtgate/planbuilder/operators/aggregator.go b/go/vt/vtgate/planbuilder/operators/aggregator.go index 45ccb041ddd..256372c172f 100644 --- a/go/vt/vtgate/planbuilder/operators/aggregator.go +++ b/go/vt/vtgate/planbuilder/operators/aggregator.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine/opcode" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -35,7 +34,7 @@ type ( // Both all aggregations and no grouping, and the inverse // of all grouping and no aggregations are valid configurations of this operator Aggregator struct { - Source ops.Operator + Source Operator Columns []*sqlparser.AliasedExpr Grouping []GroupBy @@ -60,7 +59,7 @@ type ( } ) -func (a *Aggregator) Clone(inputs []ops.Operator) ops.Operator { +func (a *Aggregator) Clone(inputs []Operator) Operator { kopy := *a kopy.Source = inputs[0] kopy.Columns = slices.Clone(a.Columns) @@ -69,30 +68,27 @@ func (a *Aggregator) Clone(inputs []ops.Operator) ops.Operator { return &kopy } -func (a *Aggregator) Inputs() []ops.Operator { - return []ops.Operator{a.Source} +func (a *Aggregator) Inputs() []Operator { + return []Operator{a.Source} } -func (a *Aggregator) SetInputs(operators []ops.Operator) { +func (a *Aggregator) SetInputs(operators []Operator) { if len(operators) != 1 { panic(fmt.Sprintf("unexpected number of operators as input in aggregator: %d", len(operators))) } a.Source = operators[0] } -func (a *Aggregator) AddPredicate(_ *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { - return &Filter{ - Source: a, - Predicates: []sqlparser.Expr{expr}, - } +func (a *Aggregator) AddPredicate(_ *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + return newFilter(a, expr) } -func (a *Aggregator) addColumnWithoutPushing(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExpr, addToGroupBy bool) int { +func (a *Aggregator) addColumnWithoutPushing(_ *plancontext.PlanningContext, expr *sqlparser.AliasedExpr, addToGroupBy bool) int { offset := len(a.Columns) a.Columns = append(a.Columns, expr) if addToGroupBy { - groupBy := NewGroupBy(expr.Expr, expr.Expr, expr) + groupBy := NewGroupBy(expr.Expr) groupBy.ColOffset = offset a.Grouping = append(a.Grouping, groupBy) } else { @@ -121,7 +117,21 @@ func (a *Aggregator) isDerived() bool { return a.DT != nil } -func (a *Aggregator) FindCol(ctx *plancontext.PlanningContext, in sqlparser.Expr, _ bool) int { +func (a *Aggregator) derivedName() string { + if a.DT == nil { + return "" + } + + return a.DT.Alias +} + +func (a *Aggregator) FindCol(ctx *plancontext.PlanningContext, in sqlparser.Expr, underRoute bool) int { + if underRoute && a.isDerived() { + // We don't want to use columns on this operator if it's a derived table under a route. + // In this case, we need to add a Projection on top of this operator to make the column available + return -1 + } + expr := a.DT.RewriteExpression(ctx, in) if offset, found := canReuseColumn(ctx, a.Columns, expr, extractExpr); found { return offset @@ -149,7 +159,7 @@ func (a *Aggregator) AddColumn(ctx *plancontext.PlanningContext, reuse bool, gro // This process also sets the weight string column offset, eliminating the need for a later addition in the aggregator operator's planOffset. if wsExpr, isWS := rewritten.(*sqlparser.WeightStringFuncExpr); isWS { idx := slices.IndexFunc(a.Grouping, func(by GroupBy) bool { - return ctx.SemTable.EqualsExprWithDeps(wsExpr.Expr, by.SimplifiedExpr) + return ctx.SemTable.EqualsExprWithDeps(wsExpr.Expr, by.Inner) }) if idx >= 0 { a.Grouping[idx].WSOffset = len(a.Columns) @@ -180,7 +190,6 @@ func (a *Aggregator) findColInternal(ctx *plancontext.PlanningContext, ae *sqlpa if offset >= 0 { return offset } - expr = a.DT.RewriteExpression(ctx, expr) // Aggregator is little special and cannot work if the input offset are not matched with the aggregation columns. // So, before pushing anything from above the aggregator offset planning needs to be completed. @@ -188,15 +197,9 @@ func (a *Aggregator) findColInternal(ctx *plancontext.PlanningContext, ae *sqlpa if offset, found := canReuseColumn(ctx, a.Columns, expr, extractExpr); found { return offset } - colName, isColName := expr.(*sqlparser.ColName) - for i, col := range a.Columns { - if isColName && colName.Name.EqualString(col.As.String()) { - return i - } - } if addToGroupBy { - panic(vterrors.VT13001("did not expect to add group by here")) + panic(vterrors.VT13001(fmt.Sprintf("did not expect to add group by here: %s", sqlparser.String(expr)))) } return -1 @@ -243,26 +246,26 @@ func (a *Aggregator) ShortDescription() string { var grouping []string for _, gb := range a.Grouping { - grouping = append(grouping, sqlparser.String(gb.SimplifiedExpr)) + grouping = append(grouping, sqlparser.String(gb.Inner)) } return fmt.Sprintf("%s%s group by %s", org, strings.Join(columns, ", "), strings.Join(grouping, ",")) } -func (a *Aggregator) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (a *Aggregator) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return a.Source.GetOrdering(ctx) } -func (a *Aggregator) planOffsets(ctx *plancontext.PlanningContext) { +func (a *Aggregator) planOffsets(ctx *plancontext.PlanningContext) Operator { if a.offsetPlanned { - return + return nil } defer func() { a.offsetPlanned = true }() if !a.Pushed { a.planOffsetsNotPushed(ctx) - return + return nil } for idx, gb := range a.Grouping { @@ -270,11 +273,11 @@ func (a *Aggregator) planOffsets(ctx *plancontext.PlanningContext) { offset := a.internalAddColumn(ctx, aeWrap(gb.Inner), false) a.Grouping[idx].ColOffset = offset } - if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.SimplifiedExpr) { + if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.Inner) { continue } - offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(gb.SimplifiedExpr)), true) + offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(gb.Inner)), true) a.Grouping[idx].WSOffset = offset } @@ -282,9 +285,11 @@ func (a *Aggregator) planOffsets(ctx *plancontext.PlanningContext) { if !aggr.NeedsWeightString(ctx) { continue } - offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(aggr.Func.GetArg())), true) + arg := aggr.getPushColumn() + offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(arg)), true) a.Aggregations[idx].WSOffset = offset } + return nil } func (aggr Aggr) getPushColumn() sqlparser.Expr { @@ -295,10 +300,13 @@ func (aggr Aggr) getPushColumn() sqlparser.Expr { return sqlparser.NewIntLiteral("1") case opcode.AggregateGroupConcat: if len(aggr.Func.GetArgs()) > 1 { - panic("more than 1 column") + panic(vterrors.VT12001("group_concat with more than 1 column")) } - fallthrough + return aggr.Func.GetArg() default: + if len(aggr.Func.GetArgs()) > 1 { + panic(vterrors.VT03001(sqlparser.String(aggr.Func))) + } return aggr.Func.GetArg() } } @@ -368,11 +376,11 @@ func (a *Aggregator) pushRemainingGroupingColumnsAndWeightStrings(ctx *planconte a.Grouping[idx].ColOffset = offset } - if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.SimplifiedExpr) { + if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.Inner) { continue } - offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(gb.SimplifiedExpr)), false) + offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(gb.Inner)), false) a.Grouping[idx].WSOffset = offset } for idx, aggr := range a.Aggregations { @@ -380,7 +388,8 @@ func (a *Aggregator) pushRemainingGroupingColumnsAndWeightStrings(ctx *planconte continue } - offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(aggr.Func.GetArg())), false) + arg := aggr.getPushColumn() + offset := a.internalAddColumn(ctx, aeWrap(weightStringFor(arg)), false) a.Aggregations[idx].WSOffset = offset } } @@ -402,7 +411,7 @@ func (a *Aggregator) internalAddColumn(ctx *plancontext.PlanningContext, aliased // SplitAggregatorBelowRoute returns the aggregator that will live under the Route. // This is used when we are splitting the aggregation so one part is done // at the mysql level and one part at the vtgate level -func (a *Aggregator) SplitAggregatorBelowRoute(input []ops.Operator) *Aggregator { +func (a *Aggregator) SplitAggregatorBelowRoute(input []Operator) *Aggregator { newOp := a.Clone(input).(*Aggregator) newOp.Pushed = false newOp.Original = false @@ -414,4 +423,21 @@ func (a *Aggregator) introducesTableID() semantics.TableSet { return a.DT.introducesTableID() } -var _ ops.Operator = (*Aggregator)(nil) +func (a *Aggregator) checkForInvalidAggregations() { + for _, aggr := range a.Aggregations { + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + aggrFunc, isAggregate := node.(sqlparser.AggrFunc) + if !isAggregate { + return true, nil + } + args := aggrFunc.GetArgs() + if args != nil && len(args) != 1 { + panic(vterrors.VT03001(sqlparser.String(node))) + } + return true, nil + + }, aggr.Original.Expr) + } +} + +var _ Operator = (*Aggregator)(nil) diff --git a/go/vt/vtgate/planbuilder/operators/apply_join.go b/go/vt/vtgate/planbuilder/operators/apply_join.go index 138c17f2da7..c182bb2fb83 100644 --- a/go/vt/vtgate/planbuilder/operators/apply_join.go +++ b/go/vt/vtgate/planbuilder/operators/apply_join.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) @@ -33,19 +32,16 @@ type ( // ApplyJoin is a nested loop join - for each row on the LHS, // we'll execute the plan on the RHS, feeding data from left to right ApplyJoin struct { - LHS, RHS ops.Operator + LHS, RHS Operator // LeftJoin will be true in the case of an outer join LeftJoin bool - // Before offset planning - Predicate sqlparser.Expr - // JoinColumns keeps track of what AST expression is represented in the Columns array - JoinColumns []JoinColumn + JoinColumns *applyJoinColumns // JoinPredicates are join predicates that have been broken up into left hand side and right hand side parts. - JoinPredicates []JoinColumn + JoinPredicates *applyJoinColumns // ExtraVars are columns we need to copy from left to right not needed by any predicates or projections, // these are needed by other operators further down the right hand side of the join @@ -61,7 +57,7 @@ type ( Vars map[string]int } - // JoinColumn is where we store information about columns passing through the join operator + // applyJoinColumn is where we store information about columns passing through the join operator // It can be in one of three possible configurations: // - Pure left // We are projecting a column that comes from the left. The RHSExpr will be nil for these @@ -71,8 +67,8 @@ type ( // Here we need to transmit columns from the LHS to the RHS, // so they can be used for the result of this expression that is using data from both sides. // All fields will be used for these - JoinColumn struct { - Original *sqlparser.AliasedExpr // this is the original expression being passed through + applyJoinColumn struct { + Original sqlparser.Expr // this is the original expression being passed through LHSExprs []BindVarExpr RHSExpr sqlparser.Expr GroupBy bool // if this is true, we need to push this down to our inputs with addToGroupBy set to true @@ -86,57 +82,59 @@ type ( } ) -func NewApplyJoin(lhs, rhs ops.Operator, predicate sqlparser.Expr, leftOuterJoin bool) *ApplyJoin { - return &ApplyJoin{ - LHS: lhs, - RHS: rhs, - Vars: map[string]int{}, - Predicate: predicate, - LeftJoin: leftOuterJoin, +func NewApplyJoin(ctx *plancontext.PlanningContext, lhs, rhs Operator, predicate sqlparser.Expr, leftOuterJoin bool) *ApplyJoin { + aj := &ApplyJoin{ + LHS: lhs, + RHS: rhs, + Vars: map[string]int{}, + LeftJoin: leftOuterJoin, + JoinColumns: &applyJoinColumns{}, + JoinPredicates: &applyJoinColumns{}, } + aj.AddJoinPredicate(ctx, predicate) + return aj } // Clone implements the Operator interface -func (aj *ApplyJoin) Clone(inputs []ops.Operator) ops.Operator { +func (aj *ApplyJoin) Clone(inputs []Operator) Operator { kopy := *aj kopy.LHS = inputs[0] kopy.RHS = inputs[1] kopy.Columns = slices.Clone(aj.Columns) - kopy.JoinColumns = slices.Clone(aj.JoinColumns) - kopy.JoinPredicates = slices.Clone(aj.JoinPredicates) + kopy.JoinColumns = aj.JoinColumns.clone() + kopy.JoinPredicates = aj.JoinPredicates.clone() kopy.Vars = maps.Clone(aj.Vars) - kopy.Predicate = sqlparser.CloneExpr(aj.Predicate) kopy.ExtraLHSVars = slices.Clone(aj.ExtraLHSVars) return &kopy } -func (aj *ApplyJoin) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { - return AddPredicate(ctx, aj, expr, false, newFilter) +func (aj *ApplyJoin) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + return AddPredicate(ctx, aj, expr, false, newFilterSinglePredicate) } // Inputs implements the Operator interface -func (aj *ApplyJoin) Inputs() []ops.Operator { - return []ops.Operator{aj.LHS, aj.RHS} +func (aj *ApplyJoin) Inputs() []Operator { + return []Operator{aj.LHS, aj.RHS} } // SetInputs implements the Operator interface -func (aj *ApplyJoin) SetInputs(inputs []ops.Operator) { +func (aj *ApplyJoin) SetInputs(inputs []Operator) { aj.LHS, aj.RHS = inputs[0], inputs[1] } -func (aj *ApplyJoin) GetLHS() ops.Operator { +func (aj *ApplyJoin) GetLHS() Operator { return aj.LHS } -func (aj *ApplyJoin) GetRHS() ops.Operator { +func (aj *ApplyJoin) GetRHS() Operator { return aj.RHS } -func (aj *ApplyJoin) SetLHS(operator ops.Operator) { +func (aj *ApplyJoin) SetLHS(operator Operator) { aj.LHS = operator } -func (aj *ApplyJoin) SetRHS(operator ops.Operator) { +func (aj *ApplyJoin) SetRHS(operator Operator) { aj.RHS = operator } @@ -148,48 +146,37 @@ func (aj *ApplyJoin) IsInner() bool { return !aj.LeftJoin } -func (aj *ApplyJoin) AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error { - aj.Predicate = ctx.SemTable.AndExpressions(expr, aj.Predicate) - - col, err := BreakExpressionInLHSandRHS(ctx, expr, TableID(aj.LHS)) - if err != nil { - return err +func (aj *ApplyJoin) AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { + if expr == nil { + return } - aj.JoinPredicates = append(aj.JoinPredicates, col) + col := breakExpressionInLHSandRHSForApplyJoin(ctx, expr, TableID(aj.LHS)) + aj.JoinPredicates.add(col) rhs := aj.RHS.AddPredicate(ctx, col.RHSExpr) aj.RHS = rhs - - return nil -} - -func (aj *ApplyJoin) pushColRight(ctx *plancontext.PlanningContext, e *sqlparser.AliasedExpr, addToGroupBy bool) (int, error) { - offset := aj.RHS.AddColumn(ctx, true, addToGroupBy, e) - return offset, nil } func (aj *ApplyJoin) GetColumns(*plancontext.PlanningContext) []*sqlparser.AliasedExpr { - return slice.Map(aj.JoinColumns, joinColumnToAliasedExpr) + return slice.Map(aj.JoinColumns.columns, func(from applyJoinColumn) *sqlparser.AliasedExpr { + return aeWrap(from.Original) + }) } func (aj *ApplyJoin) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs { return transformColumnsToSelectExprs(ctx, aj) } -func (aj *ApplyJoin) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (aj *ApplyJoin) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return aj.LHS.GetOrdering(ctx) } -func joinColumnToAliasedExpr(c JoinColumn) *sqlparser.AliasedExpr { - return c.Original +func joinColumnToExpr(column applyJoinColumn) sqlparser.Expr { + return column.Original } -func joinColumnToExpr(column JoinColumn) sqlparser.Expr { - return column.Original.Expr -} - -func (aj *ApplyJoin) getJoinColumnFor(ctx *plancontext.PlanningContext, orig *sqlparser.AliasedExpr, e sqlparser.Expr, addToGroupBy bool) (col JoinColumn, err error) { +func (aj *ApplyJoin) getJoinColumnFor(ctx *plancontext.PlanningContext, orig *sqlparser.AliasedExpr, e sqlparser.Expr, addToGroupBy bool) (col applyJoinColumn) { defer func() { - col.Original = orig + col.Original = orig.Expr }() lhs := TableID(aj.LHS) rhs := TableID(aj.RHS) @@ -203,23 +190,22 @@ func (aj *ApplyJoin) getJoinColumnFor(ctx *plancontext.PlanningContext, orig *sq case deps.IsSolvedBy(rhs): col.RHSExpr = e case deps.IsSolvedBy(both): - col, err = BreakExpressionInLHSandRHS(ctx, e, TableID(aj.LHS)) - if err != nil { - return JoinColumn{}, err - } + col = breakExpressionInLHSandRHSForApplyJoin(ctx, e, TableID(aj.LHS)) default: - return JoinColumn{}, vterrors.VT13002(sqlparser.String(e)) + panic(vterrors.VT13002(sqlparser.String(e))) } return } -func (aj *ApplyJoin) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, _ bool) int { - offset, found := canReuseColumn(ctx, aj.JoinColumns, expr, joinColumnToExpr) - if !found { - return -1 +func applyJoinCompare(ctx *plancontext.PlanningContext, expr sqlparser.Expr) func(e applyJoinColumn) bool { + return func(e applyJoinColumn) bool { + return ctx.SemTable.EqualsExprWithDeps(e.Original, expr) } - return offset +} + +func (aj *ApplyJoin) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, _ bool) int { + return slices.IndexFunc(aj.JoinColumns.columns, applyJoinCompare(ctx, expr)) } func (aj *ApplyJoin) AddColumn( @@ -234,18 +220,15 @@ func (aj *ApplyJoin) AddColumn( return offset } } - col, err := aj.getJoinColumnFor(ctx, expr, expr.Expr, groupBy) - if err != nil { - panic(err) - } - offset := len(aj.JoinColumns) - aj.JoinColumns = append(aj.JoinColumns, col) + col := aj.getJoinColumnFor(ctx, expr, expr.Expr, groupBy) + offset := len(aj.JoinColumns.columns) + aj.JoinColumns.add(col) return offset } -func (aj *ApplyJoin) planOffsets(ctx *plancontext.PlanningContext) { - for _, col := range aj.JoinColumns { - // Read the type description for JoinColumn to understand the following code +func (aj *ApplyJoin) planOffsets(ctx *plancontext.PlanningContext) Operator { + for _, col := range aj.JoinColumns.columns { + // Read the type description for applyJoinColumn to understand the following code for _, lhsExpr := range col.LHSExprs { offset := aj.LHS.AddColumn(ctx, true, col.GroupBy, aeWrap(lhsExpr.Expr)) if col.RHSExpr == nil { @@ -261,7 +244,7 @@ func (aj *ApplyJoin) planOffsets(ctx *plancontext.PlanningContext) { } } - for _, col := range aj.JoinPredicates { + for _, col := range aj.JoinPredicates.columns { for _, lhsExpr := range col.LHSExprs { offset := aj.LHS.AddColumn(ctx, true, false, aeWrap(lhsExpr.Expr)) aj.Vars[lhsExpr.Name] = offset @@ -272,6 +255,8 @@ func (aj *ApplyJoin) planOffsets(ctx *plancontext.PlanningContext) { offset := aj.LHS.AddColumn(ctx, true, false, aeWrap(lhsExpr.Expr)) aj.Vars[lhsExpr.Name] = offset } + + return nil } func (aj *ApplyJoin) addOffset(offset int) { @@ -279,11 +264,14 @@ func (aj *ApplyJoin) addOffset(offset int) { } func (aj *ApplyJoin) ShortDescription() string { - pred := sqlparser.String(aj.Predicate) - columns := slice.Map(aj.JoinColumns, func(from JoinColumn) string { - return sqlparser.String(from.Original) - }) - firstPart := fmt.Sprintf("on %s columns: %s", pred, strings.Join(columns, ", ")) + fn := func(cols *applyJoinColumns) string { + out := slice.Map(cols.columns, func(jc applyJoinColumn) string { + return jc.String() + }) + return strings.Join(out, ", ") + } + + firstPart := fmt.Sprintf("on %s columns: %s", fn(aj.JoinPredicates), fn(aj.JoinColumns)) if len(aj.ExtraLHSVars) == 0 { return firstPart } @@ -293,14 +281,14 @@ func (aj *ApplyJoin) ShortDescription() string { } func (aj *ApplyJoin) isColNameMovedFromL2R(bindVarName string) bool { - for _, jc := range aj.JoinColumns { + for _, jc := range aj.JoinColumns.columns { for _, bve := range jc.LHSExprs { if bve.Name == bindVarName { return true } } } - for _, jp := range aj.JoinPredicates { + for _, jp := range aj.JoinPredicates.columns { for _, bve := range jp.LHSExprs { if bve.Name == bindVarName { return true @@ -316,9 +304,9 @@ func (aj *ApplyJoin) isColNameMovedFromL2R(bindVarName string) bool { } // findOrAddColNameBindVarName goes through the JoinColumns and looks for the given colName coming from the LHS of the join -// and returns the argument name if found. if it's not found, a new JoinColumn passing this through will be added -func (aj *ApplyJoin) findOrAddColNameBindVarName(ctx *plancontext.PlanningContext, col *sqlparser.ColName) (string, error) { - for i, thisCol := range aj.JoinColumns { +// and returns the argument name if found. if it's not found, a new applyJoinColumn passing this through will be added +func (aj *ApplyJoin) findOrAddColNameBindVarName(ctx *plancontext.PlanningContext, col *sqlparser.ColName) string { + for i, thisCol := range aj.JoinColumns.columns { idx := slices.IndexFunc(thisCol.LHSExprs, func(e BindVarExpr) bool { return ctx.SemTable.EqualsExpr(e.Expr, col) }) @@ -330,17 +318,17 @@ func (aj *ApplyJoin) findOrAddColNameBindVarName(ctx *plancontext.PlanningContex expr := thisCol.LHSExprs[idx] bvname := ctx.GetReservedArgumentFor(expr.Expr) expr.Name = bvname - aj.JoinColumns[i].LHSExprs[idx] = expr + aj.JoinColumns.columns[i].LHSExprs[idx] = expr } - return thisCol.LHSExprs[idx].Name, nil + return thisCol.LHSExprs[idx].Name } } - for _, thisCol := range aj.JoinPredicates { + for _, thisCol := range aj.JoinPredicates.columns { idx := slices.IndexFunc(thisCol.LHSExprs, func(e BindVarExpr) bool { return ctx.SemTable.EqualsExpr(e.Expr, col) }) if idx != -1 { - return thisCol.LHSExprs[idx].Name, nil + return thisCol.LHSExprs[idx].Name } } @@ -348,7 +336,7 @@ func (aj *ApplyJoin) findOrAddColNameBindVarName(ctx *plancontext.PlanningContex return ctx.SemTable.EqualsExpr(e.Expr, col) }) if idx != -1 { - return aj.ExtraLHSVars[idx].Name, nil + return aj.ExtraLHSVars[idx].Name } // we didn't find it, so we need to add it @@ -357,32 +345,40 @@ func (aj *ApplyJoin) findOrAddColNameBindVarName(ctx *plancontext.PlanningContex Name: bvName, Expr: col, }) - return bvName, nil + return bvName } func (a *ApplyJoin) LHSColumnsNeeded(ctx *plancontext.PlanningContext) (needed sqlparser.Exprs) { f := func(from BindVarExpr) sqlparser.Expr { return from.Expr } - for _, jc := range a.JoinColumns { + for _, jc := range a.JoinColumns.columns { needed = append(needed, slice.Map(jc.LHSExprs, f)...) } - for _, jc := range a.JoinPredicates { + for _, jc := range a.JoinPredicates.columns { needed = append(needed, slice.Map(jc.LHSExprs, f)...) } needed = append(needed, slice.Map(a.ExtraLHSVars, f)...) return ctx.SemTable.Uniquify(needed) } -func (jc JoinColumn) IsPureLeft() bool { +func (jc applyJoinColumn) String() string { + rhs := sqlparser.String(jc.RHSExpr) + lhs := slice.Map(jc.LHSExprs, func(e BindVarExpr) string { + return sqlparser.String(e.Expr) + }) + return fmt.Sprintf("[%s | %s | %s]", strings.Join(lhs, ", "), rhs, sqlparser.String(jc.Original)) +} + +func (jc applyJoinColumn) IsPureLeft() bool { return jc.RHSExpr == nil } -func (jc JoinColumn) IsPureRight() bool { +func (jc applyJoinColumn) IsPureRight() bool { return len(jc.LHSExprs) == 0 } -func (jc JoinColumn) IsMixedLeftAndRight() bool { +func (jc applyJoinColumn) IsMixedLeftAndRight() bool { return len(jc.LHSExprs) > 0 && jc.RHSExpr != nil } diff --git a/go/vt/vtgate/planbuilder/operators/ast_to_op.go b/go/vt/vtgate/planbuilder/operators/ast_to_op.go index bc4aaf8a4e6..14d4de1bf51 100644 --- a/go/vt/vtgate/planbuilder/operators/ast_to_op.go +++ b/go/vt/vtgate/planbuilder/operators/ast_to_op.go @@ -21,48 +21,37 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) const foreignKeyConstraintValues = "fkc_vals" +const foreignKeyUpdateExpr = "fkc_upd" // translateQueryToOp creates an operator tree that represents the input SELECT or UNION query -func translateQueryToOp(ctx *plancontext.PlanningContext, selStmt sqlparser.Statement) (op ops.Operator, err error) { +func translateQueryToOp(ctx *plancontext.PlanningContext, selStmt sqlparser.Statement) Operator { switch node := selStmt.(type) { case *sqlparser.Select: - op, err = createOperatorFromSelect(ctx, node) + return createOperatorFromSelect(ctx, node) case *sqlparser.Union: - op, err = createOperatorFromUnion(ctx, node) + return createOperatorFromUnion(ctx, node) case *sqlparser.Update: - op, err = createOperatorFromUpdate(ctx, node) + return createOperatorFromUpdate(ctx, node) case *sqlparser.Delete: - op, err = createOperatorFromDelete(ctx, node) + return createOperatorFromDelete(ctx, node) case *sqlparser.Insert: - op, err = createOperatorFromInsert(ctx, node) + return createOperatorFromInsert(ctx, node) default: - err = vterrors.VT12001(fmt.Sprintf("operator: %T", selStmt)) + panic(vterrors.VT12001(fmt.Sprintf("operator: %T", selStmt))) } - if err != nil { - return nil, err - } - - return op, nil } -func createOperatorFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.Select) (ops.Operator, error) { - op, err := crossJoin(ctx, sel.From) - if err != nil { - return nil, err - } +func createOperatorFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.Select) Operator { + op := crossJoin(ctx, sel.From) if sel.Where != nil { - op, err = addWherePredicates(ctx, sel.Where.Expr, op) - if err != nil { - return nil, err - } + op = addWherePredicates(ctx, sel.Where.Expr, op) } if sel.Comments != nil || sel.Lock != sqlparser.NoLock { @@ -75,26 +64,28 @@ func createOperatorFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.S op = newHorizon(op, sel) - return op, nil + return op } -func addWherePredicates(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op ops.Operator) (ops.Operator, error) { +func addWherePredicates(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op Operator) Operator { sqc := &SubQueryBuilder{} + op = addWherePredsToSubQueryBuilder(ctx, expr, op, sqc) + return sqc.getRootOperator(op, nil) +} + +func addWherePredsToSubQueryBuilder(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op Operator, sqc *SubQueryBuilder) Operator { outerID := TableID(op) exprs := sqlparser.SplitAndExpression(nil, expr) for _, expr := range exprs { - sqlparser.RemoveKeyspaceFromColName(expr) - subq, err := sqc.handleSubquery(ctx, expr, outerID) - if err != nil { - return nil, err - } + sqlparser.RemoveKeyspaceInCol(expr) + subq := sqc.handleSubquery(ctx, expr, outerID) if subq != nil { continue } op = op.AddPredicate(ctx, expr) addColumnEquality(ctx, expr) } - return sqc.getRootOperator(op, nil), nil + return op } // cloneASTAndSemState clones the AST and the semantic state of the input node. @@ -110,7 +101,7 @@ func cloneASTAndSemState[T sqlparser.SQLNode](ctx *plancontext.PlanningContext, // findTablesContained returns the TableSet of all the contained func findTablesContained(ctx *plancontext.PlanningContext, node sqlparser.SQLNode) (result semantics.TableSet) { - _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { t, ok := node.(*sqlparser.AliasedTableExpr) if !ok { return true, nil @@ -122,32 +113,13 @@ func findTablesContained(ctx *plancontext.PlanningContext, node sqlparser.SQLNod return } -func rewriteRemainingColumns( - ctx *plancontext.PlanningContext, - stmt sqlparser.SelectStatement, - subqID semantics.TableSet, -) sqlparser.SelectStatement { - return sqlparser.CopyOnRewrite(stmt, nil, func(cursor *sqlparser.CopyOnWriteCursor) { - colname, isColname := cursor.Node().(*sqlparser.ColName) - if !isColname { - return - } - deps := ctx.SemTable.RecursiveDeps(colname) - if deps.IsSolvedBy(subqID) { - return - } - rsv := ctx.GetReservedArgumentFor(colname) - cursor.Replace(sqlparser.NewArgument(rsv)) - }, nil).(sqlparser.SelectStatement) -} - // joinPredicateCollector is used to inspect the predicates inside the subquery, looking for any // comparisons between the inner and the outer side. // They can be used for merging the two parts of the query together type joinPredicateCollector struct { predicates sqlparser.Exprs remainingPredicates sqlparser.Exprs - joinColumns []JoinColumn + joinColumns []applyJoinColumn totalID, subqID, @@ -157,73 +129,63 @@ type joinPredicateCollector struct { func (jpc *joinPredicateCollector) inspectPredicate( ctx *plancontext.PlanningContext, predicate sqlparser.Expr, -) error { +) { pred := predicate deps := ctx.SemTable.RecursiveDeps(predicate) // if the subquery is not enough, but together we have all we need, // then we can use this predicate to connect the subquery to the outer query if !deps.IsSolvedBy(jpc.subqID) && deps.IsSolvedBy(jpc.totalID) { jpc.predicates = append(jpc.predicates, predicate) - jc, err := BreakExpressionInLHSandRHS(ctx, predicate, jpc.outerID) - if err != nil { - return err - } + jc := breakExpressionInLHSandRHSForApplyJoin(ctx, predicate, jpc.outerID) jpc.joinColumns = append(jpc.joinColumns, jc) pred = jc.RHSExpr } jpc.remainingPredicates = append(jpc.remainingPredicates, pred) - return nil } -func createOperatorFromUnion(ctx *plancontext.PlanningContext, node *sqlparser.Union) (ops.Operator, error) { - opLHS, err := translateQueryToOp(ctx, node.Left) - if err != nil { - return nil, err - } - +func createOperatorFromUnion(ctx *plancontext.PlanningContext, node *sqlparser.Union) Operator { _, isRHSUnion := node.Right.(*sqlparser.Union) if isRHSUnion { - return nil, vterrors.VT12001("nesting of UNIONs on the right-hand side") + panic(vterrors.VT12001("nesting of UNIONs on the right-hand side")) } - opRHS, err := translateQueryToOp(ctx, node.Right) - if err != nil { - return nil, err - } - + opLHS := translateQueryToOp(ctx, node.Left) + opRHS := translateQueryToOp(ctx, node.Right) lexprs := ctx.SemTable.SelectExprs(node.Left) rexprs := ctx.SemTable.SelectExprs(node.Right) unionCols := ctx.SemTable.SelectExprs(node) - union := newUnion([]ops.Operator{opLHS, opRHS}, []sqlparser.SelectExprs{lexprs, rexprs}, unionCols, node.Distinct) - return newHorizon(union, node), nil + union := newUnion([]Operator{opLHS, opRHS}, []sqlparser.SelectExprs{lexprs, rexprs}, unionCols, node.Distinct) + return newHorizon(union, node) } // createOpFromStmt creates an operator from the given statement. It takes in two additional arguments— // 1. verifyAllFKs: For this given statement, do we need to verify validity of all the foreign keys on the vtgate level. // 2. fkToIgnore: The foreign key constraint to specifically ignore while planning the statement. This field is used in UPDATE CASCADE planning, wherein while planning the child update // query, we need to ignore the parent foreign key constraint that caused the cascade in question. -func createOpFromStmt(ctx *plancontext.PlanningContext, stmt sqlparser.Statement, verifyAllFKs bool, fkToIgnore string) (ops.Operator, error) { - var err error - ctx, err = plancontext.CreatePlanningContext(stmt, ctx.ReservedVars, ctx.VSchema, ctx.PlannerVersion) +func createOpFromStmt(inCtx *plancontext.PlanningContext, stmt sqlparser.Statement, verifyAllFKs bool, fkToIgnore string) Operator { + ctx, err := plancontext.CreatePlanningContext(stmt, inCtx.ReservedVars, inCtx.VSchema, inCtx.PlannerVersion) if err != nil { - return nil, err + panic(err) } // TODO (@GuptaManan100, @harshit-gangal): When we add cross-shard foreign keys support, // we should augment the semantic analysis to also tell us whether the given query has any cross shard parent foreign keys to validate. // If there are, then we have to run the query with FOREIGN_KEY_CHECKS off because we can't be sure if the DML will succeed on MySQL with the checks on. // So, we should set VerifyAllFKs to true. i.e. we should add `|| ctx.SemTable.RequireForeignKeyChecksOff()` to the below condition. - ctx.VerifyAllFKs = verifyAllFKs + if verifyAllFKs { + // If ctx.VerifyAllFKs is already true we don't want to turn it off. + ctx.VerifyAllFKs = verifyAllFKs + } // From all the parent foreign keys involved, we should remove the one that we need to ignore. err = ctx.SemTable.RemoveParentForeignKey(fkToIgnore) if err != nil { - return nil, err + panic(err) } // Now, we can filter the foreign keys further based on the planning context, specifically whether we are running // this query with FOREIGN_KEY_CHECKS off or not. If the foreign key checks are enabled, then we don't need to verify - // the validity of shard-scoped RESTRICT foreign keys, since MySQL will do that for us. Similarily, we don't need to verify + // the validity of shard-scoped RESTRICT foreign keys, since MySQL will do that for us. Similarly, we don't need to verify // if the shard-scoped parent foreign key constraints are valid. switch stmt.(type) { case *sqlparser.Update, *sqlparser.Insert: @@ -232,13 +194,21 @@ func createOpFromStmt(ctx *plancontext.PlanningContext, stmt sqlparser.Statement err = ctx.SemTable.RemoveNonRequiredForeignKeys(ctx.VerifyAllFKs, vindexes.DeleteAction) } if err != nil { - return nil, err + panic(err) } - return PlanQuery(ctx, stmt) + op, err := PlanQuery(ctx, stmt) + if err != nil { + panic(err) + } + + // need to remember which predicates have been broken up during join planning + inCtx.KeepPredicateInfo(ctx) + + return op } -func getOperatorFromTableExpr(ctx *plancontext.PlanningContext, tableExpr sqlparser.TableExpr, onlyTable bool) (ops.Operator, error) { +func getOperatorFromTableExpr(ctx *plancontext.PlanningContext, tableExpr sqlparser.TableExpr, onlyTable bool) Operator { switch tableExpr := tableExpr.(type) { case *sqlparser.AliasedTableExpr: return getOperatorFromAliasedTableExpr(ctx, tableExpr, onlyTable) @@ -247,19 +217,13 @@ func getOperatorFromTableExpr(ctx *plancontext.PlanningContext, tableExpr sqlpar case *sqlparser.ParenTableExpr: return crossJoin(ctx, tableExpr.Exprs) default: - return nil, vterrors.VT13001(fmt.Sprintf("unable to use: %T table type", tableExpr)) + panic(vterrors.VT13001(fmt.Sprintf("unable to use: %T table type", tableExpr))) } } -func getOperatorFromJoinTableExpr(ctx *plancontext.PlanningContext, tableExpr *sqlparser.JoinTableExpr) (ops.Operator, error) { - lhs, err := getOperatorFromTableExpr(ctx, tableExpr.LeftExpr, false) - if err != nil { - return nil, err - } - rhs, err := getOperatorFromTableExpr(ctx, tableExpr.RightExpr, false) - if err != nil { - return nil, err - } +func getOperatorFromJoinTableExpr(ctx *plancontext.PlanningContext, tableExpr *sqlparser.JoinTableExpr) Operator { + lhs := getOperatorFromTableExpr(ctx, tableExpr.LeftExpr, false) + rhs := getOperatorFromTableExpr(ctx, tableExpr.RightExpr, false) switch tableExpr.Join { case sqlparser.NormalJoinType: @@ -267,17 +231,17 @@ func getOperatorFromJoinTableExpr(ctx *plancontext.PlanningContext, tableExpr *s case sqlparser.LeftJoinType, sqlparser.RightJoinType: return createOuterJoin(tableExpr, lhs, rhs) default: - return nil, vterrors.VT13001("unsupported: %s", tableExpr.Join.ToString()) + panic(vterrors.VT13001("unsupported: %s", tableExpr.Join.ToString())) } } -func getOperatorFromAliasedTableExpr(ctx *plancontext.PlanningContext, tableExpr *sqlparser.AliasedTableExpr, onlyTable bool) (ops.Operator, error) { +func getOperatorFromAliasedTableExpr(ctx *plancontext.PlanningContext, tableExpr *sqlparser.AliasedTableExpr, onlyTable bool) Operator { tableID := ctx.SemTable.TableSetFor(tableExpr) switch tbl := tableExpr.Expr.(type) { case sqlparser.TableName: tableInfo, err := ctx.SemTable.TableInfoFor(tableID) if err != nil { - return nil, err + panic(err) } if vt, isVindex := tableInfo.(*semantics.VindexTable); isVindex { @@ -291,73 +255,68 @@ func getOperatorFromAliasedTableExpr(ctx *plancontext.PlanningContext, tableExpr }, Vindex: vt.Vindex, Solved: solves, - }, nil + } } qg := newQueryGraph() isInfSchema := tableInfo.IsInfSchema() qt := &QueryTable{Alias: tableExpr, Table: tbl, ID: tableID, IsInfSchema: isInfSchema} qg.Tables = append(qg.Tables, qt) - return qg, nil + return qg case *sqlparser.DerivedTable: if onlyTable && tbl.Select.GetLimit() == nil { tbl.Select.SetOrderBy(nil) } - inner, err := translateQueryToOp(ctx, tbl.Select) - if err != nil { - return nil, err - } + inner := translateQueryToOp(ctx, tbl.Select) if horizon, ok := inner.(*Horizon); ok { horizon.TableId = &tableID horizon.Alias = tableExpr.As.String() horizon.ColumnAliases = tableExpr.Columns - qp, err := CreateQPFromSelectStatement(ctx, tbl.Select) - if err != nil { - return nil, err - } + qp := CreateQPFromSelectStatement(ctx, tbl.Select) horizon.QP = qp } - return inner, nil + return inner default: - return nil, vterrors.VT13001(fmt.Sprintf("unable to use: %T", tbl)) + panic(vterrors.VT13001(fmt.Sprintf("unable to use: %T", tbl))) } } -func crossJoin(ctx *plancontext.PlanningContext, exprs sqlparser.TableExprs) (ops.Operator, error) { - var output ops.Operator +func crossJoin(ctx *plancontext.PlanningContext, exprs sqlparser.TableExprs) Operator { + var output Operator for _, tableExpr := range exprs { - op, err := getOperatorFromTableExpr(ctx, tableExpr, len(exprs) == 1) - if err != nil { - return nil, err - } + op := getOperatorFromTableExpr(ctx, tableExpr, len(exprs) == 1) if output == nil { output = op } else { output = createJoin(ctx, output, op) } } - return output, nil + return output } -func createQueryTableForDML(ctx *plancontext.PlanningContext, tableExpr sqlparser.TableExpr, whereClause *sqlparser.Where) (semantics.TableInfo, *QueryTable, error) { +func createQueryTableForDML( + ctx *plancontext.PlanningContext, + tableExpr sqlparser.TableExpr, + whereClause *sqlparser.Where, +) (semantics.TableInfo, *QueryTable) { alTbl, ok := tableExpr.(*sqlparser.AliasedTableExpr) if !ok { - return nil, nil, vterrors.VT13001("expected AliasedTableExpr") + panic(vterrors.VT13001("expected AliasedTableExpr")) } tblName, ok := alTbl.Expr.(sqlparser.TableName) if !ok { - return nil, nil, vterrors.VT13001("expected TableName") + panic(vterrors.VT13001("expected TableName")) } tableID := ctx.SemTable.TableSetFor(alTbl) tableInfo, err := ctx.SemTable.TableInfoFor(tableID) if err != nil { - return nil, nil, err + panic(err) } if tableInfo.IsInfSchema() { - return nil, nil, vterrors.VT12001("update information schema tables") + panic(vterrors.VT12001("update information schema tables")) } var predicates []sqlparser.Expr @@ -370,7 +329,7 @@ func createQueryTableForDML(ctx *plancontext.PlanningContext, tableExpr sqlparse Table: tblName, Predicates: predicates, } - return tableInfo, qt, nil + return tableInfo, qt } func addColumnEquality(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { @@ -397,27 +356,18 @@ func createSelectionOp( selectExprs []sqlparser.SelectExpr, tableExprs sqlparser.TableExprs, where *sqlparser.Where, + orderBy sqlparser.OrderBy, limit *sqlparser.Limit, lock sqlparser.Lock, -) (ops.Operator, error) { +) Operator { selectionStmt := &sqlparser.Select{ SelectExprs: selectExprs, From: tableExprs, Where: where, + OrderBy: orderBy, Limit: limit, Lock: lock, } // There are no foreign keys to check for a select query, so we can pass anything for verifyAllFKs and fkToIgnore. return createOpFromStmt(ctx, selectionStmt, false /* verifyAllFKs */, "" /* fkToIgnore */) } - -func selectParentColumns(fk vindexes.ChildFKInfo, lastOffset int) ([]int, []sqlparser.SelectExpr) { - var cols []int - var exprs []sqlparser.SelectExpr - for _, column := range fk.ParentColumns { - cols = append(cols, lastOffset) - exprs = append(exprs, aeWrap(sqlparser.NewColName(column.String()))) - lastOffset++ - } - return cols, exprs -} diff --git a/go/vt/vtgate/planbuilder/operators/comments.go b/go/vt/vtgate/planbuilder/operators/comments.go index 9ede4b9e0da..912fa4138d9 100644 --- a/go/vt/vtgate/planbuilder/operators/comments.go +++ b/go/vt/vtgate/planbuilder/operators/comments.go @@ -21,32 +21,31 @@ import ( "strings" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // LockAndComment contains any comments or locking directives we want on all queries down from this operator type LockAndComment struct { - Source ops.Operator + Source Operator Comments *sqlparser.ParsedComments Lock sqlparser.Lock } -func (l *LockAndComment) Clone(inputs []ops.Operator) ops.Operator { +func (l *LockAndComment) Clone(inputs []Operator) Operator { klon := *l klon.Source = inputs[0] return &klon } -func (l *LockAndComment) Inputs() []ops.Operator { - return []ops.Operator{l.Source} +func (l *LockAndComment) Inputs() []Operator { + return []Operator{l.Source} } -func (l *LockAndComment) SetInputs(operators []ops.Operator) { +func (l *LockAndComment) SetInputs(operators []Operator) { l.Source = operators[0] } -func (l *LockAndComment) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (l *LockAndComment) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { l.Source = l.Source.AddPredicate(ctx, expr) return l } @@ -76,6 +75,6 @@ func (l *LockAndComment) ShortDescription() string { return strings.Join(s, " ") } -func (l *LockAndComment) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (l *LockAndComment) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return l.Source.GetOrdering(ctx) } diff --git a/go/vt/vtgate/planbuilder/operators/delete.go b/go/vt/vtgate/planbuilder/operators/delete.go index f02444671c1..c22da14c35f 100644 --- a/go/vt/vtgate/planbuilder/operators/delete.go +++ b/go/vt/vtgate/planbuilder/operators/delete.go @@ -22,189 +22,332 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) type Delete struct { - QTable *QueryTable - VTable *vindexes.Table - OwnedVindexQuery string - AST *sqlparser.Delete + *DMLCommon - noInputs noColumns noPredicates } +type TargetTable struct { + ID semantics.TableSet + VTable *vindexes.Table + Name sqlparser.TableName +} + // Introduces implements the PhysicalOperator interface func (d *Delete) introducesTableID() semantics.TableSet { - return d.QTable.ID + return d.Target.ID } // Clone implements the Operator interface -func (d *Delete) Clone([]ops.Operator) ops.Operator { - return &Delete{ - QTable: d.QTable, - VTable: d.VTable, - OwnedVindexQuery: d.OwnedVindexQuery, - AST: d.AST, +func (d *Delete) Clone(inputs []Operator) Operator { + newD := *d + newD.SetInputs(inputs) + return &newD +} + +func (d *Delete) Inputs() []Operator { + return []Operator{d.Source} +} + +func (d *Delete) SetInputs(inputs []Operator) { + if len(inputs) != 1 { + panic(vterrors.VT13001("unexpected number of inputs for Delete operator")) } + d.Source = inputs[0] } func (d *Delete) TablesUsed() []string { - if d.VTable != nil { - return SingleQualifiedIdentifier(d.VTable.Keyspace, d.VTable.Name) - } - return nil + return SingleQualifiedIdentifier(d.Target.VTable.Keyspace, d.Target.VTable.Name) } -func (d *Delete) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (d *Delete) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } func (d *Delete) ShortDescription() string { - return fmt.Sprintf("%s.%s %s", d.VTable.Keyspace.Name, d.VTable.Name.String(), sqlparser.String(d.AST.Where)) -} - -func (d *Delete) Statement() sqlparser.Statement { - return d.AST + ovq := "" + if d.OwnedVindexQuery != nil { + var cols, orderby, limit string + cols = fmt.Sprintf("COLUMNS: [%s]", sqlparser.String(d.OwnedVindexQuery.SelectExprs)) + if len(d.OwnedVindexQuery.OrderBy) > 0 { + orderby = fmt.Sprintf(" ORDERBY: [%s]", sqlparser.String(d.OwnedVindexQuery.OrderBy)) + } + if d.OwnedVindexQuery.Limit != nil { + limit = fmt.Sprintf(" LIMIT: [%s]", sqlparser.String(d.OwnedVindexQuery.Limit)) + } + ovq = fmt.Sprintf(" vindexQuery(%s%s%s)", cols, orderby, limit) + } + return fmt.Sprintf("%s.%s%s", d.Target.VTable.Keyspace.Name, d.Target.VTable.Name.String(), ovq) } -func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlparser.Delete) (ops.Operator, error) { - tableInfo, qt, err := createQueryTableForDML(ctx, deleteStmt.TableExprs[0], deleteStmt.Where) - if err != nil { - return nil, err - } +func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlparser.Delete) (op Operator) { + childFks := ctx.SemTable.GetChildForeignKeysForTable(deleteStmt.Targets[0]) - vindexTable, routing, err := buildVindexTableForDML(ctx, tableInfo, qt, "delete") - if err != nil { - return nil, err + // We check if delete with input plan is required. DML with input planning is generally + // slower, because it does a selection and then creates a delete statement wherein we have to + // list all the primary key values. + if deleteWithInputPlanningRequired(childFks, deleteStmt) { + return deleteWithInputPlanningForFk(ctx, deleteStmt) } delClone := sqlparser.CloneRefOfDelete(deleteStmt) - // Create the delete operator first. - delOp, err := createDeleteOperator(ctx, deleteStmt, qt, vindexTable, routing) - if err != nil { - return nil, err - } + var vTbl *vindexes.Table + op, vTbl = createDeleteOperator(ctx, deleteStmt) if deleteStmt.Comments != nil { - delOp = &LockAndComment{ - Source: delOp, + op = &LockAndComment{ + Source: op, Comments: deleteStmt.Comments, } } - childFks := ctx.SemTable.GetChildForeignKeysList() - // If there are no foreign key constraints, then we don't need to do anything. + // If there are no foreign key constraints, then we don't need to do anything special. if len(childFks) == 0 { - return delOp, nil + return op } - // If the delete statement has a limit, we don't support it yet. + + return createFkCascadeOpForDelete(ctx, op, delClone, childFks, vTbl) +} + +func deleteWithInputPlanningRequired(childFks []vindexes.ChildFKInfo, deleteStmt *sqlparser.Delete) bool { + // If there are no foreign keys, we don't need to use delete with input. + if len(childFks) == 0 { + return false + } + // Limit requires delete with input. if deleteStmt.Limit != nil { - return nil, vterrors.VT12001("foreign keys management at vitess with limit") + return true } + // If there are no limit clauses, and it is not a multi-delete, we don't need delete with input. + // TODO: In the future, we can check if the tables involved in the multi-table delete are related by foreign keys or not. + // If they aren't then we don't need the multi-table delete. But this check isn't so straight-forward. We need to check if the two + // tables are connected in the undirected graph built from the tables related by foreign keys. + return !deleteStmt.IsSingleAliasExpr() +} - return createFkCascadeOpForDelete(ctx, delOp, delClone, childFks) +func deleteWithInputPlanningForFk(ctx *plancontext.PlanningContext, del *sqlparser.Delete) Operator { + delClone := ctx.SemTable.Clone(del).(*sqlparser.Delete) + del.Limit = nil + del.OrderBy = nil + + selectStmt := &sqlparser.Select{ + From: delClone.TableExprs, + Where: delClone.Where, + OrderBy: delClone.OrderBy, + Limit: delClone.Limit, + Lock: sqlparser.ForUpdateLock, + } + ts := ctx.SemTable.Targets[del.Targets[0].Name] + ti, err := ctx.SemTable.TableInfoFor(ts) + if err != nil { + panic(vterrors.VT13001(err.Error())) + } + vTbl := ti.GetVindexTable() + + var leftComp sqlparser.ValTuple + cols := make([]*sqlparser.ColName, 0, len(vTbl.PrimaryKey)) + for _, col := range vTbl.PrimaryKey { + colName := sqlparser.NewColNameWithQualifier(col.String(), vTbl.GetTableName()) + selectStmt.SelectExprs = append(selectStmt.SelectExprs, aeWrap(colName)) + cols = append(cols, colName) + leftComp = append(leftComp, colName) + ctx.SemTable.Recursive[colName] = ts + } + // optimize for case when there is only single column on left hand side. + var lhs sqlparser.Expr = leftComp + if len(leftComp) == 1 { + lhs = leftComp[0] + } + compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, lhs, sqlparser.ListArg(engine.DmlVals), nil) + + del.Targets = sqlparser.TableNames{del.Targets[0]} + del.TableExprs = sqlparser.TableExprs{ti.GetAliasedTableExpr()} + del.Where = sqlparser.NewWhere(sqlparser.WhereClause, compExpr) + + return &DMLWithInput{ + DML: createOperatorFromDelete(ctx, del), + Source: createOperatorFromSelect(ctx, selectStmt), + cols: cols, + } } -func createDeleteOperator( - ctx *plancontext.PlanningContext, - deleteStmt *sqlparser.Delete, - qt *QueryTable, - vindexTable *vindexes.Table, - routing Routing) (ops.Operator, error) { - del := &Delete{ - QTable: qt, - VTable: vindexTable, - AST: deleteStmt, +func createDeleteOperator(ctx *plancontext.PlanningContext, del *sqlparser.Delete) (Operator, *vindexes.Table) { + op := crossJoin(ctx, del.TableExprs) + + sqc := &SubQueryBuilder{} + if del.Where != nil { + op = addWherePredsToSubQueryBuilder(ctx, del.Where.Expr, op, sqc) + } + + target := del.Targets[0] + tblID, exists := ctx.SemTable.Targets[target.Name] + if !exists { + panic(vterrors.VT13001("delete target table should be part of semantic analyzer")) } - route := &Route{ - Source: del, - Routing: routing, + tblInfo, err := ctx.SemTable.TableInfoFor(tblID) + if err != nil { + panic(err) } - if !vindexTable.Keyspace.Sharded { - return route, nil + vTbl := tblInfo.GetVindexTable() + // Reference table should delete from the source table. + if vTbl.Type == vindexes.TypeReference && vTbl.Source != nil { + vTbl = updateQueryGraphWithSource(ctx, op, tblID, vTbl) } - primaryVindex, vindexAndPredicates, err := getVindexInformation(qt.ID, vindexTable) + name, err := tblInfo.Name() if err != nil { - return nil, err + panic(err) } - tr, ok := routing.(*ShardedRouting) - if ok { - tr.VindexPreds = vindexAndPredicates + targetTbl := TargetTable{ + ID: tblID, + VTable: vTbl, + Name: name, } - var ovq string - if len(vindexTable.Owned) > 0 { - tblExpr := &sqlparser.AliasedTableExpr{Expr: sqlparser.TableName{Name: vindexTable.Name}, As: qt.Alias.As} - ovq = generateOwnedVindexQuery(tblExpr, deleteStmt, vindexTable, primaryVindex.Columns) + var ovq *sqlparser.Select + if vTbl.Keyspace.Sharded && vTbl.Type == vindexes.TypeTable { + primaryVindex, _ := getVindexInformation(tblID, vTbl) + if len(vTbl.Owned) > 0 { + ovq = generateOwnedVindexQuery(del, targetTbl, primaryVindex.Columns) + } } - del.OwnedVindexQuery = ovq + delOp := &Delete{ + DMLCommon: &DMLCommon{ + Ignore: del.Ignore, + Target: targetTbl, + OwnedVindexQuery: ovq, + Source: op, + }, + } - sqc := &SubQueryBuilder{} - for _, predicate := range qt.Predicates { - if subq, err := sqc.handleSubquery(ctx, predicate, qt.ID); err != nil { - return nil, err - } else if subq != nil { - continue + if del.Limit != nil { + addOrdering(ctx, del.OrderBy, delOp) + delOp.Source = &Limit{ + Source: delOp.Source, + AST: del.Limit, } - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err + } + + return sqc.getRootOperator(delOp, nil), vTbl +} + +func generateOwnedVindexQuery(del *sqlparser.Delete, table TargetTable, ksidCols []sqlparser.IdentifierCI) *sqlparser.Select { + var selExprs sqlparser.SelectExprs + for _, col := range ksidCols { + colName := makeColName(col, table, sqlparser.MultiTable(del.TableExprs)) + selExprs = append(selExprs, aeWrap(colName)) + } + for _, cv := range table.VTable.Owned { + for _, col := range cv.Columns { + colName := makeColName(col, table, sqlparser.MultiTable(del.TableExprs)) + selExprs = append(selExprs, aeWrap(colName)) } } + return &sqlparser.Select{ + SelectExprs: selExprs, + OrderBy: del.OrderBy, + Limit: del.Limit, + Lock: sqlparser.ForUpdateLock, + } +} - if routing.OpCode() == engine.Scatter && deleteStmt.Limit != nil { - // TODO systay: we should probably check for other op code types - IN could also hit multiple shards (2022-04-07) - return nil, vterrors.VT12001("multi shard DELETE with LIMIT") +func makeColName(col sqlparser.IdentifierCI, table TargetTable, isMultiTbl bool) *sqlparser.ColName { + if isMultiTbl { + return sqlparser.NewColNameWithQualifier(col.String(), table.Name) } + return sqlparser.NewColName(col.String()) +} - return sqc.getRootOperator(route, nil), nil +func addOrdering(ctx *plancontext.PlanningContext, orderBy sqlparser.OrderBy, op Operator) { + es := &expressionSet{} + ordering := &Ordering{} + ordering.SetInputs(op.Inputs()) + for _, order := range orderBy { + if sqlparser.IsNull(order.Expr) { + // ORDER BY null can safely be ignored + continue + } + if !es.add(ctx, order.Expr) { + continue + } + ordering.Order = append(ordering.Order, OrderBy{ + Inner: sqlparser.CloneRefOfOrder(order), + SimplifiedExpr: order.Expr, + }) + } + if len(ordering.Order) > 0 { + op.SetInputs([]Operator{ordering}) + } +} + +func updateQueryGraphWithSource(ctx *plancontext.PlanningContext, input Operator, tblID semantics.TableSet, vTbl *vindexes.Table) *vindexes.Table { + sourceTable, _, _, _, _, err := ctx.VSchema.FindTableOrVindex(vTbl.Source.TableName) + if err != nil { + panic(err) + } + vTbl = sourceTable + TopDown(input, TableID, func(op Operator, lhsTables semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { + qg, ok := op.(*QueryGraph) + if !ok { + return op, NoRewrite + } + if len(qg.Tables) > 1 { + panic(vterrors.VT12001("DELETE on reference table with join")) + } + for _, tbl := range qg.Tables { + if tbl.ID != tblID { + continue + } + tbl.Alias = sqlparser.NewAliasedTableExpr(sqlparser.NewTableName(vTbl.Name.String()), tbl.Alias.As.String()) + tbl.Table, _ = tbl.Alias.TableName() + } + return op, Rewrote("change query table point to source table") + }, func(operator Operator) VisitRule { + _, ok := operator.(*QueryGraph) + return VisitRule(ok) + }) + return vTbl } -func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp ops.Operator, delStmt *sqlparser.Delete, childFks []vindexes.ChildFKInfo) (ops.Operator, error) { +func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp Operator, delStmt *sqlparser.Delete, childFks []vindexes.ChildFKInfo, deletedTbl *vindexes.Table) Operator { var fkChildren []*FkChild var selectExprs []sqlparser.SelectExpr for _, fk := range childFks { // Any RESTRICT type foreign keys that arrive here, // are cross-shard/cross-keyspace RESTRICT cases, which we don't currently support. if fk.OnDelete.IsRestrict() { - return nil, vterrors.VT12002() + panic(vterrors.VT12002()) } // We need to select all the parent columns for the foreign key constraint, to use in the update of the child table. - cols, exprs := selectParentColumns(fk, len(selectExprs)) - selectExprs = append(selectExprs, exprs...) + var offsets []int + offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, deletedTbl.GetTableName()) - fkChild, err := createFkChildForDelete(ctx, fk, cols) - if err != nil { - return nil, err - } - fkChildren = append(fkChildren, fkChild) - } - selectionOp, err := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, sqlparser.ForUpdateLock) - if err != nil { - return nil, err + fkChildren = append(fkChildren, + createFkChildForDelete(ctx, fk, offsets)) } + selectionOp := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, nil, getUpdateLock(deletedTbl)) return &FkCascade{ Selection: selectionOp, Children: fkChildren, Parent: parentOp, - }, nil + } } -func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, cols []int) (*FkChild, error) { +func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, cols []int) *FkChild { bvName := ctx.ReservedVars.ReserveVariable(foreignKeyConstraintValues) - + parsedComments := getParsedCommentsForFkChecks(ctx) var childStmt sqlparser.Statement switch fk.OnDelete { case sqlparser.Cascade: @@ -216,6 +359,7 @@ func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildF } compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, valTuple, sqlparser.NewListArg(bvName), nil) childStmt = &sqlparser.Delete{ + Comments: parsedComments, TableExprs: []sqlparser.TableExpr{sqlparser.NewAliasedTableExpr(fk.Table.GetTableName(), "")}, Where: &sqlparser.Where{Type: sqlparser.WhereClause, Expr: compExpr}, } @@ -234,22 +378,20 @@ func createFkChildForDelete(ctx *plancontext.PlanningContext, fk vindexes.ChildF compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, valTuple, sqlparser.NewListArg(bvName), nil) childStmt = &sqlparser.Update{ Exprs: updExprs, + Comments: parsedComments, TableExprs: []sqlparser.TableExpr{sqlparser.NewAliasedTableExpr(fk.Table.GetTableName(), "")}, Where: &sqlparser.Where{Type: sqlparser.WhereClause, Expr: compExpr}, } case sqlparser.SetDefault: - return nil, vterrors.VT09016() + panic(vterrors.VT09016()) } // For the child statement of a DELETE query, we don't need to verify all the FKs on VTgate or ignore any foreign key explicitly. - childOp, err := createOpFromStmt(ctx, childStmt, false /* verifyAllFKs */, "" /* fkToIgnore */) - if err != nil { - return nil, err - } + childOp := createOpFromStmt(ctx, childStmt, false /* verifyAllFKs */, "" /* fkToIgnore */) return &FkChild{ BVName: bvName, Cols: cols, Op: childOp, - }, nil + } } diff --git a/go/vt/vtgate/planbuilder/operators/distinct.go b/go/vt/vtgate/planbuilder/operators/distinct.go index 88503514615..eeddd928f66 100644 --- a/go/vt/vtgate/planbuilder/operators/distinct.go +++ b/go/vt/vtgate/planbuilder/operators/distinct.go @@ -21,13 +21,12 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type ( Distinct struct { - Source ops.Operator + Source Operator QP *QueryProjection // When we go from AST to operator, we place DISTINCT ops in the required places in the op tree @@ -46,14 +45,10 @@ type ( } ) -func (d *Distinct) planOffsets(ctx *plancontext.PlanningContext) { +func (d *Distinct) planOffsets(ctx *plancontext.PlanningContext) Operator { columns := d.GetColumns(ctx) for idx, col := range columns { - e, err := d.QP.GetSimplifiedExpr(ctx, col.Expr) - if err != nil { - // ambiguous columns are not a problem for DISTINCT - e = col.Expr - } + e := col.Expr var wsCol *int typ, _ := ctx.SemTable.TypeForExpr(e) @@ -63,14 +58,16 @@ func (d *Distinct) planOffsets(ctx *plancontext.PlanningContext) { } d.Columns = append(d.Columns, engine.CheckCol{ - Col: idx, - WsCol: wsCol, - Type: typ, + Col: idx, + WsCol: wsCol, + Type: typ, + CollationEnv: ctx.VSchema.Environment().CollationEnv(), }) } + return nil } -func (d *Distinct) Clone(inputs []ops.Operator) ops.Operator { +func (d *Distinct) Clone(inputs []Operator) Operator { return &Distinct{ Required: d.Required, Source: inputs[0], @@ -81,15 +78,15 @@ func (d *Distinct) Clone(inputs []ops.Operator) ops.Operator { } } -func (d *Distinct) Inputs() []ops.Operator { - return []ops.Operator{d.Source} +func (d *Distinct) Inputs() []Operator { + return []Operator{d.Source} } -func (d *Distinct) SetInputs(operators []ops.Operator) { +func (d *Distinct) SetInputs(operators []Operator) { d.Source = operators[0] } -func (d *Distinct) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (d *Distinct) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { d.Source = d.Source.AddPredicate(ctx, expr) return d } @@ -117,7 +114,7 @@ func (d *Distinct) ShortDescription() string { return "Performance" } -func (d *Distinct) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (d *Distinct) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return d.Source.GetOrdering(ctx) } diff --git a/go/vt/vtgate/planbuilder/operators/dml_planning.go b/go/vt/vtgate/planbuilder/operators/dml_planning.go index 8f87a71c95f..6f71b41162e 100644 --- a/go/vt/vtgate/planbuilder/operators/dml_planning.go +++ b/go/vt/vtgate/planbuilder/operators/dml_planning.go @@ -19,26 +19,31 @@ package operators import ( "fmt" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) +type DMLCommon struct { + Ignore sqlparser.Ignore + Target TargetTable + OwnedVindexQuery *sqlparser.Select + Source Operator +} + // getVindexInformation returns the vindex and VindexPlusPredicates for the DML, // If it cannot find a unique vindex match, it returns an error. func getVindexInformation(id semantics.TableSet, table *vindexes.Table) ( *vindexes.ColumnVindex, - []*VindexPlusPredicates, - error) { + []*VindexPlusPredicates) { // Check that we have a primary vindex which is valid if len(table.ColumnVindexes) == 0 || !table.ColumnVindexes[0].IsUnique() { - return nil, nil, vterrors.VT09001(table.Name) + panic(vterrors.VT09001(table.Name)) } primaryVindex := table.ColumnVindexes[0] @@ -55,66 +60,80 @@ func getVindexInformation(id semantics.TableSet, table *vindexes.Table) ( TableID: id, }) } - return primaryVindex, vindexesAndPredicates, nil + return primaryVindex, vindexesAndPredicates +} + +func createAssignmentExpressions( + ctx *plancontext.PlanningContext, + assignments []SetExpr, + vcol sqlparser.IdentifierCI, + subQueriesArgOnChangedVindex []string, + vindexValueMap map[string]evalengine.Expr, + compExprs []sqlparser.Expr, +) ([]string, []sqlparser.Expr) { + // Searching in order of columns in colvindex. + found := false + for _, assignment := range assignments { + if !vcol.Equal(assignment.Name.Name) { + continue + } + if found { + panic(vterrors.VT03015(assignment.Name.Name)) + } + found = true + pv, err := evalengine.Translate(assignment.Expr.EvalExpr, &evalengine.Config{ + ResolveType: ctx.SemTable.TypeForExpr, + Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), + }) + if err != nil { + panic(invalidUpdateExpr(assignment.Name.Name.String(), assignment.Expr.EvalExpr)) + } + + if assignment.Expr.Info != nil { + sqe, ok := assignment.Expr.Info.(SubQueryExpression) + if ok { + for _, sq := range sqe { + subQueriesArgOnChangedVindex = append(subQueriesArgOnChangedVindex, sq.ArgName) + } + } + } + + vindexValueMap[vcol.String()] = pv + compExprs = append(compExprs, sqlparser.NewComparisonExpr(sqlparser.EqualOp, assignment.Name, assignment.Expr.EvalExpr, nil)) + } + return subQueriesArgOnChangedVindex, compExprs } -func buildChangedVindexesValues(ctx *plancontext.PlanningContext, update *sqlparser.Update, table *vindexes.Table, ksidCols []sqlparser.IdentifierCI, assignments []SetExpr) (vv map[string]*engine.VindexValues, ownedVindexQuery string, subQueriesArgOnChangedVindex []string, err error) { +func buildChangedVindexesValues( + ctx *plancontext.PlanningContext, + update *sqlparser.Update, + table *vindexes.Table, + ksidCols []sqlparser.IdentifierCI, + assignments []SetExpr, +) (vv map[string]*engine.VindexValues, ownedVindexQuery *sqlparser.Select, subQueriesArgOnChangedVindex []string) { changedVindexes := make(map[string]*engine.VindexValues) - buf, offset := initialQuery(ksidCols, table) + selExprs, offset := initialQuery(ksidCols, table) for i, vindex := range table.ColumnVindexes { vindexValueMap := make(map[string]evalengine.Expr) - first := true + var compExprs []sqlparser.Expr for _, vcol := range vindex.Columns { - // Searching in order of columns in colvindex. - found := false - for _, assignment := range assignments { - if !vcol.Equal(assignment.Name.Name) { - continue - } - if found { - return nil, "", nil, vterrors.VT03015(assignment.Name.Name) - } - found = true - pv, err := evalengine.Translate(assignment.Expr.EvalExpr, &evalengine.Config{ - ResolveType: ctx.SemTable.TypeForExpr, - Collation: ctx.SemTable.Collation, - }) - if err != nil { - return nil, "", nil, invalidUpdateExpr(assignment.Name.Name.String(), assignment.Expr.EvalExpr) - } - - if assignment.Expr.Info != nil { - sqe, ok := assignment.Expr.Info.(SubQueryExpression) - if ok { - for _, sq := range sqe { - subQueriesArgOnChangedVindex = append(subQueriesArgOnChangedVindex, sq.ArgName) - } - } - } - - vindexValueMap[vcol.String()] = pv - if first { - buf.Myprintf(", %s", assignment.String()) - first = false - } else { - buf.Myprintf(" and %s", assignment.String()) - } - } + subQueriesArgOnChangedVindex, compExprs = + createAssignmentExpressions(ctx, assignments, vcol, subQueriesArgOnChangedVindex, vindexValueMap, compExprs) } if len(vindexValueMap) == 0 { // Vindex not changing, continue continue } - - if update.Limit != nil && len(update.OrderBy) == 0 { - return nil, "", nil, vterrors.VT12001(fmt.Sprintf("you need to provide the ORDER BY clause when using LIMIT; invalid update on vindex: %v", vindex.Name)) - } if i == 0 { - return nil, "", nil, vterrors.VT12001(fmt.Sprintf("you cannot UPDATE primary vindex columns; invalid update on vindex: %v", vindex.Name)) + panic(vterrors.VT12001(fmt.Sprintf("you cannot UPDATE primary vindex columns; invalid update on vindex: %v", vindex.Name))) } if _, ok := vindex.Vindex.(vindexes.Lookup); !ok { - return nil, "", nil, vterrors.VT12001(fmt.Sprintf("you can only UPDATE lookup vindexes; invalid update on vindex: %v", vindex.Name)) + panic(vterrors.VT12001(fmt.Sprintf("you can only UPDATE lookup vindexes; invalid update on vindex: %v", vindex.Name))) } + + // Checks done, let's actually add the expressions and the vindex map + selExprs = append(selExprs, aeWrap(sqlparser.AndExpressions(compExprs...))) changedVindexes[vindex.Name] = &engine.VindexValues{ EvalExprMap: vindexValueMap, Offset: offset, @@ -122,36 +141,32 @@ func buildChangedVindexesValues(ctx *plancontext.PlanningContext, update *sqlpar offset++ } if len(changedVindexes) == 0 { - return nil, "", nil, nil + return nil, nil, nil } // generate rest of the owned vindex query. - aTblExpr, ok := update.TableExprs[0].(*sqlparser.AliasedTableExpr) - if !ok { - return nil, "", nil, vterrors.VT12001("UPDATE on complex table expression") + ovq := &sqlparser.Select{ + SelectExprs: selExprs, + OrderBy: update.OrderBy, + Limit: update.Limit, + Lock: sqlparser.ForUpdateLock, } - tblExpr := &sqlparser.AliasedTableExpr{Expr: sqlparser.TableName{Name: table.Name}, As: aTblExpr.As} - buf.Myprintf(" from %v%v%v%v for update", tblExpr, update.Where, update.OrderBy, update.Limit) - return changedVindexes, buf.String(), subQueriesArgOnChangedVindex, nil + return changedVindexes, ovq, subQueriesArgOnChangedVindex } -func initialQuery(ksidCols []sqlparser.IdentifierCI, table *vindexes.Table) (*sqlparser.TrackedBuffer, int) { - buf := sqlparser.NewTrackedBuffer(nil) +func initialQuery(ksidCols []sqlparser.IdentifierCI, table *vindexes.Table) (sqlparser.SelectExprs, int) { + var selExprs sqlparser.SelectExprs offset := 0 for _, col := range ksidCols { - if offset == 0 { - buf.Myprintf("select %v", col) - } else { - buf.Myprintf(", %v", col) - } + selExprs = append(selExprs, aeWrap(sqlparser.NewColName(col.String()))) offset++ } for _, cv := range table.Owned { for _, column := range cv.Columns { - buf.Myprintf(", %v", column) + selExprs = append(selExprs, aeWrap(sqlparser.NewColName(column.String()))) offset++ } } - return buf, offset + return selExprs, offset } func invalidUpdateExpr(upd string, expr sqlparser.Expr) error { diff --git a/go/vt/vtgate/planbuilder/operators/dml_with_input.go b/go/vt/vtgate/planbuilder/operators/dml_with_input.go new file mode 100644 index 00000000000..e15a1042a47 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/dml_with_input.go @@ -0,0 +1,84 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "fmt" + + "vitess.io/vitess/go/slice" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" +) + +// DMLWithInput is used to represent a DML Operator taking input from a Source Operator +type DMLWithInput struct { + Source Operator + DML Operator + + cols []*sqlparser.ColName + Offsets []int + + noColumns + noPredicates +} + +func (d *DMLWithInput) Clone(inputs []Operator) Operator { + newD := *d + newD.SetInputs(inputs) + return &newD +} + +func (d *DMLWithInput) Inputs() []Operator { + return []Operator{d.Source, d.DML} +} + +func (d *DMLWithInput) SetInputs(inputs []Operator) { + if len(inputs) != 2 { + panic("unexpected number of inputs for DMLWithInput operator") + } + d.Source = inputs[0] + d.DML = inputs[1] +} + +func (d *DMLWithInput) ShortDescription() string { + colStrings := slice.Map(d.cols, func(from *sqlparser.ColName) string { + return sqlparser.String(from) + }) + out := "" + for idx, colString := range colStrings { + out += colString + if len(d.Offsets) > idx { + out += fmt.Sprintf(":%d", d.Offsets[idx]) + } + out += " " + } + return out +} + +func (d *DMLWithInput) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { + return nil +} + +func (d *DMLWithInput) planOffsets(ctx *plancontext.PlanningContext) Operator { + for _, col := range d.cols { + offset := d.Source.AddColumn(ctx, true, false, aeWrap(col)) + d.Offsets = append(d.Offsets, offset) + } + return d +} + +var _ Operator = (*DMLWithInput)(nil) diff --git a/go/vt/vtgate/planbuilder/operators/expressions.go b/go/vt/vtgate/planbuilder/operators/expressions.go index 7ab27e787e8..1b9194c35fa 100644 --- a/go/vt/vtgate/planbuilder/operators/expressions.go +++ b/go/vt/vtgate/planbuilder/operators/expressions.go @@ -22,16 +22,16 @@ import ( "vitess.io/vitess/go/vt/vtgate/semantics" ) -// BreakExpressionInLHSandRHS takes an expression and +// breakExpressionInLHSandRHSForApplyJoin takes an expression and // extracts the parts that are coming from one of the sides into `ColName`s that are needed -func BreakExpressionInLHSandRHS( +func breakExpressionInLHSandRHSForApplyJoin( ctx *plancontext.PlanningContext, expr sqlparser.Expr, lhs semantics.TableSet, -) (col JoinColumn, err error) { +) (col applyJoinColumn) { rewrittenExpr := sqlparser.CopyOnRewrite(expr, nil, func(cursor *sqlparser.CopyOnWriteCursor) { nodeExpr, ok := cursor.Node().(sqlparser.Expr) - if !ok || !fetchByOffset(nodeExpr) { + if !ok || !mustFetchFromInput(nodeExpr) { return } deps := ctx.SemTable.RecursiveDeps(nodeExpr) @@ -51,10 +51,8 @@ func BreakExpressionInLHSandRHS( cursor.Replace(arg) }, nil).(sqlparser.Expr) - if err != nil { - return JoinColumn{}, err - } - ctx.JoinPredicates[expr] = append(ctx.JoinPredicates[expr], rewrittenExpr) + ctx.AddJoinPredicates(expr, rewrittenExpr) col.RHSExpr = rewrittenExpr + col.Original = expr return } diff --git a/go/vt/vtgate/planbuilder/operators/filter.go b/go/vt/vtgate/planbuilder/operators/filter.go index ed43910b75d..c2432a40da9 100644 --- a/go/vt/vtgate/planbuilder/operators/filter.go +++ b/go/vt/vtgate/planbuilder/operators/filter.go @@ -24,14 +24,12 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) type Filter struct { - Source ops.Operator + Source Operator Predicates []sqlparser.Expr // PredicateWithOffsets is the evalengine expression that will finally be used. @@ -41,14 +39,18 @@ type Filter struct { Truncate int } -func newFilter(op ops.Operator, expr sqlparser.Expr) ops.Operator { +func newFilterSinglePredicate(op Operator, expr sqlparser.Expr) Operator { + return newFilter(op, expr) +} + +func newFilter(op Operator, expr ...sqlparser.Expr) Operator { return &Filter{ - Source: op, Predicates: []sqlparser.Expr{expr}, + Source: op, Predicates: expr, } } // Clone implements the Operator interface -func (f *Filter) Clone(inputs []ops.Operator) ops.Operator { +func (f *Filter) Clone(inputs []Operator) Operator { return &Filter{ Source: inputs[0], Predicates: slices.Clone(f.Predicates), @@ -58,12 +60,12 @@ func (f *Filter) Clone(inputs []ops.Operator) ops.Operator { } // Inputs implements the Operator interface -func (f *Filter) Inputs() []ops.Operator { - return []ops.Operator{f.Source} +func (f *Filter) Inputs() []Operator { + return []Operator{f.Source} } // SetInputs implements the Operator interface -func (f *Filter) SetInputs(ops []ops.Operator) { +func (f *Filter) SetInputs(ops []Operator) { f.Source = ops[0] } @@ -80,7 +82,7 @@ func (f *Filter) UnsolvedPredicates(st *semantics.SemTable) []sqlparser.Expr { return result } -func (f *Filter) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (f *Filter) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { f.Source = f.Source.AddPredicate(ctx, expr) return f } @@ -101,28 +103,29 @@ func (f *Filter) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Sele return f.Source.GetSelectExprs(ctx) } -func (f *Filter) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (f *Filter) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return f.Source.GetOrdering(ctx) } -func (f *Filter) Compact(*plancontext.PlanningContext) (ops.Operator, *rewrite.ApplyResult, error) { +func (f *Filter) Compact(*plancontext.PlanningContext) (Operator, *ApplyResult) { if len(f.Predicates) == 0 { - return f.Source, rewrite.NewTree("filter with no predicates removed", f), nil + return f.Source, Rewrote("filter with no predicates removed") } other, isFilter := f.Source.(*Filter) if !isFilter { - return f, rewrite.SameTree, nil + return f, NoRewrite } f.Source = other.Source f.Predicates = append(f.Predicates, other.Predicates...) - return f, rewrite.NewTree("two filters merged into one", f), nil + return f, Rewrote("two filters merged into one") } -func (f *Filter) planOffsets(ctx *plancontext.PlanningContext) { +func (f *Filter) planOffsets(ctx *plancontext.PlanningContext) Operator { cfg := &evalengine.Config{ ResolveType: ctx.SemTable.TypeForExpr, Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), } predicate := sqlparser.AndExpressions(f.Predicates...) @@ -136,6 +139,7 @@ func (f *Filter) planOffsets(ctx *plancontext.PlanningContext) { } f.PredicateWithOffsets = eexpr + return nil } func (f *Filter) ShortDescription() string { diff --git a/go/vt/vtgate/planbuilder/operators/fk_cascade.go b/go/vt/vtgate/planbuilder/operators/fk_cascade.go index 90c797d55e8..f24b59ca5ab 100644 --- a/go/vt/vtgate/planbuilder/operators/fk_cascade.go +++ b/go/vt/vtgate/planbuilder/operators/fk_cascade.go @@ -19,15 +19,16 @@ package operators import ( "slices" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" + "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // FkChild is used to represent a foreign key child table operation type FkChild struct { - BVName string - Cols []int // indexes - Op ops.Operator + BVName string + Cols []int // indexes + NonLiteralInfo []engine.NonLiteralUpdateInfo + Op Operator noColumns noPredicates @@ -37,19 +38,19 @@ type FkChild struct { // as an operator. This operator is created for DML queries that require // cascades (for example, ON DELETE CASCADE). type FkCascade struct { - Selection ops.Operator + Selection Operator Children []*FkChild - Parent ops.Operator + Parent Operator noColumns noPredicates } -var _ ops.Operator = (*FkCascade)(nil) +var _ Operator = (*FkCascade)(nil) // Inputs implements the Operator interface -func (fkc *FkCascade) Inputs() []ops.Operator { - var inputs []ops.Operator +func (fkc *FkCascade) Inputs() []Operator { + var inputs []Operator inputs = append(inputs, fkc.Parent) inputs = append(inputs, fkc.Selection) for _, child := range fkc.Children { @@ -59,7 +60,7 @@ func (fkc *FkCascade) Inputs() []ops.Operator { } // SetInputs implements the Operator interface -func (fkc *FkCascade) SetInputs(operators []ops.Operator) { +func (fkc *FkCascade) SetInputs(operators []Operator) { if len(operators) < 2 { panic("incorrect count of inputs for FkCascade") } @@ -74,7 +75,7 @@ func (fkc *FkCascade) SetInputs(operators []ops.Operator) { } // Clone implements the Operator interface -func (fkc *FkCascade) Clone(inputs []ops.Operator) ops.Operator { +func (fkc *FkCascade) Clone(inputs []Operator) Operator { if len(inputs) < 2 { panic("incorrect count of inputs for FkCascade") } @@ -88,16 +89,17 @@ func (fkc *FkCascade) Clone(inputs []ops.Operator) ops.Operator { } newFkc.Children = append(newFkc.Children, &FkChild{ - BVName: fkc.Children[idx-2].BVName, - Cols: slices.Clone(fkc.Children[idx-2].Cols), - Op: operator, + BVName: fkc.Children[idx-2].BVName, + Cols: slices.Clone(fkc.Children[idx-2].Cols), + NonLiteralInfo: slices.Clone(fkc.Children[idx-2].NonLiteralInfo), + Op: operator, }) } return newFkc } // GetOrdering implements the Operator interface -func (fkc *FkCascade) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (fkc *FkCascade) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } diff --git a/go/vt/vtgate/planbuilder/operators/fk_verify.go b/go/vt/vtgate/planbuilder/operators/fk_verify.go index 39e1092c8d9..8275a8d462f 100644 --- a/go/vt/vtgate/planbuilder/operators/fk_verify.go +++ b/go/vt/vtgate/planbuilder/operators/fk_verify.go @@ -17,14 +17,13 @@ limitations under the License. package operators import ( - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // VerifyOp keeps the information about the foreign key verification operation. // It is a Parent verification or a Child verification. type VerifyOp struct { - Op ops.Operator + Op Operator Typ string } @@ -33,17 +32,17 @@ type VerifyOp struct { // verifications on the existence of the rows in the parent table (for example, INSERT and UPDATE). type FkVerify struct { Verify []*VerifyOp - Input ops.Operator + Input Operator noColumns noPredicates } -var _ ops.Operator = (*FkVerify)(nil) +var _ Operator = (*FkVerify)(nil) // Inputs implements the Operator interface -func (fkv *FkVerify) Inputs() []ops.Operator { - inputs := []ops.Operator{fkv.Input} +func (fkv *FkVerify) Inputs() []Operator { + inputs := []Operator{fkv.Input} for _, v := range fkv.Verify { inputs = append(inputs, v.Op) } @@ -51,7 +50,7 @@ func (fkv *FkVerify) Inputs() []ops.Operator { } // SetInputs implements the Operator interface -func (fkv *FkVerify) SetInputs(operators []ops.Operator) { +func (fkv *FkVerify) SetInputs(operators []Operator) { fkv.Input = operators[0] if len(fkv.Verify) != len(operators)-1 { panic("mismatched number of verify inputs") @@ -62,7 +61,7 @@ func (fkv *FkVerify) SetInputs(operators []ops.Operator) { } // Clone implements the Operator interface -func (fkv *FkVerify) Clone(inputs []ops.Operator) ops.Operator { +func (fkv *FkVerify) Clone(inputs []Operator) Operator { newFkv := &FkVerify{ Verify: fkv.Verify, } @@ -71,7 +70,7 @@ func (fkv *FkVerify) Clone(inputs []ops.Operator) ops.Operator { } // GetOrdering implements the Operator interface -func (fkv *FkVerify) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (fkv *FkVerify) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } diff --git a/go/vt/vtgate/planbuilder/operators/fuzz.go b/go/vt/vtgate/planbuilder/operators/fuzz.go index 6ee6b0bab83..c92810e3ae8 100644 --- a/go/vt/vtgate/planbuilder/operators/fuzz.go +++ b/go/vt/vtgate/planbuilder/operators/fuzz.go @@ -30,7 +30,7 @@ func FuzzAnalyse(data []byte) int { if err != nil { return 0 } - tree, err := sqlparser.Parse(query) + tree, err := sqlparser.NewTestParser().Parse(query) if err != nil { return -1 } diff --git a/go/vt/vtgate/planbuilder/operators/hash_join.go b/go/vt/vtgate/planbuilder/operators/hash_join.go new file mode 100644 index 00000000000..0ad46bcbc82 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/hash_join.go @@ -0,0 +1,452 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "fmt" + "slices" + "strings" + + "vitess.io/vitess/go/slice" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" +) + +type ( + HashJoin struct { + LHS, RHS Operator + + // LeftJoin will be true in the case of an outer join + LeftJoin bool + + // Before offset planning + JoinComparisons []Comparison + + // These columns are the output columns of the hash join. While in operator mode we keep track of complex expression, + // but once we move to the engine primitives, the hash join only passes through column from either left or right. + // anything more complex will be solved by a projection on top of the hash join + columns *hashJoinColumns + + // After offset planning + + // Columns stores the column indexes of the columns coming from the left and right side + // negative value comes from LHS and positive from RHS + ColumnOffsets []int + + // These are the values that will be hashed together + LHSKeys, RHSKeys []int + + offset bool + } + + Comparison struct { + LHS, RHS sqlparser.Expr + } + + hashJoinColumn struct { + side joinSide + expr sqlparser.Expr + } + + joinSide int +) + +const ( + Unknown joinSide = iota + Left + Right +) + +var _ Operator = (*HashJoin)(nil) +var _ JoinOp = (*HashJoin)(nil) + +func NewHashJoin(lhs, rhs Operator, outerJoin bool) *HashJoin { + hj := &HashJoin{ + LHS: lhs, + RHS: rhs, + LeftJoin: outerJoin, + columns: &hashJoinColumns{}, + } + return hj +} + +func (hj *HashJoin) Clone(inputs []Operator) Operator { + kopy := *hj + kopy.LHS, kopy.RHS = inputs[0], inputs[1] + kopy.columns = hj.columns.clone() + kopy.LHSKeys = slices.Clone(hj.LHSKeys) + kopy.RHSKeys = slices.Clone(hj.RHSKeys) + kopy.JoinComparisons = slices.Clone(hj.JoinComparisons) + return &kopy +} + +func (hj *HashJoin) Inputs() []Operator { + return []Operator{hj.LHS, hj.RHS} +} + +func (hj *HashJoin) SetInputs(operators []Operator) { + hj.LHS, hj.RHS = operators[0], operators[1] +} + +func (hj *HashJoin) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + return AddPredicate(ctx, hj, expr, false, newFilterSinglePredicate) +} + +func (hj *HashJoin) AddColumn(ctx *plancontext.PlanningContext, reuseExisting bool, addToGroupBy bool, expr *sqlparser.AliasedExpr) int { + if reuseExisting { + offset := hj.FindCol(ctx, expr.Expr, false) + if offset >= 0 { + return offset + } + } + + hj.columns.add(expr.Expr) + return len(hj.columns.columns) - 1 +} + +func (hj *HashJoin) planOffsets(ctx *plancontext.PlanningContext) Operator { + if hj.offset { + return nil + } + hj.offset = true + for _, cmp := range hj.JoinComparisons { + lOffset := hj.LHS.AddColumn(ctx, true, false, aeWrap(cmp.LHS)) + hj.LHSKeys = append(hj.LHSKeys, lOffset) + rOffset := hj.RHS.AddColumn(ctx, true, false, aeWrap(cmp.RHS)) + hj.RHSKeys = append(hj.RHSKeys, rOffset) + } + + needsProj := false + lID := TableID(hj.LHS) + rID := TableID(hj.RHS) + eexprs := slice.Map(hj.columns.columns, func(in hashJoinColumn) *ProjExpr { + var column *ProjExpr + var pureOffset bool + + switch in.side { + case Unknown: + column, pureOffset = hj.addColumn(ctx, in.expr) + case Left: + column, pureOffset = hj.addSingleSidedColumn(ctx, in.expr, lID, hj.LHS, lhsOffset) + case Right: + column, pureOffset = hj.addSingleSidedColumn(ctx, in.expr, rID, hj.RHS, rhsOffset) + default: + panic("not expected") + } + if !pureOffset { + needsProj = true + } + return column + }) + + if !needsProj { + return nil + } + proj := newAliasedProjection(hj) + proj.addProjExpr(eexprs...) + return proj +} + +func (hj *HashJoin) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, _ bool) int { + for offset, col := range hj.columns.columns { + if ctx.SemTable.EqualsExprWithDeps(expr, col.expr) { + return offset + } + } + return -1 +} + +func (hj *HashJoin) GetColumns(*plancontext.PlanningContext) []*sqlparser.AliasedExpr { + return slice.Map(hj.columns.columns, func(from hashJoinColumn) *sqlparser.AliasedExpr { + return aeWrap(from.expr) + }) +} + +func (hj *HashJoin) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs { + return transformColumnsToSelectExprs(ctx, hj) +} + +func (hj *HashJoin) ShortDescription() string { + comparisons := slice.Map(hj.JoinComparisons, func(from Comparison) string { + return from.String() + }) + cmp := strings.Join(comparisons, " AND ") + + if len(hj.columns.columns) > 0 { + cols := slice.Map(hj.columns.columns, func(from hashJoinColumn) (result string) { + switch from.side { + case Unknown: + result = "U" + case Left: + result = "L" + case Right: + result = "R" + } + result += fmt.Sprintf("(%s)", sqlparser.String(from.expr)) + return + }) + return fmt.Sprintf("%s columns [%v]", cmp, strings.Join(cols, ", ")) + } + + return cmp +} + +func (hj *HashJoin) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { + return nil // hash joins will never promise an output order +} + +func (hj *HashJoin) GetLHS() Operator { + return hj.LHS +} + +func (hj *HashJoin) GetRHS() Operator { + return hj.RHS +} + +func (hj *HashJoin) SetLHS(op Operator) { + hj.LHS = op +} + +func (hj *HashJoin) SetRHS(op Operator) { + hj.RHS = op +} + +func (hj *HashJoin) MakeInner() { + hj.LeftJoin = false +} + +func (hj *HashJoin) IsInner() bool { + return !hj.LeftJoin +} + +func (hj *HashJoin) AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { + cmp, ok := expr.(*sqlparser.ComparisonExpr) + if !ok || !canBeSolvedWithHashJoin(cmp.Operator) { + panic(vterrors.VT12001(fmt.Sprintf("can't use [%s] with hash joins", sqlparser.String(expr)))) + } + lExpr := cmp.Left + lDeps := ctx.SemTable.RecursiveDeps(lExpr) + rExpr := cmp.Right + rDeps := ctx.SemTable.RecursiveDeps(rExpr) + lID := TableID(hj.LHS) + rID := TableID(hj.RHS) + if !lDeps.IsSolvedBy(lID) || !rDeps.IsSolvedBy(rID) { + // we'll switch and see if things work out then + lExpr, rExpr = rExpr, lExpr + lDeps, rDeps = rDeps, lDeps + } + + if !lDeps.IsSolvedBy(lID) || !rDeps.IsSolvedBy(rID) { + panic(vterrors.VT12001(fmt.Sprintf("can't use [%s] with hash joins", sqlparser.String(expr)))) + } + + hj.JoinComparisons = append(hj.JoinComparisons, Comparison{ + LHS: lExpr, + RHS: rExpr, + }) +} + +func canBeSolvedWithHashJoin(op sqlparser.ComparisonExprOperator) bool { + switch op { + case sqlparser.EqualOp, sqlparser.NullSafeEqualOp: + return true + default: + return false + } +} + +func (c Comparison) String() string { + return sqlparser.String(c.LHS) + " = " + sqlparser.String(c.RHS) +} +func lhsOffset(i int) int { return (i * -1) - 1 } +func rhsOffset(i int) int { return i + 1 } +func (hj *HashJoin) addColumn(ctx *plancontext.PlanningContext, in sqlparser.Expr) (*ProjExpr, bool) { + lId, rId := TableID(hj.LHS), TableID(hj.RHS) + r := new(replacer) // this is the expression we will put in instead of whatever we find there + pre := func(node, parent sqlparser.SQLNode) bool { + expr, ok := node.(sqlparser.Expr) + if !ok { + return true + } + deps := ctx.SemTable.RecursiveDeps(expr) + check := func(id semantics.TableSet, op Operator, offsetter func(int) int) int { + if !deps.IsSolvedBy(id) { + return -1 + } + inOffset := op.FindCol(ctx, expr, false) + if inOffset == -1 { + if !mustFetchFromInput(expr) { + return -1 + } + + // aha! this is an expression that we have to get from the input. let's force it in there + inOffset = op.AddColumn(ctx, false, false, aeWrap(expr)) + } + + // we turn the + internalOffset := offsetter(inOffset) + + // ok, we have an offset from the input operator. Let's check if we already have it + // in our list of incoming columns + + for idx, offset := range hj.ColumnOffsets { + if internalOffset == offset { + return idx + } + } + + hj.ColumnOffsets = append(hj.ColumnOffsets, internalOffset) + + return len(hj.ColumnOffsets) - 1 + } + + if lOffset := check(lId, hj.LHS, lhsOffset); lOffset >= 0 { + r.replaceExpr = sqlparser.NewOffset(lOffset, expr) + return false // we want to stop going down the expression tree and start coming back up again + } + + if rOffset := check(rId, hj.RHS, rhsOffset); rOffset >= 0 { + r.replaceExpr = sqlparser.NewOffset(rOffset, expr) + return false + } + + return true + } + + rewrittenExpr := sqlparser.CopyOnRewrite(in, pre, r.post, ctx.SemTable.CopySemanticInfo).(sqlparser.Expr) + cfg := &evalengine.Config{ + ResolveType: ctx.SemTable.TypeForExpr, + Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), + } + eexpr, err := evalengine.Translate(rewrittenExpr, cfg) + if err != nil { + panic(err) + } + + _, isPureOffset := rewrittenExpr.(*sqlparser.Offset) + + return &ProjExpr{ + Original: aeWrap(in), + EvalExpr: rewrittenExpr, + ColExpr: rewrittenExpr, + Info: &EvalEngine{EExpr: eexpr}, + }, isPureOffset +} + +// JoinPredicate produces an AST representation of the join condition this join has +func (hj *HashJoin) JoinPredicate() sqlparser.Expr { + exprs := slice.Map(hj.JoinComparisons, func(from Comparison) sqlparser.Expr { + return &sqlparser.ComparisonExpr{ + Left: from.LHS, + Right: from.RHS, + } + }) + return sqlparser.AndExpressions(exprs...) +} + +type replacer struct { + replaceExpr sqlparser.Expr +} + +func (r *replacer) post(cursor *sqlparser.CopyOnWriteCursor) { + if r.replaceExpr != nil { + node := cursor.Node() + _, ok := node.(sqlparser.Expr) + if !ok { + panic(fmt.Sprintf("can't replace this node with an expression: %s", sqlparser.String(node))) + } + cursor.Replace(r.replaceExpr) + r.replaceExpr = nil + } +} + +func (hj *HashJoin) addSingleSidedColumn( + ctx *plancontext.PlanningContext, + in sqlparser.Expr, + tableID semantics.TableSet, + op Operator, + offsetter func(int) int, +) (*ProjExpr, bool) { + r := new(replacer) + pre := func(node, parent sqlparser.SQLNode) bool { + expr, ok := node.(sqlparser.Expr) + if !ok { + return true + } + deps := ctx.SemTable.RecursiveDeps(expr) + check := func(op Operator) int { + if !deps.IsSolvedBy(tableID) { + return -1 + } + inOffset := op.FindCol(ctx, expr, false) + if inOffset == -1 { + if !mustFetchFromInput(expr) { + return -1 + } + + // aha! this is an expression that we have to get from the input. let's force it in there + inOffset = op.AddColumn(ctx, false, false, aeWrap(expr)) + } + + // we have to turn the incoming offset to an outgoing offset of the columns this operator is exposing + internalOffset := offsetter(inOffset) + + // ok, we have an offset from the input operator. Let's check if we already have it + // in our list of incoming columns + for idx, offset := range hj.ColumnOffsets { + if internalOffset == offset { + return idx + } + } + + hj.ColumnOffsets = append(hj.ColumnOffsets, internalOffset) + + return len(hj.ColumnOffsets) - 1 + } + + if offset := check(op); offset >= 0 { + r.replaceExpr = sqlparser.NewOffset(offset, expr) + return false // we want to stop going down the expression tree and start coming back up again + } + + return true + } + + rewrittenExpr := sqlparser.CopyOnRewrite(in, pre, r.post, ctx.SemTable.CopySemanticInfo).(sqlparser.Expr) + cfg := &evalengine.Config{ + ResolveType: ctx.SemTable.TypeForExpr, + Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), + } + eexpr, err := evalengine.Translate(rewrittenExpr, cfg) + if err != nil { + panic(err) + } + + _, isPureOffset := rewrittenExpr.(*sqlparser.Offset) + + return &ProjExpr{ + Original: aeWrap(in), + EvalExpr: rewrittenExpr, + ColExpr: rewrittenExpr, + Info: &EvalEngine{EExpr: eexpr}, + }, isPureOffset +} diff --git a/go/vt/vtgate/planbuilder/operators/hash_join_test.go b/go/vt/vtgate/planbuilder/operators/hash_join_test.go new file mode 100644 index 00000000000..2bf1d08d2b6 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/hash_join_test.go @@ -0,0 +1,112 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/test/vschemawrapper" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +func TestJoinPredicates(t *testing.T) { + lcol := sqlparser.NewColName("lhs") + rcol := sqlparser.NewColName("rhs") + ctx := &plancontext.PlanningContext{SemTable: semantics.EmptySemTable()} + lid := semantics.SingleTableSet(0) + rid := semantics.SingleTableSet(1) + ctx.SemTable.Recursive[lcol] = lid + ctx.SemTable.Recursive[rcol] = rid + lhs := &fakeOp{id: lid} + rhs := &fakeOp{id: rid} + hj := &HashJoin{ + LHS: lhs, + RHS: rhs, + LeftJoin: false, + columns: &hashJoinColumns{}, + } + + cmp := &sqlparser.ComparisonExpr{ + Operator: sqlparser.EqualOp, + Left: lcol, + Right: rcol, + } + hj.AddJoinPredicate(ctx, cmp) + require.Len(t, hj.JoinComparisons, 1) + hj.planOffsets(ctx) + require.Len(t, hj.LHSKeys, 1) + require.Len(t, hj.RHSKeys, 1) +} + +func TestOffsetPlanning(t *testing.T) { + lcol1, lcol2 := sqlparser.NewColName("lhs1"), sqlparser.NewColName("lhs2") + rcol1, rcol2 := sqlparser.NewColName("rhs1"), sqlparser.NewColName("rhs2") + ctx := &plancontext.PlanningContext{ + SemTable: semantics.EmptySemTable(), + VSchema: &vschemawrapper.VSchemaWrapper{ + V: &vindexes.VSchema{}, + SysVarEnabled: true, + Env: vtenv.NewTestEnv(), + }, + } + lid := semantics.SingleTableSet(0) + rid := semantics.SingleTableSet(1) + ctx.SemTable.Recursive[lcol1] = lid + ctx.SemTable.Recursive[lcol2] = lid + ctx.SemTable.Recursive[rcol1] = rid + ctx.SemTable.Recursive[rcol2] = rid + lhs := &fakeOp{id: lid} + rhs := &fakeOp{id: rid} + + tests := []struct { + expr sqlparser.Expr + expectedColOffsets []int + }{{ + expr: lcol1, + expectedColOffsets: []int{-1}, + }, { + expr: rcol1, + expectedColOffsets: []int{1}, + }, { + expr: sqlparser.AndExpressions(lcol1, lcol2), + expectedColOffsets: []int{-1, -2}, + }, { + expr: sqlparser.AndExpressions(lcol1, rcol1, lcol2, rcol2), + expectedColOffsets: []int{-1, 1, -2, 2}, + }} + + for _, test := range tests { + t.Run(sqlparser.String(test.expr), func(t *testing.T) { + hj := &HashJoin{ + LHS: lhs, + RHS: rhs, + LeftJoin: false, + columns: &hashJoinColumns{}, + } + hj.AddColumn(ctx, true, false, aeWrap(test.expr)) + hj.planOffsets(ctx) + assert.Equal(t, test.expectedColOffsets, hj.ColumnOffsets) + }) + } +} diff --git a/go/vt/vtgate/planbuilder/operators/helpers.go b/go/vt/vtgate/planbuilder/operators/helpers.go index 21be634d7d8..0049a919e2a 100644 --- a/go/vt/vtgate/planbuilder/operators/helpers.go +++ b/go/vt/vtgate/planbuilder/operators/helpers.go @@ -21,46 +21,44 @@ import ( "sort" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) // compact will optimise the operator tree into a smaller but equivalent version -func compact(ctx *plancontext.PlanningContext, op ops.Operator) (ops.Operator, error) { +func compact(ctx *plancontext.PlanningContext, op Operator) Operator { type compactable interface { // Compact implement this interface for operators that have easy to see optimisations - Compact(ctx *plancontext.PlanningContext) (ops.Operator, *rewrite.ApplyResult, error) + Compact(ctx *plancontext.PlanningContext) (Operator, *ApplyResult) } - newOp, err := rewrite.BottomUp(op, TableID, func(op ops.Operator, _ semantics.TableSet, _ bool) (ops.Operator, *rewrite.ApplyResult, error) { + newOp := BottomUp(op, TableID, func(op Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) { newOp, ok := op.(compactable) if !ok { - return op, rewrite.SameTree, nil + return op, NoRewrite } return newOp.Compact(ctx) }, stopAtRoute) - return newOp, err + return newOp } -func checkValid(op ops.Operator) error { +func checkValid(op Operator) { type checkable interface { - CheckValid() error + CheckValid() } - return rewrite.Visit(op, func(this ops.Operator) error { + _ = Visit(op, func(this Operator) error { if chk, ok := this.(checkable); ok { - return chk.CheckValid() + chk.CheckValid() } return nil }) } -func Clone(op ops.Operator) ops.Operator { +func Clone(op Operator) Operator { inputs := op.Inputs() - clones := make([]ops.Operator, len(inputs)) + clones := make([]Operator, len(inputs)) for i, input := range inputs { clones[i] = Clone(input) } @@ -72,8 +70,8 @@ type tableIDIntroducer interface { introducesTableID() semantics.TableSet } -func TableID(op ops.Operator) (result semantics.TableSet) { - _ = rewrite.Visit(op, func(this ops.Operator) error { +func TableID(op Operator) (result semantics.TableSet) { + _ = Visit(op, func(this Operator) error { if tbl, ok := this.(tableIDIntroducer); ok { result = result.Merge(tbl.introducesTableID()) } @@ -87,9 +85,9 @@ type TableUser interface { TablesUsed() []string } -func TablesUsed(op ops.Operator) []string { +func TablesUsed(op Operator) []string { addString, collect := collectSortedUniqueStrings() - _ = rewrite.Visit(op, func(this ops.Operator) error { + _ = Visit(op, func(this Operator) error { if tbl, ok := this.(TableUser); ok { for _, u := range tbl.TablesUsed() { addString(u) @@ -100,29 +98,7 @@ func TablesUsed(op ops.Operator) []string { return collect() } -func UnresolvedPredicates(op ops.Operator, st *semantics.SemTable) (result []sqlparser.Expr) { - type unresolved interface { - // UnsolvedPredicates returns any predicates that have dependencies on the given Operator and - // on the outside of it (a parent Select expression, any other table not used by Operator, etc.). - // This is used for sub-queries. An example query could be: - // SELECT * FROM tbl WHERE EXISTS (SELECT 1 FROM otherTbl WHERE tbl.col = otherTbl.col) - // The subquery would have one unsolved predicate: `tbl.col = otherTbl.col` - // It's a predicate that belongs to the inner query, but it needs data from the outer query - // These predicates dictate which data we have to send from the outer side to the inner - UnsolvedPredicates(semTable *semantics.SemTable) []sqlparser.Expr - } - - _ = rewrite.Visit(op, func(this ops.Operator) error { - if tbl, ok := this.(unresolved); ok { - result = append(result, tbl.UnsolvedPredicates(st)...) - } - - return nil - }) - return -} - -func CostOf(op ops.Operator) (cost int) { +func CostOf(op Operator) (cost int) { type costly interface { // Cost returns the cost for this operator. All the costly operators in the tree are summed together to get the // total cost of the operator tree. @@ -131,7 +107,7 @@ func CostOf(op ops.Operator) (cost int) { Cost() int } - _ = rewrite.Visit(op, func(op ops.Operator) error { + _ = Visit(op, func(op Operator) error { if costlyOp, ok := op.(costly); ok { cost += costlyOp.Cost() } diff --git a/go/vt/vtgate/planbuilder/operators/horizon.go b/go/vt/vtgate/planbuilder/operators/horizon.go index 919767d550f..34f6dc79217 100644 --- a/go/vt/vtgate/planbuilder/operators/horizon.go +++ b/go/vt/vtgate/planbuilder/operators/horizon.go @@ -22,7 +22,6 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -35,7 +34,7 @@ import ( // Project/Aggregate/Sort/Limit operations, some which can be pushed down, // and some that have to be evaluated at the vtgate level. type Horizon struct { - Source ops.Operator + Source Operator // If this is a derived table, the two following fields will contain the tableID and name of it TableId *semantics.TableSet @@ -52,12 +51,12 @@ type Horizon struct { ColumnsOffset []int } -func newHorizon(src ops.Operator, query sqlparser.SelectStatement) *Horizon { +func newHorizon(src Operator, query sqlparser.SelectStatement) *Horizon { return &Horizon{Source: src, Query: query} } // Clone implements the Operator interface -func (h *Horizon) Clone(inputs []ops.Operator) ops.Operator { +func (h *Horizon) Clone(inputs []Operator) Operator { klone := *h klone.Source = inputs[0] klone.ColumnAliases = sqlparser.CloneColumns(h.ColumnAliases) @@ -77,16 +76,16 @@ func (h *Horizon) IsMergeable(ctx *plancontext.PlanningContext) bool { } // Inputs implements the Operator interface -func (h *Horizon) Inputs() []ops.Operator { - return []ops.Operator{h.Source} +func (h *Horizon) Inputs() []Operator { + return []Operator{h.Source} } // SetInputs implements the Operator interface -func (h *Horizon) SetInputs(ops []ops.Operator) { +func (h *Horizon) SetInputs(ops []Operator) { h.Source = ops[0] } -func (h *Horizon) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (h *Horizon) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { if _, isUNion := h.Source.(*Union); isUNion { // If we have a derived table on top of a UNION, we can let the UNION do the expression rewriting h.Source = h.Source.AddPredicate(ctx, expr) @@ -95,17 +94,14 @@ func (h *Horizon) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser. tableInfo, err := ctx.SemTable.TableInfoForExpr(expr) if err != nil { if errors.Is(err, semantics.ErrNotSingleTable) { - return &Filter{ - Source: h, - Predicates: []sqlparser.Expr{expr}, - } + return newFilter(h, expr) } panic(err) } newExpr := semantics.RewriteDerivedTableExpression(expr, tableInfo) if sqlparser.ContainsAggregation(newExpr) { - return &Filter{Source: h, Predicates: []sqlparser.Expr{expr}} + return newFilter(h, expr) } h.Source = h.Source.AddPredicate(ctx, newExpr) return h @@ -145,7 +141,13 @@ func canReuseColumn[T any]( return } -func (h *Horizon) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, _ bool) int { +func (h *Horizon) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int { + if underRoute && h.IsDerived() { + // We don't want to use columns on this operator if it's a derived table under a route. + // In this case, we need to add a Projection on top of this operator to make the column available + return -1 + } + for idx, se := range sqlparser.GetFirstSelect(h.Query).SelectExprs { ae, ok := se.(*sqlparser.AliasedExpr) if !ok { @@ -175,12 +177,9 @@ func (h *Horizon) GetSelectExprs(*plancontext.PlanningContext) sqlparser.SelectE return sqlparser.GetFirstSelect(h.Query).SelectExprs } -func (h *Horizon) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (h *Horizon) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { if h.QP == nil { - _, err := h.getQP(ctx) - if err != nil { - panic(err) - } + h.getQP(ctx) } return h.QP.OrderExprs } @@ -190,20 +189,15 @@ func (h *Horizon) selectStatement() sqlparser.SelectStatement { return h.Query } -func (h *Horizon) src() ops.Operator { +func (h *Horizon) src() Operator { return h.Source } -func (h *Horizon) getQP(ctx *plancontext.PlanningContext) (*QueryProjection, error) { - if h.QP != nil { - return h.QP, nil - } - qp, err := CreateQPFromSelectStatement(ctx, h.Query) - if err != nil { - return nil, err +func (h *Horizon) getQP(ctx *plancontext.PlanningContext) *QueryProjection { + if h.QP == nil { + h.QP = CreateQPFromSelectStatement(ctx, h.Query) } - h.QP = qp - return h.QP, nil + return h.QP } func (h *Horizon) ShortDescription() string { diff --git a/go/vt/vtgate/planbuilder/operators/horizon_expanding.go b/go/vt/vtgate/planbuilder/operators/horizon_expanding.go index 7ec141a1b8b..68880bef90b 100644 --- a/go/vt/vtgate/planbuilder/operators/horizon_expanding.go +++ b/go/vt/vtgate/planbuilder/operators/horizon_expanding.go @@ -23,12 +23,10 @@ import ( "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) -func expandHorizon(ctx *plancontext.PlanningContext, horizon *Horizon) (ops.Operator, *rewrite.ApplyResult, error) { +func expandHorizon(ctx *plancontext.PlanningContext, horizon *Horizon) (Operator, *ApplyResult) { statement := horizon.selectStatement() switch sel := statement.(type) { case *sqlparser.Select: @@ -36,16 +34,13 @@ func expandHorizon(ctx *plancontext.PlanningContext, horizon *Horizon) (ops.Oper case *sqlparser.Union: return expandUnionHorizon(ctx, horizon, sel) } - return nil, nil, vterrors.VT13001(fmt.Sprintf("unexpected statement type %T", statement)) + panic(vterrors.VT13001(fmt.Sprintf("unexpected statement type %T", statement))) } -func expandUnionHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, union *sqlparser.Union) (ops.Operator, *rewrite.ApplyResult, error) { +func expandUnionHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, union *sqlparser.Union) (Operator, *ApplyResult) { op := horizon.Source - qp, err := horizon.getQP(ctx) - if err != nil { - return nil, nil, err - } + qp := horizon.getQP(ctx) if len(qp.OrderExprs) > 0 { op = &Ordering{ @@ -72,20 +67,15 @@ func expandUnionHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, unio } if op == horizon.Source { - return op, rewrite.NewTree("removed UNION horizon not used", op), nil + return op, Rewrote("removed UNION horizon not used") } - return op, rewrite.NewTree("expand UNION horizon into smaller components", op), nil + return op, Rewrote("expand UNION horizon into smaller components") } -func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel *sqlparser.Select) (ops.Operator, *rewrite.ApplyResult, error) { +func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel *sqlparser.Select) (Operator, *ApplyResult) { op := createProjectionFromSelect(ctx, horizon) - - qp, err := horizon.getQP(ctx) - if err != nil { - return nil, nil, err - } - + qp := horizon.getQP(ctx) var extracted []string if qp.HasAggr { extracted = append(extracted, "Aggregation") @@ -103,18 +93,12 @@ func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel } if sel.Having != nil { - op, err = addWherePredicates(ctx, sel.Having.Expr, op) - if err != nil { - return nil, nil, err - } + op = addWherePredicates(ctx, sel.Having.Expr, op) extracted = append(extracted, "Filter") } if len(qp.OrderExprs) > 0 { - op = &Ordering{ - Source: op, - Order: qp.OrderExprs, - } + op = expandOrderBy(ctx, op, qp) extracted = append(extracted, "Ordering") } @@ -126,15 +110,46 @@ func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel extracted = append(extracted, "Limit") } - return op, rewrite.NewTree(fmt.Sprintf("expand SELECT horizon into (%s)", strings.Join(extracted, ", ")), op), nil + return op, Rewrote(fmt.Sprintf("expand SELECT horizon into (%s)", strings.Join(extracted, ", "))) } -func createProjectionFromSelect(ctx *plancontext.PlanningContext, horizon *Horizon) (out ops.Operator) { - qp, err := horizon.getQP(ctx) - if err != nil { - panic(err) +func expandOrderBy(ctx *plancontext.PlanningContext, op Operator, qp *QueryProjection) Operator { + proj := newAliasedProjection(op) + var newOrder []OrderBy + sqc := &SubQueryBuilder{} + for _, expr := range qp.OrderExprs { + newExpr, subqs := sqc.pullOutValueSubqueries(ctx, expr.SimplifiedExpr, TableID(op), false) + if newExpr == nil { + // no subqueries found, let's move on + newOrder = append(newOrder, expr) + continue + } + proj.addSubqueryExpr(aeWrap(newExpr), newExpr, subqs...) + newOrder = append(newOrder, OrderBy{ + Inner: &sqlparser.Order{ + Expr: newExpr, + Direction: expr.Inner.Direction, + }, + SimplifiedExpr: newExpr, + }) + } + if len(proj.Columns.GetColumns()) > 0 { + // if we had to project columns for the ordering, + // we need the projection as source + op = proj + } + + return &Ordering{ + Source: op, + Order: newOrder, + } +} + +func createProjectionFromSelect(ctx *plancontext.PlanningContext, horizon *Horizon) Operator { + qp := horizon.getQP(ctx) + var dt *DerivedTable if horizon.TableId != nil { dt = &DerivedTable{ @@ -147,18 +162,16 @@ func createProjectionFromSelect(ctx *plancontext.PlanningContext, horizon *Horiz if !qp.NeedsAggregation() { projX := createProjectionWithoutAggr(ctx, qp, horizon.src()) projX.DT = dt - out = projX - - return out + return projX } - aggregations, complexAggr, err := qp.AggregationExpressions(ctx, true) - if err != nil { - panic(err) - } + return createProjectionWithAggr(ctx, qp, dt, horizon.src()) +} - a := &Aggregator{ - Source: horizon.src(), +func createProjectionWithAggr(ctx *plancontext.PlanningContext, qp *QueryProjection, dt *DerivedTable, src Operator) Operator { + aggregations, complexAggr := qp.AggregationExpressions(ctx, true) + aggrOp := &Aggregator{ + Source: src, Original: true, QP: qp, Grouping: qp.GetGrouping(), @@ -166,13 +179,26 @@ func createProjectionFromSelect(ctx *plancontext.PlanningContext, horizon *Horiz DT: dt, } + // Go through all aggregations and check for any subquery. + sqc := &SubQueryBuilder{} + outerID := TableID(src) + for idx, aggr := range aggregations { + expr := aggr.Original.Expr + newExpr, subqs := sqc.pullOutValueSubqueries(ctx, expr, outerID, false) + if newExpr != nil { + aggregations[idx].SubQueryExpression = subqs + } + } + aggrOp.Source = sqc.getRootOperator(src, nil) + + // create the projection columns from aggregator. if complexAggr { - return createProjectionForComplexAggregation(a, qp) + return createProjectionForComplexAggregation(aggrOp, qp) } - return createProjectionForSimpleAggregation(ctx, a, qp) + return createProjectionForSimpleAggregation(ctx, aggrOp, qp) } -func createProjectionForSimpleAggregation(ctx *plancontext.PlanningContext, a *Aggregator, qp *QueryProjection) ops.Operator { +func createProjectionForSimpleAggregation(ctx *plancontext.PlanningContext, a *Aggregator, qp *QueryProjection) Operator { outer: for colIdx, expr := range qp.SelectExprs { ae, err := expr.GetAliasedExpr() @@ -181,7 +207,7 @@ outer: } addedToCol := false for idx, groupBy := range a.Grouping { - if ctx.SemTable.EqualsExprWithDeps(groupBy.SimplifiedExpr, ae.Expr) { + if ctx.SemTable.EqualsExprWithDeps(groupBy.Inner, ae.Expr) { if !addedToCol { a.Columns = append(a.Columns, ae) addedToCol = true @@ -206,7 +232,7 @@ outer: return a } -func createProjectionForComplexAggregation(a *Aggregator, qp *QueryProjection) ops.Operator { +func createProjectionForComplexAggregation(a *Aggregator, qp *QueryProjection) Operator { p := newAliasedProjection(a) p.DT = a.DT for _, expr := range qp.SelectExprs { @@ -215,14 +241,11 @@ func createProjectionForComplexAggregation(a *Aggregator, qp *QueryProjection) o panic(err) } - _, err = p.addProjExpr(newProjExpr(ae)) - if err != nil { - panic(err) - } + p.addProjExpr(newProjExpr(ae)) } for i, by := range a.Grouping { a.Grouping[i].ColOffset = len(a.Columns) - a.Columns = append(a.Columns, aeWrap(by.SimplifiedExpr)) + a.Columns = append(a.Columns, aeWrap(by.Inner)) } for i, aggregation := range a.Aggregations { a.Aggregations[i].ColOffset = len(a.Columns) @@ -231,7 +254,7 @@ func createProjectionForComplexAggregation(a *Aggregator, qp *QueryProjection) o return p } -func createProjectionWithoutAggr(ctx *plancontext.PlanningContext, qp *QueryProjection, src ops.Operator) *Projection { +func createProjectionWithoutAggr(ctx *plancontext.PlanningContext, qp *QueryProjection, src Operator) *Projection { // first we need to check if we have all columns or there are still unexpanded stars aes, err := slice.MapWithError(qp.SelectExprs, func(from SelectExpr) (*sqlparser.AliasedExpr, error) { ae, ok := from.Col.(*sqlparser.AliasedExpr) @@ -250,43 +273,31 @@ func createProjectionWithoutAggr(ctx *plancontext.PlanningContext, qp *QueryProj sqc := &SubQueryBuilder{} outerID := TableID(src) for _, ae := range aes { - org := sqlparser.CloneRefOfAliasedExpr(ae) + org := ctx.SemTable.Clone(ae).(*sqlparser.AliasedExpr) expr := ae.Expr - newExpr, subqs, err := sqc.pullOutValueSubqueries(ctx, expr, outerID, false) - if err != nil { - panic(err) - } + newExpr, subqs := sqc.pullOutValueSubqueries(ctx, expr, outerID, false) if newExpr == nil { // there was no subquery in this expression - _, err := proj.addUnexploredExpr(org, expr) - if err != nil { - panic(err) - } + proj.addUnexploredExpr(org, expr) } else { - err := proj.addSubqueryExpr(org, newExpr, subqs...) - if err != nil { - panic(err) - } + proj.addSubqueryExpr(org, newExpr, subqs...) } } proj.Source = sqc.getRootOperator(src, nil) return proj } -func newStarProjection(src ops.Operator, qp *QueryProjection) *Projection { +func newStarProjection(src Operator, qp *QueryProjection) *Projection { cols := sqlparser.SelectExprs{} for _, expr := range qp.SelectExprs { - err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { _, isSubQ := node.(*sqlparser.Subquery) if !isSubQ { return true, nil } - return false, vterrors.VT09015() + panic(vterrors.VT09015()) }, expr.Col) - if err != nil { - panic(err) - } cols = append(cols, expr.Col) } diff --git a/go/vt/vtgate/planbuilder/operators/info_schema_planning.go b/go/vt/vtgate/planbuilder/operators/info_schema_planning.go index 4f096e1ac65..f8dc9b9d281 100644 --- a/go/vt/vtgate/planbuilder/operators/info_schema_planning.go +++ b/go/vt/vtgate/planbuilder/operators/info_schema_planning.go @@ -23,7 +23,6 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" @@ -41,15 +40,16 @@ type InfoSchemaRouting struct { Table *QueryTable } -func (isr *InfoSchemaRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (isr *InfoSchemaRouting) UpdateRoutingParams(ctx *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.SysTableTableSchema = nil for _, expr := range isr.SysTableTableSchema { eexpr, err := evalengine.Translate(expr, &evalengine.Config{ Collation: collations.SystemCollation.Collation, ResolveColumn: NotImplementedSchemaInfoResolver, + Environment: ctx.VSchema.Environment(), }) if err != nil { - return err + panic(err) } rp.SysTableTableSchema = append(rp.SysTableTableSchema, eexpr) } @@ -59,14 +59,14 @@ func (isr *InfoSchemaRouting) UpdateRoutingParams(_ *plancontext.PlanningContext eexpr, err := evalengine.Translate(expr, &evalengine.Config{ Collation: collations.SystemCollation.Collation, ResolveColumn: NotImplementedSchemaInfoResolver, + Environment: ctx.VSchema.Environment(), }) if err != nil { - return err + panic(err) } rp.SysTableTableName[k] = eexpr } - return nil } func (isr *InfoSchemaRouting) Clone() Routing { @@ -77,10 +77,10 @@ func (isr *InfoSchemaRouting) Clone() Routing { } } -func (isr *InfoSchemaRouting) updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (Routing, error) { +func (isr *InfoSchemaRouting) updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Routing { isTableSchema, bvName, out := extractInfoSchemaRoutingPredicate(ctx, expr) if out == nil { - return isr, nil + return isr } if isr.SysTableTableName == nil { @@ -92,14 +92,14 @@ func (isr *InfoSchemaRouting) updateRoutingLogic(ctx *plancontext.PlanningContex if sqlparser.Equals.Expr(out, s) { // we already have this expression in the list // stating it again does not add value - return isr, nil + return isr } } isr.SysTableTableSchema = append(isr.SysTableTableSchema, out) } else { isr.SysTableTableName[bvName] = out } - return isr, nil + return isr } func (isr *InfoSchemaRouting) Cost() int { @@ -122,7 +122,7 @@ func extractInfoSchemaRoutingPredicate(ctx *plancontext.PlanningContext, in sqlp return false, "", nil } - isSchemaName, col := isTableOrSchemaRoutable(cmp) + isSchemaName, col := isTableOrSchemaRoutable(cmp, ctx.VSchema.Environment().MySQLVersion()) rhs := cmp.Right if col == nil || !shouldRewrite(rhs) { return false, "", nil @@ -133,6 +133,7 @@ func extractInfoSchemaRoutingPredicate(ctx *plancontext.PlanningContext, in sqlp _, err := evalengine.Translate(rhs, &evalengine.Config{ Collation: collations.SystemCollation.Collation, ResolveColumn: NotImplementedSchemaInfoResolver, + Environment: ctx.VSchema.Environment(), }) if err != nil { // if we can't translate this to an evalengine expression, @@ -153,14 +154,14 @@ func extractInfoSchemaRoutingPredicate(ctx *plancontext.PlanningContext, in sqlp // isTableOrSchemaRoutable searches for a comparison where one side is a table or schema name column. // if it finds the correct column name being used, // it also makes sure that the LHS of the comparison contains the column, and the RHS the value sought after -func isTableOrSchemaRoutable(cmp *sqlparser.ComparisonExpr) ( +func isTableOrSchemaRoutable(cmp *sqlparser.ComparisonExpr, version string) ( isSchema bool, // tells if we are dealing with a table or a schema name comparator col *sqlparser.ColName, // which is the colName we are comparing against ) { - if col, schema, table := IsTableSchemaOrName(cmp.Left); schema || table { + if col, schema, table := IsTableSchemaOrName(cmp.Left, version); schema || table { return schema, col } - if col, schema, table := IsTableSchemaOrName(cmp.Right); schema || table { + if col, schema, table := IsTableSchemaOrName(cmp.Right, version); schema || table { // to make the rest of the code easier, we shuffle these around so the ColName is always on the LHS cmp.Right, cmp.Left = cmp.Left, cmp.Right return schema, col @@ -286,16 +287,15 @@ func shouldRewrite(e sqlparser.Expr) bool { return true } -func IsTableSchemaOrName(e sqlparser.Expr) (col *sqlparser.ColName, isTableSchema bool, isTableName bool) { +func IsTableSchemaOrName(e sqlparser.Expr, version string) (col *sqlparser.ColName, isTableSchema bool, isTableName bool) { col, ok := e.(*sqlparser.ColName) if !ok { return nil, false, false } - return col, isDbNameCol(col), isTableNameCol(col) + return col, isDbNameCol(col, version), isTableNameCol(col) } -func isDbNameCol(col *sqlparser.ColName) bool { - version := servenv.MySQLServerVersion() +func isDbNameCol(col *sqlparser.ColName, version string) bool { var schemaColumns map[string]any if strings.HasPrefix(version, "5.7") { schemaColumns = schemaColumns57 diff --git a/go/vt/vtgate/planbuilder/operators/insert.go b/go/vt/vtgate/planbuilder/operators/insert.go index a48e53c18b1..9d0048d9322 100644 --- a/go/vt/vtgate/planbuilder/operators/insert.go +++ b/go/vt/vtgate/planbuilder/operators/insert.go @@ -24,7 +24,6 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -79,13 +78,13 @@ func (i *Insert) ShortDescription() string { return i.VTable.String() } -func (i *Insert) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (i *Insert) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } -var _ ops.Operator = (*Insert)(nil) +var _ Operator = (*Insert)(nil) -func (i *Insert) Clone([]ops.Operator) ops.Operator { +func (i *Insert) Clone([]Operator) Operator { return &Insert{ VTable: i.VTable, AST: i.AST, @@ -105,52 +104,258 @@ func (i *Insert) Statement() sqlparser.Statement { return i.AST } -func createOperatorFromInsert(ctx *plancontext.PlanningContext, ins *sqlparser.Insert) (ops.Operator, error) { - tableInfo, qt, err := createQueryTableForDML(ctx, ins.Table, nil) - if err != nil { - return nil, err +func createOperatorFromInsert(ctx *plancontext.PlanningContext, ins *sqlparser.Insert) Operator { + tableInfo, qt := createQueryTableForDML(ctx, ins.Table, nil) + + vTbl, routing := buildVindexTableForDML(ctx, tableInfo, qt, "insert") + + deleteBeforeInsert := false + if ins.Action == sqlparser.ReplaceAct && + (ctx.SemTable.ForeignKeysPresent() || vTbl.Keyspace.Sharded) && + (len(vTbl.PrimaryKey) > 0 || len(vTbl.UniqueKeys) > 0) { + // this needs a delete before insert as there can be row clash which needs to be deleted first. + ins.Action = sqlparser.InsertAct + deleteBeforeInsert = true } - vindexTable, routing, err := buildVindexTableForDML(ctx, tableInfo, qt, "insert") - if err != nil { - return nil, err + insOp := checkAndCreateInsertOperator(ctx, ins, vTbl, routing) + + if !deleteBeforeInsert { + return insOp } - insOp, err := createInsertOperator(ctx, ins, vindexTable, routing) - if err != nil { - return nil, err + rows, isRows := ins.Rows.(sqlparser.Values) + if !isRows { + panic(vterrors.VT12001("REPLACE INTO using select statement")) } - if ins.Comments != nil { - insOp = &LockAndComment{ - Source: insOp, - Comments: ins.Comments, - } + pkCompExpr := pkCompExpression(vTbl, ins, rows) + uniqKeyCompExprs := uniqKeyCompExpressions(vTbl, ins, rows) + whereExpr := getWhereCondExpr(append(uniqKeyCompExprs, pkCompExpr)) + + delStmt := &sqlparser.Delete{ + Comments: ins.Comments, + TableExprs: sqlparser.TableExprs{sqlparser.CloneRefOfAliasedTableExpr(ins.Table)}, + Where: sqlparser.NewWhere(sqlparser.WhereClause, whereExpr), } + delOp := createOpFromStmt(ctx, delStmt, false, "") + return &Sequential{Sources: []Operator{delOp, insOp}} +} + +func checkAndCreateInsertOperator(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, vTbl *vindexes.Table, routing Routing) Operator { + insOp := createInsertOperator(ctx, ins, vTbl, routing) // Find the foreign key mode and for unmanaged foreign-key-mode, we don't need to do anything. - ksMode, err := ctx.VSchema.ForeignKeyMode(vindexTable.Keyspace.Name) + ksMode, err := ctx.VSchema.ForeignKeyMode(vTbl.Keyspace.Name) if err != nil { - return nil, err + panic(err) } if ksMode != vschemapb.Keyspace_managed { - return insOp, nil + return insOp } parentFKs := ctx.SemTable.GetParentForeignKeysList() childFks := ctx.SemTable.GetChildForeignKeysList() - if len(childFks) == 0 && len(parentFKs) == 0 { - return insOp, nil - } if len(parentFKs) > 0 { - return nil, vterrors.VT12002() + panic(vterrors.VT12002()) + } + if len(childFks) > 0 { + if ins.Action == sqlparser.ReplaceAct { + panic(vterrors.VT12001("REPLACE INTO with foreign keys")) + } + if len(ins.OnDup) > 0 { + rows := getRowsOrError(ins) + return createUpsertOperator(ctx, ins, insOp, rows, vTbl) + } + } + return insOp +} + +func getRowsOrError(ins *sqlparser.Insert) sqlparser.Values { + if rows, ok := ins.Rows.(sqlparser.Values); ok { + return rows + } + panic(vterrors.VT12001("ON DUPLICATE KEY UPDATE with foreign keys with select statement")) +} + +func getWhereCondExpr(compExprs []*sqlparser.ComparisonExpr) sqlparser.Expr { + var outputExpr sqlparser.Expr + for _, expr := range compExprs { + if expr == nil { + continue + } + if outputExpr == nil { + outputExpr = expr + continue + } + outputExpr = &sqlparser.OrExpr{ + Left: outputExpr, + Right: expr, + } + } + return outputExpr +} + +func pkCompExpression(vTbl *vindexes.Table, ins *sqlparser.Insert, rows sqlparser.Values) *sqlparser.ComparisonExpr { + if len(vTbl.PrimaryKey) == 0 { + return nil + } + pIndexes, pColTuple := findPKIndexes(vTbl, ins) + + var pValTuple sqlparser.ValTuple + for _, row := range rows { + var rowTuple sqlparser.ValTuple + for _, pIdx := range pIndexes { + if pIdx.idx == -1 { + rowTuple = append(rowTuple, pIdx.def) + } else { + rowTuple = append(rowTuple, row[pIdx.idx]) + } + } + pValTuple = append(pValTuple, rowTuple) + } + return sqlparser.NewComparisonExpr(sqlparser.InOp, pColTuple, pValTuple, nil) +} + +type pComp struct { + idx int + def sqlparser.Expr + col sqlparser.IdentifierCI +} + +func findPKIndexes(vTbl *vindexes.Table, ins *sqlparser.Insert) (pIndexes []pComp, pColTuple sqlparser.ValTuple) { + for _, pCol := range vTbl.PrimaryKey { + var def sqlparser.Expr + idx := ins.Columns.FindColumn(pCol) + if idx == -1 { + def = findDefault(vTbl, pCol) + if def == nil { + // If default value is empty, nothing to compare as it will always be false. + return nil, nil + } + } + pIndexes = append(pIndexes, pComp{idx, def, pCol}) + pColTuple = append(pColTuple, sqlparser.NewColName(pCol.String())) + } + return +} + +func findDefault(vTbl *vindexes.Table, pCol sqlparser.IdentifierCI) sqlparser.Expr { + for _, column := range vTbl.Columns { + if column.Name.Equal(pCol) { + return column.Default + } } - return nil, vterrors.VT12001("ON DUPLICATE KEY UPDATE with foreign keys") + panic(vterrors.VT03014(pCol.String(), vTbl.Name.String())) } -func createInsertOperator(ctx *plancontext.PlanningContext, insStmt *sqlparser.Insert, vTbl *vindexes.Table, routing Routing) (ops.Operator, error) { +type uComp struct { + idx int + def sqlparser.Expr +} + +func uniqKeyCompExpressions(vTbl *vindexes.Table, ins *sqlparser.Insert, rows sqlparser.Values) (comps []*sqlparser.ComparisonExpr) { + noOfUniqKeys := len(vTbl.UniqueKeys) + if noOfUniqKeys == 0 { + return nil + } + + type uIdx struct { + Indexes [][]uComp + uniqKey sqlparser.Exprs + } + + allIndexes := make([]uIdx, 0, noOfUniqKeys) + allColTuples := make([]sqlparser.ValTuple, 0, noOfUniqKeys) + for _, uniqKey := range vTbl.UniqueKeys { + var uIndexes [][]uComp + var uColTuple sqlparser.ValTuple + skipKey := false + for _, expr := range uniqKey { + var offsets []uComp + offsets, skipKey = createUniqueKeyComp(ins, expr, vTbl) + if skipKey { + break + } + uIndexes = append(uIndexes, offsets) + uColTuple = append(uColTuple, expr) + } + if skipKey { + continue + } + allIndexes = append(allIndexes, uIdx{uIndexes, uniqKey}) + allColTuples = append(allColTuples, uColTuple) + } + + allValTuples := make([]sqlparser.ValTuple, len(allColTuples)) + for _, row := range rows { + for i, uk := range allIndexes { + var rowTuple sqlparser.ValTuple + for j, offsets := range uk.Indexes { + colIdx := 0 + valExpr := sqlparser.CopyOnRewrite(uk.uniqKey[j], nil, func(cursor *sqlparser.CopyOnWriteCursor) { + _, isCol := cursor.Node().(*sqlparser.ColName) + if !isCol { + return + } + if offsets[colIdx].idx == -1 { + cursor.Replace(offsets[colIdx].def) + } else { + cursor.Replace(row[offsets[colIdx].idx]) + } + colIdx++ + }, nil).(sqlparser.Expr) + rowTuple = append(rowTuple, valExpr) + } + allValTuples[i] = append(allValTuples[i], rowTuple) + } + } + + compExprs := make([]*sqlparser.ComparisonExpr, 0, noOfUniqKeys) + for i, valTuple := range allValTuples { + compExprs = append(compExprs, sqlparser.NewComparisonExpr(sqlparser.InOp, allColTuples[i], valTuple, nil)) + } + return compExprs +} + +func createUniqueKeyComp(ins *sqlparser.Insert, expr sqlparser.Expr, vTbl *vindexes.Table) ([]uComp, bool) { + col, isCol := expr.(*sqlparser.ColName) + if isCol { + var def sqlparser.Expr + idx := ins.Columns.FindColumn(col.Name) + if idx == -1 { + def = findDefault(vTbl, col.Name) + if def == nil { + // default value is empty, nothing to compare as it will always be false. + return nil, true + } + } + return []uComp{{idx, def}}, false + } + var offsets []uComp + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { + col, ok := node.(*sqlparser.ColName) + if !ok { + return true, nil + } + var def sqlparser.Expr + idx := ins.Columns.FindColumn(col.Name) + if idx == -1 { + def = findDefault(vTbl, col.Name) + // no default, replace it with null value. + if def == nil { + def = &sqlparser.NullVal{} + } + } + offsets = append(offsets, uComp{idx, def}) + return false, nil + }, expr) + return offsets, false +} + +func createInsertOperator(ctx *plancontext.PlanningContext, insStmt *sqlparser.Insert, vTbl *vindexes.Table, routing Routing) (op Operator) { if _, target := routing.(*TargetedRouting); target { - return nil, vterrors.VT09017("INSERT with a target destination is not allowed") + panic(vterrors.VT09017("INSERT with a target destination is not allowed")) } insOp := &Insert{ @@ -169,15 +374,12 @@ func createInsertOperator(ctx *plancontext.PlanningContext, insStmt *sqlparser.I if vTbl.ColumnListAuthoritative { insStmt = populateInsertColumnlist(insStmt, vTbl) } else { - return nil, vterrors.VT09004() + panic(vterrors.VT09004()) } } // modify column list or values for autoincrement column. - autoIncGen, err := modifyForAutoinc(ctx, insStmt, vTbl) - if err != nil { - return nil, err - } + autoIncGen := modifyForAutoinc(ctx, insStmt, vTbl) insOp.AutoIncrement = autoIncGen // set insert ignore. @@ -186,24 +388,34 @@ func createInsertOperator(ctx *plancontext.PlanningContext, insStmt *sqlparser.I insOp.ColVindexes = getColVindexes(insOp) switch rows := insStmt.Rows.(type) { case sqlparser.Values: - route.Source, err = insertRowsPlan(ctx, insOp, insStmt, rows) - if err != nil { - return nil, err - } + op = route + route.Source = insertRowsPlan(ctx, insOp, insStmt, rows) case sqlparser.SelectStatement: - return insertSelectPlan(ctx, insOp, route, insStmt, rows) + op = insertSelectPlan(ctx, insOp, route, insStmt, rows) + } + if insStmt.Comments != nil { + op = &LockAndComment{ + Source: op, + Comments: insStmt.Comments, + } } - return route, nil + return op } -func insertSelectPlan(ctx *plancontext.PlanningContext, insOp *Insert, routeOp *Route, ins *sqlparser.Insert, sel sqlparser.SelectStatement) (*InsertSelection, error) { +func insertSelectPlan( + ctx *plancontext.PlanningContext, + insOp *Insert, + routeOp *Route, + ins *sqlparser.Insert, + sel sqlparser.SelectStatement, +) *InsertSelection { if columnMismatch(insOp.AutoIncrement, ins, sel) { - return nil, vterrors.VT03006() + panic(vterrors.VT03006()) } selOp, err := PlanQuery(ctx, sel) if err != nil { - return nil, err + panic(err) } // output of the select plan will be used to insert rows into the table. @@ -228,28 +440,24 @@ func insertSelectPlan(ctx *plancontext.PlanningContext, insOp *Insert, routeOp * } if len(insOp.ColVindexes) == 0 { - return insertSelect, nil + return insertSelect } colVindexes := insOp.ColVindexes vv := make([][]int, len(colVindexes)) for idx, colVindex := range colVindexes { for _, col := range colVindex.Columns { - err := checkAndErrIfVindexChanging(sqlparser.UpdateExprs(ins.OnDup), col) - if err != nil { - return nil, err - } - + checkAndErrIfVindexChanging(sqlparser.UpdateExprs(ins.OnDup), col) colNum := findColumn(ins, col) // sharding column values should be provided in the insert. if colNum == -1 && idx == 0 { - return nil, vterrors.VT09003(col) + panic(vterrors.VT09003(col)) } vv[idx] = append(vv[idx], colNum) } } insOp.VindexValueOffset = vv - return insertSelect, nil + return insertSelect } func columnMismatch(gen *Generate, ins *sqlparser.Insert, sel sqlparser.SelectStatement) bool { @@ -277,15 +485,15 @@ func columnMismatch(gen *Generate, ins *sqlparser.Insert, sel sqlparser.SelectSt return false } -func insertRowsPlan(ctx *plancontext.PlanningContext, insOp *Insert, ins *sqlparser.Insert, rows sqlparser.Values) (*Insert, error) { +func insertRowsPlan(ctx *plancontext.PlanningContext, insOp *Insert, ins *sqlparser.Insert, rows sqlparser.Values) *Insert { for _, row := range rows { if len(ins.Columns) != len(row) { - return nil, vterrors.VT03006() + panic(vterrors.VT03006()) } } if len(insOp.ColVindexes) == 0 { - return insOp, nil + return insOp } colVindexes := insOp.ColVindexes @@ -293,19 +501,17 @@ func insertRowsPlan(ctx *plancontext.PlanningContext, insOp *Insert, ins *sqlpar for vIdx, colVindex := range colVindexes { routeValues[vIdx] = make([][]evalengine.Expr, len(colVindex.Columns)) for colIdx, col := range colVindex.Columns { - err := checkAndErrIfVindexChanging(sqlparser.UpdateExprs(ins.OnDup), col) - if err != nil { - return nil, err - } + checkAndErrIfVindexChanging(sqlparser.UpdateExprs(ins.OnDup), col) routeValues[vIdx][colIdx] = make([]evalengine.Expr, len(rows)) colNum, _ := findOrAddColumn(ins, col) for rowNum, row := range rows { innerpv, err := evalengine.Translate(row[colNum], &evalengine.Config{ ResolveType: ctx.SemTable.TypeForExpr, Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), }) if err != nil { - return nil, err + panic(err) } routeValues[vIdx][colIdx][rowNum] = innerpv } @@ -322,7 +528,7 @@ func insertRowsPlan(ctx *plancontext.PlanningContext, insOp *Insert, ins *sqlpar } } insOp.VindexValues = routeValues - return insOp, nil + return insOp } func valuesProvided(rows sqlparser.InsertRows) bool { @@ -350,18 +556,17 @@ func getColVindexes(insOp *Insert) (colVindexes []*vindexes.ColumnVindex) { return } -func checkAndErrIfVindexChanging(setClauses sqlparser.UpdateExprs, col sqlparser.IdentifierCI) error { +func checkAndErrIfVindexChanging(setClauses sqlparser.UpdateExprs, col sqlparser.IdentifierCI) { for _, assignment := range setClauses { if col.Equal(assignment.Name.Name) { valueExpr, isValuesFuncExpr := assignment.Expr.(*sqlparser.ValuesFuncExpr) // update on duplicate key is changing the vindex column, not supported. if !isValuesFuncExpr || !valueExpr.Name.Name.Equal(assignment.Name.Name) { - return vterrors.VT12001("DML cannot update vindex column") + panic(vterrors.VT12001("DML cannot update vindex column")) } - return nil + return } } - return nil } // findOrAddColumn finds the position of a column in the insert. If it's @@ -404,9 +609,9 @@ func populateInsertColumnlist(ins *sqlparser.Insert, table *vindexes.Table) *sql // modifyForAutoinc modifies the AST and the plan to generate necessary autoinc values. // For row values cases, bind variable names are generated using baseName. -func modifyForAutoinc(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, vTable *vindexes.Table) (*Generate, error) { +func modifyForAutoinc(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, vTable *vindexes.Table) *Generate { if vTable.AutoIncrement == nil { - return nil, nil + return nil } gen := &Generate{ Keyspace: vTable.AutoIncrement.Sequence.Keyspace, @@ -420,6 +625,9 @@ func modifyForAutoinc(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, v case sqlparser.Values: autoIncValues := make(sqlparser.ValTuple, 0, len(rows)) for rowNum, row := range rows { + if len(ins.Columns) != len(row) { + panic(vterrors.VT03006()) + } // Support the DEFAULT keyword by treating it as null if _, ok := row[colNum].(*sqlparser.Default); ok { row[colNum] = &sqlparser.NullVal{} @@ -431,10 +639,11 @@ func modifyForAutoinc(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, v gen.Values, err = evalengine.Translate(autoIncValues, &evalengine.Config{ ResolveType: ctx.SemTable.TypeForExpr, Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), }) if err != nil { - return nil, err + panic(err) } } - return gen, nil + return gen } diff --git a/go/vt/vtgate/planbuilder/operators/insert_selection.go b/go/vt/vtgate/planbuilder/operators/insert_selection.go index 5ae49ee2c55..70bda0a990a 100644 --- a/go/vt/vtgate/planbuilder/operators/insert_selection.go +++ b/go/vt/vtgate/planbuilder/operators/insert_selection.go @@ -17,15 +17,14 @@ limitations under the License. package operators import ( - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // InsertSelection operator represents an INSERT into SELECT FROM query. // It holds the operators for running the selection and insertion. type InsertSelection struct { - Select ops.Operator - Insert ops.Operator + Select Operator + Insert Operator // ForceNonStreaming when true, select first then insert, this is to avoid locking rows by select for insert. ForceNonStreaming bool @@ -34,7 +33,7 @@ type InsertSelection struct { noPredicates } -func (is *InsertSelection) Clone(inputs []ops.Operator) ops.Operator { +func (is *InsertSelection) Clone(inputs []Operator) Operator { return &InsertSelection{ Select: inputs[0], Insert: inputs[1], @@ -42,11 +41,11 @@ func (is *InsertSelection) Clone(inputs []ops.Operator) ops.Operator { } } -func (is *InsertSelection) Inputs() []ops.Operator { - return []ops.Operator{is.Select, is.Insert} +func (is *InsertSelection) Inputs() []Operator { + return []Operator{is.Select, is.Insert} } -func (is *InsertSelection) SetInputs(inputs []ops.Operator) { +func (is *InsertSelection) SetInputs(inputs []Operator) { is.Select = inputs[0] is.Insert = inputs[1] } @@ -58,8 +57,8 @@ func (is *InsertSelection) ShortDescription() string { return "" } -func (is *InsertSelection) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (is *InsertSelection) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } -var _ ops.Operator = (*InsertSelection)(nil) +var _ Operator = (*InsertSelection)(nil) diff --git a/go/vt/vtgate/planbuilder/operators/join.go b/go/vt/vtgate/planbuilder/operators/join.go index 828b15f5b79..787d7fedfcc 100644 --- a/go/vt/vtgate/planbuilder/operators/join.go +++ b/go/vt/vtgate/planbuilder/operators/join.go @@ -19,24 +19,22 @@ package operators import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // Join represents a join. If we have a predicate, this is an inner join. If no predicate exists, it is a cross join type Join struct { - LHS, RHS ops.Operator + LHS, RHS Operator Predicate sqlparser.Expr LeftJoin bool noColumns } -var _ ops.Operator = (*Join)(nil) +var _ Operator = (*Join)(nil) // Clone implements the Operator interface -func (j *Join) Clone(inputs []ops.Operator) ops.Operator { +func (j *Join) Clone(inputs []Operator) Operator { clone := *j clone.LHS = inputs[0] clone.RHS = inputs[1] @@ -48,30 +46,30 @@ func (j *Join) Clone(inputs []ops.Operator) ops.Operator { } } -func (j *Join) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (j *Join) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } // Inputs implements the Operator interface -func (j *Join) Inputs() []ops.Operator { - return []ops.Operator{j.LHS, j.RHS} +func (j *Join) Inputs() []Operator { + return []Operator{j.LHS, j.RHS} } // SetInputs implements the Operator interface -func (j *Join) SetInputs(ops []ops.Operator) { +func (j *Join) SetInputs(ops []Operator) { j.LHS, j.RHS = ops[0], ops[1] } -func (j *Join) Compact(ctx *plancontext.PlanningContext) (ops.Operator, *rewrite.ApplyResult, error) { +func (j *Join) Compact(ctx *plancontext.PlanningContext) (Operator, *ApplyResult) { if j.LeftJoin { // we can't merge outer joins into a single QG - return j, rewrite.SameTree, nil + return j, NoRewrite } lqg, lok := j.LHS.(*QueryGraph) rqg, rok := j.RHS.(*QueryGraph) if !lok || !rok { - return j, rewrite.SameTree, nil + return j, NoRewrite } newOp := &QueryGraph{ @@ -82,23 +80,23 @@ func (j *Join) Compact(ctx *plancontext.PlanningContext) (ops.Operator, *rewrite if j.Predicate != nil { newOp.collectPredicate(ctx, j.Predicate) } - return newOp, rewrite.NewTree("merge querygraphs into a single one", newOp), nil + return newOp, Rewrote("merge querygraphs into a single one") } -func createOuterJoin(tableExpr *sqlparser.JoinTableExpr, lhs, rhs ops.Operator) (ops.Operator, error) { +func createOuterJoin(tableExpr *sqlparser.JoinTableExpr, lhs, rhs Operator) Operator { if tableExpr.Join == sqlparser.RightJoinType { lhs, rhs = rhs, lhs } subq, _ := getSubQuery(tableExpr.Condition.On) if subq != nil { - return nil, vterrors.VT12001("subquery in outer join predicate") + panic(vterrors.VT12001("subquery in outer join predicate")) } predicate := tableExpr.Condition.On - sqlparser.RemoveKeyspaceFromColName(predicate) - return &Join{LHS: lhs, RHS: rhs, LeftJoin: true, Predicate: predicate}, nil + sqlparser.RemoveKeyspaceInCol(predicate) + return &Join{LHS: lhs, RHS: rhs, LeftJoin: true, Predicate: predicate} } -func createJoin(ctx *plancontext.PlanningContext, LHS, RHS ops.Operator) ops.Operator { +func createJoin(ctx *plancontext.PlanningContext, LHS, RHS Operator) Operator { lqg, lok := LHS.(*QueryGraph) rqg, rok := RHS.(*QueryGraph) if lok && rok { @@ -112,45 +110,42 @@ func createJoin(ctx *plancontext.PlanningContext, LHS, RHS ops.Operator) ops.Ope return &Join{LHS: LHS, RHS: RHS} } -func createInnerJoin(ctx *plancontext.PlanningContext, tableExpr *sqlparser.JoinTableExpr, lhs, rhs ops.Operator) (ops.Operator, error) { +func createInnerJoin(ctx *plancontext.PlanningContext, tableExpr *sqlparser.JoinTableExpr, lhs, rhs Operator) Operator { op := createJoin(ctx, lhs, rhs) sqc := &SubQueryBuilder{} outerID := TableID(op) joinPredicate := tableExpr.Condition.On - sqlparser.RemoveKeyspaceFromColName(joinPredicate) + sqlparser.RemoveKeyspaceInCol(joinPredicate) exprs := sqlparser.SplitAndExpression(nil, joinPredicate) for _, pred := range exprs { - subq, err := sqc.handleSubquery(ctx, pred, outerID) - if err != nil { - return nil, err - } + subq := sqc.handleSubquery(ctx, pred, outerID) if subq != nil { continue } op = op.AddPredicate(ctx, pred) } - return sqc.getRootOperator(op, nil), nil + return sqc.getRootOperator(op, nil) } -func (j *Join) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { - return AddPredicate(ctx, j, expr, false, newFilter) +func (j *Join) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + return AddPredicate(ctx, j, expr, false, newFilterSinglePredicate) } var _ JoinOp = (*Join)(nil) -func (j *Join) GetLHS() ops.Operator { +func (j *Join) GetLHS() Operator { return j.LHS } -func (j *Join) GetRHS() ops.Operator { +func (j *Join) GetRHS() Operator { return j.RHS } -func (j *Join) SetLHS(operator ops.Operator) { +func (j *Join) SetLHS(operator Operator) { j.LHS = operator } -func (j *Join) SetRHS(operator ops.Operator) { +func (j *Join) SetRHS(operator Operator) { j.RHS = operator } @@ -162,9 +157,8 @@ func (j *Join) IsInner() bool { return !j.LeftJoin } -func (j *Join) AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error { +func (j *Join) AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { j.Predicate = ctx.SemTable.AndExpressions(j.Predicate, expr) - return nil } func (j *Join) ShortDescription() string { diff --git a/go/vt/vtgate/planbuilder/operators/join_merging.go b/go/vt/vtgate/planbuilder/operators/join_merging.go index 52c9c4e5837..0cc5da9121f 100644 --- a/go/vt/vtgate/planbuilder/operators/join_merging.go +++ b/go/vt/vtgate/planbuilder/operators/join_merging.go @@ -21,14 +21,13 @@ import ( "reflect" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // mergeJoinInputs checks whether two operators can be merged into a single one. // If they can be merged, a new operator with the merged routing is returned // If they cannot be merged, nil is returned. -func mergeJoinInputs(ctx *plancontext.PlanningContext, lhs, rhs ops.Operator, joinPredicates []sqlparser.Expr, m merger) *Route { +func mergeJoinInputs(ctx *plancontext.PlanningContext, lhs, rhs Operator, joinPredicates []sqlparser.Expr, m merger) *Route { lhsRoute, rhsRoute, routingA, routingB, a, b, sameKeyspace := prepareInputRoutes(lhs, rhs) if lhsRoute == nil { return nil @@ -66,7 +65,7 @@ func mergeJoinInputs(ctx *plancontext.PlanningContext, lhs, rhs ops.Operator, jo } } -func prepareInputRoutes(lhs ops.Operator, rhs ops.Operator) (*Route, *Route, Routing, Routing, routingType, routingType, bool) { +func prepareInputRoutes(lhs Operator, rhs Operator) (*Route, *Route, Routing, Routing, routingType, routingType, bool) { lhsRoute, rhsRoute := operatorsToRoutes(lhs, rhs) if lhsRoute == nil || rhsRoute == nil { return nil, nil, nil, nil, 0, 0, false @@ -204,7 +203,7 @@ func mergeShardedRouting(r1 *ShardedRouting, r2 *ShardedRouting) *ShardedRouting } func (jm *joinMerger) getApplyJoin(ctx *plancontext.PlanningContext, op1, op2 *Route) *ApplyJoin { - return NewApplyJoin(op1.Source, op2.Source, ctx.SemTable.AndExpressions(jm.predicates...), !jm.innerJoin) + return NewApplyJoin(ctx, op1.Source, op2.Source, ctx.SemTable.AndExpressions(jm.predicates...), !jm.innerJoin) } func (jm *joinMerger) merge(ctx *plancontext.PlanningContext, op1, op2 *Route, r Routing) *Route { diff --git a/go/vt/vtgate/planbuilder/operators/joins.go b/go/vt/vtgate/planbuilder/operators/joins.go index 3b5c31c5dce..266b9b8288f 100644 --- a/go/vt/vtgate/planbuilder/operators/joins.go +++ b/go/vt/vtgate/planbuilder/operators/joins.go @@ -18,20 +18,19 @@ package operators import ( "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) type JoinOp interface { - ops.Operator - GetLHS() ops.Operator - GetRHS() ops.Operator - SetLHS(ops.Operator) - SetRHS(ops.Operator) + Operator + GetLHS() Operator + GetRHS() Operator + SetLHS(Operator) + SetRHS(Operator) MakeInner() IsInner() bool - AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error + AddJoinPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) } func AddPredicate( @@ -39,8 +38,8 @@ func AddPredicate( join JoinOp, expr sqlparser.Expr, joinPredicates bool, - newFilter func(ops.Operator, sqlparser.Expr) ops.Operator, -) ops.Operator { + newFilter func(Operator, sqlparser.Expr) Operator, +) Operator { deps := ctx.SemTable.RecursiveDeps(expr) switch { case deps.IsSolvedBy(TableID(join.GetLHS())): @@ -79,10 +78,7 @@ func AddPredicate( return newFilter(join, expr) } - err := join.AddJoinPredicate(ctx, expr) - if err != nil { - panic(err) - } + join.AddJoinPredicate(ctx, expr) return join } diff --git a/go/vt/vtgate/planbuilder/operators/limit.go b/go/vt/vtgate/planbuilder/operators/limit.go index a6ea925b135..152872ff8ff 100644 --- a/go/vt/vtgate/planbuilder/operators/limit.go +++ b/go/vt/vtgate/planbuilder/operators/limit.go @@ -17,13 +17,14 @@ limitations under the License. package operators import ( + "strconv" + "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type Limit struct { - Source ops.Operator + Source Operator AST *sqlparser.Limit // Pushed marks whether the limit has been pushed down to the inputs but still need to keep the operator around. @@ -32,22 +33,23 @@ type Limit struct { Pushed bool } -func (l *Limit) Clone(inputs []ops.Operator) ops.Operator { +func (l *Limit) Clone(inputs []Operator) Operator { return &Limit{ Source: inputs[0], AST: sqlparser.CloneRefOfLimit(l.AST), + Pushed: l.Pushed, } } -func (l *Limit) Inputs() []ops.Operator { - return []ops.Operator{l.Source} +func (l *Limit) Inputs() []Operator { + return []Operator{l.Source} } -func (l *Limit) SetInputs(operators []ops.Operator) { +func (l *Limit) SetInputs(operators []Operator) { l.Source = operators[0] } -func (l *Limit) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (l *Limit) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { l.Source = l.Source.AddPredicate(ctx, expr) return l } @@ -68,10 +70,10 @@ func (l *Limit) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Selec return l.Source.GetSelectExprs(ctx) } -func (l *Limit) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (l *Limit) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return l.Source.GetOrdering(ctx) } func (l *Limit) ShortDescription() string { - return sqlparser.String(l.AST) + return sqlparser.String(l.AST) + " Pushed:" + strconv.FormatBool(l.Pushed) } diff --git a/go/vt/vtgate/planbuilder/operators/misc_routing.go b/go/vt/vtgate/planbuilder/operators/misc_routing.go index 81301f975b4..575aa7b4e9a 100644 --- a/go/vt/vtgate/planbuilder/operators/misc_routing.go +++ b/go/vt/vtgate/planbuilder/operators/misc_routing.go @@ -64,10 +64,9 @@ var ( _ Routing = (*SequenceRouting)(nil) ) -func (tr *TargetedRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (tr *TargetedRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.Keyspace = tr.keyspace rp.TargetDestination = tr.TargetDestination - return nil } func (tr *TargetedRouting) Clone() Routing { @@ -75,8 +74,8 @@ func (tr *TargetedRouting) Clone() Routing { return &newTr } -func (tr *TargetedRouting) updateRoutingLogic(_ *plancontext.PlanningContext, _ sqlparser.Expr) (Routing, error) { - return tr, nil +func (tr *TargetedRouting) updateRoutingLogic(_ *plancontext.PlanningContext, _ sqlparser.Expr) Routing { + return tr } func (tr *TargetedRouting) Cost() int { @@ -91,17 +90,16 @@ func (tr *TargetedRouting) Keyspace() *vindexes.Keyspace { return tr.keyspace } -func (n *NoneRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (n *NoneRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.Keyspace = n.keyspace - return nil } func (n *NoneRouting) Clone() Routing { return n } -func (n *NoneRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) (Routing, error) { - return n, nil +func (n *NoneRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) Routing { + return n } func (n *NoneRouting) Cost() int { @@ -116,9 +114,8 @@ func (n *NoneRouting) Keyspace() *vindexes.Keyspace { return n.keyspace } -func (rr *AnyShardRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (rr *AnyShardRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.Keyspace = rr.keyspace - return nil } func (rr *AnyShardRouting) Clone() Routing { @@ -128,8 +125,8 @@ func (rr *AnyShardRouting) Clone() Routing { } } -func (rr *AnyShardRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) (Routing, error) { - return rr, nil +func (rr *AnyShardRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) Routing { + return rr } func (rr *AnyShardRouting) Cost() int { @@ -159,16 +156,14 @@ func (rr *AnyShardRouting) AlternateInKeyspace(keyspace *vindexes.Keyspace) *Rou return nil } -func (dr *DualRouting) UpdateRoutingParams(*plancontext.PlanningContext, *engine.RoutingParameters) error { - return nil -} +func (dr *DualRouting) UpdateRoutingParams(*plancontext.PlanningContext, *engine.RoutingParameters) {} func (dr *DualRouting) Clone() Routing { return &DualRouting{} } -func (dr *DualRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) (Routing, error) { - return dr, nil +func (dr *DualRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) Routing { + return dr } func (dr *DualRouting) Cost() int { @@ -183,18 +178,17 @@ func (dr *DualRouting) Keyspace() *vindexes.Keyspace { return nil } -func (sr *SequenceRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (sr *SequenceRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.Opcode = engine.Next rp.Keyspace = sr.keyspace - return nil } func (sr *SequenceRouting) Clone() Routing { return &SequenceRouting{keyspace: sr.keyspace} } -func (sr *SequenceRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) (Routing, error) { - return sr, nil +func (sr *SequenceRouting) updateRoutingLogic(*plancontext.PlanningContext, sqlparser.Expr) Routing { + return sr } func (sr *SequenceRouting) Cost() int { diff --git a/go/vt/vtgate/planbuilder/operators/offset_planning.go b/go/vt/vtgate/planbuilder/operators/offset_planning.go index 7e7be49874a..638d3d80907 100644 --- a/go/vt/vtgate/planbuilder/operators/offset_planning.go +++ b/go/vt/vtgate/planbuilder/operators/offset_planning.go @@ -21,36 +21,42 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) // planOffsets will walk the tree top down, adding offset information to columns in the tree for use in further optimization, -func planOffsets(ctx *plancontext.PlanningContext, root ops.Operator) (ops.Operator, error) { +func planOffsets(ctx *plancontext.PlanningContext, root Operator) Operator { type offsettable interface { - planOffsets(ctx *plancontext.PlanningContext) + Operator + planOffsets(ctx *plancontext.PlanningContext) Operator } - visitor := func(in ops.Operator, _ semantics.TableSet, _ bool) (ops.Operator, *rewrite.ApplyResult, error) { - var err error + visitor := func(in Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) { switch op := in.(type) { case *Horizon: - return nil, nil, vterrors.VT13001(fmt.Sprintf("should not see %T here", in)) + panic(vterrors.VT13001(fmt.Sprintf("should not see %T here", in))) case offsettable: - op.planOffsets(ctx) - } - if err != nil { - return nil, nil, err + newOp := op.planOffsets(ctx) + + if newOp == nil { + newOp = op + } + + if DebugOperatorTree { + fmt.Println("Planned offsets for:") + fmt.Println(ToTree(newOp)) + } + return newOp, nil } - return in, rewrite.SameTree, nil + return in, NoRewrite } - return rewrite.TopDown(root, TableID, visitor, stopAtRoute) + return TopDown(root, TableID, visitor, stopAtRoute) } -func fetchByOffset(e sqlparser.SQLNode) bool { +// mustFetchFromInput returns true for expressions that have to be fetched from the input and cannot be evaluated +func mustFetchFromInput(e sqlparser.SQLNode) bool { switch e.(type) { case *sqlparser.ColName, sqlparser.AggrFunc: return true @@ -60,17 +66,16 @@ func fetchByOffset(e sqlparser.SQLNode) bool { } // useOffsets rewrites an expression to use values from the input -func useOffsets(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op ops.Operator) sqlparser.Expr { +func useOffsets(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op Operator) sqlparser.Expr { var exprOffset *sqlparser.Offset in := op.Inputs()[0] found := func(e sqlparser.Expr, offset int) { exprOffset = sqlparser.NewOffset(offset, e) } - notFound := func(e sqlparser.Expr) error { + notFound := func(e sqlparser.Expr) { _, addToGroupBy := e.(*sqlparser.ColName) offset := in.AddColumn(ctx, true, addToGroupBy, aeWrap(e)) exprOffset = sqlparser.NewOffset(offset, e) - return nil } visitor := getOffsetRewritingVisitor(ctx, in.FindCol, found, notFound) @@ -90,25 +95,24 @@ func useOffsets(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op ops.Op // addColumnsToInput adds columns needed by an operator to its input. // This happens only when the filter expression can be retrieved as an offset from the underlying mysql. -func addColumnsToInput(ctx *plancontext.PlanningContext, root ops.Operator) (ops.Operator, error) { - visitor := func(in ops.Operator, _ semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +func addColumnsToInput(ctx *plancontext.PlanningContext, root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { filter, ok := in.(*Filter) if !ok { - return in, rewrite.SameTree, nil + return in, NoRewrite } proj, areOnTopOfProj := filter.Source.(selectExpressions) if !areOnTopOfProj { // not much we can do here - return in, rewrite.SameTree, nil + return in, NoRewrite } addedColumns := false found := func(expr sqlparser.Expr, i int) {} - notFound := func(e sqlparser.Expr) error { + notFound := func(e sqlparser.Expr) { _, addToGroupBy := e.(*sqlparser.ColName) proj.addColumnWithoutPushing(ctx, aeWrap(e), addToGroupBy) addedColumns = true - return nil } visitor := getOffsetRewritingVisitor(ctx, proj.FindCol, found, notFound) @@ -116,22 +120,22 @@ func addColumnsToInput(ctx *plancontext.PlanningContext, root ops.Operator) (ops _ = sqlparser.CopyOnRewrite(expr, visitor, nil, ctx.SemTable.CopySemanticInfo) } if addedColumns { - return in, rewrite.NewTree("added columns because filter needs it", in), nil + return in, Rewrote("added columns because filter needs it") } - return in, rewrite.SameTree, nil + return in, NoRewrite } - return rewrite.TopDown(root, TableID, visitor, stopAtRoute) + return TopDown(root, TableID, visitor, stopAtRoute) } // addColumnsToInput adds columns needed by an operator to its input. // This happens only when the filter expression can be retrieved as an offset from the underlying mysql. -func pullDistinctFromUNION(_ *plancontext.PlanningContext, root ops.Operator) (ops.Operator, error) { - visitor := func(in ops.Operator, _ semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +func pullDistinctFromUNION(_ *plancontext.PlanningContext, root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { union, ok := in.(*Union) if !ok || !union.distinct { - return in, rewrite.SameTree, nil + return in, NoRewrite } union.distinct = false @@ -140,10 +144,10 @@ func pullDistinctFromUNION(_ *plancontext.PlanningContext, root ops.Operator) (o Required: true, Source: union, } - return distinct, rewrite.NewTree("pulled out DISTINCT from union", union), nil + return distinct, Rewrote("pulled out DISTINCT from union") } - return rewrite.TopDown(root, TableID, visitor, stopAtRoute) + return TopDown(root, TableID, visitor, stopAtRoute) } func getOffsetRewritingVisitor( @@ -153,13 +157,9 @@ func getOffsetRewritingVisitor( // this function will be called when an expression has been found on the input found func(sqlparser.Expr, int), // if we have an expression that mush be fetched, this method will be called - notFound func(sqlparser.Expr) error, + notFound func(sqlparser.Expr), ) func(node, parent sqlparser.SQLNode) bool { - var err error return func(node, parent sqlparser.SQLNode) bool { - if err != nil { - return false - } e, ok := node.(sqlparser.Expr) if !ok { return true @@ -170,8 +170,8 @@ func getOffsetRewritingVisitor( return false } - if fetchByOffset(e) { - err = notFound(e) + if mustFetchFromInput(e) { + notFound(e) return false } diff --git a/go/vt/vtgate/planbuilder/operators/operator.go b/go/vt/vtgate/planbuilder/operators/operator.go index 4e58fca9214..d639643dda1 100644 --- a/go/vt/vtgate/planbuilder/operators/operator.go +++ b/go/vt/vtgate/planbuilder/operators/operator.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Vitess Authors. +Copyright 2022 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ The operators go through a few phases while planning: All the post-processing - aggregations, sorting, limit etc. are at this stage contained in Horizon structs. We try to push these down under routes, and expand the ones that can't be pushed down into individual operators such as Projection, - Agreggation, Limit, etc. + Aggregation, Limit, etc. 2. Planning Once the initial plan has been fully built, we go through a number of phases. recursively running rewriters on the tree in a fixed point fashion, until we've gone @@ -36,138 +36,61 @@ The operators go through a few phases while planning: package operators import ( - "fmt" - - "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type ( - // helper type that implements Inputs() returning nil - noInputs struct{} + // Operator forms the tree of operators, representing the declarative query provided. + // The operator tree is no actually runnable, it's an intermediate representation used + // while query planning + // The mental model are operators that pull data from each other, the root being the + // full query output, and the leaves are most often `Route`s, representing communication + // with one or more shards. We want to push down as much work as possible under these Routes + Operator interface { + // Clone will return a copy of this operator, protected so changed to the original will not impact the clone + Clone(inputs []Operator) Operator - // helper type that implements AddColumn() returning an error - noColumns struct{} + // Inputs returns the inputs for this operator + Inputs() []Operator - // helper type that implements AddPredicate() returning an error - noPredicates struct{} -) + // SetInputs changes the inputs for this op + SetInputs([]Operator) -// PlanQuery creates a query plan for a given SQL statement -func PlanQuery(ctx *plancontext.PlanningContext, stmt sqlparser.Statement) (result ops.Operator, err error) { - defer PanicHandler(&err) + // AddPredicate is used to push predicates. It pushed it as far down as is possible in the tree. + // If we encounter a join and the predicate depends on both sides of the join, the predicate will be split into two parts, + // where data is fetched from the LHS of the join to be used in the evaluation on the RHS + // TODO: we should remove this and replace it with rewriters + AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator - op, err := translateQueryToOp(ctx, stmt) - if err != nil { - return nil, err - } + AddColumn(ctx *plancontext.PlanningContext, reuseExisting bool, addToGroupBy bool, expr *sqlparser.AliasedExpr) int - if rewrite.DebugOperatorTree { - fmt.Println("Initial tree:") - fmt.Println(ops.ToTree(op)) - } + FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int - if op, err = compact(ctx, op); err != nil { - return nil, err - } + GetColumns(ctx *plancontext.PlanningContext) []*sqlparser.AliasedExpr + GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs - if err = checkValid(op); err != nil { - return nil, err - } - - if op, err = planQuery(ctx, op); err != nil { - return nil, err - } + ShortDescription() string - _, isRoute := op.(*Route) - if !isRoute && ctx.SemTable.NotSingleRouteErr != nil { - // If we got here, we don't have a single shard plan - return nil, ctx.SemTable.NotSingleRouteErr + GetOrdering(ctx *plancontext.PlanningContext) []OrderBy } - return op, err -} + // OrderBy contains the expression to used in order by and also if ordering is needed at VTGate level then what the weight_string function expression to be sent down for evaluation. + OrderBy struct { + Inner *sqlparser.Order -func PanicHandler(err *error) { - if r := recover(); r != nil { - badness, ok := r.(error) - if !ok { - panic(r) - } - - *err = badness - } -} - -// Inputs implements the Operator interface -func (noInputs) Inputs() []ops.Operator { - return nil -} - -// SetInputs implements the Operator interface -func (noInputs) SetInputs(ops []ops.Operator) { - if len(ops) > 0 { - panic("the noInputs operator does not have inputs") - } -} - -// AddColumn implements the Operator interface -func (noColumns) AddColumn(*plancontext.PlanningContext, bool, bool, *sqlparser.AliasedExpr) int { - panic(vterrors.VT13001("noColumns operators have no column")) -} - -func (noColumns) GetColumns(*plancontext.PlanningContext) []*sqlparser.AliasedExpr { - panic(vterrors.VT13001("noColumns operators have no column")) -} - -func (noColumns) FindCol(*plancontext.PlanningContext, sqlparser.Expr, bool) int { - panic(vterrors.VT13001("noColumns operators have no column")) -} - -func (noColumns) GetSelectExprs(*plancontext.PlanningContext) sqlparser.SelectExprs { - panic(vterrors.VT13001("noColumns operators have no column")) -} - -// AddPredicate implements the Operator interface -func (noPredicates) AddPredicate(*plancontext.PlanningContext, sqlparser.Expr) ops.Operator { - panic(vterrors.VT13001("the noColumns operator cannot accept predicates")) -} - -// tryTruncateColumnsAt will see if we can truncate the columns by just asking the operator to do it for us -func tryTruncateColumnsAt(op ops.Operator, truncateAt int) bool { - type columnTruncator interface { - setTruncateColumnCount(offset int) - } - - truncator, ok := op.(columnTruncator) - if ok { - truncator.setTruncateColumnCount(truncateAt) - return true + // See GroupBy#SimplifiedExpr for more details about this + SimplifiedExpr sqlparser.Expr } +) - switch op := op.(type) { - case *Limit: - return tryTruncateColumnsAt(op.Source, truncateAt) - case *SubQuery: - for _, offset := range op.Vars { - if offset >= truncateAt { - return false - } - } - return tryTruncateColumnsAt(op.Outer, truncateAt) - default: - return false +// Map takes in a mapping function and applies it to both the expression in OrderBy. +func (ob OrderBy) Map(mappingFunc func(sqlparser.Expr) sqlparser.Expr) OrderBy { + return OrderBy{ + Inner: &sqlparser.Order{ + Expr: mappingFunc(ob.Inner.Expr), + Direction: ob.Inner.Direction, + }, + SimplifiedExpr: mappingFunc(ob.SimplifiedExpr), } } - -func transformColumnsToSelectExprs(ctx *plancontext.PlanningContext, op ops.Operator) sqlparser.SelectExprs { - columns := op.GetColumns(ctx) - selExprs := slice.Map(columns, func(from *sqlparser.AliasedExpr) sqlparser.SelectExpr { - return from - }) - return selExprs -} diff --git a/go/vt/vtgate/planbuilder/operators/operator_funcs.go b/go/vt/vtgate/planbuilder/operators/operator_funcs.go deleted file mode 100644 index 7f7aaff29c5..00000000000 --- a/go/vt/vtgate/planbuilder/operators/operator_funcs.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2021 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package operators - -import ( - "fmt" - - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" -) - -// RemovePredicate is used when we turn a predicate into a plan operator, -// and the predicate needs to be removed as an AST construct -func RemovePredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr, op ops.Operator) (ops.Operator, error) { - switch op := op.(type) { - case *Route: - newSrc, err := RemovePredicate(ctx, expr, op.Source) - if err != nil { - return nil, err - } - op.Source = newSrc - return op, err - case *ApplyJoin: - isRemoved := false - deps := ctx.SemTable.RecursiveDeps(expr) - if deps.IsSolvedBy(TableID(op.LHS)) { - newSrc, err := RemovePredicate(ctx, expr, op.LHS) - if err != nil { - return nil, err - } - op.LHS = newSrc - isRemoved = true - } - - if deps.IsSolvedBy(TableID(op.RHS)) { - newSrc, err := RemovePredicate(ctx, expr, op.RHS) - if err != nil { - return nil, err - } - op.RHS = newSrc - isRemoved = true - } - - var keep []sqlparser.Expr - for _, e := range sqlparser.SplitAndExpression(nil, op.Predicate) { - if ctx.SemTable.EqualsExprWithDeps(expr, e) { - isRemoved = true - } else { - keep = append(keep, e) - } - } - - if !isRemoved { - return nil, vterrors.VT12001(fmt.Sprintf("remove '%s' predicate on cross-shard join query", sqlparser.String(expr))) - } - - op.Predicate = ctx.SemTable.AndExpressions(keep...) - return op, nil - case *Filter: - idx := -1 - for i, predicate := range op.Predicates { - if ctx.SemTable.EqualsExprWithDeps(predicate, expr) { - idx = i - } - } - if idx == -1 { - // the predicate is not here. let's remove it from our source - newSrc, err := RemovePredicate(ctx, expr, op.Source) - if err != nil { - return nil, err - } - op.Source = newSrc - return op, nil - } - if len(op.Predicates) == 1 { - // no predicates left on this operator, so we just remove it - return op.Source, nil - } - - // remove the predicate from this filter - op.Predicates = append(op.Predicates[:idx], op.Predicates[idx+1:]...) - return op, nil - - default: - return nil, vterrors.VT13001("this should not happen - tried to remove predicate from the operator table") - } -} diff --git a/go/vt/vtgate/planbuilder/operators/ops/op.go b/go/vt/vtgate/planbuilder/operators/ops/op.go deleted file mode 100644 index 1117b947814..00000000000 --- a/go/vt/vtgate/planbuilder/operators/ops/op.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2022 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ops - -import ( - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" -) - -type ( - // Operator forms the tree of operators, representing the declarative query provided. - // The operator tree is no actually runnable, it's an intermediate representation used - // while query planning - // The mental model are operators that pull data from each other, the root being the - // full query output, and the leaves are most often `Route`s, representing communication - // with one or more shards. We want to push down as much work as possible under these Routes - Operator interface { - // Clone will return a copy of this operator, protected so changed to the original will not impact the clone - Clone(inputs []Operator) Operator - - // Inputs returns the inputs for this operator - Inputs() []Operator - - // SetInputs changes the inputs for this op - SetInputs([]Operator) - - // AddPredicate is used to push predicates. It pushed it as far down as is possible in the tree. - // If we encounter a join and the predicate depends on both sides of the join, the predicate will be split into two parts, - // where data is fetched from the LHS of the join to be used in the evaluation on the RHS - // TODO: we should remove this and replace it with rewriters - AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator - - AddColumn(ctx *plancontext.PlanningContext, reuseExisting bool, addToGroupBy bool, expr *sqlparser.AliasedExpr) int - - FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int - - GetColumns(ctx *plancontext.PlanningContext) []*sqlparser.AliasedExpr - GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs - - ShortDescription() string - - GetOrdering(ctx *plancontext.PlanningContext) []OrderBy - } - - // OrderBy contains the expression to used in order by and also if ordering is needed at VTGate level then what the weight_string function expression to be sent down for evaluation. - OrderBy struct { - Inner *sqlparser.Order - - // See GroupBy#SimplifiedExpr for more details about this - SimplifiedExpr sqlparser.Expr - } -) - -// Map takes in a mapping function and applies it to both the expression in OrderBy. -func (ob OrderBy) Map(mappingFunc func(sqlparser.Expr) sqlparser.Expr) OrderBy { - return OrderBy{ - Inner: &sqlparser.Order{ - Expr: mappingFunc(ob.Inner.Expr), - Direction: ob.Inner.Direction, - }, - SimplifiedExpr: mappingFunc(ob.SimplifiedExpr), - } -} diff --git a/go/vt/vtgate/planbuilder/operators/ordering.go b/go/vt/vtgate/planbuilder/operators/ordering.go index b3d0310eadb..bc088ca2220 100644 --- a/go/vt/vtgate/planbuilder/operators/ordering.go +++ b/go/vt/vtgate/planbuilder/operators/ordering.go @@ -22,20 +22,19 @@ import ( "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type Ordering struct { - Source ops.Operator + Source Operator Offset []int WOffset []int - Order []ops.OrderBy + Order []OrderBy ResultColumns int } -func (o *Ordering) Clone(inputs []ops.Operator) ops.Operator { +func (o *Ordering) Clone(inputs []Operator) Operator { return &Ordering{ Source: inputs[0], Offset: slices.Clone(o.Offset), @@ -45,15 +44,15 @@ func (o *Ordering) Clone(inputs []ops.Operator) ops.Operator { } } -func (o *Ordering) Inputs() []ops.Operator { - return []ops.Operator{o.Source} +func (o *Ordering) Inputs() []Operator { + return []Operator{o.Source} } -func (o *Ordering) SetInputs(operators []ops.Operator) { +func (o *Ordering) SetInputs(operators []Operator) { o.Source = operators[0] } -func (o *Ordering) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (o *Ordering) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { o.Source = o.Source.AddPredicate(ctx, expr) return o } @@ -74,11 +73,11 @@ func (o *Ordering) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Se return o.Source.GetSelectExprs(ctx) } -func (o *Ordering) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (o *Ordering) GetOrdering(*plancontext.PlanningContext) []OrderBy { return o.Order } -func (o *Ordering) planOffsets(ctx *plancontext.PlanningContext) { +func (o *Ordering) planOffsets(ctx *plancontext.PlanningContext) Operator { for _, order := range o.Order { offset := o.Source.AddColumn(ctx, true, false, aeWrap(order.SimplifiedExpr)) o.Offset = append(o.Offset, offset) @@ -92,10 +91,11 @@ func (o *Ordering) planOffsets(ctx *plancontext.PlanningContext) { offset = o.Source.AddColumn(ctx, true, false, aeWrap(wsExpr)) o.WOffset = append(o.WOffset, offset) } + return nil } func (o *Ordering) ShortDescription() string { - ordering := slice.Map(o.Order, func(o ops.OrderBy) string { + ordering := slice.Map(o.Order, func(o OrderBy) string { return sqlparser.String(o.SimplifiedExpr) }) return strings.Join(ordering, ", ") diff --git a/go/vt/vtgate/planbuilder/operators/phases.go b/go/vt/vtgate/planbuilder/operators/phases.go index ba13a828d0b..3937105c494 100644 --- a/go/vt/vtgate/planbuilder/operators/phases.go +++ b/go/vt/vtgate/planbuilder/operators/phases.go @@ -17,10 +17,12 @@ limitations under the License. package operators import ( + "io" + "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -36,6 +38,7 @@ const ( delegateAggregation addAggrOrdering cleanOutPerfDistinct + dmlWithInput subquerySettling DONE ) @@ -56,9 +59,11 @@ func (p Phase) String() string { return "optimize Distinct operations" case subquerySettling: return "settle subqueries" + case dmlWithInput: + return "expand update/delete to dml with input" + default: + panic(vterrors.VT13001("unhandled default case")) } - - return "unknown" } func (p Phase) shouldRun(s semantics.QuerySignature) bool { @@ -73,134 +78,221 @@ func (p Phase) shouldRun(s semantics.QuerySignature) bool { return s.Distinct case subquerySettling: return s.SubQueries + case dmlWithInput: + return s.DML + default: + return true } - return true } -func (p Phase) act(ctx *plancontext.PlanningContext, op ops.Operator) (ops.Operator, error) { +func (p Phase) act(ctx *plancontext.PlanningContext, op Operator) Operator { switch p { case pullDistinctFromUnion: return pullDistinctFromUNION(ctx, op) case delegateAggregation: return enableDelegateAggregation(ctx, op) case addAggrOrdering: - return addOrderBysForAggregations(ctx, op) + return addOrderingForAllAggregations(ctx, op) case cleanOutPerfDistinct: return removePerformanceDistinctAboveRoute(ctx, op) case subquerySettling: - return settleSubqueries(ctx, op), nil + return settleSubqueries(ctx, op) + case dmlWithInput: + return findDMLAboveRoute(ctx, op) + default: + return op } +} + +type phaser struct { + current Phase +} - return op, nil +func (p *phaser) next(ctx *plancontext.PlanningContext) Phase { + for { + curr := p.current + if curr == DONE { + return DONE + } + + p.current++ + + if curr.shouldRun(ctx.SemTable.QuerySignature) { + return curr + } + } } -// getPhases returns the ordered phases that the planner will undergo. -// These phases ensure the appropriate collaboration between rewriters. -func getPhases(ctx *plancontext.PlanningContext) (phases []Phase) { - for p := Phase(0); p < DONE; p++ { - if p.shouldRun(ctx.SemTable.QuerySignature) { - phases = append(phases, p) +func findDMLAboveRoute(ctx *plancontext.PlanningContext, root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { + switch op := in.(type) { + case *Delete: + return createDMLWithInput(ctx, op, op.Source, op.DMLCommon) + case *Update: + return createDMLWithInput(ctx, op, op.Source, op.DMLCommon) + } + return in, NoRewrite + } + + return BottomUp(root, TableID, visitor, stopAtRoute) +} + +func createDMLWithInput(ctx *plancontext.PlanningContext, op, src Operator, in *DMLCommon) (Operator, *ApplyResult) { + if len(in.Target.VTable.PrimaryKey) == 0 { + panic(vterrors.VT09015()) + } + dm := &DMLWithInput{} + var leftComp sqlparser.ValTuple + proj := newAliasedProjection(src) + for _, col := range in.Target.VTable.PrimaryKey { + colName := sqlparser.NewColNameWithQualifier(col.String(), in.Target.Name) + proj.AddColumn(ctx, true, false, aeWrap(colName)) + dm.cols = append(dm.cols, colName) + leftComp = append(leftComp, colName) + ctx.SemTable.Recursive[colName] = in.Target.ID + } + + dm.Source = proj + + var targetTable *Table + _ = Visit(src, func(operator Operator) error { + if tbl, ok := operator.(*Table); ok && tbl.QTable.ID == in.Target.ID { + targetTable = tbl + return io.EOF } + return nil + }) + if targetTable == nil { + panic(vterrors.VT13001("target DELETE table not found")) + } + + // optimize for case when there is only single column on left hand side. + var lhs sqlparser.Expr = leftComp + if len(leftComp) == 1 { + lhs = leftComp[0] + } + compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, lhs, sqlparser.ListArg(engine.DmlVals), nil) + targetQT := targetTable.QTable + qt := &QueryTable{ + ID: targetQT.ID, + Alias: sqlparser.CloneRefOfAliasedTableExpr(targetQT.Alias), + Table: sqlparser.CloneTableName(targetQT.Table), + Predicates: []sqlparser.Expr{compExpr}, } - return + + qg := &QueryGraph{Tables: []*QueryTable{qt}} + in.Source = qg + + if in.OwnedVindexQuery != nil { + in.OwnedVindexQuery.From = sqlparser.TableExprs{targetQT.Alias} + in.OwnedVindexQuery.Where = sqlparser.NewWhere(sqlparser.WhereClause, compExpr) + in.OwnedVindexQuery.OrderBy = nil + in.OwnedVindexQuery.Limit = nil + } + dm.DML = op + + return dm, Rewrote("changed Delete to DMLWithInput") } -func removePerformanceDistinctAboveRoute(_ *plancontext.PlanningContext, op ops.Operator) (ops.Operator, error) { - return rewrite.BottomUp(op, TableID, func(innerOp ops.Operator, _ semantics.TableSet, _ bool) (ops.Operator, *rewrite.ApplyResult, error) { +func removePerformanceDistinctAboveRoute(_ *plancontext.PlanningContext, op Operator) Operator { + return BottomUp(op, TableID, func(innerOp Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) { d, ok := innerOp.(*Distinct) if !ok || d.Required { - return innerOp, rewrite.SameTree, nil + return innerOp, NoRewrite } - return d.Source, rewrite.NewTree("removed distinct not required that was not pushed under route", d), nil + return d.Source, Rewrote("removed distinct not required that was not pushed under route") }, stopAtRoute) } -func enableDelegateAggregation(ctx *plancontext.PlanningContext, op ops.Operator) (ops.Operator, error) { +func enableDelegateAggregation(ctx *plancontext.PlanningContext, op Operator) Operator { return addColumnsToInput(ctx, op) } -func addOrderBysForAggregations(ctx *plancontext.PlanningContext, root ops.Operator) (ops.Operator, error) { - visitor := func(in ops.Operator, _ semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +// addOrderingForAllAggregations is run we have pushed down Aggregators as far down as possible. +func addOrderingForAllAggregations(ctx *plancontext.PlanningContext, root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { aggrOp, ok := in.(*Aggregator) if !ok { - return in, rewrite.SameTree, nil + return in, NoRewrite } - requireOrdering, err := needsOrdering(ctx, aggrOp) - if err != nil { - return nil, nil, err - } - if !requireOrdering { - return in, rewrite.SameTree, nil + requireOrdering := needsOrdering(ctx, aggrOp) + var res *ApplyResult + if requireOrdering { + addOrderingFor(aggrOp) + res = Rewrote("added ordering before aggregation") } - orderBys := slice.Map(aggrOp.Grouping, func(from GroupBy) ops.OrderBy { - return from.AsOrderBy() - }) - if aggrOp.DistinctExpr != nil { - orderBys = append(orderBys, ops.OrderBy{ - Inner: &sqlparser.Order{ - Expr: aggrOp.DistinctExpr, - }, - SimplifiedExpr: aggrOp.DistinctExpr, - }) - } - aggrOp.Source = &Ordering{ - Source: aggrOp.Source, - Order: orderBys, - } - return in, rewrite.NewTree("added ordering before aggregation", in), nil + return in, res } - return rewrite.BottomUp(root, TableID, visitor, stopAtRoute) + return BottomUp(root, TableID, visitor, stopAtRoute) +} + +func addOrderingFor(aggrOp *Aggregator) { + orderBys := slice.Map(aggrOp.Grouping, func(from GroupBy) OrderBy { + return from.AsOrderBy() + }) + if aggrOp.DistinctExpr != nil { + orderBys = append(orderBys, OrderBy{ + Inner: &sqlparser.Order{ + Expr: aggrOp.DistinctExpr, + }, + SimplifiedExpr: aggrOp.DistinctExpr, + }) + } + aggrOp.Source = &Ordering{ + Source: aggrOp.Source, + Order: orderBys, + } } -func needsOrdering(ctx *plancontext.PlanningContext, in *Aggregator) (bool, error) { +func needsOrdering(ctx *plancontext.PlanningContext, in *Aggregator) bool { requiredOrder := slice.Map(in.Grouping, func(from GroupBy) sqlparser.Expr { - return from.SimplifiedExpr + return from.Inner }) if in.DistinctExpr != nil { requiredOrder = append(requiredOrder, in.DistinctExpr) } if len(requiredOrder) == 0 { - return false, nil + return false } srcOrdering := in.Source.GetOrdering(ctx) if len(srcOrdering) < len(requiredOrder) { - return true, nil + return true } for idx, gb := range requiredOrder { if !ctx.SemTable.EqualsExprWithDeps(srcOrdering[idx].SimplifiedExpr, gb) { - return true, nil + return true } } - return false, nil + return false } -func addGroupByOnRHSOfJoin(root ops.Operator) (ops.Operator, error) { - visitor := func(in ops.Operator, _ semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +func addGroupByOnRHSOfJoin(root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { join, ok := in.(*ApplyJoin) if !ok { - return in, rewrite.SameTree, nil + return in, NoRewrite } return addLiteralGroupingToRHS(join) } - return rewrite.TopDown(root, TableID, visitor, stopAtRoute) + return TopDown(root, TableID, visitor, stopAtRoute) } -func addLiteralGroupingToRHS(in *ApplyJoin) (ops.Operator, *rewrite.ApplyResult, error) { - _ = rewrite.Visit(in.RHS, func(op ops.Operator) error { +func addLiteralGroupingToRHS(in *ApplyJoin) (Operator, *ApplyResult) { + _ = Visit(in.RHS, func(op Operator) error { aggr, isAggr := op.(*Aggregator) if !isAggr { return nil } if len(aggr.Grouping) == 0 { gb := sqlparser.NewIntLiteral(".0") - aggr.Grouping = append(aggr.Grouping, NewGroupBy(gb, gb, aeWrap(gb))) + aggr.Grouping = append(aggr.Grouping, NewGroupBy(gb)) } return nil }) - return in, rewrite.SameTree, nil + return in, NoRewrite } diff --git a/go/vt/vtgate/planbuilder/operators/plan_query.go b/go/vt/vtgate/planbuilder/operators/plan_query.go new file mode 100644 index 00000000000..d5794a1bcf1 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/plan_query.go @@ -0,0 +1,163 @@ +/* +Copyright 2021 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package operators contains the operators used to plan queries. +/* +The operators go through a few phases while planning: +1. Initial plan + In this first pass, we build an operator tree from the incoming parsed query. + At the leaves, it will contain QueryGraphs - these are the tables in the FROM clause + that we can easily do join ordering on because they are all inner joins. + All the post-processing - aggregations, sorting, limit etc. are at this stage + contained in Horizon structs. We try to push these down under routes, and expand + the ones that can't be pushed down into individual operators such as Projection, + Agreggation, Limit, etc. +2. Planning + Once the initial plan has been fully built, we go through a number of phases. + recursively running rewriters on the tree in a fixed point fashion, until we've gone + over all phases and the tree has stop changing. +3. Offset planning + Now is the time to stop working with AST objects and transform remaining expressions being + used on top of vtgate to either offsets on inputs or evalengine expressions. +*/ +package operators + +import ( + "fmt" + "runtime" + + "vitess.io/vitess/go/slice" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" +) + +type ( + // helper type that implements Inputs() returning nil + noInputs struct{} + + // helper type that implements AddColumn() returning an error + noColumns struct{} + + // helper type that implements AddPredicate() returning an error + noPredicates struct{} +) + +// PlanQuery creates a query plan for a given SQL statement +func PlanQuery(ctx *plancontext.PlanningContext, stmt sqlparser.Statement) (result Operator, err error) { + defer PanicHandler(&err) + + op := translateQueryToOp(ctx, stmt) + + if DebugOperatorTree { + fmt.Println("Initial tree:") + fmt.Println(ToTree(op)) + } + + op = compact(ctx, op) + checkValid(op) + op = planQuery(ctx, op) + + _, isRoute := op.(*Route) + if !isRoute && ctx.SemTable.NotSingleRouteErr != nil { + // If we got here, we don't have a single shard plan + return nil, ctx.SemTable.NotSingleRouteErr + } + + return op, err +} + +func PanicHandler(err *error) { + if r := recover(); r != nil { + switch badness := r.(type) { + case runtime.Error: + panic(r) + case error: + *err = badness + default: + panic(r) + } + } +} + +// Inputs implements the Operator interface +func (noInputs) Inputs() []Operator { + return nil +} + +// SetInputs implements the Operator interface +func (noInputs) SetInputs(ops []Operator) { + if len(ops) > 0 { + panic("the noInputs operator does not have inputs") + } +} + +// AddColumn implements the Operator interface +func (noColumns) AddColumn(*plancontext.PlanningContext, bool, bool, *sqlparser.AliasedExpr) int { + panic(vterrors.VT13001("noColumns operators have no column")) +} + +func (noColumns) GetColumns(*plancontext.PlanningContext) []*sqlparser.AliasedExpr { + panic(vterrors.VT13001("noColumns operators have no column")) +} + +func (noColumns) FindCol(*plancontext.PlanningContext, sqlparser.Expr, bool) int { + panic(vterrors.VT13001("noColumns operators have no column")) +} + +func (noColumns) GetSelectExprs(*plancontext.PlanningContext) sqlparser.SelectExprs { + panic(vterrors.VT13001("noColumns operators have no column")) +} + +// AddPredicate implements the Operator interface +func (noPredicates) AddPredicate(*plancontext.PlanningContext, sqlparser.Expr) Operator { + panic(vterrors.VT13001("the noColumns operator cannot accept predicates")) +} + +// tryTruncateColumnsAt will see if we can truncate the columns by just asking the operator to do it for us +func tryTruncateColumnsAt(op Operator, truncateAt int) bool { + type columnTruncator interface { + setTruncateColumnCount(offset int) + } + + truncator, ok := op.(columnTruncator) + if ok { + truncator.setTruncateColumnCount(truncateAt) + return true + } + + switch op := op.(type) { + case *Limit: + return tryTruncateColumnsAt(op.Source, truncateAt) + case *SubQuery: + for _, offset := range op.Vars { + if offset >= truncateAt { + return false + } + } + return tryTruncateColumnsAt(op.Outer, truncateAt) + default: + return false + } +} + +func transformColumnsToSelectExprs(ctx *plancontext.PlanningContext, op Operator) sqlparser.SelectExprs { + columns := op.GetColumns(ctx) + selExprs := slice.Map(columns, func(from *sqlparser.AliasedExpr) sqlparser.SelectExpr { + return from + }) + return selExprs +} diff --git a/go/vt/vtgate/planbuilder/operators/projection.go b/go/vt/vtgate/planbuilder/operators/projection.go index 1c751467890..1eae4e0e06e 100644 --- a/go/vt/vtgate/planbuilder/operators/projection.go +++ b/go/vt/vtgate/planbuilder/operators/projection.go @@ -25,8 +25,6 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -34,7 +32,7 @@ import ( // Projection is used when we need to evaluate expressions on the vtgate // It uses the evalengine to accomplish its goal type Projection struct { - Source ops.Operator + Source Operator // Columns contain the expressions as viewed from the outside of this operator Columns ProjCols @@ -79,7 +77,6 @@ type ( ProjCols interface { GetColumns() []*sqlparser.AliasedExpr GetSelectExprs() sqlparser.SelectExprs - AddColumn(*sqlparser.AliasedExpr) (ProjCols, int, error) } // Used when there are stars in the expressions that we were unable to expand @@ -128,7 +125,7 @@ func newProjExprWithInner(ae *sqlparser.AliasedExpr, in sqlparser.Expr) *ProjExp } } -func newAliasedProjection(src ops.Operator) *Projection { +func newAliasedProjection(src Operator) *Projection { return &Projection{ Source: src, Columns: AliasedProjections{}, @@ -139,17 +136,13 @@ func (sp StarProjections) GetColumns() []*sqlparser.AliasedExpr { panic(vterrors.VT09015()) } -func (sp StarProjections) AddColumn(*sqlparser.AliasedExpr) (ProjCols, int, error) { - return nil, 0, vterrors.VT09015() -} - func (sp StarProjections) GetSelectExprs() sqlparser.SelectExprs { return sqlparser.SelectExprs(sp) } func (ap AliasedProjections) GetColumns() []*sqlparser.AliasedExpr { return slice.Map(ap, func(from *ProjExpr) *sqlparser.AliasedExpr { - return aeWrap(from.ColExpr) + return from.Original }) } @@ -159,14 +152,9 @@ func (ap AliasedProjections) GetSelectExprs() sqlparser.SelectExprs { }) } -func (ap AliasedProjections) AddColumn(col *sqlparser.AliasedExpr) (ProjCols, int, error) { - offset := len(ap) - return append(ap, newProjExpr(col)), offset, nil -} - func (pe *ProjExpr) String() string { var alias, expr, info string - if !pe.Original.As.IsEmpty() { + if pe.Original.As.NotEmpty() { alias = " AS " + pe.Original.As.String() } if sqlparser.Equals.Expr(pe.EvalExpr, pe.ColExpr) { @@ -194,22 +182,19 @@ var _ selectExpressions = (*Projection)(nil) // createSimpleProjection returns a projection where all columns are offsets. // used to change the name and order of the columns in the final output -func createSimpleProjection(ctx *plancontext.PlanningContext, qp *QueryProjection, src ops.Operator) (*Projection, error) { +func createSimpleProjection(ctx *plancontext.PlanningContext, qp *QueryProjection, src Operator) *Projection { p := newAliasedProjection(src) for _, e := range qp.SelectExprs { ae, err := e.GetAliasedExpr() if err != nil { - return nil, err + panic(err) } offset := p.Source.AddColumn(ctx, true, false, ae) expr := newProjExpr(ae) expr.Info = Offset(offset) - _, err = p.addProjExpr(expr) - if err != nil { - return nil, err - } + p.addProjExpr(expr) } - return p, nil + return p } // canPush returns false if the projection has subquery expressions in it and the subqueries have not yet @@ -244,6 +229,14 @@ func (p *Projection) isDerived() bool { return p.DT != nil } +func (p *Projection) derivedName() string { + if p.DT == nil { + return "" + } + + return p.DT.Alias +} + func (p *Projection) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Expr, underRoute bool) int { ap, err := p.GetAliasedProjections() if err != nil { @@ -263,57 +256,45 @@ func (p *Projection) FindCol(ctx *plancontext.PlanningContext, expr sqlparser.Ex return -1 } -func (p *Projection) addProjExpr(pe *ProjExpr) (int, error) { +func (p *Projection) addProjExpr(pe ...*ProjExpr) int { ap, err := p.GetAliasedProjections() if err != nil { - return 0, err + panic(err) } offset := len(ap) - ap = append(ap, pe) + ap = append(ap, pe...) p.Columns = ap - return offset, nil + return offset } -func (p *Projection) addUnexploredExpr(ae *sqlparser.AliasedExpr, e sqlparser.Expr) (int, error) { +func (p *Projection) addUnexploredExpr(ae *sqlparser.AliasedExpr, e sqlparser.Expr) int { return p.addProjExpr(newProjExprWithInner(ae, e)) } -func (p *Projection) addSubqueryExpr(ae *sqlparser.AliasedExpr, expr sqlparser.Expr, sqs ...*SubQuery) error { +func (p *Projection) addSubqueryExpr(ae *sqlparser.AliasedExpr, expr sqlparser.Expr, sqs ...*SubQuery) { pe := newProjExprWithInner(ae, expr) pe.Info = SubQueryExpression(sqs) - _, err := p.addProjExpr(pe) - return err + _ = p.addProjExpr(pe) } func (p *Projection) addColumnWithoutPushing(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExpr, _ bool) int { - column, err := p.addColumn(ctx, true, false, expr, false) - if err != nil { - panic(err) - } - return column + return p.addColumn(ctx, true, false, expr, false) } func (p *Projection) addColumnsWithoutPushing(ctx *plancontext.PlanningContext, reuse bool, _ []bool, exprs []*sqlparser.AliasedExpr) []int { offsets := make([]int, len(exprs)) for idx, expr := range exprs { - offset, err := p.addColumn(ctx, reuse, false, expr, false) - if err != nil { - panic(err) - } + offset := p.addColumn(ctx, reuse, false, expr, false) offsets[idx] = offset } return offsets } func (p *Projection) AddColumn(ctx *plancontext.PlanningContext, reuse bool, addToGroupBy bool, ae *sqlparser.AliasedExpr) int { - column, err := p.addColumn(ctx, reuse, addToGroupBy, ae, true) - if err != nil { - panic(err) - } - return column + return p.addColumn(ctx, reuse, addToGroupBy, ae, true) } func (p *Projection) addColumn( @@ -322,13 +303,13 @@ func (p *Projection) addColumn( addToGroupBy bool, ae *sqlparser.AliasedExpr, push bool, -) (int, error) { +) int { expr := p.DT.RewriteExpression(ctx, ae.Expr) if reuse { offset := p.FindCol(ctx, expr, false) if offset >= 0 { - return offset, nil + return offset } } @@ -337,7 +318,7 @@ func (p *Projection) addColumn( if ok { cols, ok := p.Columns.(AliasedProjections) if !ok { - return 0, vterrors.VT09015() + panic(vterrors.VT09015()) } for _, projExpr := range cols { if ctx.SemTable.EqualsExprWithDeps(ws.Expr, projExpr.ColExpr) { @@ -364,7 +345,7 @@ func (po Offset) expr() {} func (po *EvalEngine) expr() {} func (po SubQueryExpression) expr() {} -func (p *Projection) Clone(inputs []ops.Operator) ops.Operator { +func (p *Projection) Clone(inputs []Operator) Operator { return &Projection{ Source: inputs[0], Columns: p.Columns, // TODO don't think we need to deep clone here @@ -373,15 +354,15 @@ func (p *Projection) Clone(inputs []ops.Operator) ops.Operator { } } -func (p *Projection) Inputs() []ops.Operator { - return []ops.Operator{p.Source} +func (p *Projection) Inputs() []Operator { + return []Operator{p.Source} } -func (p *Projection) SetInputs(operators []ops.Operator) { +func (p *Projection) SetInputs(operators []Operator) { p.Source = operators[0] } -func (p *Projection) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (p *Projection) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { // we just pass through the predicate to our source p.Source = p.Source.AddPredicate(ctx, expr) return p @@ -399,7 +380,7 @@ func (p *Projection) GetSelectExprs(*plancontext.PlanningContext) sqlparser.Sele var output sqlparser.SelectExprs for _, pe := range cols { ae := &sqlparser.AliasedExpr{Expr: pe.EvalExpr} - if !pe.Original.As.IsEmpty() { + if pe.Original.As.NotEmpty() { ae.As = pe.Original.As } else if !sqlparser.Equals.Expr(ae.Expr, pe.Original.Expr) { ae.As = sqlparser.NewIdentifierCI(pe.Original.ColumnName()) @@ -412,7 +393,7 @@ func (p *Projection) GetSelectExprs(*plancontext.PlanningContext) sqlparser.Sele } } -func (p *Projection) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (p *Projection) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return p.Source.GetOrdering(ctx) } @@ -454,10 +435,10 @@ func (p *Projection) ShortDescription() string { return strings.Join(result, ", ") } -func (p *Projection) Compact(ctx *plancontext.PlanningContext) (ops.Operator, *rewrite.ApplyResult, error) { +func (p *Projection) Compact(ctx *plancontext.PlanningContext) (Operator, *ApplyResult) { ap, err := p.GetAliasedProjections() if err != nil { - return p, rewrite.SameTree, nil + return p, NoRewrite } // for projections that are not derived tables, we can check if it is safe to remove or not @@ -471,7 +452,7 @@ func (p *Projection) Compact(ctx *plancontext.PlanningContext) (ops.Operator, *r } if !needed { - return p.Source, rewrite.NewTree("removed projection only passing through the input", p), nil + return p.Source, Rewrote("removed projection only passing through the input") } switch src := p.Source.(type) { @@ -480,68 +461,63 @@ func (p *Projection) Compact(ctx *plancontext.PlanningContext) (ops.Operator, *r case *ApplyJoin: return p.compactWithJoin(ctx, src) } - return p, rewrite.SameTree, nil + return p, NoRewrite } -func (p *Projection) compactWithJoin(ctx *plancontext.PlanningContext, join *ApplyJoin) (ops.Operator, *rewrite.ApplyResult, error) { +func (p *Projection) compactWithJoin(ctx *plancontext.PlanningContext, join *ApplyJoin) (Operator, *ApplyResult) { ap, err := p.GetAliasedProjections() if err != nil { - return p, rewrite.SameTree, nil + return p, NoRewrite } var newColumns []int - var newColumnsAST []JoinColumn + newColumnsAST := &applyJoinColumns{} for _, col := range ap { switch colInfo := col.Info.(type) { case Offset: newColumns = append(newColumns, join.Columns[colInfo]) - newColumnsAST = append(newColumnsAST, join.JoinColumns[colInfo]) + newColumnsAST.add(join.JoinColumns.columns[colInfo]) case nil: if !ctx.SemTable.EqualsExprWithDeps(col.EvalExpr, col.ColExpr) { // the inner expression is different from what we are presenting to the outside - this means we need to evaluate - return p, rewrite.SameTree, nil + return p, NoRewrite } - offset := slices.IndexFunc(join.JoinColumns, func(jc JoinColumn) bool { - return ctx.SemTable.EqualsExprWithDeps(jc.Original.Expr, col.ColExpr) - }) + offset := slices.IndexFunc(join.JoinColumns.columns, applyJoinCompare(ctx, col.ColExpr)) if offset < 0 { - return p, rewrite.SameTree, nil + return p, NoRewrite } if len(join.Columns) > 0 { newColumns = append(newColumns, join.Columns[offset]) } - newColumnsAST = append(newColumnsAST, join.JoinColumns[offset]) + newColumnsAST.add(join.JoinColumns.columns[offset]) default: - return p, rewrite.SameTree, nil + return p, NoRewrite } } join.Columns = newColumns join.JoinColumns = newColumnsAST - return join, rewrite.NewTree("remove projection from before join", join), nil + return join, Rewrote("remove projection from before join") } -func (p *Projection) compactWithRoute(ctx *plancontext.PlanningContext, rb *Route) (ops.Operator, *rewrite.ApplyResult, error) { +func (p *Projection) compactWithRoute(ctx *plancontext.PlanningContext, rb *Route) (Operator, *ApplyResult) { ap, err := p.GetAliasedProjections() if err != nil { - return p, rewrite.SameTree, nil + return p, NoRewrite } for i, col := range ap { offset, ok := col.Info.(Offset) if !ok || int(offset) != i { - return p, rewrite.SameTree, nil + return p, NoRewrite } } columns := rb.GetColumns(ctx) - if err != nil { - return nil, nil, err - } if len(columns) == len(ap) { - return rb, rewrite.NewTree("remove projection from before route", rb), nil + return rb, Rewrote("remove projection from before route") } rb.ResultColumns = len(columns) - return rb, rewrite.SameTree, nil + return rb, NoRewrite } // needsEvaluation finds the expression given by this argument and checks if the inside and outside expressions match @@ -561,7 +537,7 @@ func (p *Projection) needsEvaluation(ctx *plancontext.PlanningContext, e sqlpars return false } -func (p *Projection) planOffsets(ctx *plancontext.PlanningContext) { +func (p *Projection) planOffsets(ctx *plancontext.PlanningContext) Operator { ap, err := p.GetAliasedProjections() if err != nil { panic(err) @@ -588,6 +564,7 @@ func (p *Projection) planOffsets(ctx *plancontext.PlanningContext) { eexpr, err := evalengine.Translate(rewritten, &evalengine.Config{ ResolveType: ctx.SemTable.TypeForExpr, Collation: ctx.SemTable.Collation, + Environment: ctx.VSchema.Environment(), }) if err != nil { panic(err) @@ -597,6 +574,7 @@ func (p *Projection) planOffsets(ctx *plancontext.PlanningContext) { EExpr: eexpr, } } + return nil } func (p *Projection) introducesTableID() semantics.TableSet { diff --git a/go/vt/vtgate/planbuilder/operators/projection_pushing.go b/go/vt/vtgate/planbuilder/operators/projection_pushing.go new file mode 100644 index 00000000000..59f6e6d484d --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/projection_pushing.go @@ -0,0 +1,451 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "slices" + "strconv" + + "vitess.io/vitess/go/slice" + "vitess.io/vitess/go/test/dbg" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" +) + +type ( + projector struct { + columns []*ProjExpr + columnAliases []string + explicitColumnAliases bool + tableName sqlparser.TableName + } +) + +// add introduces a new projection with the specified alias to the projector. +func (p *projector) add(pe *ProjExpr, alias string) { + p.columns = append(p.columns, pe) + if alias != "" && slices.Index(p.columnAliases, alias) > -1 { + panic("alias already used") + } + p.columnAliases = append(p.columnAliases, alias) +} + +// get finds or adds an expression in the projector, returning its SQL representation with the appropriate alias +func (p *projector) get(ctx *plancontext.PlanningContext, expr sqlparser.Expr) sqlparser.Expr { + for _, column := range p.columns { + if ctx.SemTable.EqualsExprWithDeps(expr, column.ColExpr) { + alias := p.claimUnusedAlias(column.Original) + out := sqlparser.NewColName(alias) + out.Qualifier = p.tableName + + ctx.SemTable.CopySemanticInfo(expr, out) + return out + } + } + + // we could not find the expression, so we add it + alias := sqlparser.UnescapedString(expr) + pe := newProjExpr(sqlparser.NewAliasedExpr(expr, alias)) + p.columns = append(p.columns, pe) + p.columnAliases = append(p.columnAliases, alias) + + out := sqlparser.NewColName(alias) + out.Qualifier = p.tableName + + ctx.SemTable.CopySemanticInfo(expr, out) + + return out +} + +// claimUnusedAlias generates a unique alias based on the provided expression, ensuring no duplication in the projector +func (p *projector) claimUnusedAlias(ae *sqlparser.AliasedExpr) string { + bare := ae.ColumnName() + alias := bare + for i := int64(0); slices.Index(p.columnAliases, alias) > -1; i++ { + alias = bare + strconv.FormatInt(i, 10) + } + return alias +} + +// tryPushProjection attempts to optimize a projection by pushing it down in the query plan +func tryPushProjection( + ctx *plancontext.PlanningContext, + p *Projection, +) (Operator, *ApplyResult) { + switch src := p.Source.(type) { + case *Route: + return Swap(p, src, "push projection under route") + case *Limit: + return Swap(p, src, "push projection under limit") + case *ApplyJoin: + if p.FromAggr || !p.canPush(ctx) { + return p, NoRewrite + } + return pushProjectionInApplyJoin(ctx, p, src) + case *HashJoin: + if !p.canPush(ctx) { + return p, NoRewrite + } + return pushProjectionThroughHashJoin(ctx, p, src) + case *Vindex: + if !p.canPush(ctx) { + return p, NoRewrite + } + return pushProjectionInVindex(ctx, p, src) + case *SubQueryContainer: + if !p.canPush(ctx) { + return p, NoRewrite + } + return pushProjectionToOuterContainer(ctx, p, src) + case *SubQuery: + return pushProjectionToOuter(ctx, p, src) + default: + return p, NoRewrite + } +} + +// pushProjectionThroughHashJoin optimizes projection operations within a hash join +func pushProjectionThroughHashJoin(ctx *plancontext.PlanningContext, p *Projection, hj *HashJoin) (Operator, *ApplyResult) { + cols := p.Columns.(AliasedProjections) + for _, col := range cols { + if !col.isSameInAndOut(ctx) { + return p, NoRewrite + } + hj.columns.add(col.ColExpr) + } + return hj, Rewrote("merged projection into hash join") +} + +func pushProjectionToOuter(ctx *plancontext.PlanningContext, p *Projection, sq *SubQuery) (Operator, *ApplyResult) { + ap, err := p.GetAliasedProjections() + if err != nil { + return p, NoRewrite + } + + if !reachedPhase(ctx, subquerySettling) { + return p, NoRewrite + } + + outer := TableID(sq.Outer) + for _, pe := range ap { + _, isOffset := pe.Info.(*Offset) + if isOffset { + continue + } + + if !ctx.SemTable.RecursiveDeps(pe.EvalExpr).IsSolvedBy(outer) { + return p, NoRewrite + } + + se, ok := pe.Info.(SubQueryExpression) + if ok { + pe.EvalExpr = rewriteColNameToArgument(ctx, pe.EvalExpr, se, sq) + } + } + // all projections can be pushed to the outer + sq.Outer, p.Source = p, sq.Outer + return sq, Rewrote("push projection into outer side of subquery") +} + +func pushProjectionInVindex( + ctx *plancontext.PlanningContext, + p *Projection, + src *Vindex, +) (Operator, *ApplyResult) { + ap, err := p.GetAliasedProjections() + if err != nil { + panic(err) + } + for _, pe := range ap { + src.AddColumn(ctx, true, false, aeWrap(pe.EvalExpr)) + } + return src, Rewrote("push projection into vindex") +} + +func pushProjectionToOuterContainer(ctx *plancontext.PlanningContext, p *Projection, src *SubQueryContainer) (Operator, *ApplyResult) { + ap, err := p.GetAliasedProjections() + if err != nil { + return p, NoRewrite + } + + outer := TableID(src.Outer) + for _, pe := range ap { + _, isOffset := pe.Info.(*Offset) + if isOffset { + continue + } + + if !ctx.SemTable.RecursiveDeps(pe.EvalExpr).IsSolvedBy(outer) { + return p, NoRewrite + } + + if se, ok := pe.Info.(SubQueryExpression); ok { + pe.EvalExpr = rewriteColNameToArgument(ctx, pe.EvalExpr, se, src.Inner...) + } + } + // all projections can be pushed to the outer + src.Outer, p.Source = p, src.Outer + return src, Rewrote("push projection into outer side of subquery container") +} + +// pushProjectionInApplyJoin pushes down a projection operation into an ApplyJoin operation. +// It processes each input column and creates new JoinPredicates for the ApplyJoin operation based on +// the input column's expression. It also creates new Projection operators for the left and right +// children of the ApplyJoin operation, if needed. +func pushProjectionInApplyJoin( + ctx *plancontext.PlanningContext, + p *Projection, + src *ApplyJoin, +) (Operator, *ApplyResult) { + ap, err := p.GetAliasedProjections() + if src.LeftJoin || err != nil { + // we can't push down expression evaluation to the rhs if we are not sure if it will even be executed + return p, NoRewrite + } + lhs, rhs := &projector{}, &projector{} + if p.DT != nil && len(p.DT.Columns) > 0 { + lhs.explicitColumnAliases = true + rhs.explicitColumnAliases = true + } + + src.JoinColumns = &applyJoinColumns{} + for idx, pe := range ap { + var alias string + if p.DT != nil && len(p.DT.Columns) > 0 { + if len(p.DT.Columns) <= idx { + panic(vterrors.VT13001("no such alias found for derived table")) + } + alias = p.DT.Columns[idx].String() + } + splitProjectionAcrossJoin(ctx, src, lhs, rhs, pe, alias) + } + + if p.isDerived() { + exposeColumnsThroughDerivedTable(ctx, p, src, lhs, rhs) + } + + // Create and update the Projection operators for the left and right children, if needed. + src.LHS = createProjectionWithTheseColumns(ctx, src.LHS, lhs, p.DT) + src.RHS = createProjectionWithTheseColumns(ctx, src.RHS, rhs, p.DT) + + return src, Rewrote("split projection to either side of join") +} + +// splitProjectionAcrossJoin creates JoinPredicates for all projections, +// and pushes down columns as needed between the LHS and RHS of a join +func splitProjectionAcrossJoin( + ctx *plancontext.PlanningContext, + join *ApplyJoin, + lhs, rhs *projector, + pe *ProjExpr, + colAlias string, +) { + + // Check if the current expression can reuse an existing column in the ApplyJoin. + if _, found := canReuseColumn(ctx, join.JoinColumns.columns, pe.EvalExpr, joinColumnToExpr); found { + return + } + + switch pe.Info.(type) { + case nil: + join.JoinColumns.add(splitUnexploredExpression(ctx, join, lhs, rhs, pe, colAlias)) + case Offset: + // for offsets, we'll just treat the expression as unexplored, and later stages will handle the new offset + join.JoinColumns.add(splitUnexploredExpression(ctx, join, lhs, rhs, pe, colAlias)) + case SubQueryExpression: + join.JoinColumns.add(splitSubqueryExpression(ctx, join, lhs, rhs, pe, colAlias)) + default: + panic(dbg.S(pe.Info)) + } +} + +func splitSubqueryExpression( + ctx *plancontext.PlanningContext, + join *ApplyJoin, + lhs, rhs *projector, + pe *ProjExpr, + alias string, +) applyJoinColumn { + col := join.getJoinColumnFor(ctx, pe.Original, pe.ColExpr, false) + return pushDownSplitJoinCol(col, lhs, pe, alias, rhs) +} + +func splitUnexploredExpression( + ctx *plancontext.PlanningContext, + join *ApplyJoin, + lhs, rhs *projector, + pe *ProjExpr, + alias string, +) applyJoinColumn { + // Get a applyJoinColumn for the current expression. + col := join.getJoinColumnFor(ctx, pe.Original, pe.ColExpr, false) + + return pushDownSplitJoinCol(col, lhs, pe, alias, rhs) +} + +func pushDownSplitJoinCol(col applyJoinColumn, lhs *projector, pe *ProjExpr, alias string, rhs *projector) applyJoinColumn { + // Update the left and right child columns and names based on the applyJoinColumn type. + switch { + case col.IsPureLeft(): + lhs.add(pe, alias) + case col.IsPureRight(): + rhs.add(pe, alias) + case col.IsMixedLeftAndRight(): + for _, lhsExpr := range col.LHSExprs { + var lhsAlias string + if alias != "" { + // we need to add an explicit column alias here. let's try just the ColName as is first + lhsAlias = sqlparser.String(lhsExpr.Expr) + } + lhs.add(newProjExpr(aeWrap(lhsExpr.Expr)), lhsAlias) + } + innerPE := newProjExprWithInner(pe.Original, col.RHSExpr) + innerPE.ColExpr = col.RHSExpr + innerPE.Info = pe.Info + rhs.add(innerPE, alias) + } + return col +} + +// exposeColumnsThroughDerivedTable rewrites expressions within a join that is inside a derived table +// in order to make them accessible outside the derived table. This is necessary when swapping the +// positions of the derived table and join operation. +// +// For example, consider the input query: +// select ... from (select T1.foo from T1 join T2 on T1.id = T2.id) as t +// If we push the derived table under the join, with T1 on the LHS of the join, we need to expose +// the values of T1.id through the derived table, or they will not be accessible on the RHS. +// +// The function iterates through each join predicate, rewriting the expressions in the predicate's +// LHS expressions to include the derived table. This allows the expressions to be accessed outside +// the derived table. +func exposeColumnsThroughDerivedTable(ctx *plancontext.PlanningContext, p *Projection, src *ApplyJoin, lhs, rhs *projector) { + derivedTbl, err := ctx.SemTable.TableInfoFor(p.DT.TableID) + if err != nil { + panic(err) + } + derivedTblName, err := derivedTbl.Name() + if err != nil { + panic(err) + } + lhs.tableName = derivedTblName + rhs.tableName = derivedTblName + + lhsIDs := TableID(src.LHS) + rhsIDs := TableID(src.RHS) + rewriteColumnsForJoin(ctx, src.JoinPredicates.columns, lhsIDs, rhsIDs, lhs, rhs, false) + rewriteColumnsForJoin(ctx, src.JoinColumns.columns, lhsIDs, rhsIDs, lhs, rhs, true) +} + +func rewriteColumnsForJoin( + ctx *plancontext.PlanningContext, + columns []applyJoinColumn, + lhsIDs, rhsIDs semantics.TableSet, + lhs, rhs *projector, + exposeRHS bool, // we only want to expose the returned columns from the RHS. + // For predicates, we don't need to expose the RHS columns +) { + for colIdx, column := range columns { + for lhsIdx, bve := range column.LHSExprs { + // since this is on the LHSExprs, we know that dependencies are from that side of the join + column.LHSExprs[lhsIdx].Expr = lhs.get(ctx, bve.Expr) + } + if column.IsPureLeft() { + continue + } + + // now we need to go over the predicate and find + var rewriteTo sqlparser.Expr + + pre := func(node, _ sqlparser.SQLNode) bool { + _, isSQ := node.(*sqlparser.Subquery) + if isSQ { + return false + } + expr, ok := node.(sqlparser.Expr) + if !ok { + return true + } + deps := ctx.SemTable.RecursiveDeps(expr) + + switch { + case deps.IsEmpty(): + return true + case deps.IsSolvedBy(lhsIDs): + rewriteTo = lhs.get(ctx, expr) + return false + case deps.IsSolvedBy(rhsIDs): + if exposeRHS { + rewriteTo = rhs.get(ctx, expr) + } + return false + default: + return true + } + } + + post := func(cursor *sqlparser.CopyOnWriteCursor) { + if rewriteTo != nil { + cursor.Replace(rewriteTo) + rewriteTo = nil + return + } + } + newOriginal := sqlparser.CopyOnRewrite(column.Original, pre, post, ctx.SemTable.CopySemanticInfo).(sqlparser.Expr) + column.Original = newOriginal + + columns[colIdx] = column + } +} + +// prefixColNames adds qualifier prefixes to all ColName:s. +// We want to be more explicit than the user was to make sure we never produce invalid SQL +func prefixColNames(ctx *plancontext.PlanningContext, tblName sqlparser.TableName, e sqlparser.Expr) sqlparser.Expr { + return sqlparser.CopyOnRewrite(e, nil, func(cursor *sqlparser.CopyOnWriteCursor) { + col, ok := cursor.Node().(*sqlparser.ColName) + if !ok { + return + } + cursor.Replace(sqlparser.NewColNameWithQualifier(col.Name.String(), tblName)) + }, ctx.SemTable.CopySemanticInfo).(sqlparser.Expr) +} + +func createProjectionWithTheseColumns( + ctx *plancontext.PlanningContext, + src Operator, + p *projector, + dt *DerivedTable, +) Operator { + if len(p.columns) == 0 { + return src + } + proj := createProjection(ctx, src, "") + proj.Columns = AliasedProjections(p.columns) + if dt != nil { + kopy := *dt + if p.explicitColumnAliases { + kopy.Columns = slice.Map(p.columnAliases, func(s string) sqlparser.IdentifierCI { + return sqlparser.NewIdentifierCI(s) + }) + } + proj.DT = &kopy + } + + return proj +} diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index e66abd02feb..e31d06122da 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -21,40 +21,20 @@ import ( "io" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) -type ( - projector struct { - columns []*ProjExpr - columnAliases sqlparser.Columns - explicitColumnAliases bool - } -) - -func planQuery(ctx *plancontext.PlanningContext, root ops.Operator) (output ops.Operator, err error) { - output, err = runPhases(ctx, root) - if err != nil { - return nil, err - } +func planQuery(ctx *plancontext.PlanningContext, root Operator) Operator { + output := runPhases(ctx, root) + output = planOffsets(ctx, output) - output, err = planOffsets(ctx, output) - if err != nil { - return nil, err - } - - if rewrite.DebugOperatorTree { + if DebugOperatorTree { fmt.Println("After offset planning:") - fmt.Println(ops.ToTree(output)) + fmt.Println(ToTree(output)) } - output, err = compact(ctx, output) - if err != nil { - return nil, err - } + output = compact(ctx, output) return addTruncationOrProjectionToReturnOutput(ctx, root, output) } @@ -63,35 +43,26 @@ func planQuery(ctx *plancontext.PlanningContext, root ops.Operator) (output ops. // If we can push it under a route - done. // If we can't, we will instead expand the Horizon into // smaller operators and try to push these down as far as possible -func runPhases(ctx *plancontext.PlanningContext, root ops.Operator) (op ops.Operator, err error) { - op = root - for _, phase := range getPhases(ctx) { +func runPhases(ctx *plancontext.PlanningContext, root Operator) Operator { + op := root + + p := phaser{} + for phase := p.next(ctx); phase != DONE; phase = p.next(ctx) { ctx.CurrentPhase = int(phase) - if rewrite.DebugOperatorTree { + if DebugOperatorTree { fmt.Printf("PHASE: %s\n", phase.String()) } - op, err = phase.act(ctx, op) - if err != nil { - return nil, err - } - - op, err = runRewriters(ctx, op) - if err != nil { - return nil, err - } - - op, err = compact(ctx, op) - if err != nil { - return nil, err - } + op = phase.act(ctx, op) + op = runRewriters(ctx, op) + op = compact(ctx, op) } return addGroupByOnRHSOfJoin(op) } -func runRewriters(ctx *plancontext.PlanningContext, root ops.Operator) (ops.Operator, error) { - visitor := func(in ops.Operator, _ semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +func runRewriters(ctx *plancontext.PlanningContext, root Operator) Operator { + visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { switch in := in.(type) { case *Horizon: return pushOrExpandHorizon(ctx, in) @@ -117,24 +88,71 @@ func runRewriters(ctx *plancontext.PlanningContext, root ops.Operator) (ops.Oper return optimizeQueryGraph(ctx, in) case *LockAndComment: return pushLockAndComment(in) + case *Delete: + return tryPushDelete(in) + case *Update: + return tryPushUpdate(in) default: - return in, rewrite.SameTree, nil + return in, NoRewrite } } - return rewrite.FixedPointBottomUp(root, TableID, visitor, stopAtRoute) + return FixedPointBottomUp(root, TableID, visitor, stopAtRoute) +} + +func tryPushDelete(in *Delete) (Operator, *ApplyResult) { + if src, ok := in.Source.(*Route); ok { + return pushDMLUnderRoute(in, src, "pushed delete under route") + } + return in, NoRewrite +} + +func tryPushUpdate(in *Update) (Operator, *ApplyResult) { + if src, ok := in.Source.(*Route); ok { + return pushDMLUnderRoute(in, src, "pushed update under route") + } + return in, NoRewrite +} + +func pushDMLUnderRoute(in Operator, src *Route, msg string) (Operator, *ApplyResult) { + switch r := src.Routing.(type) { + case *SequenceRouting: + // Sequences are just unsharded routes + src.Routing = &AnyShardRouting{ + keyspace: r.keyspace, + } + case *AnyShardRouting: + // References would have an unsharded source + // Alternates are not required. + r.Alternates = nil + } + return Swap(in, src, msg) } -func pushLockAndComment(l *LockAndComment) (ops.Operator, *rewrite.ApplyResult, error) { +func pushLockAndComment(l *LockAndComment) (Operator, *ApplyResult) { switch src := l.Source.(type) { case *Horizon, *QueryGraph: // we want to wait until the horizons have been pushed under a route or expanded // that way we know that we've replaced the QueryGraphs with Routes - return l, rewrite.SameTree, nil + return l, NoRewrite case *Route: src.Comments = l.Comments src.Lock = l.Lock - return src, rewrite.NewTree("put lock and comment into route", l), nil + return src, Rewrote("put lock and comment into route") + case *SubQueryContainer: + src.Outer = &LockAndComment{ + Source: src.Outer, + Comments: l.Comments, + Lock: l.Lock, + } + for _, sq := range src.Inner { + sq.Subquery = &LockAndComment{ + Source: sq.Subquery, + Comments: l.Comments, + Lock: l.Lock, + } + } + return src, Rewrote("push lock and comment into subquery container") default: inputs := src.Inputs() for i, op := range inputs { @@ -145,23 +163,20 @@ func pushLockAndComment(l *LockAndComment) (ops.Operator, *rewrite.ApplyResult, } } src.SetInputs(inputs) - return src, rewrite.NewTree("pushed down lock and comments", l), nil + return src, Rewrote("pushed down lock and comments") } } -func pushOrExpandHorizon(ctx *plancontext.PlanningContext, in *Horizon) (ops.Operator, *rewrite.ApplyResult, error) { +func pushOrExpandHorizon(ctx *plancontext.PlanningContext, in *Horizon) (Operator, *ApplyResult) { if in.IsDerived() { - newOp, result, err := pushDerived(ctx, in) - if err != nil { - return nil, nil, err - } - if result != rewrite.SameTree { - return newOp, result, nil + newOp, result := pushDerived(ctx, in) + if result != NoRewrite { + return newOp, result } } if !reachedPhase(ctx, initialPlanning) { - return in, rewrite.SameTree, nil + return in, NoRewrite } if ctx.SemTable.QuerySignature.SubQueries { @@ -170,15 +185,12 @@ func pushOrExpandHorizon(ctx *plancontext.PlanningContext, in *Horizon) (ops.Ope rb, isRoute := in.src().(*Route) if isRoute && rb.IsSingleShard() { - return rewrite.Swap(in, rb, "push horizon into route") + return Swap(in, rb, "push horizon into route") } sel, isSel := in.selectStatement().(*sqlparser.Select) - qp, err := in.getQP(ctx) - if err != nil { - return nil, nil, err - } + qp := in.getQP(ctx) needsOrdering := len(qp.OrderExprs) > 0 hasHaving := isSel && sel.Having != nil @@ -191,329 +203,45 @@ func pushOrExpandHorizon(ctx *plancontext.PlanningContext, in *Horizon) (ops.Ope in.selectStatement().GetLimit() == nil if canPush { - return rewrite.Swap(in, rb, "push horizon into route") + return Swap(in, rb, "push horizon into route") } return expandHorizon(ctx, in) } -func tryPushProjection( - ctx *plancontext.PlanningContext, - p *Projection, -) (ops.Operator, *rewrite.ApplyResult, error) { - switch src := p.Source.(type) { - case *Route: - return rewrite.Swap(p, src, "push projection under route") - case *ApplyJoin: - if p.FromAggr || !p.canPush(ctx) { - return p, rewrite.SameTree, nil - } - return pushProjectionInApplyJoin(ctx, p, src) - case *Vindex: - if !p.canPush(ctx) { - return p, rewrite.SameTree, nil - } - return pushProjectionInVindex(ctx, p, src) - case *SubQueryContainer: - if !p.canPush(ctx) { - return p, rewrite.SameTree, nil - } - return pushProjectionToOuterContainer(ctx, p, src) - case *SubQuery: - return pushProjectionToOuter(ctx, p, src) - case *Limit: - return rewrite.Swap(p, src, "push projection under limit") - default: - return p, rewrite.SameTree, nil - } -} - -func pushProjectionToOuter(ctx *plancontext.PlanningContext, p *Projection, sq *SubQuery) (ops.Operator, *rewrite.ApplyResult, error) { - ap, err := p.GetAliasedProjections() - if err != nil { - return p, rewrite.SameTree, nil - } - - if !reachedPhase(ctx, subquerySettling) || err != nil { - return p, rewrite.SameTree, nil - } - - outer := TableID(sq.Outer) - for _, pe := range ap { - _, isOffset := pe.Info.(*Offset) - if isOffset { - continue - } - - if !ctx.SemTable.RecursiveDeps(pe.EvalExpr).IsSolvedBy(outer) { - return p, rewrite.SameTree, nil - } - - se, ok := pe.Info.(SubQueryExpression) - if ok { - pe.EvalExpr = rewriteColNameToArgument(ctx, pe.EvalExpr, se, sq) - } - } - // all projections can be pushed to the outer - sq.Outer, p.Source = p, sq.Outer - return sq, rewrite.NewTree("push projection into outer side of subquery", p), nil -} - -func pushProjectionInVindex( - ctx *plancontext.PlanningContext, - p *Projection, - src *Vindex, -) (ops.Operator, *rewrite.ApplyResult, error) { - ap, err := p.GetAliasedProjections() - if err != nil { - return nil, nil, err - } - for _, pe := range ap { - src.AddColumn(ctx, true, false, aeWrap(pe.EvalExpr)) - } - return src, rewrite.NewTree("push projection into vindex", p), nil -} - -func (p *projector) add(pe *ProjExpr, col *sqlparser.IdentifierCI) { - p.columns = append(p.columns, pe) - if col != nil { - p.columnAliases = append(p.columnAliases, *col) - } -} - -// pushProjectionInApplyJoin pushes down a projection operation into an ApplyJoin operation. -// It processes each input column and creates new JoinPredicates for the ApplyJoin operation based on -// the input column's expression. It also creates new Projection operators for the left and right -// children of the ApplyJoin operation, if needed. -func pushProjectionInApplyJoin( - ctx *plancontext.PlanningContext, - p *Projection, - src *ApplyJoin, -) (ops.Operator, *rewrite.ApplyResult, error) { - ap, err := p.GetAliasedProjections() - if src.LeftJoin || err != nil { - // we can't push down expression evaluation to the rhs if we are not sure if it will even be executed - return p, rewrite.SameTree, nil - } - lhs, rhs := &projector{}, &projector{} - if p.DT != nil && len(p.DT.Columns) > 0 { - lhs.explicitColumnAliases = true - rhs.explicitColumnAliases = true - } - - src.JoinColumns = nil - for idx, pe := range ap { - var col *sqlparser.IdentifierCI - if p.DT != nil && idx < len(p.DT.Columns) { - col = &p.DT.Columns[idx] - } - err := splitProjectionAcrossJoin(ctx, src, lhs, rhs, pe, col) - if err != nil { - return nil, nil, err - } - } - - if p.isDerived() { - err := exposeColumnsThroughDerivedTable(ctx, p, src, lhs) - if err != nil { - return nil, nil, err - } - } - - // Create and update the Projection operators for the left and right children, if needed. - src.LHS, err = createProjectionWithTheseColumns(ctx, src.LHS, lhs, p.DT) - if err != nil { - return nil, nil, err - } - - src.RHS, err = createProjectionWithTheseColumns(ctx, src.RHS, rhs, p.DT) - if err != nil { - return nil, nil, err - } - - return src, rewrite.NewTree("split projection to either side of join", src), nil -} - -// splitProjectionAcrossJoin creates JoinPredicates for all projections, -// and pushes down columns as needed between the LHS and RHS of a join -func splitProjectionAcrossJoin( - ctx *plancontext.PlanningContext, - join *ApplyJoin, - lhs, rhs *projector, - pe *ProjExpr, - colAlias *sqlparser.IdentifierCI, -) error { - - // Check if the current expression can reuse an existing column in the ApplyJoin. - if _, found := canReuseColumn(ctx, join.JoinColumns, pe.EvalExpr, joinColumnToExpr); found { - return nil - } - - col, err := splitUnexploredExpression(ctx, join, lhs, rhs, pe, colAlias) - if err != nil { - return err - } - - // Add the new JoinColumn to the ApplyJoin's JoinPredicates. - join.JoinColumns = append(join.JoinColumns, col) - return nil -} - -func splitUnexploredExpression( - ctx *plancontext.PlanningContext, - join *ApplyJoin, - lhs, rhs *projector, - pe *ProjExpr, - colAlias *sqlparser.IdentifierCI, -) (JoinColumn, error) { - // Get a JoinColumn for the current expression. - col, err := join.getJoinColumnFor(ctx, pe.Original, pe.ColExpr, false) - if err != nil { - return JoinColumn{}, err - } - - // Update the left and right child columns and names based on the JoinColumn type. - switch { - case col.IsPureLeft(): - lhs.add(pe, colAlias) - case col.IsPureRight(): - rhs.add(pe, colAlias) - case col.IsMixedLeftAndRight(): - for _, lhsExpr := range col.LHSExprs { - var lhsAlias *sqlparser.IdentifierCI - if colAlias != nil { - // we need to add an explicit column alias here. let's try just the ColName as is first - ci := sqlparser.NewIdentifierCI(sqlparser.String(lhsExpr.Expr)) - lhsAlias = &ci - } - lhs.add(newProjExpr(aeWrap(lhsExpr.Expr)), lhsAlias) - } - innerPE := newProjExprWithInner(pe.Original, col.RHSExpr) - innerPE.ColExpr = col.RHSExpr - innerPE.Info = pe.Info - rhs.add(innerPE, colAlias) - } - return col, nil -} - -// exposeColumnsThroughDerivedTable rewrites expressions within a join that is inside a derived table -// in order to make them accessible outside the derived table. This is necessary when swapping the -// positions of the derived table and join operation. -// -// For example, consider the input query: -// select ... from (select T1.foo from T1 join T2 on T1.id = T2.id) as t -// If we push the derived table under the join, with T1 on the LHS of the join, we need to expose -// the values of T1.id through the derived table, or they will not be accessible on the RHS. -// -// The function iterates through each join predicate, rewriting the expressions in the predicate's -// LHS expressions to include the derived table. This allows the expressions to be accessed outside -// the derived table. -func exposeColumnsThroughDerivedTable(ctx *plancontext.PlanningContext, p *Projection, src *ApplyJoin, lhs *projector) error { - derivedTbl, err := ctx.SemTable.TableInfoFor(p.DT.TableID) - if err != nil { - return err - } - derivedTblName, err := derivedTbl.Name() - if err != nil { - return err - } - for _, predicate := range src.JoinPredicates { - for idx, bve := range predicate.LHSExprs { - expr := bve.Expr - tbl, err := ctx.SemTable.TableInfoForExpr(expr) - if err != nil { - return err - } - tblName, err := tbl.Name() - if err != nil { - return err - } - - expr = semantics.RewriteDerivedTableExpression(expr, derivedTbl) - out := prefixColNames(tblName, expr) - - alias := sqlparser.UnescapedString(out) - predicate.LHSExprs[idx].Expr = sqlparser.NewColNameWithQualifier(alias, derivedTblName) - identifierCI := sqlparser.NewIdentifierCI(alias) - projExpr := newProjExprWithInner(&sqlparser.AliasedExpr{Expr: out, As: identifierCI}, out) - var colAlias *sqlparser.IdentifierCI - if lhs.explicitColumnAliases { - colAlias = &identifierCI - } - lhs.add(projExpr, colAlias) - } - } - return nil -} - -// prefixColNames adds qualifier prefixes to all ColName:s. -// We want to be more explicit than the user was to make sure we never produce invalid SQL -func prefixColNames(tblName sqlparser.TableName, e sqlparser.Expr) sqlparser.Expr { - return sqlparser.CopyOnRewrite(e, nil, func(cursor *sqlparser.CopyOnWriteCursor) { - col, ok := cursor.Node().(*sqlparser.ColName) - if !ok { - return - } - col.Qualifier = tblName - }, nil).(sqlparser.Expr) -} - -func createProjectionWithTheseColumns( - ctx *plancontext.PlanningContext, - src ops.Operator, - p *projector, - dt *DerivedTable, -) (ops.Operator, error) { - if len(p.columns) == 0 { - return src, nil - } - proj, err := createProjection(ctx, src) - if err != nil { - return nil, err - } - proj.Columns = AliasedProjections(p.columns) - if dt != nil { - kopy := *dt - kopy.Columns = p.columnAliases - proj.DT = &kopy - } - - return proj, nil -} - -func tryPushLimit(in *Limit) (ops.Operator, *rewrite.ApplyResult, error) { +func tryPushLimit(in *Limit) (Operator, *ApplyResult) { switch src := in.Source.(type) { case *Route: return tryPushingDownLimitInRoute(in, src) case *Aggregator: - return in, rewrite.SameTree, nil + return in, NoRewrite default: return setUpperLimit(in) } } -func tryPushingDownLimitInRoute(in *Limit, src *Route) (ops.Operator, *rewrite.ApplyResult, error) { - if src.IsSingleShard() { - return rewrite.Swap(in, src, "push limit under route") +func tryPushingDownLimitInRoute(in *Limit, src *Route) (Operator, *ApplyResult) { + if src.IsSingleShardOrByDestination() { + return Swap(in, src, "push limit under route") } return setUpperLimit(in) } -func setUpperLimit(in *Limit) (ops.Operator, *rewrite.ApplyResult, error) { +func setUpperLimit(in *Limit) (Operator, *ApplyResult) { if in.Pushed { - return in, rewrite.SameTree, nil + return in, NoRewrite } in.Pushed = true - visitor := func(op ops.Operator, _ semantics.TableSet, _ bool) (ops.Operator, *rewrite.ApplyResult, error) { - return op, rewrite.SameTree, nil + visitor := func(op Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) { + return op, NoRewrite } - var result *rewrite.ApplyResult - shouldVisit := func(op ops.Operator) rewrite.VisitRule { + var result *ApplyResult + shouldVisit := func(op Operator) VisitRule { switch op := op.(type) { case *Join, *ApplyJoin, *SubQueryContainer, *SubQuery: // we can't push limits down on either side - return rewrite.SkipChildren + return SkipChildren case *Route: newSrc := &Limit{ Source: op.Source, @@ -521,46 +249,46 @@ func setUpperLimit(in *Limit) (ops.Operator, *rewrite.ApplyResult, error) { Pushed: false, } op.Source = newSrc - result = result.Merge(rewrite.NewTree("push limit under route", newSrc)) - return rewrite.SkipChildren + result = result.Merge(Rewrote("push upper limit under route")) + return SkipChildren default: - return rewrite.VisitChildren + return VisitChildren } } - _, err := rewrite.TopDown(in.Source, TableID, visitor, shouldVisit) - if err != nil { - return nil, nil, err - } - return in, result, nil + TopDown(in.Source, TableID, visitor, shouldVisit) + + return in, result } -func tryPushOrdering(ctx *plancontext.PlanningContext, in *Ordering) (ops.Operator, *rewrite.ApplyResult, error) { +func tryPushOrdering(ctx *plancontext.PlanningContext, in *Ordering) (Operator, *ApplyResult) { switch src := in.Source.(type) { case *Route: - return rewrite.Swap(in, src, "push ordering under route") + return Swap(in, src, "push ordering under route") + case *Filter: + return Swap(in, src, "push ordering under filter") case *ApplyJoin: if canPushLeft(ctx, src, in.Order) { // ApplyJoin is stable in regard to the columns coming from the LHS, // so if all the ordering columns come from the LHS, we can push down the Ordering there src.LHS, in.Source = in, src.LHS - return src, rewrite.NewTree("push down ordering on the LHS of a join", in), nil + return src, Rewrote("push down ordering on the LHS of a join") } case *Ordering: // we'll just remove the order underneath. The top order replaces whatever was incoming in.Source = src.Source - return in, rewrite.NewTree("remove double ordering", src), nil + return in, Rewrote("remove double ordering") case *Projection: // we can move ordering under a projection if it's not introducing a column we're sorting by for _, by := range in.Order { - if !fetchByOffset(by.SimplifiedExpr) { - return in, rewrite.SameTree, nil + if !mustFetchFromInput(by.SimplifiedExpr) { + return in, NoRewrite } } - return rewrite.Swap(in, src, "push ordering under projection") + return Swap(in, src, "push ordering under projection") case *Aggregator: if !src.QP.AlignGroupByAndOrderBy(ctx) && !overlaps(ctx, in.Order, src.Grouping) { - return in, rewrite.SameTree, nil + return in, NoRewrite } return pushOrderingUnderAggr(ctx, in, src) @@ -569,30 +297,30 @@ func tryPushOrdering(ctx *plancontext.PlanningContext, in *Ordering) (ops.Operat for _, order := range in.Order { deps := ctx.SemTable.RecursiveDeps(order.Inner.Expr) if !deps.IsSolvedBy(outerTableID) { - return in, rewrite.SameTree, nil + return in, NoRewrite } } src.Outer, in.Source = in, src.Outer - return src, rewrite.NewTree("push ordering into outer side of subquery", in), nil + return src, Rewrote("push ordering into outer side of subquery") case *SubQuery: outerTableID := TableID(src.Outer) for _, order := range in.Order { deps := ctx.SemTable.RecursiveDeps(order.Inner.Expr) if !deps.IsSolvedBy(outerTableID) { - return in, rewrite.SameTree, nil + return in, NoRewrite } } src.Outer, in.Source = in, src.Outer - return src, rewrite.NewTree("push ordering into outer side of subquery", in), nil + return src, Rewrote("push ordering into outer side of subquery") } - return in, rewrite.SameTree, nil + return in, NoRewrite } -func overlaps(ctx *plancontext.PlanningContext, order []ops.OrderBy, grouping []GroupBy) bool { +func overlaps(ctx *plancontext.PlanningContext, order []OrderBy, grouping []GroupBy) bool { ordering: for _, orderBy := range order { for _, groupBy := range grouping { - if ctx.SemTable.EqualsExprWithDeps(orderBy.SimplifiedExpr, groupBy.SimplifiedExpr) { + if ctx.SemTable.EqualsExprWithDeps(orderBy.SimplifiedExpr, groupBy.Inner) { continue ordering } } @@ -602,13 +330,13 @@ ordering: return true } -func pushOrderingUnderAggr(ctx *plancontext.PlanningContext, order *Ordering, aggregator *Aggregator) (ops.Operator, *rewrite.ApplyResult, error) { +func pushOrderingUnderAggr(ctx *plancontext.PlanningContext, order *Ordering, aggregator *Aggregator) (Operator, *ApplyResult) { // If Aggregator is a derived table, then we should rewrite the ordering before pushing. if aggregator.isDerived() { for idx, orderExpr := range order.Order { ti, err := ctx.SemTable.TableInfoFor(aggregator.DT.TableID) if err != nil { - return nil, nil, err + panic(err) } newOrderExpr := orderExpr.Map(func(expr sqlparser.Expr) sqlparser.Expr { return semantics.RewriteDerivedTableExpression(expr, ti) @@ -624,7 +352,7 @@ func pushOrderingUnderAggr(ctx *plancontext.PlanningContext, order *Ordering, ag used := make([]bool, len(aggregator.Grouping)) for _, orderExpr := range order.Order { for grpIdx, by := range aggregator.Grouping { - if !used[grpIdx] && ctx.SemTable.EqualsExprWithDeps(by.SimplifiedExpr, orderExpr.SimplifiedExpr) { + if !used[grpIdx] && ctx.SemTable.EqualsExprWithDeps(by.Inner, orderExpr.SimplifiedExpr) { newGrouping = append(newGrouping, by) used[grpIdx] = true } @@ -662,12 +390,12 @@ func pushOrderingUnderAggr(ctx *plancontext.PlanningContext, order *Ordering, ag order.Source = aggrSource.Source aggrSource.Source = nil // removing from plan tree aggregator.Source = order - return aggregator, rewrite.NewTree("push ordering under aggregation, removing extra ordering", aggregator), nil + return aggregator, Rewrote("push ordering under aggregation, removing extra ordering") } - return rewrite.Swap(order, aggregator, "push ordering under aggregation") + return Swap(order, aggregator, "push ordering under aggregation") } -func canPushLeft(ctx *plancontext.PlanningContext, aj *ApplyJoin, order []ops.OrderBy) bool { +func canPushLeft(ctx *plancontext.PlanningContext, aj *ApplyJoin, order []OrderBy) bool { lhs := TableID(aj.LHS) for _, order := range order { deps := ctx.SemTable.DirectDeps(order.Inner.Expr) @@ -678,7 +406,7 @@ func canPushLeft(ctx *plancontext.PlanningContext, aj *ApplyJoin, order []ops.Or return true } -func isOuterTable(op ops.Operator, ts semantics.TableSet) bool { +func isOuterTable(op Operator, ts semantics.TableSet) bool { aj, ok := op.(*ApplyJoin) if ok && aj.LeftJoin && TableID(aj.RHS).IsOverlapping(ts) { return true @@ -693,43 +421,39 @@ func isOuterTable(op ops.Operator, ts semantics.TableSet) bool { return false } -func tryPushFilter(ctx *plancontext.PlanningContext, in *Filter) (ops.Operator, *rewrite.ApplyResult, error) { +func tryPushFilter(ctx *plancontext.PlanningContext, in *Filter) (Operator, *ApplyResult) { switch src := in.Source.(type) { case *Projection: return pushFilterUnderProjection(ctx, in, src) case *Route: for _, pred := range in.Predicates { - var err error deps := ctx.SemTable.RecursiveDeps(pred) if !isOuterTable(src, deps) { // we can only update based on predicates on inner tables - src.Routing, err = src.Routing.updateRoutingLogic(ctx, pred) - if err != nil { - return nil, nil, err - } + src.Routing = src.Routing.updateRoutingLogic(ctx, pred) } } - return rewrite.Swap(in, src, "push filter into Route") + return Swap(in, src, "push filter into Route") case *SubQuery: outerTableID := TableID(src.Outer) for _, pred := range in.Predicates { deps := ctx.SemTable.RecursiveDeps(pred) if !deps.IsSolvedBy(outerTableID) { - return in, rewrite.SameTree, nil + return in, NoRewrite } } src.Outer, in.Source = in, src.Outer - return src, rewrite.NewTree("push filter to outer query in subquery container", in), nil + return src, Rewrote("push filter to outer query in subquery container") } - return in, rewrite.SameTree, nil + return in, NoRewrite } -func pushFilterUnderProjection(ctx *plancontext.PlanningContext, filter *Filter, projection *Projection) (ops.Operator, *rewrite.ApplyResult, error) { +func pushFilterUnderProjection(ctx *plancontext.PlanningContext, filter *Filter, projection *Projection) (Operator, *ApplyResult) { for _, p := range filter.Predicates { cantPush := false _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - if !fetchByOffset(node) { + if !mustFetchFromInput(node) { return true, nil } @@ -742,64 +466,64 @@ func pushFilterUnderProjection(ctx *plancontext.PlanningContext, filter *Filter, }, p) if cantPush { - return filter, rewrite.SameTree, nil + return filter, NoRewrite } } - return rewrite.Swap(filter, projection, "push filter under projection") + return Swap(filter, projection, "push filter under projection") } -func tryPushDistinct(in *Distinct) (ops.Operator, *rewrite.ApplyResult, error) { +func tryPushDistinct(in *Distinct) (Operator, *ApplyResult) { if in.Required && in.PushedPerformance { - return in, rewrite.SameTree, nil + return in, NoRewrite } switch src := in.Source.(type) { case *Route: if isDistinct(src.Source) && src.IsSingleShard() { - return src, rewrite.NewTree("distinct not needed", in), nil + return src, Rewrote("distinct not needed") } if src.IsSingleShard() || !in.Required { - return rewrite.Swap(in, src, "push distinct under route") + return Swap(in, src, "push distinct under route") } if isDistinct(src.Source) { - return in, rewrite.SameTree, nil + return in, NoRewrite } src.Source = &Distinct{Source: src.Source} in.PushedPerformance = true - return in, rewrite.NewTree("added distinct under route - kept original", src), nil + return in, Rewrote("added distinct under route - kept original") case *Distinct: src.Required = false src.PushedPerformance = false - return src, rewrite.NewTree("remove double distinct", src), nil + return src, Rewrote("remove double distinct") case *Union: for i := range src.Sources { src.Sources[i] = &Distinct{Source: src.Sources[i]} } in.PushedPerformance = true - return in, rewrite.NewTree("push down distinct under union", src), nil - case *ApplyJoin: - src.LHS = &Distinct{Source: src.LHS} - src.RHS = &Distinct{Source: src.RHS} + return in, Rewrote("push down distinct under union") + case JoinOp: + src.SetLHS(&Distinct{Source: src.GetLHS()}) + src.SetRHS(&Distinct{Source: src.GetRHS()}) in.PushedPerformance = true if in.Required { - return in, rewrite.NewTree("push distinct under join - kept original", in.Source), nil + return in, Rewrote("push distinct under join - kept original") } - return in.Source, rewrite.NewTree("push distinct under join", in.Source), nil + return in.Source, Rewrote("push distinct under join") case *Ordering: in.Source = src.Source - return in, rewrite.NewTree("remove ordering under distinct", in), nil + return in, Rewrote("remove ordering under distinct") } - return in, rewrite.SameTree, nil + return in, NoRewrite } -func isDistinct(op ops.Operator) bool { +func isDistinct(op Operator) bool { switch op := op.(type) { case *Distinct: return true @@ -814,73 +538,63 @@ func isDistinct(op ops.Operator) bool { } } -func tryPushUnion(ctx *plancontext.PlanningContext, op *Union) (ops.Operator, *rewrite.ApplyResult, error) { - if res := compactUnion(op); res != rewrite.SameTree { - return op, res, nil +func tryPushUnion(ctx *plancontext.PlanningContext, op *Union) (Operator, *ApplyResult) { + if res := compactUnion(op); res != NoRewrite { + return op, res } - var sources []ops.Operator + var sources []Operator var selects []sqlparser.SelectExprs - var err error if op.distinct { - sources, selects, err = mergeUnionInputInAnyOrder(ctx, op) + sources, selects = mergeUnionInputInAnyOrder(ctx, op) } else { - sources, selects, err = mergeUnionInputsInOrder(ctx, op) - } - if err != nil { - return nil, nil, err + sources, selects = mergeUnionInputsInOrder(ctx, op) } if len(sources) == 1 { result := sources[0].(*Route) if result.IsSingleShard() || !op.distinct { - return result, rewrite.NewTree("push union under route", op), nil + return result, Rewrote("push union under route") } return &Distinct{ Source: result, Required: true, - }, rewrite.NewTree("push union under route", op), nil + }, Rewrote("push union under route") } if len(sources) == len(op.Sources) { - return op, rewrite.SameTree, nil + return op, NoRewrite } - return newUnion(sources, selects, op.unionColumns, op.distinct), rewrite.NewTree("merge union inputs", op), nil + return newUnion(sources, selects, op.unionColumns, op.distinct), Rewrote("merge union inputs") } // addTruncationOrProjectionToReturnOutput uses the original Horizon to make sure that the output columns line up with what the user asked for -func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, oldHorizon ops.Operator, output ops.Operator) (ops.Operator, error) { +func addTruncationOrProjectionToReturnOutput(ctx *plancontext.PlanningContext, oldHorizon Operator, output Operator) Operator { horizon, ok := oldHorizon.(*Horizon) if !ok { - return output, nil + return output } cols := output.GetSelectExprs(ctx) sel := sqlparser.GetFirstSelect(horizon.Query) if len(sel.SelectExprs) == len(cols) { - return output, nil + return output } if tryTruncateColumnsAt(output, len(sel.SelectExprs)) { - return output, nil + return output } - qp, err := horizon.getQP(ctx) - if err != nil { - return nil, err - } - proj, err := createSimpleProjection(ctx, qp, output) - if err != nil { - return nil, err - } - return proj, nil + qp := horizon.getQP(ctx) + proj := createSimpleProjection(ctx, qp, output) + return proj } -func stopAtRoute(operator ops.Operator) rewrite.VisitRule { +func stopAtRoute(operator Operator) VisitRule { _, isRoute := operator.(*Route) - return rewrite.VisitRule(!isRoute) + return VisitRule(!isRoute) } func aeWrap(e sqlparser.Expr) *sqlparser.AliasedExpr { diff --git a/go/vt/vtgate/planbuilder/operators/querygraph.go b/go/vt/vtgate/planbuilder/operators/querygraph.go index b0e6b4440be..bc731f29df6 100644 --- a/go/vt/vtgate/planbuilder/operators/querygraph.go +++ b/go/vt/vtgate/planbuilder/operators/querygraph.go @@ -20,7 +20,6 @@ import ( "strings" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -63,7 +62,7 @@ type ( } ) -var _ ops.Operator = (*QueryGraph)(nil) +var _ Operator = (*QueryGraph)(nil) // Introduces implements the tableIDIntroducer interface func (qg *QueryGraph) introducesTableID() semantics.TableSet { @@ -163,7 +162,7 @@ func (qg *QueryGraph) UnsolvedPredicates(_ *semantics.SemTable) []sqlparser.Expr } // Clone implements the Operator interface -func (qg *QueryGraph) Clone([]ops.Operator) ops.Operator { +func (qg *QueryGraph) Clone([]Operator) Operator { result := &QueryGraph{ Tables: nil, innerJoins: nil, @@ -176,11 +175,11 @@ func (qg *QueryGraph) Clone([]ops.Operator) ops.Operator { return result } -func (qg *QueryGraph) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (qg *QueryGraph) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } -func (qg *QueryGraph) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (qg *QueryGraph) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { for _, e := range sqlparser.SplitAndExpression(nil, expr) { qg.collectPredicate(ctx, e) } diff --git a/go/vt/vtgate/planbuilder/operators/queryprojection.go b/go/vt/vtgate/planbuilder/operators/queryprojection.go index b7040807300..14bea4f4674 100644 --- a/go/vt/vtgate/planbuilder/operators/queryprojection.go +++ b/go/vt/vtgate/planbuilder/operators/queryprojection.go @@ -28,7 +28,6 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine/opcode" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -47,34 +46,21 @@ type ( HasAggr bool Distinct bool groupByExprs []GroupBy - OrderExprs []ops.OrderBy - HasStar bool + OrderExprs []OrderBy // AddedColumn keeps a counter for expressions added to solve HAVING expressions the user is not selecting AddedColumn int hasCheckedAlignment bool - - // TODO Remove once all horizon planning is done on the operators - CanPushSorting bool } // GroupBy contains the expression to used in group by and also if grouping is needed at VTGate level then what the weight_string function expression to be sent down for evaluation. GroupBy struct { Inner sqlparser.Expr - // The simplified expressions is the "unaliased expression". - // In the following query, the group by has the inner expression - // `x` and the `SimplifiedExpr` is `table.col + 10`: - // select table.col + 10 as x, count(*) from tbl group by x - SimplifiedExpr sqlparser.Expr - // The index at which the user expects to see this column. Set to nil, if the user does not ask for it InnerIndex *int - // The original aliased expression that this group by is referring - aliasedExpr *sqlparser.AliasedExpr - // points to the column on the same aggregator ColOffset int WSOffset int @@ -100,12 +86,14 @@ type ( // the offsets point to columns on the same aggregator ColOffset int WSOffset int + + SubQueryExpression []*SubQuery } AggrRewriter struct { - qp *QueryProjection - st *semantics.SemTable - Err error + qp *QueryProjection + st *semantics.SemTable + failed bool } ) @@ -115,7 +103,7 @@ func (aggr Aggr) NeedsWeightString(ctx *plancontext.PlanningContext) bool { func (aggr Aggr) GetTypeCollation(ctx *plancontext.PlanningContext) evalengine.Type { if aggr.Func == nil { - return evalengine.UnknownType() + return evalengine.Type{} } switch aggr.OpCode { case opcode.AggregateMin, opcode.AggregateMax, opcode.AggregateSumDistinct, opcode.AggregateCountDistinct: @@ -123,17 +111,15 @@ func (aggr Aggr) GetTypeCollation(ctx *plancontext.PlanningContext) evalengine.T return typ } - return evalengine.UnknownType() + return evalengine.Type{} } // NewGroupBy creates a new group by from the given fields. -func NewGroupBy(inner, simplified sqlparser.Expr, aliasedExpr *sqlparser.AliasedExpr) GroupBy { +func NewGroupBy(inner sqlparser.Expr) GroupBy { return GroupBy{ - Inner: inner, - SimplifiedExpr: simplified, - aliasedExpr: aliasedExpr, - ColOffset: -1, - WSOffset: -1, + Inner: inner, + ColOffset: -1, + WSOffset: -1, } } @@ -148,33 +134,13 @@ func NewAggr(opCode opcode.AggregateOpcode, f sqlparser.AggrFunc, original *sqlp } } -func (b GroupBy) AsOrderBy() ops.OrderBy { - return ops.OrderBy{ +func (b GroupBy) AsOrderBy() OrderBy { + return OrderBy{ Inner: &sqlparser.Order{ Expr: b.Inner, Direction: sqlparser.AscOrder, }, - SimplifiedExpr: b.SimplifiedExpr, - } -} - -func (b GroupBy) AsAliasedExpr() *sqlparser.AliasedExpr { - if b.aliasedExpr != nil { - return b.aliasedExpr - } - col, isColName := b.Inner.(*sqlparser.ColName) - if isColName && b.SimplifiedExpr != b.Inner { - return &sqlparser.AliasedExpr{ - Expr: b.SimplifiedExpr, - As: col.Name, - } - } - if !isColName && b.SimplifiedExpr != b.Inner { - panic("this should not happen - different inner and weighStringExpr and not a column alias") - } - - return &sqlparser.AliasedExpr{ - Expr: b.SimplifiedExpr, + SimplifiedExpr: b.Inner, } } @@ -202,35 +168,26 @@ func (s SelectExpr) GetAliasedExpr() (*sqlparser.AliasedExpr, error) { } // createQPFromSelect creates the QueryProjection for the input *sqlparser.Select -func createQPFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.Select) (*QueryProjection, error) { +func createQPFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.Select) *QueryProjection { qp := &QueryProjection{ Distinct: sel.Distinct, } - if err := qp.addSelectExpressions(sel); err != nil { - return nil, err - } - if err := qp.addGroupBy(ctx, sel.GroupBy); err != nil { - return nil, err - } - if err := qp.addOrderBy(ctx, sel.OrderBy); err != nil { - return nil, err - } + qp.addSelectExpressions(sel) + qp.addGroupBy(ctx, sel.GroupBy) + qp.addOrderBy(ctx, sel.OrderBy) if !qp.HasAggr && sel.Having != nil { qp.HasAggr = containsAggr(sel.Having.Expr) } + qp.calculateDistinct(ctx) - if err := qp.calculateDistinct(ctx); err != nil { - return nil, err - } - - return qp, nil + return qp } // RewriteDown stops the walker from entering inside aggregation functions func (ar *AggrRewriter) RewriteDown() func(sqlparser.SQLNode, sqlparser.SQLNode) bool { return func(node, _ sqlparser.SQLNode) bool { - if ar.Err != nil { + if ar.failed { return true } _, ok := node.(sqlparser.AggrFunc) @@ -241,7 +198,7 @@ func (ar *AggrRewriter) RewriteDown() func(sqlparser.SQLNode, sqlparser.SQLNode) // RewriteUp will go through an expression, add aggregations to the QP, and rewrite them to use column offset func (ar *AggrRewriter) RewriteUp() func(*sqlparser.Cursor) bool { return func(cursor *sqlparser.Cursor) bool { - if ar.Err != nil { + if ar.failed { return false } sqlNode := cursor.Node() @@ -252,7 +209,7 @@ func (ar *AggrRewriter) RewriteUp() func(*sqlparser.Cursor) bool { for offset, expr := range ar.qp.SelectExprs { ae, err := expr.GetAliasedExpr() if err != nil { - ar.Err = err + ar.failed = true return false } if ar.st.EqualsExprWithDeps(ae.Expr, fExp) { @@ -282,14 +239,10 @@ func (qp *QueryProjection) AggrRewriter(ctx *plancontext.PlanningContext) *AggrR } } -func (qp *QueryProjection) addSelectExpressions(sel *sqlparser.Select) error { +func (qp *QueryProjection) addSelectExpressions(sel *sqlparser.Select) { for _, selExp := range sel.SelectExprs { switch selExp := selExp.(type) { case *sqlparser.AliasedExpr: - err := checkForInvalidAggregations(selExp) - if err != nil { - return err - } col := SelectExpr{ Col: selExp, } @@ -300,23 +253,21 @@ func (qp *QueryProjection) addSelectExpressions(sel *sqlparser.Select) error { qp.SelectExprs = append(qp.SelectExprs, col) case *sqlparser.StarExpr: - qp.HasStar = true col := SelectExpr{ Col: selExp, } qp.SelectExprs = append(qp.SelectExprs, col) default: - return vterrors.VT13001(fmt.Sprintf("%T in select list", selExp)) + panic(vterrors.VT13001(fmt.Sprintf("%T in select list", selExp))) } } - return nil } func containsAggr(e sqlparser.SQLNode) (hasAggr bool) { _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { switch node.(type) { case *sqlparser.Offset: - // offsets here indicate that a possible aggregation has already been handled by an input + // offsets here indicate that a possible aggregation has already been handled by an input, // so we don't need to worry about aggregation in the original return false, nil case sqlparser.AggrFunc: @@ -332,21 +283,14 @@ func containsAggr(e sqlparser.SQLNode) (hasAggr bool) { } // createQPFromUnion creates the QueryProjection for the input *sqlparser.Union -func createQPFromUnion(ctx *plancontext.PlanningContext, union *sqlparser.Union) (*QueryProjection, error) { +func createQPFromUnion(ctx *plancontext.PlanningContext, union *sqlparser.Union) *QueryProjection { qp := &QueryProjection{} sel := sqlparser.GetFirstSelect(union) - err := qp.addSelectExpressions(sel) - if err != nil { - return nil, err - } - - err = qp.addOrderBy(ctx, union.OrderBy) - if err != nil { - return nil, err - } + qp.addSelectExpressions(sel) + qp.addOrderBy(ctx, union.OrderBy) - return qp, nil + return qp } type expressionSet struct { @@ -366,37 +310,28 @@ func (es *expressionSet) add(ctx *plancontext.PlanningContext, e sqlparser.Expr) return true } -func (qp *QueryProjection) addOrderBy(ctx *plancontext.PlanningContext, orderBy sqlparser.OrderBy) error { +func (qp *QueryProjection) addOrderBy(ctx *plancontext.PlanningContext, orderBy sqlparser.OrderBy) { canPushSorting := true es := &expressionSet{} for _, order := range orderBy { - simpleExpr, err := qp.GetSimplifiedExpr(ctx, order.Expr) - if err != nil { - return err - } - if sqlparser.IsNull(simpleExpr) { + if sqlparser.IsNull(order.Expr) { // ORDER BY null can safely be ignored continue } - if !es.add(ctx, simpleExpr) { + if !es.add(ctx, order.Expr) { continue } - qp.OrderExprs = append(qp.OrderExprs, ops.OrderBy{ - Inner: sqlparser.CloneRefOfOrder(order), - SimplifiedExpr: simpleExpr, + qp.OrderExprs = append(qp.OrderExprs, OrderBy{ + Inner: ctx.SemTable.Clone(order).(*sqlparser.Order), + SimplifiedExpr: order.Expr, }) - canPushSorting = canPushSorting && !containsAggr(simpleExpr) + canPushSorting = canPushSorting && !containsAggr(order.Expr) } - qp.CanPushSorting = canPushSorting - return nil } -func (qp *QueryProjection) calculateDistinct(ctx *plancontext.PlanningContext) error { +func (qp *QueryProjection) calculateDistinct(ctx *plancontext.PlanningContext) { if qp.Distinct && !qp.HasAggr { - distinct, err := qp.useGroupingOverDistinct(ctx) - if err != nil { - return err - } + distinct := qp.useGroupingOverDistinct(ctx) if distinct { // if order by exists with overlap with select expressions, we can use the aggregation with ordering over distinct. qp.Distinct = false @@ -412,11 +347,11 @@ func (qp *QueryProjection) calculateDistinct(ctx *plancontext.PlanningContext) e } if !qp.Distinct || len(qp.groupByExprs) == 0 { - return nil + return } for _, gb := range qp.groupByExprs { - _, found := canReuseColumn(ctx, qp.SelectExprs, gb.SimplifiedExpr, func(expr SelectExpr) sqlparser.Expr { + _, found := canReuseColumn(ctx, qp.SelectExprs, gb.Inner, func(expr SelectExpr) sqlparser.Expr { getExpr, err := expr.GetExpr() if err != nil { panic(err) @@ -424,38 +359,29 @@ func (qp *QueryProjection) calculateDistinct(ctx *plancontext.PlanningContext) e return getExpr }) if !found { - return nil + return } } // since we are returning all grouping expressions, we know the results are guaranteed to be unique qp.Distinct = false - return nil } -func (qp *QueryProjection) addGroupBy(ctx *plancontext.PlanningContext, groupBy sqlparser.GroupBy) error { +func (qp *QueryProjection) addGroupBy(ctx *plancontext.PlanningContext, groupBy sqlparser.GroupBy) { es := &expressionSet{} - for _, group := range groupBy { - selectExprIdx, aliasExpr := qp.FindSelectExprIndexForExpr(ctx, group) - simpleExpr, err := qp.GetSimplifiedExpr(ctx, group) - if err != nil { - return err - } - - if err = checkForInvalidGroupingExpressions(simpleExpr); err != nil { - return err - } + for _, grouping := range groupBy { + selectExprIdx := qp.FindSelectExprIndexForExpr(ctx, grouping) + checkForInvalidGroupingExpressions(grouping) - if !es.add(ctx, simpleExpr) { + if !es.add(ctx, grouping) { continue } - groupBy := NewGroupBy(group, simpleExpr, aliasExpr) + groupBy := NewGroupBy(grouping) groupBy.InnerIndex = selectExprIdx qp.groupByExprs = append(qp.groupByExprs, groupBy) } - return nil } // GetGrouping returns a copy of the grouping parameters of the QP @@ -463,88 +389,15 @@ func (qp *QueryProjection) GetGrouping() []GroupBy { return slices.Clone(qp.groupByExprs) } -func checkForInvalidAggregations(exp *sqlparser.AliasedExpr) error { - return sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - aggrFunc, isAggregate := node.(sqlparser.AggrFunc) - if !isAggregate { - return true, nil - } - args := aggrFunc.GetArgs() - if args != nil && len(args) != 1 { - return false, vterrors.VT03001(sqlparser.String(node)) - } - return true, nil - - }, exp.Expr) -} - func (qp *QueryProjection) isExprInGroupByExprs(ctx *plancontext.PlanningContext, expr sqlparser.Expr) bool { for _, groupByExpr := range qp.groupByExprs { - if ctx.SemTable.EqualsExprWithDeps(groupByExpr.SimplifiedExpr, expr) { + if ctx.SemTable.EqualsExprWithDeps(groupByExpr.Inner, expr) { return true } } return false } -// GetSimplifiedExpr takes an expression used in ORDER BY or GROUP BY, and returns an expression that is simpler to evaluate -func (qp *QueryProjection) GetSimplifiedExpr(ctx *plancontext.PlanningContext, e sqlparser.Expr) (found sqlparser.Expr, err error) { - if qp == nil { - return e, nil - } - // If the ORDER BY is against a column alias, we need to remember the expression - // behind the alias. The weightstring(.) calls needs to be done against that expression and not the alias. - // Eg - select music.foo as bar, weightstring(music.foo) from music order by bar - - in, isColName := e.(*sqlparser.ColName) - if !(isColName && in.Qualifier.IsEmpty()) { - // we are only interested in unqualified column names. if it's not a column name and not unqualified, we're done - return e, nil - } - - check := func(e sqlparser.Expr) error { - if found != nil && !ctx.SemTable.EqualsExprWithDeps(found, e) { - return &semantics.AmbiguousColumnError{Column: sqlparser.String(in)} - } - found = e - return nil - } - - for _, selectExpr := range qp.SelectExprs { - ae, ok := selectExpr.Col.(*sqlparser.AliasedExpr) - if !ok { - continue - } - aliased := !ae.As.IsEmpty() - if aliased { - if in.Name.Equal(ae.As) { - err = check(ae.Expr) - if err != nil { - return nil, err - } - } - } else { - seCol, ok := ae.Expr.(*sqlparser.ColName) - if !ok { - continue - } - if seCol.Name.Equal(in.Name) { - // If the column name matches, we have a match, even if the table name is not listed - err = check(ae.Expr) - if err != nil { - return nil, err - } - } - } - } - - if found == nil { - found = e - } - - return found, nil -} - // toString should only be used for tests func (qp *QueryProjection) toString() string { type output struct { @@ -585,83 +438,6 @@ func (qp *QueryProjection) NeedsAggregation() bool { return qp.HasAggr || len(qp.groupByExprs) > 0 } -// NeedsProjecting returns true if we have projections that need to be evaluated at the vtgate level -// and can't be pushed down to MySQL -func (qp *QueryProjection) NeedsProjecting( - ctx *plancontext.PlanningContext, - pusher func(expr *sqlparser.AliasedExpr) (int, error), -) (needsVtGateEval bool, expressions []sqlparser.Expr, colNames []string, err error) { - for _, se := range qp.SelectExprs { - var ae *sqlparser.AliasedExpr - ae, err = se.GetAliasedExpr() - if err != nil { - return false, nil, nil, err - } - - expr := ae.Expr - colNames = append(colNames, ae.ColumnName()) - - if _, isCol := expr.(*sqlparser.ColName); isCol { - offset, err := pusher(ae) - if err != nil { - return false, nil, nil, err - } - expressions = append(expressions, sqlparser.NewOffset(offset, expr)) - continue - } - - stopOnError := func(sqlparser.SQLNode, sqlparser.SQLNode) bool { - return err == nil - } - rewriter := func(cursor *sqlparser.CopyOnWriteCursor) { - col, isCol := cursor.Node().(*sqlparser.ColName) - if !isCol { - return - } - var tableInfo semantics.TableInfo - tableInfo, err = ctx.SemTable.TableInfoForExpr(col) - if err != nil { - return - } - dt, isDT := tableInfo.(*semantics.DerivedTable) - if !isDT { - return - } - - rewritten := semantics.RewriteDerivedTableExpression(col, dt) - if containsAggr(rewritten) { - offset, tErr := pusher(&sqlparser.AliasedExpr{Expr: col}) - if tErr != nil { - err = tErr - return - } - cursor.Replace(sqlparser.NewOffset(offset, col)) - } - } - newExpr := sqlparser.CopyOnRewrite(expr, stopOnError, rewriter, nil) - - if err != nil { - return - } - - if newExpr != expr { - // if we changed the expression, it means that we have to evaluate the rest at the vtgate level - expressions = append(expressions, newExpr.(sqlparser.Expr)) - needsVtGateEval = true - continue - } - - // we did not need to push any parts of this expression down. Let's check if we can push all of it - offset, err := pusher(ae) - if err != nil { - return false, nil, nil, err - } - expressions = append(expressions, sqlparser.NewOffset(offset, expr)) - } - - return -} - func (qp *QueryProjection) onlyAggr() bool { if !qp.HasAggr { return false @@ -685,33 +461,21 @@ func (qp *QueryProjection) NeedsDistinct() bool { return true } -func (qp *QueryProjection) AggregationExpressions(ctx *plancontext.PlanningContext, allowComplexExpression bool) (out []Aggr, complex bool, err error) { -orderBy: - for _, orderExpr := range qp.OrderExprs { - orderExpr := orderExpr.SimplifiedExpr - for _, expr := range qp.SelectExprs { - col, ok := expr.Col.(*sqlparser.AliasedExpr) - if !ok { - continue - } - if ctx.SemTable.EqualsExprWithDeps(col.Expr, orderExpr) { - continue orderBy // we found the expression we were looking for! - } - } - qp.SelectExprs = append(qp.SelectExprs, SelectExpr{ - Col: &sqlparser.AliasedExpr{Expr: orderExpr}, - Aggr: containsAggr(orderExpr), - }) - qp.AddedColumn++ +func (qp *QueryProjection) AggregationExpressions(ctx *plancontext.PlanningContext, allowComplexExpression bool) (out []Aggr, complex bool) { + qp.addOrderByToSelect(ctx) + addAggr := func(a Aggr) { + out = append(out, a) + } + makeComplex := func() { + complex = true } - // Here we go over the expressions we are returning. Since we know we are aggregating, // all expressions have to be either grouping expressions or aggregate expressions. // If we find an expression that is neither, we treat is as a special aggregation function AggrRandom for idx, expr := range qp.SelectExprs { aliasedExpr, err := expr.GetAliasedExpr() if err != nil { - return nil, false, err + panic(err) } idxCopy := idx @@ -719,7 +483,7 @@ orderBy: if !containsAggr(expr.Col) { getExpr, err := expr.GetExpr() if err != nil { - return nil, false, err + panic(err) } if !qp.isExprInGroupByExprs(ctx, getExpr) { aggr := NewAggr(opcode.AggregateAnyValue, nil, aliasedExpr, aliasedExpr.ColumnName()) @@ -730,37 +494,69 @@ orderBy: } _, isAggregate := aliasedExpr.Expr.(sqlparser.AggrFunc) if !isAggregate && !allowComplexExpression { - return nil, false, vterrors.VT12001("in scatter query: complex aggregate expression") + panic(vterrors.VT12001("in scatter query: complex aggregate expression")) } - sqlparser.CopyOnRewrite(aliasedExpr.Expr, func(node, parent sqlparser.SQLNode) bool { - ex, isExpr := node.(sqlparser.Expr) - if !isExpr { - return true - } - if aggr, isAggr := node.(sqlparser.AggrFunc); isAggr { - ae := aeWrap(aggr) - if aggr == aliasedExpr.Expr { - ae = aliasedExpr - } - aggrFunc := createAggrFromAggrFunc(aggr, ae) - aggrFunc.Index = &idxCopy - out = append(out, aggrFunc) - return false + sqlparser.CopyOnRewrite(aliasedExpr.Expr, qp.extractAggr(ctx, idx, aliasedExpr, addAggr, makeComplex), nil, nil) + } + return +} + +func (qp *QueryProjection) extractAggr( + ctx *plancontext.PlanningContext, + idx int, + aliasedExpr *sqlparser.AliasedExpr, + addAggr func(a Aggr), + makeComplex func(), +) func(node sqlparser.SQLNode, parent sqlparser.SQLNode) bool { + return func(node, parent sqlparser.SQLNode) bool { + ex, isExpr := node.(sqlparser.Expr) + if !isExpr { + return true + } + if aggr, isAggr := node.(sqlparser.AggrFunc); isAggr { + ae := aeWrap(aggr) + if aggr == aliasedExpr.Expr { + ae = aliasedExpr } - if containsAggr(node) { - complex = true - return true + aggrFunc := createAggrFromAggrFunc(aggr, ae) + aggrFunc.Index = &idx + addAggr(aggrFunc) + return false + } + if containsAggr(node) { + makeComplex() + return true + } + if !qp.isExprInGroupByExprs(ctx, ex) { + aggr := NewAggr(opcode.AggregateAnyValue, nil, aeWrap(ex), "") + aggr.Index = &idx + addAggr(aggr) + } + return false + } +} + +func (qp *QueryProjection) addOrderByToSelect(ctx *plancontext.PlanningContext) { +orderBy: + // We need to return all columns that are being used for ordering + for _, orderExpr := range qp.OrderExprs { + orderExpr := orderExpr.SimplifiedExpr + for _, expr := range qp.SelectExprs { + col, ok := expr.Col.(*sqlparser.AliasedExpr) + if !ok { + continue } - if !qp.isExprInGroupByExprs(ctx, ex) { - aggr := NewAggr(opcode.AggregateAnyValue, nil, aeWrap(ex), "") - aggr.Index = &idxCopy - out = append(out, aggr) + if ctx.SemTable.EqualsExprWithDeps(col.Expr, orderExpr) { + continue orderBy // we found the expression we were looking for! } - return false - }, nil, nil) + } + qp.SelectExprs = append(qp.SelectExprs, SelectExpr{ + Col: &sqlparser.AliasedExpr{Expr: orderExpr}, + Aggr: containsAggr(orderExpr), + }) + qp.AddedColumn++ } - return } func createAggrFromAggrFunc(fnc sqlparser.AggrFunc, aliasedExpr *sqlparser.AliasedExpr) Aggr { @@ -789,7 +585,7 @@ func createAggrFromAggrFunc(fnc sqlparser.AggrFunc, aliasedExpr *sqlparser.Alias // FindSelectExprIndexForExpr returns the index of the given expression in the select expressions, if it is part of it // returns -1 otherwise. -func (qp *QueryProjection) FindSelectExprIndexForExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (*int, *sqlparser.AliasedExpr) { +func (qp *QueryProjection) FindSelectExprIndexForExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr) *int { colExpr, isCol := expr.(*sqlparser.ColName) for idx, selectExpr := range qp.SelectExprs { @@ -798,16 +594,16 @@ func (qp *QueryProjection) FindSelectExprIndexForExpr(ctx *plancontext.PlanningC continue } if isCol { - isAliasExpr := !aliasedExpr.As.IsEmpty() + isAliasExpr := aliasedExpr.As.NotEmpty() if isAliasExpr && colExpr.Name.Equal(aliasedExpr.As) { - return &idx, aliasedExpr + return &idx } } if ctx.SemTable.EqualsExprWithDeps(aliasedExpr.Expr, expr) { - return &idx, aliasedExpr + return &idx } } - return nil, nil + return nil } // OldAlignGroupByAndOrderBy TODO Remove once all of horizon planning is done on the operators @@ -829,7 +625,7 @@ func (qp *QueryProjection) OldAlignGroupByAndOrderBy(ctx *plancontext.PlanningCo used := make([]bool, len(qp.groupByExprs)) for _, orderExpr := range qp.OrderExprs { for i, groupingExpr := range qp.groupByExprs { - if !used[i] && ctx.SemTable.EqualsExpr(groupingExpr.SimplifiedExpr, orderExpr.SimplifiedExpr) { + if !used[i] && ctx.SemTable.EqualsExpr(groupingExpr.Inner, orderExpr.SimplifiedExpr) { newGrouping = append(newGrouping, groupingExpr) used[i] = true } @@ -869,7 +665,7 @@ func (qp *QueryProjection) AlignGroupByAndOrderBy(ctx *plancontext.PlanningConte outer: for _, orderBy := range qp.OrderExprs { for gidx, groupBy := range qp.groupByExprs { - if ctx.SemTable.EqualsExprWithDeps(groupBy.SimplifiedExpr, orderBy.SimplifiedExpr) { + if ctx.SemTable.EqualsExprWithDeps(groupBy.Inner, orderBy.SimplifiedExpr) { newGrouping = append(newGrouping, groupBy) used[gidx] = true continue outer @@ -900,7 +696,7 @@ func (qp *QueryProjection) GetColumnCount() int { func (qp *QueryProjection) orderByOverlapWithSelectExpr(ctx *plancontext.PlanningContext) bool { for _, expr := range qp.OrderExprs { - idx, _ := qp.FindSelectExprIndexForExpr(ctx, expr.SimplifiedExpr) + idx := qp.FindSelectExprIndexForExpr(ctx, expr.SimplifiedExpr) if idx != nil { return true } @@ -908,47 +704,43 @@ func (qp *QueryProjection) orderByOverlapWithSelectExpr(ctx *plancontext.Plannin return false } -func (qp *QueryProjection) useGroupingOverDistinct(ctx *plancontext.PlanningContext) (bool, error) { +func (qp *QueryProjection) useGroupingOverDistinct(ctx *plancontext.PlanningContext) bool { if !qp.orderByOverlapWithSelectExpr(ctx) { - return false, nil + return false } var gbs []GroupBy for idx, selExpr := range qp.SelectExprs { ae, err := selExpr.GetAliasedExpr() if err != nil { // not an alias Expr, cannot continue forward. - return false, nil - } - sExpr, err := qp.GetSimplifiedExpr(ctx, ae.Expr) - if err != nil { - return false, err + return false } // check if the grouping already exists on that column. found := slices.IndexFunc(qp.groupByExprs, func(gb GroupBy) bool { - return ctx.SemTable.EqualsExprWithDeps(gb.SimplifiedExpr, sExpr) + return ctx.SemTable.EqualsExprWithDeps(gb.Inner, ae.Expr) }) if found != -1 { continue } - groupBy := NewGroupBy(ae.Expr, sExpr, ae) + groupBy := NewGroupBy(ae.Expr) selectExprIdx := idx groupBy.InnerIndex = &selectExprIdx gbs = append(gbs, groupBy) } qp.groupByExprs = append(qp.groupByExprs, gbs...) - return true, nil + return true } -func checkForInvalidGroupingExpressions(expr sqlparser.Expr) error { - return sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { +func checkForInvalidGroupingExpressions(expr sqlparser.Expr) { + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { if _, isAggregate := node.(sqlparser.AggrFunc); isAggregate { - return false, vterrors.VT03005(sqlparser.String(expr)) + panic(vterrors.VT03005(sqlparser.String(expr))) } _, isSubQ := node.(*sqlparser.Subquery) arg, isArg := node.(*sqlparser.Argument) if isSubQ || (isArg && strings.HasPrefix(arg.Name, "__sq")) { - return false, vterrors.VT12001("subqueries in GROUP BY") + panic(vterrors.VT12001("subqueries in GROUP BY")) } return true, nil }, expr) @@ -972,12 +764,12 @@ func CompareRefInt(a *int, b *int) bool { return *a < *b } -func CreateQPFromSelectStatement(ctx *plancontext.PlanningContext, stmt sqlparser.SelectStatement) (*QueryProjection, error) { +func CreateQPFromSelectStatement(ctx *plancontext.PlanningContext, stmt sqlparser.SelectStatement) *QueryProjection { switch sel := stmt.(type) { case *sqlparser.Select: return createQPFromSelect(ctx, sel) case *sqlparser.Union: return createQPFromUnion(ctx, sel) } - return nil, vterrors.VT13001("can only create query projection from Union and Select statements") + panic(vterrors.VT13001("can only create query projection from Union and Select statements")) } diff --git a/go/vt/vtgate/planbuilder/operators/queryprojection_test.go b/go/vt/vtgate/planbuilder/operators/queryprojection_test.go deleted file mode 100644 index 7c92b716d7c..00000000000 --- a/go/vt/vtgate/planbuilder/operators/queryprojection_test.go +++ /dev/null @@ -1,245 +0,0 @@ -/* -Copyright 2021 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package operators - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/vt/vtgate/semantics" -) - -func TestQP(t *testing.T) { - tcases := []struct { - sql string - - expErr string - expOrder []ops.OrderBy - }{ - { - sql: "select * from user", - }, - { - sql: "select 1, count(1) from user", - }, - { - sql: "select max(id) from user", - }, - { - sql: "select 1, count(1) from user order by 1", - expOrder: []ops.OrderBy{ - {Inner: &sqlparser.Order{Expr: sqlparser.NewIntLiteral("1")}, SimplifiedExpr: sqlparser.NewIntLiteral("1")}, - }, - }, - { - sql: "select id from user order by col, id, 1", - expOrder: []ops.OrderBy{ - {Inner: &sqlparser.Order{Expr: sqlparser.NewColName("col")}, SimplifiedExpr: sqlparser.NewColName("col")}, - {Inner: &sqlparser.Order{Expr: sqlparser.NewColName("id")}, SimplifiedExpr: sqlparser.NewColName("id")}, - }, - }, - { - sql: "SELECT CONCAT(last_name,', ',first_name) AS full_name FROM mytable ORDER BY full_name", // alias in order not supported - expOrder: []ops.OrderBy{ - { - Inner: &sqlparser.Order{Expr: sqlparser.NewColName("full_name")}, - SimplifiedExpr: &sqlparser.FuncExpr{ - Name: sqlparser.NewIdentifierCI("CONCAT"), - Exprs: sqlparser.SelectExprs{ - &sqlparser.AliasedExpr{Expr: sqlparser.NewColName("last_name")}, - &sqlparser.AliasedExpr{Expr: sqlparser.NewStrLiteral(", ")}, - &sqlparser.AliasedExpr{Expr: sqlparser.NewColName("first_name")}, - }, - }, - }, - }, - }, { - sql: "select count(*) b from user group by b", - expErr: "cannot group on 'count(*)'", - }, - } - ctx := &plancontext.PlanningContext{SemTable: semantics.EmptySemTable()} - for _, tcase := range tcases { - t.Run(tcase.sql, func(t *testing.T) { - stmt, err := sqlparser.Parse(tcase.sql) - require.NoError(t, err) - - sel := stmt.(*sqlparser.Select) - _, err = semantics.Analyze(sel, "", &semantics.FakeSI{}) - require.NoError(t, err) - - qp, err := createQPFromSelect(ctx, sel) - if tcase.expErr != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tcase.expErr) - } else { - require.NoError(t, err) - assert.Equal(t, len(sel.SelectExprs), len(qp.SelectExprs)) - require.Equal(t, len(tcase.expOrder), len(qp.OrderExprs), "not enough order expressions in QP") - for index, expOrder := range tcase.expOrder { - assert.True(t, sqlparser.Equals.SQLNode(expOrder.Inner, qp.OrderExprs[index].Inner), "want: %+v, got %+v", sqlparser.String(expOrder.Inner), sqlparser.String(qp.OrderExprs[index].Inner)) - assert.True(t, sqlparser.Equals.SQLNode(expOrder.SimplifiedExpr, qp.OrderExprs[index].SimplifiedExpr), "want: %v, got %v", sqlparser.String(expOrder.SimplifiedExpr), sqlparser.String(qp.OrderExprs[index].SimplifiedExpr)) - } - } - }) - } -} - -func TestQPSimplifiedExpr(t *testing.T) { - testCases := []struct { - query, expected string - }{ - { - query: "select intcol, count(*) from user group by 1", - expected: ` -{ - "Select": [ - "intcol", - "aggr: count(*)" - ], - "Grouping": [ - "intcol" - ], - "OrderBy": [], - "Distinct": false -}`, - }, - { - query: "select intcol, textcol from user order by 1, textcol", - expected: ` -{ - "Select": [ - "intcol", - "textcol" - ], - "Grouping": [], - "OrderBy": [ - "intcol asc", - "textcol asc" - ], - "Distinct": false -}`, - }, - { - query: "select intcol, textcol, count(id) from user group by intcol, textcol, extracol order by 2 desc", - expected: ` -{ - "Select": [ - "intcol", - "textcol", - "aggr: count(id)" - ], - "Grouping": [ - "intcol", - "textcol", - "extracol" - ], - "OrderBy": [ - "textcol desc" - ], - "Distinct": false -}`, - }, - { - query: "select distinct col1, col2 from user group by col1, col2", - expected: ` -{ - "Select": [ - "col1", - "col2" - ], - "Grouping": [], - "OrderBy": [], - "Distinct": true -}`, - }, - { - query: "select distinct count(*) from user", - expected: ` -{ - "Select": [ - "aggr: count(*)" - ], - "Grouping": [], - "OrderBy": [], - "Distinct": false -}`, - }, - } - - for _, tc := range testCases { - t.Run(tc.query, func(t *testing.T) { - ast, err := sqlparser.Parse(tc.query) - require.NoError(t, err) - sel := ast.(*sqlparser.Select) - _, err = semantics.Analyze(sel, "", &semantics.FakeSI{}) - require.NoError(t, err) - ctx := &plancontext.PlanningContext{SemTable: semantics.EmptySemTable()} - qp, err := createQPFromSelect(ctx, sel) - require.NoError(t, err) - require.Equal(t, tc.expected[1:], qp.toString()) - }) - } -} - -func TestCompareRefInt(t *testing.T) { - one := 1 - two := 2 - tests := []struct { - name string - a *int - b *int - want bool - }{ - { - name: "1<2", - a: &one, - b: &two, - want: true, - }, { - name: "2<1", - a: &two, - b: &one, - want: false, - }, { - name: "2>>>>>>> " + message) } - return &ApplyResult{Transformations: []Rewrite{{Message: message, Op: op}}} + return &ApplyResult{Transformations: []Rewrite{{Message: message}}} } func (ar *ApplyResult) Merge(other *ApplyResult) *ApplyResult { @@ -83,13 +81,13 @@ func (ar *ApplyResult) Changed() bool { } // Visit allows for the walking of the operator tree. If any error is returned, the walk is aborted -func Visit(root ops.Operator, visitor func(ops.Operator) error) error { - _, _, err := breakableTopDown(root, func(op ops.Operator) (ops.Operator, *ApplyResult, VisitRule, error) { +func Visit(root Operator, visitor func(Operator) error) error { + _, _, err := breakableTopDown(root, func(op Operator) (Operator, *ApplyResult, VisitRule, error) { err := visitor(op) if err != nil { - return nil, SameTree, SkipChildren, err + return nil, NoRewrite, SkipChildren, err } - return op, SameTree, VisitChildren, nil + return op, NoRewrite, VisitChildren, nil }) return err } @@ -98,16 +96,13 @@ func Visit(root ops.Operator, visitor func(ops.Operator) error) error { // the given operator tree from the bottom up. Each callback [f] returns a ApplyResult that is aggregated // into a final output indicating whether the operator tree was changed. func BottomUp( - root ops.Operator, - resolveID func(ops.Operator) semantics.TableSet, + root Operator, + resolveID func(Operator) semantics.TableSet, visit VisitF, shouldVisit ShouldVisit, -) (ops.Operator, error) { - op, _, err := bottomUp(root, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) - if err != nil { - return nil, err - } - return op, nil +) Operator { + op, _ := bottomUp(root, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) + return op } var DebugOperatorTree = false @@ -123,39 +118,23 @@ func EnableDebugPrinting() (reset func()) { // FixedPointBottomUp rewrites an operator tree much like BottomUp does, // but does the rewriting repeatedly, until a tree walk is done with no changes to the tree. func FixedPointBottomUp( - root ops.Operator, - resolveID func(ops.Operator) semantics.TableSet, + root Operator, + resolveID func(Operator) semantics.TableSet, visit VisitF, shouldVisit ShouldVisit, -) (op ops.Operator, err error) { +) (op Operator) { var id *ApplyResult op = root // will loop while the rewriting changes anything - for ok := true; ok; ok = id != SameTree { + for ok := true; ok; ok = id != NoRewrite { if DebugOperatorTree { - fmt.Println(ops.ToTree(op)) + fmt.Println(ToTree(op)) } // Continue the top-down rewriting process as long as changes were made during the last traversal - op, id, err = bottomUp(op, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) - if err != nil { - return nil, err - } + op, id = bottomUp(op, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) } - return op, nil -} - -// BottomUpAll rewrites an operator tree from the bottom up. BottomUp applies a transformation function to -// the given operator tree from the bottom up. Each callback [f] returns a ApplyResult that is aggregated -// into a final output indicating whether the operator tree was changed. -func BottomUpAll( - root ops.Operator, - resolveID func(ops.Operator) semantics.TableSet, - visit VisitF, -) (ops.Operator, error) { - return BottomUp(root, resolveID, visit, func(ops.Operator) VisitRule { - return VisitChildren - }) + return op } // TopDown rewrites an operator tree from the bottom up. BottomUp applies a transformation function to @@ -169,31 +148,28 @@ func BottomUpAll( // - shouldVisit: The ShouldVisit function to control which nodes and ancestors to visit and which to skip. // // Returns: -// - ops.Operator: The root of the (potentially) transformed operator tree. +// - Operator: The root of the (potentially) transformed operator tree. // - error: An error if any occurred during the traversal. func TopDown( - root ops.Operator, - resolveID func(ops.Operator) semantics.TableSet, + root Operator, + resolveID func(Operator) semantics.TableSet, visit VisitF, shouldVisit ShouldVisit, -) (op ops.Operator, err error) { - op, _, err = topDown(root, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) - if err != nil { - return nil, err - } +) Operator { + op, _ := topDown(root, semantics.EmptyTableSet(), resolveID, visit, shouldVisit, true) - return op, nil + return op } // Swap takes a tree like a->b->c and swaps `a` and `b`, so we end up with b->a->c -func Swap(parent, child ops.Operator, message string) (ops.Operator, *ApplyResult, error) { +func Swap(parent, child Operator, message string) (Operator, *ApplyResult) { c := child.Inputs() if len(c) != 1 { - return nil, nil, vterrors.VT13001("Swap can only be used on single input operators") + panic(vterrors.VT13001("Swap can only be used on single input operators")) } aInputs := slices.Clone(parent.Inputs()) - var tmp ops.Operator + var tmp Operator for i, in := range aInputs { if in == child { tmp = aInputs[i] @@ -202,30 +178,30 @@ func Swap(parent, child ops.Operator, message string) (ops.Operator, *ApplyResul } } if tmp == nil { - return nil, nil, vterrors.VT13001("Swap can only be used when the second argument is an input to the first") + panic(vterrors.VT13001("Swap can only be used when the second argument is an input to the first")) } - child.SetInputs([]ops.Operator{parent}) + child.SetInputs([]Operator{parent}) parent.SetInputs(aInputs) - return child, NewTree(message, parent), nil + return child, Rewrote(message) } func bottomUp( - root ops.Operator, + root Operator, rootID semantics.TableSet, - resolveID func(ops.Operator) semantics.TableSet, + resolveID func(Operator) semantics.TableSet, rewriter VisitF, shouldVisit ShouldVisit, isRoot bool, -) (ops.Operator, *ApplyResult, error) { +) (Operator, *ApplyResult) { if shouldVisit != nil && !shouldVisit(root) { - return root, SameTree, nil + return root, NoRewrite } oldInputs := root.Inputs() var anythingChanged *ApplyResult - newInputs := make([]ops.Operator, len(oldInputs)) + newInputs := make([]Operator, len(oldInputs)) childID := rootID // noLHSTableSet is used to mark which operators that do not send data from the LHS to the RHS @@ -241,10 +217,7 @@ func bottomUp( if _, isUnion := root.(noLHSTableSet); !isUnion && i > 0 { childID = childID.Merge(resolveID(oldInputs[0])) } - in, changed, err := bottomUp(operator, childID, resolveID, rewriter, shouldVisit, false) - if err != nil { - return nil, nil, err - } + in, changed := bottomUp(operator, childID, resolveID, rewriter, shouldVisit, false) anythingChanged = anythingChanged.Merge(changed) newInputs[i] = in } @@ -253,18 +226,15 @@ func bottomUp( root = root.Clone(newInputs) } - newOp, treeIdentity, err := rewriter(root, rootID, isRoot) - if err != nil { - return nil, nil, err - } + newOp, treeIdentity := rewriter(root, rootID, isRoot) anythingChanged = anythingChanged.Merge(treeIdentity) - return newOp, anythingChanged, nil + return newOp, anythingChanged } func breakableTopDown( - in ops.Operator, - rewriter func(ops.Operator) (ops.Operator, *ApplyResult, VisitRule, error), -) (ops.Operator, *ApplyResult, error) { + in Operator, + rewriter func(Operator) (Operator, *ApplyResult, VisitRule, error), +) (Operator, *ApplyResult, error) { newOp, identity, visit, err := rewriter(in) if err != nil || visit == SkipChildren { return newOp, identity, err @@ -273,17 +243,17 @@ func breakableTopDown( var anythingChanged *ApplyResult oldInputs := newOp.Inputs() - newInputs := make([]ops.Operator, len(oldInputs)) + newInputs := make([]Operator, len(oldInputs)) for i, oldInput := range oldInputs { newInputs[i], identity, err = breakableTopDown(oldInput, rewriter) anythingChanged = anythingChanged.Merge(identity) if err != nil { - return nil, SameTree, err + return nil, NoRewrite, err } } if anythingChanged.Changed() { - return newOp, SameTree, nil + return newOp, NoRewrite, nil } return newOp.Clone(newInputs), anythingChanged, nil @@ -293,20 +263,17 @@ func breakableTopDown( // top down and applies the given transformation function. It also returns the ApplyResult // indicating whether the tree was changed func topDown( - root ops.Operator, + root Operator, rootID semantics.TableSet, - resolveID func(ops.Operator) semantics.TableSet, + resolveID func(Operator) semantics.TableSet, rewriter VisitF, shouldVisit ShouldVisit, isRoot bool, -) (ops.Operator, *ApplyResult, error) { - newOp, anythingChanged, err := rewriter(root, rootID, isRoot) - if err != nil { - return nil, nil, err - } +) (Operator, *ApplyResult) { + newOp, anythingChanged := rewriter(root, rootID, isRoot) if !shouldVisit(root) { - return newOp, anythingChanged, nil + return newOp, anythingChanged } if anythingChanged.Changed() { @@ -314,7 +281,7 @@ func topDown( } oldInputs := root.Inputs() - newInputs := make([]ops.Operator, len(oldInputs)) + newInputs := make([]Operator, len(oldInputs)) childID := rootID type noLHSTableSet interface{ NoLHSTableSet() } @@ -323,17 +290,14 @@ func topDown( if _, isUnion := root.(noLHSTableSet); !isUnion && i > 0 { childID = childID.Merge(resolveID(oldInputs[0])) } - in, changed, err := topDown(operator, childID, resolveID, rewriter, shouldVisit, false) - if err != nil { - return nil, nil, err - } + in, changed := topDown(operator, childID, resolveID, rewriter, shouldVisit, false) anythingChanged = anythingChanged.Merge(changed) newInputs[i] = in } - if anythingChanged != SameTree { - return root.Clone(newInputs), anythingChanged, nil + if anythingChanged != NoRewrite { + return root.Clone(newInputs), anythingChanged } - return root, SameTree, nil + return root, NoRewrite } diff --git a/go/vt/vtgate/planbuilder/operators/route.go b/go/vt/vtgate/planbuilder/operators/route.go index d4b2c43ecff..5742877fbe3 100644 --- a/go/vt/vtgate/planbuilder/operators/route.go +++ b/go/vt/vtgate/planbuilder/operators/route.go @@ -21,11 +21,13 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/slice" + "vitess.io/vitess/go/vt/key" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -33,7 +35,7 @@ import ( type ( Route struct { - Source ops.Operator + Source Operator // Routes that have been merged into this one. MergedWith []*Route @@ -89,7 +91,7 @@ type ( Routing interface { // UpdateRoutingParams allows a Routing to control the routing params that will be used by the engine Route // OpCode is already set, and the default keyspace is set for read queries - UpdateRoutingParams(ctx *plancontext.PlanningContext, rp *engine.RoutingParameters) error + UpdateRoutingParams(ctx *plancontext.PlanningContext, rp *engine.RoutingParameters) // Clone returns a copy of the routing. Since we are trying different variation of merging, // one Routing can be used in different constellations. @@ -103,27 +105,27 @@ type ( // updateRoutingLogic updates the routing to take predicates into account. This can be used for routing // using vindexes or for figuring out which keyspace an information_schema query should be sent to. - updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (Routing, error) + updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Routing } ) // UpdateRoutingLogic first checks if we are dealing with a predicate that -func UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr, r Routing) (Routing, error) { +func UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr, r Routing) Routing { ks := r.Keyspace() if ks == nil { var err error ks, err = ctx.VSchema.AnyKeyspace() if err != nil { - return nil, err + panic(err) } } nr := &NoneRouting{keyspace: ks} - if isConstantFalse(expr) { - return nr, nil + if isConstantFalse(ctx.VSchema.Environment(), expr, ctx.VSchema.ConnCollation()) { + return nr } - exit := func() (Routing, error) { + exit := func() Routing { return r.updateRoutingLogic(ctx, expr) } @@ -135,7 +137,7 @@ func UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr, r if cmp.Operator != sqlparser.NullSafeEqualOp && (sqlparser.IsNull(cmp.Left) || sqlparser.IsNull(cmp.Right)) { // any comparison against a literal null, except a null safe equality (<=>), will return null - return nr, nil + return nr } tuples, ok := cmp.Right.(sqlparser.ValTuple) @@ -148,13 +150,13 @@ func UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr, r for _, n := range tuples { // If any of the values in the tuple is a literal null, we know that this comparison will always return NULL if sqlparser.IsNull(n) { - return nr, nil + return nr } } case sqlparser.InOp: // WHERE col IN (null) if len(tuples) == 1 && sqlparser.IsNull(tuples[0]) { - return nr, nil + return nr } } @@ -163,9 +165,12 @@ func UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr, r // isConstantFalse checks whether this predicate can be evaluated at plan-time. If it returns `false` or `null`, // we know that the query will not return anything, and this can be used to produce better plans -func isConstantFalse(expr sqlparser.Expr) bool { - eenv := evalengine.EmptyExpressionEnv() - eexpr, err := evalengine.Translate(expr, nil) +func isConstantFalse(env *vtenv.Environment, expr sqlparser.Expr, collation collations.ID) bool { + eenv := evalengine.EmptyExpressionEnv(env) + eexpr, err := evalengine.Translate(expr, &evalengine.Config{ + Collation: collation, + Environment: env, + }) if err != nil { return false } @@ -173,7 +178,7 @@ func isConstantFalse(expr sqlparser.Expr) bool { if err != nil { return false } - if eres.Value(collations.Default()).IsNull() { + if eres.Value(collation).IsNull() { return false } b, err := eres.ToBooleanStrict() @@ -189,7 +194,7 @@ func (r *Route) Cost() int { } // Clone implements the Operator interface -func (r *Route) Clone(inputs []ops.Operator) ops.Operator { +func (r *Route) Clone(inputs []Operator) Operator { cloneRoute := *r cloneRoute.Source = inputs[0] cloneRoute.Routing = r.Routing.Clone() @@ -197,12 +202,12 @@ func (r *Route) Clone(inputs []ops.Operator) ops.Operator { } // Inputs implements the Operator interface -func (r *Route) Inputs() []ops.Operator { - return []ops.Operator{r.Source} +func (r *Route) Inputs() []Operator { + return []Operator{r.Source} } // SetInputs implements the Operator interface -func (r *Route) SetInputs(ops []ops.Operator) { +func (r *Route) SetInputs(ops []Operator) { r.Source = ops[0] } @@ -256,7 +261,9 @@ func (option *VindexOption) updateWithNewColumn( opcode func(*vindexes.ColumnVindex) engine.Opcode, ) bool { option.ColsSeen[colLoweredName] = true - option.ValueExprs = append(option.ValueExprs, valueExpr) + if valueExpr != nil { + option.ValueExprs = append(option.ValueExprs, valueExpr) + } option.Values[indexOfCol] = value option.Predicates[indexOfCol] = node option.Ready = len(option.ColsSeen) == len(colVindex.Columns) @@ -276,6 +283,14 @@ func (r *Route) IsSingleShard() bool { return false } +func (r *Route) IsSingleShardOrByDestination() bool { + switch r.Routing.OpCode() { + case engine.Unsharded, engine.DBA, engine.Next, engine.EqualUnique, engine.Reference, engine.ByDestination: + return true + } + return false +} + func tupleAccess(expr sqlparser.Expr, coordinates []int) sqlparser.Expr { tuple, _ := expr.(sqlparser.ValTuple) for _, idx := range coordinates { @@ -356,12 +371,11 @@ func (vpp *VindexPlusPredicates) bestOption() *VindexOption { func createRoute( ctx *plancontext.PlanningContext, queryTable *QueryTable, - solves semantics.TableSet, -) (ops.Operator, error) { +) Operator { if queryTable.IsInfSchema { return createInfSchemaRoute(ctx, queryTable) } - return findVSchemaTableAndCreateRoute(ctx, queryTable, queryTable.Table, solves, true /*planAlternates*/) + return findVSchemaTableAndCreateRoute(ctx, queryTable, queryTable.Table, true /*planAlternates*/) } // findVSchemaTableAndCreateRoute consults the VSchema to find a suitable @@ -370,34 +384,64 @@ func findVSchemaTableAndCreateRoute( ctx *plancontext.PlanningContext, queryTable *QueryTable, tableName sqlparser.TableName, - solves semantics.TableSet, planAlternates bool, -) (*Route, error) { - vschemaTable, _, _, _, target, err := ctx.VSchema.FindTableOrVindex(tableName) - if target != nil { - return nil, vterrors.VT09017("SELECT with a target destination is not allowed") - } +) *Route { + vschemaTable, _, _, tabletType, target, err := ctx.VSchema.FindTableOrVindex(tableName) if err != nil { - return nil, err + panic(err) } + targeted := createTargetedRouting(ctx, target, tabletType, vschemaTable) + return createRouteFromVSchemaTable( ctx, queryTable, vschemaTable, - solves, planAlternates, + targeted, ) } +func createTargetedRouting(ctx *plancontext.PlanningContext, target key.Destination, tabletType topodatapb.TabletType, vschemaTable *vindexes.Table) Routing { + switch ctx.Statement.(type) { + case *sqlparser.Update: + if tabletType != topodatapb.TabletType_PRIMARY { + panic(vterrors.VT09002("update")) + } + case *sqlparser.Delete: + if tabletType != topodatapb.TabletType_PRIMARY { + panic(vterrors.VT09002("delete")) + } + case *sqlparser.Insert: + if tabletType != topodatapb.TabletType_PRIMARY { + panic(vterrors.VT09002("insert")) + } + if target != nil { + panic(vterrors.VT09017("INSERT with a target destination is not allowed")) + } + case sqlparser.SelectStatement: + if target != nil { + panic(vterrors.VT09017("SELECT with a target destination is not allowed")) + } + } + + if target != nil { + return &TargetedRouting{ + keyspace: vschemaTable.Keyspace, + TargetDestination: target, + } + } + return nil +} + // createRouteFromTable creates a route from the given VSchema table. func createRouteFromVSchemaTable( ctx *plancontext.PlanningContext, queryTable *QueryTable, vschemaTable *vindexes.Table, - solves semantics.TableSet, planAlternates bool, -) (*Route, error) { + targeted Routing, +) *Route { if vschemaTable.Name.String() != queryTable.Table.Name.String() { // we are dealing with a routed table queryTable = queryTable.Clone() @@ -405,7 +449,7 @@ func createRouteFromVSchemaTable( queryTable.Table.Name = vschemaTable.Name astTable, ok := queryTable.Alias.Expr.(sqlparser.TableName) if !ok { - return nil, vterrors.VT13001("a derived table should never be a routed table") + panic(vterrors.VT13001("a derived table should never be a routed table")) } realTableName := sqlparser.NewIdentifierCS(vschemaTable.Name.String()) astTable.Name = realTableName @@ -421,14 +465,16 @@ func createRouteFromVSchemaTable( }, } - // We create the appropiate Routing struct here, depending on the type of table we are dealing with. - routing := createRoutingForVTable(vschemaTable, solves) + // We create the appropriate Routing struct here, depending on the type of table we are dealing with. + var routing Routing + if targeted != nil { + routing = targeted + } else { + routing = createRoutingForVTable(ctx, vschemaTable, queryTable.ID) + } + for _, predicate := range queryTable.Predicates { - var err error - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err - } + routing = UpdateRoutingLogic(ctx, predicate, routing) } plan.Routing = routing @@ -436,27 +482,19 @@ func createRouteFromVSchemaTable( switch routing := routing.(type) { case *ShardedRouting: if routing.isScatter() && len(queryTable.Predicates) > 0 { - var err error // If we have a scatter query, it's worth spending a little extra time seeing if we can't improve it - plan.Routing, err = routing.tryImprove(ctx, queryTable) - if err != nil { - return nil, err - } + plan.Routing = routing.tryImprove(ctx, queryTable) } case *AnyShardRouting: if planAlternates { - alternates, err := createAlternateRoutesFromVSchemaTable(ctx, queryTable, vschemaTable, solves) - if err != nil { - return nil, err - } - routing.Alternates = alternates + routing.Alternates = createAlternateRoutesFromVSchemaTable(ctx, queryTable, vschemaTable) } } - return plan, nil + return plan } -func createRoutingForVTable(vschemaTable *vindexes.Table, id semantics.TableSet) Routing { +func createRoutingForVTable(ctx *plancontext.PlanningContext, vschemaTable *vindexes.Table, id semantics.TableSet) Routing { switch { case vschemaTable.Type == vindexes.TypeSequence: return &SequenceRouting{keyspace: vschemaTable.Keyspace} @@ -465,7 +503,7 @@ func createRoutingForVTable(vschemaTable *vindexes.Table, id semantics.TableSet) case vschemaTable.Type == vindexes.TypeReference || !vschemaTable.Keyspace.Sharded: return &AnyShardRouting{keyspace: vschemaTable.Keyspace} default: - return newShardedRouting(vschemaTable, id) + return newShardedRouting(ctx, vschemaTable, id) } } @@ -473,40 +511,31 @@ func createAlternateRoutesFromVSchemaTable( ctx *plancontext.PlanningContext, queryTable *QueryTable, vschemaTable *vindexes.Table, - solves semantics.TableSet, -) (map[*vindexes.Keyspace]*Route, error) { +) map[*vindexes.Keyspace]*Route { routes := make(map[*vindexes.Keyspace]*Route) switch vschemaTable.Type { case "", vindexes.TypeReference: for ksName, referenceTable := range vschemaTable.ReferencedBy { - route, err := findVSchemaTableAndCreateRoute( + route := findVSchemaTableAndCreateRoute( ctx, queryTable, sqlparser.TableName{ Name: referenceTable.Name, Qualifier: sqlparser.NewIdentifierCS(ksName), }, - solves, false, /*planAlternates*/ ) - if err != nil { - return nil, err - } routes[referenceTable.Keyspace] = route } if vschemaTable.Source != nil { - route, err := findVSchemaTableAndCreateRoute( + route := findVSchemaTableAndCreateRoute( ctx, queryTable, vschemaTable.Source.TableName, - solves, false, /*planAlternates*/ ) - if err != nil { - return nil, err - } keyspace := route.Routing.Keyspace() if keyspace != nil { routes[keyspace] = route @@ -514,15 +543,12 @@ func createAlternateRoutesFromVSchemaTable( } } - return routes, nil + return routes } -func (r *Route) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (r *Route) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { // first we see if the predicate changes how we route - newRouting, err := UpdateRoutingLogic(ctx, expr, r.Routing) - if err != nil { - panic(err) - } + newRouting := UpdateRoutingLogic(ctx, expr, r.Routing) r.Routing = newRouting // we also need to push the predicate down into the query @@ -530,16 +556,23 @@ func (r *Route) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Ex return r } -func createProjection(ctx *plancontext.PlanningContext, src ops.Operator) (*Projection, error) { +func createProjection(ctx *plancontext.PlanningContext, src Operator, derivedName string) *Projection { proj := newAliasedProjection(src) cols := src.GetColumns(ctx) for _, col := range cols { - _, err := proj.addUnexploredExpr(col, col.Expr) - if err != nil { - return nil, err + if derivedName == "" { + proj.addUnexploredExpr(col, col.Expr) + continue } + + // for derived tables, we want to use the exposed colname + tableName := sqlparser.NewTableName(derivedName) + columnName := col.ColumnName() + colName := sqlparser.NewColNameWithQualifier(columnName, tableName) + ctx.SemTable.CopySemanticInfo(col.Expr, colName) + proj.addUnexploredExpr(aeWrap(colName), colName) } - return proj, nil + return proj } func (r *Route) AddColumn(ctx *plancontext.PlanningContext, reuse bool, gb bool, expr *sqlparser.AliasedExpr) int { @@ -554,17 +587,14 @@ func (r *Route) AddColumn(ctx *plancontext.PlanningContext, reuse bool, gb bool, // if at least one column is not already present, we check if we can easily find a projection // or aggregation in our source that we can add to - op, ok, offsets := addMultipleColumnsToInput(ctx, r.Source, reuse, []bool{gb}, []*sqlparser.AliasedExpr{expr}) + derived, op, ok, offsets := addMultipleColumnsToInput(ctx, r.Source, reuse, []bool{gb}, []*sqlparser.AliasedExpr{expr}) r.Source = op if ok { return offsets[0] } // If no-one could be found, we probably don't have one yet, so we add one here - src, err := createProjection(ctx, r.Source) - if err != nil { - panic(err) - } + src := createProjection(ctx, r.Source, derived) r.Source = src offsets = src.addColumnsWithoutPushing(ctx, reuse, []bool{gb}, []*sqlparser.AliasedExpr{expr}) @@ -572,60 +602,69 @@ func (r *Route) AddColumn(ctx *plancontext.PlanningContext, reuse bool, gb bool, } type selectExpressions interface { - ops.Operator + Operator addColumnWithoutPushing(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExpr, addToGroupBy bool) int addColumnsWithoutPushing(ctx *plancontext.PlanningContext, reuse bool, addToGroupBy []bool, exprs []*sqlparser.AliasedExpr) []int - isDerived() bool + derivedName() string } -// addColumnToInput adds a column to an operator without pushing it down. -// It will return a bool indicating whether the addition was successful or not, -// and an offset to where the column can be found -func addMultipleColumnsToInput(ctx *plancontext.PlanningContext, operator ops.Operator, reuse bool, addToGroupBy []bool, exprs []*sqlparser.AliasedExpr) (ops.Operator, bool, []int) { +// addColumnToInput adds columns to an operator without pushing them down +func addMultipleColumnsToInput( + ctx *plancontext.PlanningContext, + operator Operator, + reuse bool, + addToGroupBy []bool, + exprs []*sqlparser.AliasedExpr, +) (derivedName string, // if we found a derived table, this will contain its name + projection Operator, // if an operator needed to be built, it will be returned here + found bool, // whether a matching op was found or not + offsets []int, // the offsets the expressions received +) { switch op := operator.(type) { case *SubQuery: - src, added, offset := addMultipleColumnsToInput(ctx, op.Outer, reuse, addToGroupBy, exprs) + derivedName, src, added, offset := addMultipleColumnsToInput(ctx, op.Outer, reuse, addToGroupBy, exprs) if added { op.Outer = src } - return op, added, offset + return derivedName, op, added, offset case *Distinct: - src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) + derivedName, src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) if added { op.Source = src } - return op, added, offset + return derivedName, op, added, offset case *Limit: - src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) + derivedName, src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) if added { op.Source = src } - return op, added, offset + return derivedName, op, added, offset case *Ordering: - src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) + derivedName, src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) if added { op.Source = src } - return op, added, offset + return derivedName, op, added, offset case *LockAndComment: - src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) + derivedName, src, added, offset := addMultipleColumnsToInput(ctx, op.Source, reuse, addToGroupBy, exprs) if added { op.Source = src } - return op, added, offset + return derivedName, op, added, offset case selectExpressions: - if op.isDerived() { + name := op.derivedName() + if name != "" { // if the only thing we can push to is a derived table, // we have to add a new projection and can't build on this one - return op, false, nil + return name, op, false, nil } offset := op.addColumnsWithoutPushing(ctx, reuse, addToGroupBy, exprs) - return op, true, offset + return "", op, true, offset case *Union: tableID := semantics.SingleTableSet(len(ctx.SemTable.Tables)) @@ -641,7 +680,7 @@ func addMultipleColumnsToInput(ctx *plancontext.PlanningContext, operator ops.Op } return addMultipleColumnsToInput(ctx, proj, reuse, addToGroupBy, exprs) default: - return op, false, nil + return "", op, false, nil } } @@ -657,7 +696,7 @@ func (r *Route) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Selec return r.Source.GetSelectExprs(ctx) } -func (r *Route) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (r *Route) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return r.Source.GetOrdering(ctx) } @@ -673,7 +712,7 @@ func (r *Route) TablesUsed() []string { return collect() } -func isSpecialOrderBy(o ops.OrderBy) bool { +func isSpecialOrderBy(o OrderBy) bool { if sqlparser.IsNull(o.Inner.Expr) { return true } @@ -681,17 +720,17 @@ func isSpecialOrderBy(o ops.OrderBy) bool { return isFunction && f.Name.Lowered() == "rand" } -func (r *Route) planOffsets(ctx *plancontext.PlanningContext) { +func (r *Route) planOffsets(ctx *plancontext.PlanningContext) Operator { // if operator is returning data from a single shard, we don't need to do anything more if r.IsSingleShard() { - return + return nil } // if we are getting results from multiple shards, we need to do a merge-sort // between them to get the final output correctly sorted ordering := r.Source.GetOrdering(ctx) if len(ordering) == 0 { - return + return nil } for _, order := range ordering { @@ -713,6 +752,7 @@ func (r *Route) planOffsets(ctx *plancontext.PlanningContext) { } r.Ordering = append(r.Ordering, o) } + return nil } func weightStringFor(expr sqlparser.Expr) sqlparser.Expr { diff --git a/go/vt/vtgate/planbuilder/operators/route_planning.go b/go/vt/vtgate/planbuilder/operators/route_planning.go index 079813388b3..f7276ea48c7 100644 --- a/go/vt/vtgate/planbuilder/operators/route_planning.go +++ b/go/vt/vtgate/planbuilder/operators/route_planning.go @@ -20,14 +20,11 @@ import ( "bytes" "io" - "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -38,34 +35,34 @@ type ( left, right semantics.TableSet } - opCacheMap map[tableSetPair]ops.Operator + opCacheMap map[tableSetPair]Operator ) -func pushDerived(ctx *plancontext.PlanningContext, op *Horizon) (ops.Operator, *rewrite.ApplyResult, error) { +func pushDerived(ctx *plancontext.PlanningContext, op *Horizon) (Operator, *ApplyResult) { innerRoute, ok := op.Source.(*Route) if !ok { - return op, rewrite.SameTree, nil + return op, NoRewrite } if !(innerRoute.Routing.OpCode() == engine.EqualUnique) && !op.IsMergeable(ctx) { // no need to check anything if we are sure that we will only hit a single shard - return op, rewrite.SameTree, nil + return op, NoRewrite } - return rewrite.Swap(op, op.Source, "push derived under route") + return Swap(op, op.Source, "push derived under route") } -func optimizeJoin(ctx *plancontext.PlanningContext, op *Join) (ops.Operator, *rewrite.ApplyResult, error) { +func optimizeJoin(ctx *plancontext.PlanningContext, op *Join) (Operator, *ApplyResult) { return mergeOrJoin(ctx, op.LHS, op.RHS, sqlparser.SplitAndExpression(nil, op.Predicate), !op.LeftJoin) } -func optimizeQueryGraph(ctx *plancontext.PlanningContext, op *QueryGraph) (result ops.Operator, changed *rewrite.ApplyResult, err error) { +func optimizeQueryGraph(ctx *plancontext.PlanningContext, op *QueryGraph) (result Operator, changed *ApplyResult) { switch { case ctx.PlannerVersion == querypb.ExecuteOptions_Gen4Left2Right: - result, err = leftToRightSolve(ctx, op) + result = leftToRightSolve(ctx, op) default: - result, err = greedySolve(ctx, op) + result = greedySolve(ctx, op) } unresolved := op.UnsolvedPredicates(ctx.SemTable) @@ -75,7 +72,7 @@ func optimizeQueryGraph(ctx *plancontext.PlanningContext, op *QueryGraph) (resul result = newFilter(result, ctx.SemTable.AndExpressions(unresolved...)) } - changed = rewrite.NewTree("solved query graph", result) + changed = Rewrote("solved query graph") return } @@ -84,42 +81,39 @@ func buildVindexTableForDML( tableInfo semantics.TableInfo, table *QueryTable, dmlType string, -) (*vindexes.Table, Routing, error) { +) (*vindexes.Table, Routing) { vindexTable := tableInfo.GetVindexTable() if vindexTable.Source != nil { sourceTable, _, _, _, _, err := ctx.VSchema.FindTableOrVindex(vindexTable.Source.TableName) if err != nil { - return nil, nil, err + panic(err) } vindexTable = sourceTable } if !vindexTable.Keyspace.Sharded { - return vindexTable, &AnyShardRouting{keyspace: vindexTable.Keyspace}, nil + return vindexTable, &AnyShardRouting{keyspace: vindexTable.Keyspace} } - var dest key.Destination - var typ topodatapb.TabletType - var err error tblName, ok := table.Alias.Expr.(sqlparser.TableName) if !ok { - return nil, nil, vterrors.VT12001("multi shard UPDATE with LIMIT") + panic(vterrors.VT12001("multi shard UPDATE with LIMIT")) } - _, _, _, typ, dest, err = ctx.VSchema.FindTableOrVindex(tblName) + _, _, _, typ, dest, err := ctx.VSchema.FindTableOrVindex(tblName) if err != nil { - return nil, nil, err + panic(err) } if dest == nil { routing := &ShardedRouting{ keyspace: vindexTable.Keyspace, RouteOpCode: engine.Scatter, } - return vindexTable, routing, nil + return vindexTable, routing } if typ != topodatapb.TabletType_PRIMARY { - return nil, nil, vterrors.VT09002(dmlType) + panic(vterrors.VT09002(dmlType)) } // we are dealing with an explicitly targeted DML @@ -127,48 +121,7 @@ func buildVindexTableForDML( keyspace: vindexTable.Keyspace, TargetDestination: dest, } - return vindexTable, routing, nil -} - -func generateOwnedVindexQuery(tblExpr sqlparser.TableExpr, del *sqlparser.Delete, table *vindexes.Table, ksidCols []sqlparser.IdentifierCI) string { - buf := sqlparser.NewTrackedBuffer(nil) - for idx, col := range ksidCols { - if idx == 0 { - buf.Myprintf("select %v", col) - } else { - buf.Myprintf(", %v", col) - } - } - for _, cv := range table.Owned { - for _, column := range cv.Columns { - buf.Myprintf(", %v", column) - } - } - buf.Myprintf(" from %v%v%v%v for update", tblExpr, del.Where, del.OrderBy, del.Limit) - return buf.String() -} - -func getUpdateVindexInformation( - ctx *plancontext.PlanningContext, - updStmt *sqlparser.Update, - vindexTable *vindexes.Table, - tableID semantics.TableSet, - assignments []SetExpr, -) ([]*VindexPlusPredicates, map[string]*engine.VindexValues, string, []string, error) { - if !vindexTable.Keyspace.Sharded { - return nil, nil, "", nil, nil - } - - primaryVindex, vindexAndPredicates, err := getVindexInformation(tableID, vindexTable) - if err != nil { - return nil, nil, "", nil, err - } - - changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex, err := buildChangedVindexesValues(ctx, updStmt, vindexTable, primaryVindex.Columns, assignments) - if err != nil { - return nil, nil, "", nil, err - } - return vindexAndPredicates, changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex, nil + return vindexTable, routing } /* @@ -177,67 +130,50 @@ func getUpdateVindexInformation( and removes the two inputs to this cheapest plan and instead adds the join. As an optimization, it first only considers joining tables that have predicates defined between them */ -func greedySolve(ctx *plancontext.PlanningContext, qg *QueryGraph) (ops.Operator, error) { - routeOps, err := seedOperatorList(ctx, qg) +func greedySolve(ctx *plancontext.PlanningContext, qg *QueryGraph) Operator { + routeOps := seedOperatorList(ctx, qg) planCache := opCacheMap{} - if err != nil { - return nil, err - } - op, err := mergeRoutes(ctx, qg, routeOps, planCache, false) - if err != nil { - return nil, err - } - return op, nil + return mergeRoutes(ctx, qg, routeOps, planCache, false) } -func leftToRightSolve(ctx *plancontext.PlanningContext, qg *QueryGraph) (ops.Operator, error) { - plans, err := seedOperatorList(ctx, qg) - if err != nil { - return nil, err - } +func leftToRightSolve(ctx *plancontext.PlanningContext, qg *QueryGraph) Operator { + plans := seedOperatorList(ctx, qg) - var acc ops.Operator + var acc Operator for _, plan := range plans { if acc == nil { acc = plan continue } joinPredicates := qg.GetPredicates(TableID(acc), TableID(plan)) - acc, _, err = mergeOrJoin(ctx, acc, plan, joinPredicates, true) - if err != nil { - return nil, err - } + acc, _ = mergeOrJoin(ctx, acc, plan, joinPredicates, true) } - return acc, nil + return acc } // seedOperatorList returns a route for each table in the qg -func seedOperatorList(ctx *plancontext.PlanningContext, qg *QueryGraph) ([]ops.Operator, error) { - plans := make([]ops.Operator, len(qg.Tables)) +func seedOperatorList(ctx *plancontext.PlanningContext, qg *QueryGraph) []Operator { + plans := make([]Operator, len(qg.Tables)) // we start by seeding the table with the single routes for i, table := range qg.Tables { - solves := ctx.SemTable.TableSetFor(table.Alias) - plan, err := createRoute(ctx, table, solves) - if err != nil { - return nil, err - } + plan := createRoute(ctx, table) if qg.NoDeps != nil { plan = plan.AddPredicate(ctx, qg.NoDeps) } plans[i] = plan } - return plans, nil + return plans } -func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) (ops.Operator, error) { +func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) Operator { ks, err := ctx.VSchema.AnyKeyspace() if err != nil { - return nil, err + panic(err) } - var src ops.Operator = &Table{ + var src Operator = &Table{ QTable: table, VTable: &vindexes.Table{ Name: table.Table.Name, @@ -246,26 +182,20 @@ func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) ( } var routing Routing = &InfoSchemaRouting{} for _, pred := range table.Predicates { - routing, err = UpdateRoutingLogic(ctx, pred, routing) - if err != nil { - return nil, err - } + routing = UpdateRoutingLogic(ctx, pred, routing) } return &Route{ Source: src, Routing: routing, - }, nil + } } -func mergeRoutes(ctx *plancontext.PlanningContext, qg *QueryGraph, physicalOps []ops.Operator, planCache opCacheMap, crossJoinsOK bool) (ops.Operator, error) { +func mergeRoutes(ctx *plancontext.PlanningContext, qg *QueryGraph, physicalOps []Operator, planCache opCacheMap, crossJoinsOK bool) Operator { if len(physicalOps) == 0 { - return nil, nil + return nil } for len(physicalOps) > 1 { - bestTree, lIdx, rIdx, err := findBestJoin(ctx, qg, physicalOps, planCache, crossJoinsOK) - if err != nil { - return nil, err - } + bestTree, lIdx, rIdx := findBestJoin(ctx, qg, physicalOps, planCache, crossJoinsOK) // if we found a plan, we'll replace the two plans that were joined with the join plan created if bestTree != nil { // we remove one plan, and replace the other @@ -279,7 +209,7 @@ func mergeRoutes(ctx *plancontext.PlanningContext, qg *QueryGraph, physicalOps [ physicalOps = append(physicalOps, bestTree) } else { if crossJoinsOK { - return nil, vterrors.VT13001("should not happen: we should be able to merge cross joins") + panic(vterrors.VT13001("should not happen: we should be able to merge cross joins")) } // we will only fail to find a join plan when there are only cross joins left // when that happens, we switch over to allow cross joins as well. @@ -287,20 +217,20 @@ func mergeRoutes(ctx *plancontext.PlanningContext, qg *QueryGraph, physicalOps [ crossJoinsOK = true } } - return physicalOps[0], nil + return physicalOps[0] } -func removeAt(plans []ops.Operator, idx int) []ops.Operator { +func removeAt(plans []Operator, idx int) []Operator { return append(plans[:idx], plans[idx+1:]...) } func findBestJoin( ctx *plancontext.PlanningContext, qg *QueryGraph, - plans []ops.Operator, + plans []Operator, planCache opCacheMap, crossJoinsOK bool, -) (bestPlan ops.Operator, lIdx int, rIdx int, err error) { +) (bestPlan Operator, lIdx int, rIdx int) { for i, lhs := range plans { for j, rhs := range plans { if i == j { @@ -313,10 +243,7 @@ func findBestJoin( // cartesian product, which is almost always a bad idea continue } - plan, err := getJoinFor(ctx, planCache, lhs, rhs, joinPredicates) - if err != nil { - return nil, 0, 0, err - } + plan := getJoinFor(ctx, planCache, lhs, rhs, joinPredicates) if bestPlan == nil || CostOf(plan) < CostOf(bestPlan) { bestPlan = plan // remember which plans we based on, so we can remove them later @@ -325,30 +252,25 @@ func findBestJoin( } } } - return bestPlan, lIdx, rIdx, nil + return bestPlan, lIdx, rIdx } -func getJoinFor(ctx *plancontext.PlanningContext, cm opCacheMap, lhs, rhs ops.Operator, joinPredicates []sqlparser.Expr) (ops.Operator, error) { +func getJoinFor(ctx *plancontext.PlanningContext, cm opCacheMap, lhs, rhs Operator, joinPredicates []sqlparser.Expr) Operator { solves := tableSetPair{left: TableID(lhs), right: TableID(rhs)} cachedPlan := cm[solves] if cachedPlan != nil { - return cachedPlan, nil + return cachedPlan } - join, _, err := mergeOrJoin(ctx, lhs, rhs, joinPredicates, true) - if err != nil { - return nil, err - } + join, _ := mergeOrJoin(ctx, lhs, rhs, joinPredicates, true) cm[solves] = join - return join, nil + return join } // requiresSwitchingSides will return true if any of the operators with the root from the given operator tree // is of the type that should not be on the RHS of a join -func requiresSwitchingSides(ctx *plancontext.PlanningContext, op ops.Operator) bool { - required := false - - _ = rewrite.Visit(op, func(current ops.Operator) error { +func requiresSwitchingSides(ctx *plancontext.PlanningContext, op Operator) (required bool) { + _ = Visit(op, func(current Operator) error { horizon, isHorizon := current.(*Horizon) if isHorizon && !horizon.IsMergeable(ctx) { @@ -358,42 +280,37 @@ func requiresSwitchingSides(ctx *plancontext.PlanningContext, op ops.Operator) b return nil }) - - return required + return } -func mergeOrJoin(ctx *plancontext.PlanningContext, lhs, rhs ops.Operator, joinPredicates []sqlparser.Expr, inner bool) (ops.Operator, *rewrite.ApplyResult, error) { +func mergeOrJoin(ctx *plancontext.PlanningContext, lhs, rhs Operator, joinPredicates []sqlparser.Expr, inner bool) (Operator, *ApplyResult) { newPlan := mergeJoinInputs(ctx, lhs, rhs, joinPredicates, newJoinMerge(joinPredicates, inner)) if newPlan != nil { - return newPlan, rewrite.NewTree("merge routes into single operator", newPlan), nil + return newPlan, Rewrote("merge routes into single operator") } if len(joinPredicates) > 0 && requiresSwitchingSides(ctx, rhs) { - if !inner { - return nil, nil, vterrors.VT12001("LEFT JOIN with derived tables") - } - - if requiresSwitchingSides(ctx, lhs) { - return nil, nil, vterrors.VT12001("JOIN between derived tables") + if !inner || requiresSwitchingSides(ctx, lhs) { + // we can't switch sides, so let's see if we can use a HashJoin to solve it + join := NewHashJoin(lhs, rhs, !inner) + for _, pred := range joinPredicates { + join.AddJoinPredicate(ctx, pred) + } + ctx.SemTable.QuerySignature.HashJoin = true + return join, Rewrote("use a hash join because we have LIMIT on the LHS") } - join := NewApplyJoin(Clone(rhs), Clone(lhs), nil, !inner) - newOp, err := pushJoinPredicates(ctx, joinPredicates, join) - if err != nil { - return nil, nil, err - } - return newOp, rewrite.NewTree("logical join to applyJoin, switching side because derived table", newOp), nil + join := NewApplyJoin(ctx, Clone(rhs), Clone(lhs), nil, !inner) + newOp := pushJoinPredicates(ctx, joinPredicates, join) + return newOp, Rewrote("logical join to applyJoin, switching side because LIMIT") } - join := NewApplyJoin(Clone(lhs), Clone(rhs), nil, !inner) - newOp, err := pushJoinPredicates(ctx, joinPredicates, join) - if err != nil { - return nil, nil, err - } - return newOp, rewrite.NewTree("logical join to applyJoin ", newOp), nil + join := NewApplyJoin(ctx, Clone(lhs), Clone(rhs), nil, !inner) + newOp := pushJoinPredicates(ctx, joinPredicates, join) + return newOp, Rewrote("logical join to applyJoin ") } -func operatorsToRoutes(a, b ops.Operator) (*Route, *Route) { +func operatorsToRoutes(a, b Operator) (*Route, *Route) { aRoute, ok := a.(*Route) if !ok { return nil, nil @@ -431,7 +348,7 @@ func canMergeOnFilter(ctx *plancontext.PlanningContext, a, b *Route, predicate s return rVindex == lVindex } -func findColumnVindex(ctx *plancontext.PlanningContext, a ops.Operator, exp sqlparser.Expr) vindexes.SingleColumn { +func findColumnVindex(ctx *plancontext.PlanningContext, a Operator, exp sqlparser.Expr) vindexes.SingleColumn { _, isCol := exp.(*sqlparser.ColName) if !isCol { return nil @@ -456,7 +373,7 @@ func findColumnVindex(ctx *plancontext.PlanningContext, a ops.Operator, exp sqlp deps := ctx.SemTable.RecursiveDeps(expr) - _ = rewrite.Visit(a, func(rel ops.Operator) error { + _ = Visit(a, func(rel Operator) error { to, isTableOp := rel.(tableIDIntroducer) if !isTableOp { return nil @@ -610,14 +527,14 @@ func hexEqual(a, b *sqlparser.Literal) bool { return false } -func pushJoinPredicates(ctx *plancontext.PlanningContext, exprs []sqlparser.Expr, op *ApplyJoin) (ops.Operator, error) { +func pushJoinPredicates(ctx *plancontext.PlanningContext, exprs []sqlparser.Expr, op *ApplyJoin) Operator { if len(exprs) == 0 { - return op, nil + return op } for _, expr := range exprs { - AddPredicate(ctx, op, expr, true, newFilter) + AddPredicate(ctx, op, expr, true, newFilterSinglePredicate) } - return op, nil + return op } diff --git a/go/vt/vtgate/planbuilder/operators/sequential.go b/go/vt/vtgate/planbuilder/operators/sequential.go new file mode 100644 index 00000000000..2db376a97bb --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/sequential.go @@ -0,0 +1,53 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" +) + +type Sequential struct { + Sources []Operator + + noPredicates + noColumns +} + +// Clone implements the Operator interface +func (s *Sequential) Clone(inputs []Operator) Operator { + newOp := *s + newOp.Sources = inputs + return &newOp +} + +func (s *Sequential) GetOrdering(*plancontext.PlanningContext) []OrderBy { + return nil +} + +// Inputs implements the Operator interface +func (s *Sequential) Inputs() []Operator { + return s.Sources +} + +// SetInputs implements the Operator interface +func (s *Sequential) SetInputs(ops []Operator) { + s.Sources = ops +} + +func (s *Sequential) ShortDescription() string { + return "" +} diff --git a/go/vt/vtgate/planbuilder/operators/sharded_routing.go b/go/vt/vtgate/planbuilder/operators/sharded_routing.go index d54db071d46..29ee88787b5 100644 --- a/go/vt/vtgate/planbuilder/operators/sharded_routing.go +++ b/go/vt/vtgate/planbuilder/operators/sharded_routing.go @@ -52,7 +52,7 @@ type ShardedRouting struct { var _ Routing = (*ShardedRouting)(nil) -func newShardedRouting(vtable *vindexes.Table, id semantics.TableSet) Routing { +func newShardedRouting(ctx *plancontext.PlanningContext, vtable *vindexes.Table, id semantics.TableSet) Routing { routing := &ShardedRouting{ RouteOpCode: engine.Scatter, keyspace: vtable.Keyspace, @@ -77,7 +77,33 @@ func newShardedRouting(vtable *vindexes.Table, id semantics.TableSet) Routing { } } + // Find the tableInfo for the given id + ti, err := ctx.SemTable.TableInfoFor(id) + if err != nil { + panic(err) + } + + // If the tableInfo is a realTable, then get the vindexHint from it. + var vindexHint *sqlparser.IndexHint + rt, isRt := ti.(*semantics.RealTable) + if isRt { + vindexHint = rt.GetVindexHint() + } for _, columnVindex := range vtable.ColumnVindexes { + if vindexHint != nil { + switch vindexHint.Type { + case sqlparser.UseVindexOp: + // For a USE VINDEX type vindex hint, we want to skip any vindex that isn't in the indexes list. + if !indexesContains(vindexHint.Indexes, columnVindex.Name) { + continue + } + case sqlparser.IgnoreVindexOp: + // For a IGNORE VINDEX type vindex hint, we want to skip any vindex that is in the indexes list. + if indexesContains(vindexHint.Indexes, columnVindex.Name) { + continue + } + } + } // ignore any backfilling vindexes from vindex selection. if columnVindex.IsBackfilling() { continue @@ -87,6 +113,13 @@ func newShardedRouting(vtable *vindexes.Table, id semantics.TableSet) Routing { return routing } +// indexesContains is a helper function that returns whether a given string is part of the IdentifierCI list. +func indexesContains(indexes []sqlparser.IdentifierCI, name string) bool { + return slices.ContainsFunc(indexes, func(ci sqlparser.IdentifierCI) bool { + return ci.EqualString(name) + }) +} + func (tr *ShardedRouting) isScatter() bool { return tr.RouteOpCode == engine.Scatter } @@ -97,28 +130,23 @@ func (tr *ShardedRouting) isScatter() bool { // This can sometimes push a predicate to the top, so it's not hiding inside an OR // 2. If that is not enough, an additional rewrite pass is performed where we try to // turn ORs into IN, which is easier for the planner to plan -func (tr *ShardedRouting) tryImprove(ctx *plancontext.PlanningContext, queryTable *QueryTable) (Routing, error) { +func (tr *ShardedRouting) tryImprove(ctx *plancontext.PlanningContext, queryTable *QueryTable) Routing { oldPredicates := queryTable.Predicates queryTable.Predicates = nil tr.SeenPredicates = nil var routing Routing = tr - var err error for _, pred := range oldPredicates { rewritten := sqlparser.RewritePredicate(pred) predicates := sqlparser.SplitAndExpression(nil, rewritten.(sqlparser.Expr)) for _, predicate := range predicates { queryTable.Predicates = append(queryTable.Predicates, predicate) - - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err - } + routing = UpdateRoutingLogic(ctx, predicate, routing) } } // If we have something other than a sharded routing with scatter, we are done if sr, ok := routing.(*ShardedRouting); !ok || !sr.isScatter() { - return routing, nil + return routing } // if we _still_ haven't found a better route, we can run this additional rewrite on any ORs we have @@ -128,23 +156,19 @@ func (tr *ShardedRouting) tryImprove(ctx *plancontext.PlanningContext, queryTabl continue } for _, predicate := range sqlparser.ExtractINFromOR(or) { - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err - } + routing = UpdateRoutingLogic(ctx, predicate, routing) } } - return routing, nil + return routing } -func (tr *ShardedRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) error { +func (tr *ShardedRouting) UpdateRoutingParams(_ *plancontext.PlanningContext, rp *engine.RoutingParameters) { rp.Keyspace = tr.keyspace if tr.Selected != nil { rp.Vindex = tr.Selected.FoundVindex rp.Values = tr.Selected.Values } - return nil } func (tr *ShardedRouting) Clone() Routing { @@ -166,17 +190,13 @@ func (tr *ShardedRouting) Clone() Routing { } } -func (tr *ShardedRouting) updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (Routing, error) { +func (tr *ShardedRouting) updateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Routing { tr.SeenPredicates = append(tr.SeenPredicates, expr) - newRouting, newVindexFound, err := tr.searchForNewVindexes(ctx, expr) - if err != nil { - return nil, err - } - + newRouting, newVindexFound := tr.searchForNewVindexes(ctx, expr) if newRouting != nil { // we found something that we can route with something other than ShardedRouting - return newRouting, nil + return newRouting } // if we didn't open up any new vindex Options, no need to enter here @@ -184,10 +204,10 @@ func (tr *ShardedRouting) updateRoutingLogic(ctx *plancontext.PlanningContext, e tr.PickBestAvailableVindex() } - return tr, nil + return tr } -func (tr *ShardedRouting) resetRoutingLogic(ctx *plancontext.PlanningContext) (Routing, error) { +func (tr *ShardedRouting) resetRoutingLogic(ctx *plancontext.PlanningContext) Routing { tr.RouteOpCode = engine.Scatter tr.Selected = nil for i, vp := range tr.VindexPreds { @@ -196,16 +216,12 @@ func (tr *ShardedRouting) resetRoutingLogic(ctx *plancontext.PlanningContext) (R var routing Routing = tr for _, predicate := range tr.SeenPredicates { - var err error - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err - } + routing = UpdateRoutingLogic(ctx, predicate, routing) } - return routing, nil + return routing } -func (tr *ShardedRouting) searchForNewVindexes(ctx *plancontext.PlanningContext, predicate sqlparser.Expr) (Routing, bool, error) { +func (tr *ShardedRouting) searchForNewVindexes(ctx *plancontext.PlanningContext, predicate sqlparser.Expr) (Routing, bool) { newVindexFound := false switch node := predicate.(type) { case *sqlparser.ComparisonExpr: @@ -216,23 +232,23 @@ func (tr *ShardedRouting) searchForNewVindexes(ctx *plancontext.PlanningContext, newVindexFound = newVindexFound || found } - return nil, newVindexFound, nil + return nil, newVindexFound } -func (tr *ShardedRouting) planComparison(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) (routing Routing, foundNew bool, err error) { +func (tr *ShardedRouting) planComparison(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) (routing Routing, foundNew bool) { switch cmp.Operator { case sqlparser.EqualOp: found := tr.planEqualOp(ctx, cmp) - return nil, found, nil + return nil, found case sqlparser.InOp: found := tr.planInOp(ctx, cmp) - return nil, found, nil + return nil, found case sqlparser.LikeOp: found := tr.planLikeOp(ctx, cmp) - return nil, found, nil + return nil, found } - return nil, false, nil + return nil, false } func (tr *ShardedRouting) planIsExpr(ctx *plancontext.PlanningContext, node *sqlparser.IsExpr) bool { @@ -276,13 +292,13 @@ func (tr *ShardedRouting) planInOp(ctx *plancontext.PlanningContext, cmp *sqlpar opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.IN } return tr.haveMatchingVindex(ctx, cmp, vdValue, left, value, opcode, justTheVindex) case sqlparser.ValTuple: - right, rightIsValTuple := cmp.Right.(sqlparser.ValTuple) - if !rightIsValTuple { - return false + switch right := cmp.Right.(type) { + case sqlparser.ValTuple: + return tr.planCompositeInOpRecursive(ctx, cmp, left, right, nil) + case sqlparser.ListArg: + return tr.planCompositeInOpArg(ctx, cmp, left, right) } - return tr.planCompositeInOpRecursive(ctx, cmp, left, right, nil) } - return false } @@ -365,7 +381,6 @@ func (tr *ShardedRouting) haveMatchingVindex( switch v.ColVindex.Vindex.(type) { case vindexes.SingleColumn: newVindexFound = tr.processSingleColumnVindex(node, valueExpr, column, value, opcode, vfunc, v, newVindexFound) - case vindexes.MultiColumn: newVindexFound = tr.processMultiColumnVindex(node, valueExpr, column, value, opcode, vfunc, v, newVindexFound) } @@ -395,15 +410,19 @@ func (tr *ShardedRouting) processSingleColumnVindex( return newVindexFound } - vindexPlusPredicates.Options = append(vindexPlusPredicates.Options, &VindexOption{ + vo := &VindexOption{ Values: []evalengine.Expr{value}, - ValueExprs: []sqlparser.Expr{valueExpr}, Predicates: []sqlparser.Expr{node}, OpCode: routeOpcode, FoundVindex: vindex, Cost: costFor(vindexPlusPredicates.ColVindex, routeOpcode), Ready: true, - }) + } + if valueExpr != nil { + vo.ValueExprs = []sqlparser.Expr{valueExpr} + } + vindexPlusPredicates.Options = append(vindexPlusPredicates.Options, vo) + return true } @@ -532,6 +551,40 @@ func (tr *ShardedRouting) planCompositeInOpRecursive( return foundVindex } +func (tr *ShardedRouting) planCompositeInOpArg( + ctx *plancontext.PlanningContext, + cmp *sqlparser.ComparisonExpr, + left sqlparser.ValTuple, + right sqlparser.ListArg, +) bool { + foundVindex := false + for idx, expr := range left { + col, ok := expr.(*sqlparser.ColName) + if !ok { + continue + } + + // check if left col is a vindex + if !tr.hasVindex(col) { + continue + } + + value := &evalengine.TupleBindVariable{ + Key: right.String(), + Index: idx, + } + if typ, found := ctx.SemTable.TypeForExpr(col); found { + value.Type = typ.Type() + value.Collation = typ.Collation() + } + + opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.MultiEqual } + newVindex := tr.haveMatchingVindex(ctx, cmp, nil, col, value, opcode, justTheVindex) + foundVindex = newVindex || foundVindex + } + return foundVindex +} + func (tr *ShardedRouting) hasVindex(column *sqlparser.ColName) bool { for _, v := range tr.VindexPreds { for _, col := range v.ColVindex.Columns { @@ -565,6 +618,14 @@ func (tr *ShardedRouting) extraInfo() string { ) } + if len(tr.Selected.ValueExprs) == 0 { + return fmt.Sprintf( + "Vindex[%s] Seen:[%s]", + tr.Selected.FoundVindex.String(), + sqlparser.String(sqlparser.AndExpressions(tr.SeenPredicates...)), + ) + } + return fmt.Sprintf( "Vindex[%s] Values[%s] Seen:[%s]", tr.Selected.FoundVindex.String(), @@ -627,6 +688,7 @@ func makeEvalEngineExpr(ctx *plancontext.PlanningContext, n sqlparser.Expr) eval ee, _ := evalengine.Translate(expr, &evalengine.Config{ Collation: ctx.SemTable.Collation, ResolveType: ctx.SemTable.TypeForExpr, + Environment: ctx.VSchema.Environment(), }) if ee != nil { return ee diff --git a/go/vt/vtgate/planbuilder/operators/subquery.go b/go/vt/vtgate/planbuilder/operators/subquery.go index e06e595f689..537737363c8 100644 --- a/go/vt/vtgate/planbuilder/operators/subquery.go +++ b/go/vt/vtgate/planbuilder/operators/subquery.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine/opcode" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -34,8 +33,8 @@ import ( // outer query through a join. type SubQuery struct { // Fields filled in at the time of construction: - Outer ops.Operator // Outer query operator. - Subquery ops.Operator // Subquery operator. + Outer Operator // Outer query operator. + Subquery Operator // Subquery operator. FilterType opcode.PulloutOpcode // Type of subquery filter. Original sqlparser.Expr // This is the expression we should use if we can merge the inner to the outer originalSubquery *sqlparser.Subquery // Subquery representation, e.g., (SELECT foo from user LIMIT 1). @@ -43,18 +42,21 @@ type SubQuery struct { OuterPredicate sqlparser.Expr // This is the predicate that is using the subquery expression. It will not be empty for projections ArgName string // This is the name of the ColName or Argument used to replace the subquery TopLevel bool // will be false if the subquery is deeply nested - JoinColumns []JoinColumn // Broken up join predicates. + JoinColumns []applyJoinColumn // Broken up join predicates. SubqueryValueName string // Value name returned by the subquery (uncorrelated queries). HasValuesName string // Argument name passed to the subquery (uncorrelated queries). // Fields related to correlated subqueries: Vars map[string]int // Arguments copied from outer to inner, set during offset planning. outerID semantics.TableSet + // correlated stores whether this subquery is correlated or not. + // We use this information to fail the planning if we are unable to merge the subquery with a route. + correlated bool IsProjection bool } -func (sq *SubQuery) planOffsets(ctx *plancontext.PlanningContext) { +func (sq *SubQuery) planOffsets(ctx *plancontext.PlanningContext) Operator { sq.Vars = make(map[string]int) columns, err := sq.GetJoinColumns(ctx, sq.Outer) if err != nil { @@ -66,26 +68,27 @@ func (sq *SubQuery) planOffsets(ctx *plancontext.PlanningContext) { sq.Vars[lhsExpr.Name] = offset } } + return nil } -func (sq *SubQuery) OuterExpressionsNeeded(ctx *plancontext.PlanningContext, outer ops.Operator) (result []*sqlparser.ColName, err error) { +func (sq *SubQuery) OuterExpressionsNeeded(ctx *plancontext.PlanningContext, outer Operator) (result []*sqlparser.ColName) { joinColumns, err := sq.GetJoinColumns(ctx, outer) if err != nil { - return nil, err + return nil } for _, jc := range joinColumns { for _, lhsExpr := range jc.LHSExprs { col, ok := lhsExpr.Expr.(*sqlparser.ColName) if !ok { - return nil, vterrors.VT13001("joins can only compare columns: %s", sqlparser.String(lhsExpr.Expr)) + panic(vterrors.VT13001("joins can only compare columns: %s", sqlparser.String(lhsExpr.Expr))) } result = append(result, col) } } - return result, nil + return result } -func (sq *SubQuery) GetJoinColumns(ctx *plancontext.PlanningContext, outer ops.Operator) ([]JoinColumn, error) { +func (sq *SubQuery) GetJoinColumns(ctx *plancontext.PlanningContext, outer Operator) ([]applyJoinColumn, error) { if outer == nil { return nil, vterrors.VT13001("outer operator cannot be nil") } @@ -96,8 +99,8 @@ func (sq *SubQuery) GetJoinColumns(ctx *plancontext.PlanningContext, outer ops.O } } sq.outerID = outerID - mapper := func(in sqlparser.Expr) (JoinColumn, error) { - return BreakExpressionInLHSandRHS(ctx, in, outerID) + mapper := func(in sqlparser.Expr) (applyJoinColumn, error) { + return breakExpressionInLHSandRHSForApplyJoin(ctx, in, outerID), nil } joinPredicates, err := slice.MapWithError(sq.Predicates, mapper) if err != nil { @@ -108,7 +111,7 @@ func (sq *SubQuery) GetJoinColumns(ctx *plancontext.PlanningContext, outer ops.O } // Clone implements the Operator interface -func (sq *SubQuery) Clone(inputs []ops.Operator) ops.Operator { +func (sq *SubQuery) Clone(inputs []Operator) Operator { klone := *sq switch len(inputs) { case 1: @@ -125,21 +128,21 @@ func (sq *SubQuery) Clone(inputs []ops.Operator) ops.Operator { return &klone } -func (sq *SubQuery) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (sq *SubQuery) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return sq.Outer.GetOrdering(ctx) } // Inputs implements the Operator interface -func (sq *SubQuery) Inputs() []ops.Operator { +func (sq *SubQuery) Inputs() []Operator { if sq.Outer == nil { - return []ops.Operator{sq.Subquery} + return []Operator{sq.Subquery} } - return []ops.Operator{sq.Outer, sq.Subquery} + return []Operator{sq.Outer, sq.Subquery} } // SetInputs implements the Operator interface -func (sq *SubQuery) SetInputs(inputs []ops.Operator) { +func (sq *SubQuery) SetInputs(inputs []Operator) { switch len(inputs) { case 1: sq.Subquery = inputs[0] @@ -164,10 +167,10 @@ func (sq *SubQuery) ShortDescription() string { preds := append(sq.Predicates, sq.OuterPredicate) pred = " MERGE ON " + sqlparser.String(sqlparser.AndExpressions(preds...)) } - return fmt.Sprintf("%s %v%s", typ, sq.FilterType.String(), pred) + return fmt.Sprintf(":%s %s %v%s", sq.ArgName, typ, sq.FilterType.String(), pred) } -func (sq *SubQuery) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (sq *SubQuery) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { sq.Outer = sq.Outer.AddPredicate(ctx, expr) return sq } @@ -196,17 +199,20 @@ func (sq *SubQuery) GetMergePredicates() []sqlparser.Expr { return sq.Predicates } -func (sq *SubQuery) settle(ctx *plancontext.PlanningContext, outer ops.Operator) (ops.Operator, error) { +func (sq *SubQuery) settle(ctx *plancontext.PlanningContext, outer Operator) Operator { if !sq.TopLevel { - return nil, subqueryNotAtTopErr + panic(subqueryNotAtTopErr) + } + if sq.correlated && sq.FilterType != opcode.PulloutExists { + panic(correlatedSubqueryErr) } if sq.IsProjection { if len(sq.GetMergePredicates()) > 0 { // this means that we have a correlated subquery on our hands - return nil, correlatedSubqueryErr + panic(correlatedSubqueryErr) } sq.SubqueryValueName = sq.ArgName - return outer, nil + return outer } return sq.settleFilter(ctx, outer) } @@ -214,12 +220,12 @@ func (sq *SubQuery) settle(ctx *plancontext.PlanningContext, outer ops.Operator) var correlatedSubqueryErr = vterrors.VT12001("correlated subquery is only supported for EXISTS") var subqueryNotAtTopErr = vterrors.VT12001("unmergable subquery can not be inside complex expression") -func (sq *SubQuery) settleFilter(ctx *plancontext.PlanningContext, outer ops.Operator) (ops.Operator, error) { +func (sq *SubQuery) settleFilter(ctx *plancontext.PlanningContext, outer Operator) Operator { if len(sq.Predicates) > 0 { if sq.FilterType != opcode.PulloutExists { - return nil, correlatedSubqueryErr + panic(correlatedSubqueryErr) } - return outer, nil + return outer } hasValuesArg := func() string { @@ -254,16 +260,16 @@ func (sq *SubQuery) settleFilter(ctx *plancontext.PlanningContext, outer ops.Ope predicates = append(predicates, sqlparser.NewArgument(hasValuesArg()), rhsPred) sq.SubqueryValueName = sq.ArgName case opcode.PulloutNotIn: - predicates = append(predicates, sqlparser.NewNotExpr(sqlparser.NewArgument(hasValuesArg())), rhsPred) + predicates = append(predicates, &sqlparser.OrExpr{ + Left: sqlparser.NewNotExpr(sqlparser.NewArgument(hasValuesArg())), + Right: rhsPred, + }) sq.SubqueryValueName = sq.ArgName case opcode.PulloutValue: predicates = append(predicates, rhsPred) sq.SubqueryValueName = sq.ArgName } - return &Filter{ - Source: outer, - Predicates: predicates, - }, nil + return newFilter(outer, predicates...) } func dontEnterSubqueries(node, _ sqlparser.SQLNode) bool { @@ -278,22 +284,8 @@ func (sq *SubQuery) isMerged(ctx *plancontext.PlanningContext) bool { } // mapExpr rewrites all expressions according to the provided function -func (sq *SubQuery) mapExpr(f func(expr sqlparser.Expr) (sqlparser.Expr, error)) error { - newPredicates, err := slice.MapWithError(sq.Predicates, f) - if err != nil { - return err - } - sq.Predicates = newPredicates - - sq.Original, err = f(sq.Original) - if err != nil { - return err - } - - originalSubquery, err := f(sq.originalSubquery) - if err != nil { - return err - } - sq.originalSubquery = originalSubquery.(*sqlparser.Subquery) - return nil +func (sq *SubQuery) mapExpr(f func(expr sqlparser.Expr) sqlparser.Expr) { + sq.Predicates = slice.Map(sq.Predicates, f) + sq.Original = f(sq.Original) + sq.originalSubquery = f(sq.originalSubquery).(*sqlparser.Subquery) } diff --git a/go/vt/vtgate/planbuilder/operators/subquery_builder.go b/go/vt/vtgate/planbuilder/operators/subquery_builder.go index 1d1d12bbfe3..4caf3530075 100644 --- a/go/vt/vtgate/planbuilder/operators/subquery_builder.go +++ b/go/vt/vtgate/planbuilder/operators/subquery_builder.go @@ -19,7 +19,6 @@ package operators import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine/opcode" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -32,7 +31,7 @@ type SubQueryBuilder struct { outerID semantics.TableSet } -func (sqb *SubQueryBuilder) getRootOperator(op ops.Operator, decorator func(operator ops.Operator) ops.Operator) ops.Operator { +func (sqb *SubQueryBuilder) getRootOperator(op Operator, decorator func(operator Operator) Operator) Operator { if len(sqb.Inner) == 0 { return op } @@ -53,19 +52,16 @@ func (sqb *SubQueryBuilder) handleSubquery( ctx *plancontext.PlanningContext, expr sqlparser.Expr, outerID semantics.TableSet, -) (*SubQuery, error) { +) *SubQuery { subq, parentExpr := getSubQuery(expr) if subq == nil { - return nil, nil + return nil } argName := ctx.GetReservedArgumentFor(subq) - sqInner, err := createSubqueryOp(ctx, parentExpr, expr, subq, outerID, argName) - if err != nil { - return nil, err - } + sqInner := createSubqueryOp(ctx, parentExpr, expr, subq, outerID, argName) sqb.Inner = append(sqb.Inner, sqInner) - return sqInner, nil + return sqInner } func getSubQuery(expr sqlparser.Expr) (subqueryExprExists *sqlparser.Subquery, parentExpr sqlparser.Expr) { @@ -99,7 +95,7 @@ func createSubqueryOp( subq *sqlparser.Subquery, outerID semantics.TableSet, name string, -) (*SubQuery, error) { +) *SubQuery { switch parent := parent.(type) { case *sqlparser.NotExpr: switch parent.Expr.(type) { @@ -120,20 +116,14 @@ func createSubqueryOp( // and extracts subqueries into operators func (sqb *SubQueryBuilder) inspectStatement(ctx *plancontext.PlanningContext, stmt sqlparser.SelectStatement, -) (sqlparser.Exprs, []JoinColumn, error) { +) (sqlparser.Exprs, []applyJoinColumn) { switch stmt := stmt.(type) { case *sqlparser.Select: return sqb.inspectSelect(ctx, stmt) case *sqlparser.Union: - exprs1, cols1, err := sqb.inspectStatement(ctx, stmt.Left) - if err != nil { - return nil, nil, err - } - exprs2, cols2, err := sqb.inspectStatement(ctx, stmt.Right) - if err != nil { - return nil, nil, err - } - return append(exprs1, exprs2...), append(cols1, cols2...), nil + exprs1, cols1 := sqb.inspectStatement(ctx, stmt.Left) + exprs2, cols2 := sqb.inspectStatement(ctx, stmt.Right) + return append(exprs1, exprs2...), append(cols1, cols2...) } panic("unknown type") } @@ -144,22 +134,12 @@ func (sqb *SubQueryBuilder) inspectStatement(ctx *plancontext.PlanningContext, func (sqb *SubQueryBuilder) inspectSelect( ctx *plancontext.PlanningContext, sel *sqlparser.Select, -) (sqlparser.Exprs, []JoinColumn, error) { +) (sqlparser.Exprs, []applyJoinColumn) { // first we need to go through all the places where one can find predicates // and search for subqueries - newWhere, wherePreds, whereJoinCols, err := sqb.inspectWhere(ctx, sel.Where) - if err != nil { - return nil, nil, err - } - newHaving, havingPreds, havingJoinCols, err := sqb.inspectWhere(ctx, sel.Having) - if err != nil { - return nil, nil, err - } - - newFrom, onPreds, onJoinCols, err := sqb.inspectOnExpr(ctx, sel.From) - if err != nil { - return nil, nil, err - } + newWhere, wherePreds, whereJoinCols := sqb.inspectWhere(ctx, sel.Where) + newHaving, havingPreds, havingJoinCols := sqb.inspectWhere(ctx, sel.Having) + newFrom, onPreds, onJoinCols := sqb.inspectOnExpr(ctx, sel.From) // then we use the updated AST structs to build the operator // these AST elements have any subqueries replace by arguments @@ -168,8 +148,7 @@ func (sqb *SubQueryBuilder) inspectSelect( sel.From = newFrom return append(append(wherePreds, havingPreds...), onPreds...), - append(append(whereJoinCols, havingJoinCols...), onJoinCols...), - nil + append(append(whereJoinCols, havingJoinCols...), onJoinCols...) } func createSubquery( @@ -181,7 +160,7 @@ func createSubquery( argName string, filterType opcode.PulloutOpcode, isProjection bool, -) (*SubQuery, error) { +) *SubQuery { topLevel := ctx.SemTable.EqualsExpr(original, parent) original = cloneASTAndSemState(ctx, original) originalSq := cloneASTAndSemState(ctx, subq) @@ -189,20 +168,10 @@ func createSubquery( totalID := subqID.Merge(outerID) sqc := &SubQueryBuilder{totalID: totalID, subqID: subqID, outerID: outerID} - predicates, joinCols, err := sqc.inspectStatement(ctx, subq.Select) - if err != nil { - return nil, err - } - - stmt := rewriteRemainingColumns(ctx, subq.Select, subqID) - - // TODO: this should not be needed. We are using CopyOnRewrite above, but somehow this is not getting copied - ctx.SemTable.CopySemanticInfo(subq.Select, stmt) + predicates, joinCols := sqc.inspectStatement(ctx, subq.Select) + correlated := !ctx.SemTable.RecursiveDeps(subq).IsEmpty() - opInner, err := translateQueryToOp(ctx, stmt) - if err != nil { - return nil, err - } + opInner := translateQueryToOp(ctx, subq.Select) opInner = sqc.getRootOperator(opInner, nil) return &SubQuery{ @@ -215,15 +184,16 @@ func createSubquery( IsProjection: isProjection, TopLevel: topLevel, JoinColumns: joinCols, - }, nil + correlated: correlated, + } } func (sqb *SubQueryBuilder) inspectWhere( ctx *plancontext.PlanningContext, in *sqlparser.Where, -) (*sqlparser.Where, sqlparser.Exprs, []JoinColumn, error) { +) (*sqlparser.Where, sqlparser.Exprs, []applyJoinColumn) { if in == nil { - return nil, nil, nil, nil + return nil, nil, nil } jpc := &joinPredicateCollector{ totalID: sqb.totalID, @@ -231,17 +201,12 @@ func (sqb *SubQueryBuilder) inspectWhere( outerID: sqb.outerID, } for _, predicate := range sqlparser.SplitAndExpression(nil, in.Expr) { - sqlparser.RemoveKeyspaceFromColName(predicate) - subq, err := sqb.handleSubquery(ctx, predicate, sqb.totalID) - if err != nil { - return nil, nil, nil, err - } + sqlparser.RemoveKeyspaceInCol(predicate) + subq := sqb.handleSubquery(ctx, predicate, sqb.totalID) if subq != nil { continue } - if err = jpc.inspectPredicate(ctx, predicate); err != nil { - return nil, nil, nil, err - } + jpc.inspectPredicate(ctx, predicate) } if len(jpc.remainingPredicates) == 0 { @@ -250,13 +215,13 @@ func (sqb *SubQueryBuilder) inspectWhere( in.Expr = sqlparser.AndExpressions(jpc.remainingPredicates...) } - return in, jpc.predicates, jpc.joinColumns, nil + return in, jpc.predicates, jpc.joinColumns } func (sqb *SubQueryBuilder) inspectOnExpr( ctx *plancontext.PlanningContext, from []sqlparser.TableExpr, -) (newFrom []sqlparser.TableExpr, onPreds sqlparser.Exprs, onJoinCols []JoinColumn, err error) { +) (newFrom []sqlparser.TableExpr, onPreds sqlparser.Exprs, onJoinCols []applyJoinColumn) { for _, tbl := range from { tbl := sqlparser.CopyOnRewrite(tbl, dontEnterSubqueries, func(cursor *sqlparser.CopyOnWriteCursor) { cond, ok := cursor.Node().(*sqlparser.JoinCondition) @@ -271,20 +236,11 @@ func (sqb *SubQueryBuilder) inspectOnExpr( } for _, pred := range sqlparser.SplitAndExpression(nil, cond.On) { - subq, innerErr := sqb.handleSubquery(ctx, pred, sqb.totalID) - if err != nil { - err = innerErr - cursor.StopTreeWalk() - return - } + subq := sqb.handleSubquery(ctx, pred, sqb.totalID) if subq != nil { continue } - if err = jpc.inspectPredicate(ctx, pred); err != nil { - err = innerErr - cursor.StopTreeWalk() - return - } + jpc.inspectPredicate(ctx, pred) } if len(jpc.remainingPredicates) == 0 { cond.On = nil @@ -294,9 +250,6 @@ func (sqb *SubQueryBuilder) inspectOnExpr( onPreds = append(onPreds, jpc.predicates...) onJoinCols = append(onJoinCols, jpc.joinColumns...) }, ctx.SemTable.CopySemanticInfo) - if err != nil { - return - } newFrom = append(newFrom, tbl.(sqlparser.TableExpr)) } return @@ -309,7 +262,7 @@ func createComparisonSubQuery( subFromOutside *sqlparser.Subquery, outerID semantics.TableSet, name string, -) (*SubQuery, error) { +) *SubQuery { subq, outside := semantics.GetSubqueryAndOtherSide(parent) if outside == nil || subq != subFromOutside { panic("uh oh") @@ -323,10 +276,7 @@ func createComparisonSubQuery( filterType = opcode.PulloutNotIn } - subquery, err := createSubquery(ctx, original, subq, outerID, parent, name, filterType, false) - if err != nil { - return nil, err - } + subquery := createSubquery(ctx, original, subq, outerID, parent, name, filterType, false) // if we are comparing with a column from the inner subquery, // we add this extra predicate to check if the two sides are mergable or not @@ -338,7 +288,7 @@ func createComparisonSubQuery( } } - return subquery, err + return subquery } func (sqb *SubQueryBuilder) pullOutValueSubqueries( @@ -346,25 +296,22 @@ func (sqb *SubQueryBuilder) pullOutValueSubqueries( expr sqlparser.Expr, outerID semantics.TableSet, isDML bool, -) (sqlparser.Expr, []*SubQuery, error) { +) (sqlparser.Expr, []*SubQuery) { original := sqlparser.CloneExpr(expr) sqe := extractSubQueries(ctx, expr, isDML) if sqe == nil { - return nil, nil, nil + return nil, nil } var newSubqs []*SubQuery for idx, subq := range sqe.subq { - sqInner, err := createSubquery(ctx, original, subq, outerID, original, sqe.cols[idx], sqe.pullOutCode[idx], true) - if err != nil { - return nil, nil, err - } + sqInner := createSubquery(ctx, original, subq, outerID, original, sqe.cols[idx], sqe.pullOutCode[idx], true) newSubqs = append(newSubqs, sqInner) } sqb.Inner = append(sqb.Inner, newSubqs...) - return sqe.new, newSubqs, nil + return sqe.new, newSubqs } type subqueryExtraction struct { diff --git a/go/vt/vtgate/planbuilder/operators/subquery_container.go b/go/vt/vtgate/planbuilder/operators/subquery_container.go index ab8d1104623..e4feeab49d8 100644 --- a/go/vt/vtgate/planbuilder/operators/subquery_container.go +++ b/go/vt/vtgate/planbuilder/operators/subquery_container.go @@ -18,7 +18,6 @@ package operators import ( "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) @@ -27,15 +26,15 @@ type ( // The inner subqueries can be executed in any order, so we store them like this so we can see more opportunities // for merging SubQueryContainer struct { - Outer ops.Operator + Outer Operator Inner []*SubQuery } ) -var _ ops.Operator = (*SubQueryContainer)(nil) +var _ Operator = (*SubQueryContainer)(nil) // Clone implements the Operator interface -func (sqc *SubQueryContainer) Clone(inputs []ops.Operator) ops.Operator { +func (sqc *SubQueryContainer) Clone(inputs []Operator) Operator { result := &SubQueryContainer{ Outer: inputs[0], } @@ -49,13 +48,13 @@ func (sqc *SubQueryContainer) Clone(inputs []ops.Operator) ops.Operator { return result } -func (sqc *SubQueryContainer) GetOrdering(ctx *plancontext.PlanningContext) []ops.OrderBy { +func (sqc *SubQueryContainer) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { return sqc.Outer.GetOrdering(ctx) } // Inputs implements the Operator interface -func (sqc *SubQueryContainer) Inputs() []ops.Operator { - operators := []ops.Operator{sqc.Outer} +func (sqc *SubQueryContainer) Inputs() []Operator { + operators := []Operator{sqc.Outer} for _, inner := range sqc.Inner { operators = append(operators, inner) } @@ -63,7 +62,7 @@ func (sqc *SubQueryContainer) Inputs() []ops.Operator { } // SetInputs implements the Operator interface -func (sqc *SubQueryContainer) SetInputs(ops []ops.Operator) { +func (sqc *SubQueryContainer) SetInputs(ops []Operator) { sqc.Outer = ops[0] } @@ -71,7 +70,7 @@ func (sqc *SubQueryContainer) ShortDescription() string { return "" } -func (sqc *SubQueryContainer) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (sqc *SubQueryContainer) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { sqc.Outer = sqc.Outer.AddPredicate(ctx, expr) return sqc } diff --git a/go/vt/vtgate/planbuilder/operators/subquery_planning.go b/go/vt/vtgate/planbuilder/operators/subquery_planning.go index 7740ca3d46d..960cde99acc 100644 --- a/go/vt/vtgate/planbuilder/operators/subquery_planning.go +++ b/go/vt/vtgate/planbuilder/operators/subquery_planning.go @@ -26,13 +26,11 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine/opcode" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) -func isMergeable(ctx *plancontext.PlanningContext, query sqlparser.SelectStatement, op ops.Operator) bool { +func isMergeable(ctx *plancontext.PlanningContext, query sqlparser.SelectStatement, op Operator) bool { validVindex := func(expr sqlparser.Expr) bool { sc := findColumnVindex(ctx, op, expr) return sc != nil && sc.IsUnique() @@ -74,24 +72,20 @@ func isMergeable(ctx *plancontext.PlanningContext, query sqlparser.SelectStateme } } -func settleSubqueries(ctx *plancontext.PlanningContext, op ops.Operator) ops.Operator { - visit := func(op ops.Operator, lhsTables semantics.TableSet, isRoot bool) (ops.Operator, *rewrite.ApplyResult, error) { +func settleSubqueries(ctx *plancontext.PlanningContext, op Operator) Operator { + visit := func(op Operator, lhsTables semantics.TableSet, isRoot bool) (Operator, *ApplyResult) { switch op := op.(type) { case *SubQueryContainer: outer := op.Outer for _, subq := range op.Inner { - newOuter, err := subq.settle(ctx, outer) - if err != nil { - return nil, nil, err - } - subq.Outer = newOuter + subq.Outer = subq.settle(ctx, outer) outer = subq } - return outer, rewrite.NewTree("extracted subqueries from subquery container", outer), nil + return outer, Rewrote("extracted subqueries from subquery container") case *Projection: ap, err := op.GetAliasedProjections() if err != nil { - return nil, nil, err + panic(err) } for _, pe := range ap { @@ -101,14 +95,18 @@ func settleSubqueries(ctx *plancontext.PlanningContext, op ops.Operator) ops.Ope for _, setExpr := range op.Assignments { mergeSubqueryExpr(ctx, setExpr.Expr) } + case *Aggregator: + for _, aggr := range op.Aggregations { + newExpr, rewritten := rewriteMergedSubqueryExpr(ctx, aggr.SubQueryExpression, aggr.Original.Expr) + if rewritten { + aggr.Original.Expr = newExpr + } + } } - return op, rewrite.SameTree, nil - } - op, err := rewrite.BottomUp(op, TableID, visit, nil) - if err != nil { - panic(err) + return op, NoRewrite } - return op + + return BottomUp(op, TableID, visit, nil) } func mergeSubqueryExpr(ctx *plancontext.PlanningContext, pe *ProjExpr) { @@ -194,7 +192,7 @@ func tryPushSubQueryInJoin( ctx *plancontext.PlanningContext, inner *SubQuery, outer *ApplyJoin, -) (ops.Operator, *rewrite.ApplyResult, error) { +) (Operator, *ApplyResult) { lhs := TableID(outer.LHS) rhs := TableID(outer.RHS) joinID := TableID(outer) @@ -213,12 +211,9 @@ func tryPushSubQueryInJoin( // in general, we don't want to push down uncorrelated subqueries into the RHS of a join, // since this side is executed once per row from the LHS, so we would unnecessarily execute // the subquery multiple times. The exception is if we can merge the subquery with the RHS of the join. - merged, result, err := tryMergeWithRHS(ctx, inner, outer) - if err != nil { - return nil, nil, err - } + merged, result := tryMergeWithRHS(ctx, inner, outer) if merged != nil { - return merged, result, nil + return merged, result } _, ok := inner.Subquery.(*Projection) @@ -227,41 +222,37 @@ func tryPushSubQueryInJoin( // Projections are easy to push down, so if this is still at the top, // it means we have not tried pushing it yet. // Let's give it a chance to push down before we push it on the left - return nil, rewrite.SameTree, nil + return nil, NoRewrite } if deps.IsSolvedBy(lhs) { // we can safely push down the subquery on the LHS outer.LHS = addSubQuery(outer.LHS, inner) - return outer, rewrite.NewTree("push subquery into LHS of join", inner), nil + return outer, Rewrote("push subquery into LHS of join") } if outer.LeftJoin || len(inner.Predicates) == 0 { // we can't push any filters on the RHS of an outer join, and // we don't want to push uncorrelated subqueries to the RHS of a join - return nil, rewrite.SameTree, nil + return nil, NoRewrite } if deps.IsSolvedBy(rhs) { // we can push down the subquery filter on RHS of the join outer.RHS = addSubQuery(outer.RHS, inner) - return outer, rewrite.NewTree("push subquery into RHS of join", inner), nil + return outer, Rewrote("push subquery into RHS of join") } if deps.IsSolvedBy(joinID) { // we can rewrite the predicate to not use the values from the lhs, // and instead use arguments for these dependencies. // this way we can push the subquery into the RHS of this join - err := inner.mapExpr(extractLHSExpr(ctx, outer, lhs)) - if err != nil { - return nil, nil, err - } - + inner.mapExpr(extractLHSExpr(ctx, outer, lhs)) outer.RHS = addSubQuery(outer.RHS, inner) - return outer, rewrite.NewTree("push subquery into RHS of join rewriting predicates", inner), nil + return outer, Rewrote("push subquery into RHS of join rewriting predicates") } - return nil, rewrite.SameTree, nil + return nil, NoRewrite } // extractLHSExpr will return a function that extracts any ColName coming from the LHS table, @@ -270,37 +261,34 @@ func extractLHSExpr( ctx *plancontext.PlanningContext, outer *ApplyJoin, lhs semantics.TableSet, -) func(expr sqlparser.Expr) (sqlparser.Expr, error) { - return func(expr sqlparser.Expr) (sqlparser.Expr, error) { - col, err := BreakExpressionInLHSandRHS(ctx, expr, lhs) - if err != nil { - return nil, err - } +) func(expr sqlparser.Expr) sqlparser.Expr { + return func(expr sqlparser.Expr) sqlparser.Expr { + col := breakExpressionInLHSandRHSForApplyJoin(ctx, expr, lhs) if col.IsPureLeft() { - return nil, vterrors.VT13001("did not expect to find any predicates that do not need data from the inner here") + panic(vterrors.VT13001("did not expect to find any predicates that do not need data from the inner here")) } for _, bve := range col.LHSExprs { if !outer.isColNameMovedFromL2R(bve.Name) { outer.ExtraLHSVars = append(outer.ExtraLHSVars, bve) } } - return col.RHSExpr, nil + return col.RHSExpr } } // tryMergeWithRHS attempts to merge a subquery with the RHS of a join -func tryMergeWithRHS(ctx *plancontext.PlanningContext, inner *SubQuery, outer *ApplyJoin) (ops.Operator, *rewrite.ApplyResult, error) { +func tryMergeWithRHS(ctx *plancontext.PlanningContext, inner *SubQuery, outer *ApplyJoin) (Operator, *ApplyResult) { if outer.LeftJoin { - return nil, nil, nil + return nil, nil } // both sides need to be routes outerRoute, ok := outer.RHS.(*Route) if !ok { - return nil, nil, nil + return nil, nil } innerRoute, ok := inner.Subquery.(*Route) if !ok { - return nil, nil, nil + return nil, nil } newExpr := rewriteOriginalPushedToRHS(ctx, inner.Original, outer) @@ -311,18 +299,18 @@ func tryMergeWithRHS(ctx *plancontext.PlanningContext, inner *SubQuery, outer *A } newOp := mergeSubqueryInputs(ctx, innerRoute, outerRoute, inner.GetMergePredicates(), sqm) if newOp == nil { - return nil, nil, nil + return nil, nil } outer.RHS = newOp ctx.MergedSubqueries = append(ctx.MergedSubqueries, inner.originalSubquery) - return outer, rewrite.NewTree("merged subquery with rhs of join", inner), nil + return outer, Rewrote("merged subquery with rhs of join") } // addSubQuery adds a SubQuery to the given operator. If the operator is a SubQueryContainer, // it will add the SubQuery to the SubQueryContainer. If the operator is something else, it will // create a new SubQueryContainer with the given operator as the outer and the SubQuery as the inner. -func addSubQuery(in ops.Operator, inner *SubQuery) ops.Operator { +func addSubQuery(in Operator, inner *SubQuery) Operator { sql, ok := in.(*SubQueryContainer) if !ok { return &SubQueryContainer{ @@ -339,7 +327,6 @@ func addSubQuery(in ops.Operator, inner *SubQuery) ops.Operator { // this is necessary because we are pushing the subquery into the RHS of the join, and we need to use the argument names // instead of the column names func rewriteOriginalPushedToRHS(ctx *plancontext.PlanningContext, expression sqlparser.Expr, outer *ApplyJoin) sqlparser.Expr { - var err error outerID := TableID(outer.LHS) result := sqlparser.CopyOnRewrite(expression, nil, func(cursor *sqlparser.CopyOnWriteCursor) { col, ok := cursor.Node().(*sqlparser.ColName) @@ -350,46 +337,12 @@ func rewriteOriginalPushedToRHS(ctx *plancontext.PlanningContext, expression sql // this is a dependency we are being fed from the LHS of the join, so we // need to find the argument name for it and use that instead // we can't use the column name directly, because we're in the RHS of the join - name, innerErr := outer.findOrAddColNameBindVarName(ctx, col) - if err != nil { - err = innerErr - cursor.StopTreeWalk() - return - } + name := outer.findOrAddColNameBindVarName(ctx, col) cursor.Replace(sqlparser.NewArgument(name)) }, nil) - if err != nil { - panic(err) - } return result.(sqlparser.Expr) } -func pushProjectionToOuterContainer(ctx *plancontext.PlanningContext, p *Projection, src *SubQueryContainer) (ops.Operator, *rewrite.ApplyResult, error) { - ap, err := p.GetAliasedProjections() - if err != nil { - return p, rewrite.SameTree, nil - } - - outer := TableID(src.Outer) - for _, pe := range ap { - _, isOffset := pe.Info.(*Offset) - if isOffset { - continue - } - - if !ctx.SemTable.RecursiveDeps(pe.EvalExpr).IsSolvedBy(outer) { - return p, rewrite.SameTree, nil - } - - if se, ok := pe.Info.(SubQueryExpression); ok { - pe.EvalExpr = rewriteColNameToArgument(ctx, pe.EvalExpr, se, src.Inner...) - } - } - // all projections can be pushed to the outer - src.Outer, p.Source = p, src.Outer - return src, rewrite.NewTree("push projection into outer side of subquery container", p), nil -} - func rewriteColNameToArgument(ctx *plancontext.PlanningContext, in sqlparser.Expr, se SubQueryExpression, subqueries ...*SubQuery) sqlparser.Expr { rewriteIt := func(s string) sqlparser.SQLNode { for _, sq1 := range se { @@ -433,19 +386,16 @@ func rewriteColNameToArgument(ctx *plancontext.PlanningContext, in sqlparser.Exp return result.(sqlparser.Expr) } -func pushOrMergeSubQueryContainer(ctx *plancontext.PlanningContext, in *SubQueryContainer) (ops.Operator, *rewrite.ApplyResult, error) { +func pushOrMergeSubQueryContainer(ctx *plancontext.PlanningContext, in *SubQueryContainer) (Operator, *ApplyResult) { if !reachedPhase(ctx, initialPlanning) { - return in, rewrite.SameTree, nil + return in, NoRewrite } var remaining []*SubQuery - var result *rewrite.ApplyResult + var result *ApplyResult for _, inner := range in.Inner { - newOuter, _result, err := pushOrMerge(ctx, in.Outer, inner) - if err != nil { - return nil, nil, err - } - if _result == rewrite.SameTree { + newOuter, _result := pushOrMerge(ctx, in.Outer, inner) + if _result == NoRewrite { remaining = append(remaining, inner) continue } @@ -455,26 +405,26 @@ func pushOrMergeSubQueryContainer(ctx *plancontext.PlanningContext, in *SubQuery } if len(remaining) == 0 { - return in.Outer, result, nil + return in.Outer, result } in.Inner = remaining - return in, result, nil + return in, result } func tryMergeSubQuery( ctx *plancontext.PlanningContext, subQuery *SubQuery, outer *Route, -) (newOuter ops.Operator, result *rewrite.ApplyResult, err error) { +) (newOuter Operator, result *ApplyResult) { switch inner := subQuery.Subquery.(type) { case *Route: return tryMergeSubqueryWithOuter(ctx, subQuery, outer, inner) case *SubQueryContainer: return tryMergeSubqueriesRecursively(ctx, subQuery, outer, inner) } - return outer, rewrite.SameTree, nil + return outer, NoRewrite } // tryMergeSubqueriesRecursively attempts to merge a SubQueryContainer with the outer Route. @@ -483,7 +433,7 @@ func tryMergeSubqueriesRecursively( subQuery *SubQuery, outer *Route, inner *SubQueryContainer, -) (ops.Operator, *rewrite.ApplyResult, error) { +) (Operator, *ApplyResult) { exprs := subQuery.GetMergePredicates() merger := &subqueryRouteMerger{ outer: outer, @@ -492,32 +442,29 @@ func tryMergeSubqueriesRecursively( } op := mergeSubqueryInputs(ctx, inner.Outer, outer, exprs, merger) if op == nil { - return outer, rewrite.SameTree, nil + return outer, NoRewrite } op = Clone(op).(*Route) op.Source = outer.Source - var finalResult *rewrite.ApplyResult + var finalResult *ApplyResult for _, subq := range inner.Inner { - newOuter, res, err := tryMergeSubQuery(ctx, subq, op) - if err != nil { - return nil, nil, err - } - if res == rewrite.SameTree { + newOuter, res := tryMergeSubQuery(ctx, subq, op) + if res == NoRewrite { // we failed to merge one of the inners - we need to abort - return nil, rewrite.SameTree, nil + return nil, NoRewrite } op = newOuter.(*Route) finalResult = finalResult.Merge(res) } - op.Source = &Filter{Source: outer.Source, Predicates: []sqlparser.Expr{subQuery.Original}} - return op, finalResult.Merge(rewrite.NewTree("merge outer of two subqueries", subQuery)), nil + op.Source = newFilter(outer.Source, subQuery.Original) + return op, finalResult.Merge(Rewrote("merge outer of two subqueries")) } -func tryMergeSubqueryWithOuter(ctx *plancontext.PlanningContext, subQuery *SubQuery, outer *Route, inner ops.Operator) (ops.Operator, *rewrite.ApplyResult, error) { +func tryMergeSubqueryWithOuter(ctx *plancontext.PlanningContext, subQuery *SubQuery, outer *Route, inner Operator) (Operator, *ApplyResult) { if updOp, ok := outer.Source.(*Update); ok && mergingIsBlocked(subQuery, updOp) { - return outer, rewrite.SameTree, nil + return outer, NoRewrite } exprs := subQuery.GetMergePredicates() merger := &subqueryRouteMerger{ @@ -527,13 +474,13 @@ func tryMergeSubqueryWithOuter(ctx *plancontext.PlanningContext, subQuery *SubQu } op := mergeSubqueryInputs(ctx, inner, outer, exprs, merger) if op == nil { - return outer, rewrite.SameTree, nil + return outer, NoRewrite } if !subQuery.IsProjection { - op.Source = &Filter{Source: outer.Source, Predicates: []sqlparser.Expr{subQuery.Original}} + op.Source = newFilter(outer.Source, subQuery.Original) } ctx.MergedSubqueries = append(ctx.MergedSubqueries, subQuery.originalSubquery) - return op, rewrite.NewTree("merged subquery with outer", subQuery), nil + return op, Rewrote("merged subquery with outer") } // This checked if subquery is part of the changed vindex values. Subquery cannot be merged with the outer route. @@ -546,21 +493,18 @@ func mergingIsBlocked(subQuery *SubQuery, updOp *Update) bool { return false } -func pushOrMerge(ctx *plancontext.PlanningContext, outer ops.Operator, inner *SubQuery) (ops.Operator, *rewrite.ApplyResult, error) { +func pushOrMerge(ctx *plancontext.PlanningContext, outer Operator, inner *SubQuery) (Operator, *ApplyResult) { switch o := outer.(type) { case *Route: return tryMergeSubQuery(ctx, inner, o) case *ApplyJoin: - join, applyResult, err := tryPushSubQueryInJoin(ctx, inner, o) - if err != nil { - return nil, nil, err - } + join, applyResult := tryPushSubQueryInJoin(ctx, inner, o) if join == nil { - return outer, rewrite.SameTree, nil + return outer, NoRewrite } - return join, applyResult, nil + return join, applyResult default: - return outer, rewrite.SameTree, nil + return outer, NoRewrite } } @@ -618,10 +562,7 @@ func (s *subqueryRouteMerger) mergeShardedRouting(ctx *plancontext.PlanningConte }) } - routing, err := tr.resetRoutingLogic(ctx) - if err != nil { - panic(err) - } + routing := tr.resetRoutingLogic(ctx) return s.merge(ctx, old1, old2, routing) } @@ -637,14 +578,11 @@ func (s *subqueryRouteMerger) merge(ctx *plancontext.PlanningContext, inner, out } } _, isSharded := r.(*ShardedRouting) - var src ops.Operator + var src Operator if isSharded { src = s.outer.Source if !s.subq.IsProjection { - src = &Filter{ - Source: s.outer.Source, - Predicates: []sqlparser.Expr{s.original}, - } + src = newFilter(s.outer.Source, s.original) } } else { src = s.rewriteASTExpression(ctx, inner) @@ -665,7 +603,7 @@ func (s *subqueryRouteMerger) merge(ctx *plancontext.PlanningContext, inner, out // we should be able to use this method for all plan types, // but using this method for sharded queries introduces bugs // We really need to figure out why this is not working as expected -func (s *subqueryRouteMerger) rewriteASTExpression(ctx *plancontext.PlanningContext, inner *Route) ops.Operator { +func (s *subqueryRouteMerger) rewriteASTExpression(ctx *plancontext.PlanningContext, inner *Route) Operator { src := s.outer.Source stmt, _, err := ToSQL(ctx, inner.Source) if err != nil { @@ -714,10 +652,7 @@ func (s *subqueryRouteMerger) rewriteASTExpression(ctx *plancontext.PlanningCont cursor.Replace(subq) } }, ctx.SemTable.CopySemanticInfo).(sqlparser.Expr) - src = &Filter{ - Source: s.outer.Source, - Predicates: []sqlparser.Expr{sQuery}, - } + src = newFilter(s.outer.Source, sQuery) } return src } @@ -726,7 +661,7 @@ func (s *subqueryRouteMerger) rewriteASTExpression(ctx *plancontext.PlanningCont // If they can be merged, a new operator with the merged routing is returned // If they cannot be merged, nil is returned. // These rules are similar but different from join merging -func mergeSubqueryInputs(ctx *plancontext.PlanningContext, in, out ops.Operator, joinPredicates []sqlparser.Expr, m *subqueryRouteMerger) *Route { +func mergeSubqueryInputs(ctx *plancontext.PlanningContext, in, out Operator, joinPredicates []sqlparser.Expr, m *subqueryRouteMerger) *Route { inRoute, outRoute := operatorsToRoutes(in, out) if inRoute == nil || outRoute == nil { return nil diff --git a/go/vt/vtgate/planbuilder/operators/table.go b/go/vt/vtgate/planbuilder/operators/table.go index 09a99170932..4bdf6f75c8b 100644 --- a/go/vt/vtgate/planbuilder/operators/table.go +++ b/go/vt/vtgate/planbuilder/operators/table.go @@ -22,7 +22,6 @@ import ( "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -43,7 +42,7 @@ type ( ) // Clone implements the Operator interface -func (to *Table) Clone([]ops.Operator) ops.Operator { +func (to *Table) Clone([]Operator) Operator { var columns []*sqlparser.ColName for _, name := range to.Columns { columns = append(columns, sqlparser.CloneRefOfColName(name)) @@ -61,7 +60,7 @@ func (to *Table) introducesTableID() semantics.TableSet { } // AddPredicate implements the PhysicalOperator interface -func (to *Table) AddPredicate(_ *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (to *Table) AddPredicate(_ *plancontext.PlanningContext, expr sqlparser.Expr) Operator { return newFilter(to, expr) } @@ -92,7 +91,7 @@ func (to *Table) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Sele return transformColumnsToSelectExprs(ctx, to) } -func (to *Table) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (to *Table) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } @@ -116,7 +115,7 @@ func addColumn(ctx *plancontext.PlanningContext, op ColNameColumns, e sqlparser. if !ok { panic(vterrors.VT09018(fmt.Sprintf("cannot add '%s' expression to a table/vindex", sqlparser.String(e)))) } - sqlparser.RemoveKeyspaceFromColName(col) + sqlparser.RemoveKeyspaceInCol(col) cols := op.GetColNames() colAsExpr := func(c *sqlparser.ColName) sqlparser.Expr { return c } if offset, found := canReuseColumn(ctx, cols, e, colAsExpr); found { @@ -130,7 +129,7 @@ func addColumn(ctx *plancontext.PlanningContext, op ColNameColumns, e sqlparser. func (to *Table) ShortDescription() string { tbl := to.VTable.String() var alias, where string - if !to.QTable.Alias.As.IsEmpty() { + if to.QTable.Alias.As.NotEmpty() { alias = " AS " + to.QTable.Alias.As.String() } diff --git a/go/vt/vtgate/planbuilder/operators/ops/to_json.go b/go/vt/vtgate/planbuilder/operators/to_json.go similarity index 98% rename from go/vt/vtgate/planbuilder/operators/ops/to_json.go rename to go/vt/vtgate/planbuilder/operators/to_json.go index 2b8b747f433..48b7fa9a247 100644 --- a/go/vt/vtgate/planbuilder/operators/ops/to_json.go +++ b/go/vt/vtgate/planbuilder/operators/to_json.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package ops +package operators import ( "fmt" diff --git a/go/vt/vtgate/planbuilder/operators/union.go b/go/vt/vtgate/planbuilder/operators/union.go index b3d866a00a3..1d739c9f01c 100644 --- a/go/vt/vtgate/planbuilder/operators/union.go +++ b/go/vt/vtgate/planbuilder/operators/union.go @@ -23,12 +23,11 @@ import ( "vitess.io/vitess/go/slice" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) type Union struct { - Sources []ops.Operator + Sources []Operator // These are the select expressions coming from each source Selects []sqlparser.SelectExprs @@ -38,7 +37,7 @@ type Union struct { unionColumnsAsAlisedExprs []*sqlparser.AliasedExpr } -func newUnion(srcs []ops.Operator, sourceSelects []sqlparser.SelectExprs, columns sqlparser.SelectExprs, distinct bool) *Union { +func newUnion(srcs []Operator, sourceSelects []sqlparser.SelectExprs, columns sqlparser.SelectExprs, distinct bool) *Union { if columns == nil { panic("rt") } @@ -51,24 +50,24 @@ func newUnion(srcs []ops.Operator, sourceSelects []sqlparser.SelectExprs, column } // Clone implements the Operator interface -func (u *Union) Clone(inputs []ops.Operator) ops.Operator { +func (u *Union) Clone(inputs []Operator) Operator { newOp := *u newOp.Sources = inputs newOp.Selects = slices.Clone(u.Selects) return &newOp } -func (u *Union) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (u *Union) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } // Inputs implements the Operator interface -func (u *Union) Inputs() []ops.Operator { +func (u *Union) Inputs() []Operator { return u.Sources } // SetInputs implements the Operator interface -func (u *Union) SetInputs(ops []ops.Operator) { +func (u *Union) SetInputs(ops []Operator) { u.Sources = ops } @@ -93,12 +92,9 @@ Notice how `X.col = 42` has been translated to `foo = 42` and `id = 42` on respe The first SELECT of the union dictates the column names, and the second is whatever expression can be found on the same offset. The names of the RHS are discarded. */ -func (u *Union) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (u *Union) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { offsets := make(map[string]int) - sel, err := u.GetSelectFor(0) - if err != nil { - panic(err) - } + sel := u.GetSelectFor(0) for i, selectExpr := range sel.SelectExprs { ae, ok := selectExpr.(*sqlparser.AliasedExpr) if !ok { @@ -107,15 +103,9 @@ func (u *Union) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Ex offsets[ae.ColumnName()] = i } - needsFilter, exprPerSource, err := u.predicatePerSource(expr, offsets) - if err != nil { - panic(err) - } + needsFilter, exprPerSource := u.predicatePerSource(expr, offsets) if needsFilter { - return &Filter{ - Source: u, - Predicates: []sqlparser.Expr{expr}, - } + return newFilter(u, expr) } for i, src := range u.Sources { @@ -125,11 +115,10 @@ func (u *Union) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Ex return u } -func (u *Union) predicatePerSource(expr sqlparser.Expr, offsets map[string]int) (bool, []sqlparser.Expr, error) { +func (u *Union) predicatePerSource(expr sqlparser.Expr, offsets map[string]int) (bool, []sqlparser.Expr) { needsFilter := false exprPerSource := make([]sqlparser.Expr, len(u.Sources)) for i := range u.Sources { - var err error predicate := sqlparser.CopyOnRewrite(expr, nil, func(cursor *sqlparser.CopyOnWriteCursor) { col, ok := cursor.Node().(*sqlparser.ColName) if !ok { @@ -143,39 +132,29 @@ func (u *Union) predicatePerSource(expr sqlparser.Expr, offsets map[string]int) return } - var sel *sqlparser.Select - sel, err = u.GetSelectFor(i) - if err != nil { - cursor.StopTreeWalk() - return - } - + sel := u.GetSelectFor(i) ae, ok := sel.SelectExprs[idx].(*sqlparser.AliasedExpr) if !ok { - err = vterrors.VT09015() - cursor.StopTreeWalk() - return + panic(vterrors.VT09015()) } cursor.Replace(ae.Expr) }, nil).(sqlparser.Expr) - if err != nil || needsFilter { - return needsFilter, nil, err - } + exprPerSource[i] = predicate } - return needsFilter, exprPerSource, nil + return needsFilter, exprPerSource } -func (u *Union) GetSelectFor(source int) (*sqlparser.Select, error) { +func (u *Union) GetSelectFor(source int) *sqlparser.Select { src := u.Sources[source] for { switch op := src.(type) { case *Horizon: - return sqlparser.GetFirstSelect(op.Query), nil + return sqlparser.GetFirstSelect(op.Query) case *Route: src = op.Source default: - return nil, vterrors.VT13001("expected all sources of the UNION to be horizons") + panic(vterrors.VT13001("expected all sources of the UNION to be horizons")) } } } @@ -209,24 +188,19 @@ func (u *Union) AddColumn(ctx *plancontext.PlanningContext, reuse bool, gb bool, panic(vterrors.VT13001(fmt.Sprintf("could not find the argument to the weight_string function: %s", sqlparser.String(wsArg)))) } - outputOffset, err := u.addWeightStringToOffset(ctx, argIdx, gb) - if err != nil { - panic(err) - } - - return outputOffset + return u.addWeightStringToOffset(ctx, argIdx, gb) default: panic(vterrors.VT13001(fmt.Sprintf("only weight_string function is expected - got %s", sqlparser.String(expr)))) } } -func (u *Union) addWeightStringToOffset(ctx *plancontext.PlanningContext, argIdx int, addToGroupBy bool) (outputOffset int, err error) { +func (u *Union) addWeightStringToOffset(ctx *plancontext.PlanningContext, argIdx int, addToGroupBy bool) (outputOffset int) { for i, src := range u.Sources { exprs := u.Selects[i] selectExpr := exprs[argIdx] ae, ok := selectExpr.(*sqlparser.AliasedExpr) if !ok { - return 0, vterrors.VT09015() + panic(vterrors.VT09015()) } thisOffset := src.AddColumn(ctx, false, addToGroupBy, aeWrap(weightStringFor(ae.Expr))) @@ -235,7 +209,7 @@ func (u *Union) addWeightStringToOffset(ctx *plancontext.PlanningContext, argIdx outputOffset = thisOffset } else { if thisOffset != outputOffset { - return 0, vterrors.VT12001("weight_string offsets did not line up for UNION") + panic(vterrors.VT12001("weight_string offsets did not line up for UNION")) } } } diff --git a/go/vt/vtgate/planbuilder/operators/union_merging.go b/go/vt/vtgate/planbuilder/operators/union_merging.go index 4c8b02f76d8..67853e44c7f 100644 --- a/go/vt/vtgate/planbuilder/operators/union_merging.go +++ b/go/vt/vtgate/planbuilder/operators/union_merging.go @@ -19,14 +19,13 @@ package operators import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" + "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) // mergeUnionInputInAnyOrder merges sources the sources of the union in any order // can be used for UNION DISTINCT -func mergeUnionInputInAnyOrder(ctx *plancontext.PlanningContext, op *Union) ([]ops.Operator, []sqlparser.SelectExprs, error) { +func mergeUnionInputInAnyOrder(ctx *plancontext.PlanningContext, op *Union) ([]Operator, []sqlparser.SelectExprs) { sources := op.Sources selects := op.Selects @@ -43,10 +42,7 @@ func mergeUnionInputInAnyOrder(ctx *plancontext.PlanningContext, op *Union) ([]o } selA := selects[idx] selB := selects[j] - newPlan, sel, err := mergeUnionInputs(ctx, srcA, srcB, selA, selB, op.distinct) - if err != nil { - return nil, nil, err - } + newPlan, sel := mergeUnionInputs(ctx, srcA, srcB, selA, selB, op.distinct) if newPlan != nil { sources[idx] = newPlan selects[idx] = sel @@ -57,10 +53,10 @@ func mergeUnionInputInAnyOrder(ctx *plancontext.PlanningContext, op *Union) ([]o } } if !merged { - return sources, selects, nil + return sources, selects } - var newSources []ops.Operator + var newSources []Operator var newSelects []sqlparser.SelectExprs for i, source := range sources { if keep[i] || i <= idx { @@ -73,10 +69,10 @@ func mergeUnionInputInAnyOrder(ctx *plancontext.PlanningContext, op *Union) ([]o selects = newSelects } - return sources, selects, nil + return sources, selects } -func mergeUnionInputsInOrder(ctx *plancontext.PlanningContext, op *Union) ([]ops.Operator, []sqlparser.SelectExprs, error) { +func mergeUnionInputsInOrder(ctx *plancontext.PlanningContext, op *Union) ([]Operator, []sqlparser.SelectExprs) { sources := op.Sources selects := op.Selects for { @@ -85,10 +81,7 @@ func mergeUnionInputsInOrder(ctx *plancontext.PlanningContext, op *Union) ([]ops j := i + 1 srcA, selA := sources[i], selects[i] srcB, selB := sources[j], selects[j] - newPlan, sel, err := mergeUnionInputs(ctx, srcA, srcB, selA, selB, op.distinct) - if err != nil { - return nil, nil, err - } + newPlan, sel := mergeUnionInputs(ctx, srcA, srcB, selA, selB, op.distinct) if newPlan != nil { sources[i] = newPlan selects[i] = sel @@ -102,7 +95,7 @@ func mergeUnionInputsInOrder(ctx *plancontext.PlanningContext, op *Union) ([]ops } } - return sources, selects, nil + return sources, selects } // mergeUnionInputs checks whether two operators can be merged into a single one. @@ -111,13 +104,13 @@ func mergeUnionInputsInOrder(ctx *plancontext.PlanningContext, op *Union) ([]ops // this function is very similar to mergeJoinInputs func mergeUnionInputs( ctx *plancontext.PlanningContext, - lhs, rhs ops.Operator, + lhs, rhs Operator, lhsExprs, rhsExprs sqlparser.SelectExprs, distinct bool, -) (ops.Operator, sqlparser.SelectExprs, error) { +) (Operator, sqlparser.SelectExprs) { lhsRoute, rhsRoute, routingA, routingB, a, b, sameKeyspace := prepareInputRoutes(lhs, rhs) if lhsRoute == nil { - return nil, nil, nil + return nil, nil } switch { @@ -134,12 +127,12 @@ func mergeUnionInputs( return createMergedUnion(ctx, lhsRoute, rhsRoute, lhsExprs, rhsExprs, distinct, routingA) case a == sharded && b == sharded && sameKeyspace: - res, exprs, err := tryMergeUnionShardedRouting(ctx, lhsRoute, rhsRoute, lhsExprs, rhsExprs, distinct) - if err != nil || res != nil { - return res, exprs, err + res, exprs := tryMergeUnionShardedRouting(ctx, lhsRoute, rhsRoute, lhsExprs, rhsExprs, distinct) + if res != nil { + return res, exprs } } - return nil, nil, nil + return nil, nil } func tryMergeUnionShardedRouting( @@ -147,7 +140,7 @@ func tryMergeUnionShardedRouting( routeA, routeB *Route, exprsA, exprsB sqlparser.SelectExprs, distinct bool, -) (ops.Operator, sqlparser.SelectExprs, error) { +) (Operator, sqlparser.SelectExprs) { tblA := routeA.Routing.(*ShardedRouting) tblB := routeB.Routing.(*ShardedRouting) @@ -173,7 +166,7 @@ func tryMergeUnionShardedRouting( } } - return nil, nil, nil + return nil, nil } func createMergedUnion( @@ -181,45 +174,54 @@ func createMergedUnion( lhsRoute, rhsRoute *Route, lhsExprs, rhsExprs sqlparser.SelectExprs, distinct bool, - routing Routing) (ops.Operator, sqlparser.SelectExprs, error) { + routing Routing) (Operator, sqlparser.SelectExprs) { // if there are `*` on either side, or a different number of SelectExpr items, // we give up aligning the expressions and trust that we can push everything down cols := make(sqlparser.SelectExprs, len(lhsExprs)) noDeps := len(lhsExprs) != len(rhsExprs) for idx, col := range lhsExprs { - ae, ok := col.(*sqlparser.AliasedExpr) + lae, ok := col.(*sqlparser.AliasedExpr) if !ok { cols[idx] = col noDeps = true continue } - col := sqlparser.NewColName(ae.ColumnName()) + col := sqlparser.NewColName(lae.ColumnName()) cols[idx] = aeWrap(col) if noDeps { continue } - deps := ctx.SemTable.RecursiveDeps(ae.Expr) - ae, ok = rhsExprs[idx].(*sqlparser.AliasedExpr) + deps := ctx.SemTable.RecursiveDeps(lae.Expr) + rae, ok := rhsExprs[idx].(*sqlparser.AliasedExpr) if !ok { noDeps = true continue } - deps = deps.Merge(ctx.SemTable.RecursiveDeps(ae.Expr)) + deps = deps.Merge(ctx.SemTable.RecursiveDeps(rae.Expr)) + rt, foundR := ctx.SemTable.TypeForExpr(rae.Expr) + lt, foundL := ctx.SemTable.TypeForExpr(lae.Expr) + if foundR && foundL { + t, err := evalengine.AggregateEvalTypes([]evalengine.Type{rt, lt}, ctx.VSchema.Environment().CollationEnv()) + if err == nil { + ctx.SemTable.ExprTypes[col] = t + } + } + ctx.SemTable.Recursive[col] = deps } - union := newUnion([]ops.Operator{lhsRoute.Source, rhsRoute.Source}, []sqlparser.SelectExprs{lhsExprs, rhsExprs}, cols, distinct) + union := newUnion([]Operator{lhsRoute.Source, rhsRoute.Source}, []sqlparser.SelectExprs{lhsExprs, rhsExprs}, cols, distinct) selectExprs := unionSelects(lhsExprs) return &Route{ Source: union, MergedWith: []*Route{rhsRoute}, Routing: routing, - }, selectExprs, nil + }, selectExprs } -func compactUnion(u *Union) *rewrite.ApplyResult { +func compactUnion(u *Union) *ApplyResult { if u.distinct { // first we remove unnecessary DISTINCTs for idx, source := range u.Sources { @@ -231,7 +233,7 @@ func compactUnion(u *Union) *rewrite.ApplyResult { } } - var newSources []ops.Operator + var newSources []Operator var newSelects []sqlparser.SelectExprs merged := false @@ -250,10 +252,10 @@ func compactUnion(u *Union) *rewrite.ApplyResult { } if !merged { - return rewrite.SameTree + return NoRewrite } u.Sources = newSources u.Selects = newSelects - return rewrite.NewTree("merged UNIONs", u) + return Rewrote("merged UNIONs") } diff --git a/go/vt/vtgate/planbuilder/operators/update.go b/go/vt/vtgate/planbuilder/operators/update.go index 743812f9dd7..cf55f91fddd 100644 --- a/go/vt/vtgate/planbuilder/operators/update.go +++ b/go/vt/vtgate/planbuilder/operators/update.go @@ -20,12 +20,13 @@ import ( "fmt" "maps" "slices" - "strings" + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -33,21 +34,16 @@ import ( type ( Update struct { - QTable *QueryTable - VTable *vindexes.Table + *DMLCommon + Assignments []SetExpr ChangedVindexValues map[string]*engine.VindexValues - OwnedVindexQuery string - Ignore sqlparser.Ignore - OrderBy sqlparser.OrderBy - Limit *sqlparser.Limit // these subqueries cannot be merged as they are part of the changed vindex values // these values are needed to be sent over to lookup vindex for update. // On merging this information will be lost, so subquery merge is blocked. SubQueriesArgOnChangedVindex []string - noInputs noColumns noPredicates } @@ -58,86 +54,144 @@ type ( } ) +func (u *Update) Inputs() []Operator { + if u.Source == nil { + return nil + } + return []Operator{u.Source} +} + +func (u *Update) SetInputs(inputs []Operator) { + if len(inputs) != 1 { + panic(vterrors.VT13001("unexpected number of inputs for Update operator")) + } + u.Source = inputs[0] +} + func (se SetExpr) String() string { return fmt.Sprintf("%s = %s", sqlparser.String(se.Name), sqlparser.String(se.Expr.EvalExpr)) } // Introduces implements the PhysicalOperator interface func (u *Update) introducesTableID() semantics.TableSet { - return u.QTable.ID + return u.Target.ID } // Clone implements the Operator interface -func (u *Update) Clone([]ops.Operator) ops.Operator { +func (u *Update) Clone(inputs []Operator) Operator { upd := *u upd.Assignments = slices.Clone(u.Assignments) upd.ChangedVindexValues = maps.Clone(u.ChangedVindexValues) + upd.SetInputs(inputs) return &upd } -func (u *Update) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (u *Update) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } func (u *Update) TablesUsed() []string { - if u.VTable != nil { - return SingleQualifiedIdentifier(u.VTable.Keyspace, u.VTable.Name) - } - return nil + return SingleQualifiedIdentifier(u.Target.VTable.Keyspace, u.Target.VTable.Name) } func (u *Update) ShortDescription() string { - s := []string{u.VTable.String()} - if u.Limit != nil { - s = append(s, sqlparser.String(u.Limit)) - } - if len(u.OrderBy) > 0 { - s = append(s, sqlparser.String(u.OrderBy)) + ovq := "" + if u.OwnedVindexQuery != nil { + var cols, orderby, limit string + cols = fmt.Sprintf("COLUMNS: [%s]", sqlparser.String(u.OwnedVindexQuery.SelectExprs)) + if len(u.OwnedVindexQuery.OrderBy) > 0 { + orderby = fmt.Sprintf(" ORDERBY: [%s]", sqlparser.String(u.OwnedVindexQuery.OrderBy)) + } + if u.OwnedVindexQuery.Limit != nil { + limit = fmt.Sprintf(" LIMIT: [%s]", sqlparser.String(u.OwnedVindexQuery.Limit)) + } + ovq = fmt.Sprintf(" vindexQuery(%s%s%s)", cols, orderby, limit) } - return strings.Join(s, " ") + return fmt.Sprintf("%s.%s%s", u.Target.VTable.Keyspace.Name, u.Target.VTable.Name.String(), ovq) } -func createOperatorFromUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update) (ops.Operator, error) { - tableInfo, qt, err := createQueryTableForDML(ctx, updStmt.TableExprs[0], updStmt.Where) - if err != nil { - return nil, err - } +func createOperatorFromUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update) (op Operator) { + errIfUpdateNotSupported(ctx, updStmt) - vindexTable, routing, err := buildVindexTableForDML(ctx, tableInfo, qt, "update") - if err != nil { - return nil, err - } + var updClone *sqlparser.Update + var vTbl *vindexes.Table - updClone := sqlparser.CloneRefOfUpdate(updStmt) - updOp, err := createUpdateOperator(ctx, updStmt, vindexTable, qt, routing) - if err != nil { - return nil, err + op, vTbl, updClone = createUpdateOperator(ctx, updStmt) + + op = &LockAndComment{ + Source: op, + Comments: updStmt.Comments, + Lock: sqlparser.ShareModeLock, } parentFks := ctx.SemTable.GetParentForeignKeysList() childFks := ctx.SemTable.GetChildForeignKeysList() if len(childFks) == 0 && len(parentFks) == 0 { - return updOp, nil + return op } // If the delete statement has a limit, we don't support it yet. if updStmt.Limit != nil { - return nil, vterrors.VT12001("update with limit with foreign key constraints") + panic(vterrors.VT12001("update with limit with foreign key constraints")) } - return buildFkOperator(ctx, updOp, updClone, parentFks, childFks, vindexTable) + // Now we check if any of the foreign key columns that are being udpated have dependencies on other updated columns. + // This is unsafe, and we currently don't support this in Vitess. + if err := ctx.SemTable.ErrIfFkDependentColumnUpdated(updStmt.Exprs); err != nil { + panic(err) + } + + return buildFkOperator(ctx, op, updClone, parentFks, childFks, vTbl) +} + +func errIfUpdateNotSupported(ctx *plancontext.PlanningContext, stmt *sqlparser.Update) { + var vTbl *vindexes.Table + for _, ue := range stmt.Exprs { + tblInfo, err := ctx.SemTable.TableInfoForExpr(ue.Name) + if err != nil { + panic(err) + } + if _, isATable := tblInfo.(*semantics.RealTable); !isATable { + var tblName string + ate := tblInfo.GetAliasedTableExpr() + if ate != nil { + tblName = sqlparser.String(ate) + } + panic(vterrors.VT03032(tblName)) + } + + if vTbl == nil { + vTbl = tblInfo.GetVindexTable() + } + if vTbl != tblInfo.GetVindexTable() { + panic(vterrors.VT12001("multi-table UPDATE statement with multi-target column update")) + } + } } -func createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, vindexTable *vindexes.Table, qt *QueryTable, routing Routing) (ops.Operator, error) { +func createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update) (Operator, *vindexes.Table, *sqlparser.Update) { + op := crossJoin(ctx, updStmt.TableExprs) + sqc := &SubQueryBuilder{} + if updStmt.Where != nil { + op = addWherePredsToSubQueryBuilder(ctx, updStmt.Where.Expr, op, sqc) + } + + outerID := TableID(op) assignments := make([]SetExpr, len(updStmt.Exprs)) + // updClone is used in foreign key planning to create the selection statements to be used for verification and selection. + // If we encounter subqueries, we want to fix the updClone to use the replaced expression, so that the pulled out subquery's + // result is used everywhere instead of running the subquery multiple times, which is wasteful. + updClone := sqlparser.CloneRefOfUpdate(updStmt) + var tblInfo semantics.TableInfo + var err error for idx, updExpr := range updStmt.Exprs { - expr, subqs, err := sqc.pullOutValueSubqueries(ctx, updExpr.Expr, qt.ID, true) - if err != nil { - return nil, err - } + expr, subqs := sqc.pullOutValueSubqueries(ctx, updExpr.Expr, outerID, true) if len(subqs) == 0 { expr = updExpr.Expr + } else { + updClone.Exprs[idx].Expr = sqlparser.CloneExpr(expr) + ctx.SemTable.UpdateChildFKExpr(updExpr, expr) } proj := newProjExpr(aeWrap(expr)) if len(subqs) != 0 { @@ -147,94 +201,98 @@ func createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.U Name: updExpr.Name, Expr: proj, } + tblInfo, err = ctx.SemTable.TableInfoForExpr(updExpr.Name) + if err != nil { + panic(err) + } } - vp, cvv, ovq, subQueriesArgOnChangedVindex, err := getUpdateVindexInformation(ctx, updStmt, vindexTable, qt.ID, assignments) - if err != nil { - return nil, err + tblID := ctx.SemTable.TableSetFor(tblInfo.GetAliasedTableExpr()) + vTbl := tblInfo.GetVindexTable() + // Reference table should update the source table. + if vTbl.Type == vindexes.TypeReference && vTbl.Source != nil { + vTbl = updateQueryGraphWithSource(ctx, op, tblID, vTbl) } - tr, ok := routing.(*ShardedRouting) - if ok { - tr.VindexPreds = vp + name, err := tblInfo.Name() + if err != nil { + panic(err) } - for _, predicate := range qt.Predicates { - if subq, err := sqc.handleSubquery(ctx, predicate, qt.ID); err != nil { - return nil, err - } else if subq != nil { - continue - } - routing, err = UpdateRoutingLogic(ctx, predicate, routing) - if err != nil { - return nil, err - } + targetTbl := TargetTable{ + ID: tblID, + VTable: vTbl, + Name: name, } - if routing.OpCode() == engine.Scatter && updStmt.Limit != nil { - // TODO systay: we should probably check for other op code types - IN could also hit multiple shards (2022-04-07) - return nil, vterrors.VT12001("multi shard UPDATE with LIMIT") - } + _, cvv, ovq, subQueriesArgOnChangedVindex := getUpdateVindexInformation(ctx, updStmt, targetTbl, assignments) - route := &Route{ - Source: &Update{ - QTable: qt, - VTable: vindexTable, - Assignments: assignments, - ChangedVindexValues: cvv, - OwnedVindexQuery: ovq, - Ignore: updStmt.Ignore, - Limit: updStmt.Limit, - OrderBy: updStmt.OrderBy, - SubQueriesArgOnChangedVindex: subQueriesArgOnChangedVindex, + updOp := &Update{ + DMLCommon: &DMLCommon{ + Ignore: updStmt.Ignore, + Target: targetTbl, + OwnedVindexQuery: ovq, + Source: op, }, - Routing: routing, - Comments: updStmt.Comments, + Assignments: assignments, + ChangedVindexValues: cvv, + SubQueriesArgOnChangedVindex: subQueriesArgOnChangedVindex, + } + + if len(updStmt.OrderBy) > 0 { + addOrdering(ctx, updStmt.OrderBy, updOp) } - decorator := func(op ops.Operator) ops.Operator { - return &LockAndComment{ - Source: op, - Lock: sqlparser.ShareModeLock, + if updStmt.Limit != nil { + updOp.Source = &Limit{ + Source: updOp.Source, + AST: updStmt.Limit, } } - return sqc.getRootOperator(route, decorator), nil + return sqc.getRootOperator(updOp, nil), vTbl, updClone } -func buildFkOperator(ctx *plancontext.PlanningContext, updOp ops.Operator, updClone *sqlparser.Update, parentFks []vindexes.ParentFKInfo, childFks []vindexes.ChildFKInfo, updatedTable *vindexes.Table) (ops.Operator, error) { - // We only support simple expressions in update queries for foreign key handling. - if isNonLiteral(updClone.Exprs, parentFks, childFks) { - return nil, vterrors.VT12001("update expression with non-literal values with foreign key constraints") +func getUpdateVindexInformation( + ctx *plancontext.PlanningContext, + updStmt *sqlparser.Update, + table TargetTable, + assignments []SetExpr, +) ([]*VindexPlusPredicates, map[string]*engine.VindexValues, *sqlparser.Select, []string) { + if !table.VTable.Keyspace.Sharded { + return nil, nil, nil, nil } - restrictChildFks, cascadeChildFks := splitChildFks(childFks) + primaryVindex, vindexAndPredicates := getVindexInformation(table.ID, table.VTable) + changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex := buildChangedVindexesValues(ctx, updStmt, table.VTable, primaryVindex.Columns, assignments) + return vindexAndPredicates, changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex +} - op, err := createFKCascadeOp(ctx, updOp, updClone, cascadeChildFks, updatedTable) - if err != nil { - return nil, err +func buildFkOperator(ctx *plancontext.PlanningContext, updOp Operator, updClone *sqlparser.Update, parentFks []vindexes.ParentFKInfo, childFks []vindexes.ChildFKInfo, updatedTable *vindexes.Table) Operator { + // If there is a subquery container above update operator, we want to do the foreign key planning inside it, + // because we want the Inner of the subquery to execute first and its result be used for the entire foreign key update planning. + foundSubqc := false + TopDown(updOp, TableID, func(in Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) { + if op, isSubqc := in.(*SubQueryContainer); isSubqc { + foundSubqc = true + op.Outer = buildFkOperator(ctx, op.Outer, updClone, parentFks, childFks, updatedTable) + } + return in, NoRewrite + }, stopAtUpdateOp) + if foundSubqc { + return updOp } - return createFKVerifyOp(ctx, op, updClone, parentFks, restrictChildFks) + restrictChildFks, cascadeChildFks := splitChildFks(childFks) + + op := createFKCascadeOp(ctx, updOp, updClone, cascadeChildFks, updatedTable) + + return createFKVerifyOp(ctx, op, updClone, parentFks, restrictChildFks, updatedTable) } -func isNonLiteral(updExprs sqlparser.UpdateExprs, parentFks []vindexes.ParentFKInfo, childFks []vindexes.ChildFKInfo) bool { - for _, updateExpr := range updExprs { - if sqlparser.IsLiteral(updateExpr.Expr) { - continue - } - for _, parentFk := range parentFks { - if parentFk.ChildColumns.FindColumn(updateExpr.Name.Name) >= 0 { - return true - } - } - for _, childFk := range childFks { - if childFk.ParentColumns.FindColumn(updateExpr.Name.Name) >= 0 { - return true - } - } - } - return false +func stopAtUpdateOp(operator Operator) VisitRule { + _, isUpdate := operator.(*Update) + return VisitRule(!isUpdate) } // splitChildFks splits the child foreign keys into restrict and cascade list as restrict is handled through Verify operator and cascade is handled through Cascade operator. @@ -256,9 +314,9 @@ func splitChildFks(fks []vindexes.ChildFKInfo) (restrictChildFks, cascadeChildFk return } -func createFKCascadeOp(ctx *plancontext.PlanningContext, parentOp ops.Operator, updStmt *sqlparser.Update, childFks []vindexes.ChildFKInfo, updatedTable *vindexes.Table) (ops.Operator, error) { +func createFKCascadeOp(ctx *plancontext.PlanningContext, parentOp Operator, updStmt *sqlparser.Update, childFks []vindexes.ChildFKInfo, updatedTable *vindexes.Table) Operator { if len(childFks) == 0 { - return parentOp, nil + return parentOp } var fkChildren []*FkChild @@ -267,34 +325,153 @@ func createFKCascadeOp(ctx *plancontext.PlanningContext, parentOp ops.Operator, for _, fk := range childFks { // We should have already filtered out update restrict foreign keys. if fk.OnUpdate.IsRestrict() { - return nil, vterrors.VT13001("ON UPDATE RESTRICT foreign keys should already be filtered") + panic(vterrors.VT13001("ON UPDATE RESTRICT foreign keys should already be filtered")) } // We need to select all the parent columns for the foreign key constraint, to use in the update of the child table. - cols, exprs := selectParentColumns(fk, len(selectExprs)) - selectExprs = append(selectExprs, exprs...) - - fkChild, err := createFkChildForUpdate(ctx, fk, updStmt, cols, updatedTable) - if err != nil { - return nil, err + var selectOffsets []int + selectOffsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, updatedTable.GetTableName()) + + // If we are updating a foreign key column to a non-literal value then, need information about + // 1. whether the new value is different from the old value + // 2. the new value itself. + // 3. the bind variable to assign to this value. + var nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo + ue := ctx.SemTable.GetUpdateExpressionsForFk(fk.String(updatedTable)) + // We only need to store these offsets and add these expressions to SELECT when there are non-literal updates present. + if hasNonLiteralUpdate(ue) { + for _, updExpr := range ue { + // We add the expression and a comparison expression to the SELECT exprssion while storing their offsets. + var info engine.NonLiteralUpdateInfo + info, selectExprs = addNonLiteralUpdExprToSelect(ctx, updatedTable, updExpr, selectExprs) + nonLiteralUpdateInfo = append(nonLiteralUpdateInfo, info) + } } + + fkChild := createFkChildForUpdate(ctx, fk, selectOffsets, nonLiteralUpdateInfo, updatedTable) fkChildren = append(fkChildren, fkChild) } - selectionOp, err := createSelectionOp(ctx, selectExprs, updStmt.TableExprs, updStmt.Where, nil, sqlparser.ForUpdateLock) - if err != nil { - return nil, err - } + selectionOp := createSelectionOp(ctx, selectExprs, updStmt.TableExprs, updStmt.Where, updStmt.OrderBy, nil, getUpdateLock(updatedTable)) return &FkCascade{ Selection: selectionOp, Children: fkChildren, Parent: parentOp, - }, nil + } +} + +// hasNonLiteralUpdate checks if any of the update expressions have a non-literal update. +func hasNonLiteralUpdate(exprs sqlparser.UpdateExprs) bool { + for _, expr := range exprs { + if !sqlparser.IsLiteral(expr.Expr) { + return true + } + } + return false +} + +// addColumns adds the given set of columns to the select expressions provided. It tries to reuse the columns if already present in it. +// It returns the list of offsets for the columns and the updated select expressions. +func addColumns(ctx *plancontext.PlanningContext, columns sqlparser.Columns, exprs []sqlparser.SelectExpr, tableName sqlparser.TableName) ([]int, []sqlparser.SelectExpr) { + var offsets []int + selectExprs := exprs + for _, column := range columns { + ae := aeWrap(sqlparser.NewColNameWithQualifier(column.String(), tableName)) + exists := false + for idx, expr := range exprs { + if ctx.SemTable.EqualsExpr(expr.(*sqlparser.AliasedExpr).Expr, ae.Expr) { + offsets = append(offsets, idx) + exists = true + break + } + } + if !exists { + offsets = append(offsets, len(selectExprs)) + selectExprs = append(selectExprs, ae) + + } + } + return offsets, selectExprs +} + +// For an update query having non-literal updates, we add the updated expression and a comparison expression to the select query. +// For example, for a query like `update fk_table set col = id * 100 + 1` +// We would add the expression `id * 100 + 1` and the comparison expression `col <=> id * 100 + 1` to the select query. +func addNonLiteralUpdExprToSelect(ctx *plancontext.PlanningContext, updatedTable *vindexes.Table, updExpr *sqlparser.UpdateExpr, exprs []sqlparser.SelectExpr) (engine.NonLiteralUpdateInfo, []sqlparser.SelectExpr) { + // Create the comparison expression. + castedExpr := getCastedUpdateExpression(updatedTable, updExpr) + compExpr := sqlparser.NewComparisonExpr(sqlparser.NullSafeEqualOp, updExpr.Name, castedExpr, nil) + info := engine.NonLiteralUpdateInfo{ + CompExprCol: -1, + UpdateExprCol: -1, + } + // Add the expressions to the select expressions. We make sure to reuse the offset if it has already been added once. + for idx, selectExpr := range exprs { + if ctx.SemTable.EqualsExpr(selectExpr.(*sqlparser.AliasedExpr).Expr, compExpr) { + info.CompExprCol = idx + } + if ctx.SemTable.EqualsExpr(selectExpr.(*sqlparser.AliasedExpr).Expr, castedExpr) { + info.UpdateExprCol = idx + } + } + // If the expression doesn't exist, then we add the expression and store the offset. + if info.CompExprCol == -1 { + info.CompExprCol = len(exprs) + exprs = append(exprs, aeWrap(compExpr)) + } + if info.UpdateExprCol == -1 { + info.UpdateExprCol = len(exprs) + exprs = append(exprs, aeWrap(castedExpr)) + } + return info, exprs +} + +func getCastedUpdateExpression(updatedTable *vindexes.Table, updExpr *sqlparser.UpdateExpr) sqlparser.Expr { + castTypeStr := getCastTypeForColumn(updatedTable, updExpr) + if castTypeStr == "" { + return updExpr.Expr + } + return &sqlparser.CastExpr{ + Expr: updExpr.Expr, + Type: &sqlparser.ConvertType{ + Type: castTypeStr, + }, + } +} + +func getCastTypeForColumn(updatedTable *vindexes.Table, updExpr *sqlparser.UpdateExpr) string { + var ty querypb.Type + for _, column := range updatedTable.Columns { + if updExpr.Name.Name.Equal(column.Name) { + ty = column.Type + break + } + } + switch { + case sqltypes.IsNull(ty): + return "" + case sqltypes.IsSigned(ty): + return "SIGNED" + case sqltypes.IsUnsigned(ty): + return "UNSIGNED" + case sqltypes.IsFloat(ty): + return "FLOAT" + case sqltypes.IsDecimal(ty): + return "DECIMAL" + case sqltypes.IsDateOrTime(ty): + return "DATETIME" + case sqltypes.IsBinary(ty): + return "BINARY" + case sqltypes.IsText(ty): + return "CHAR" + default: + return "" + } } // createFkChildForUpdate creates the update query operator for the child table based on the foreign key constraints. -func createFkChildForUpdate(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, cols []int, updatedTable *vindexes.Table) (*FkChild, error) { +func createFkChildForUpdate(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, selectOffsets []int, nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo, updatedTable *vindexes.Table) *FkChild { // Create a ValTuple of child column names var valTuple sqlparser.ValTuple for _, column := range fk.ChildColumns { @@ -307,36 +484,42 @@ func createFkChildForUpdate(ctx *plancontext.PlanningContext, fk vindexes.ChildF compExpr := sqlparser.NewComparisonExpr(sqlparser.InOp, valTuple, sqlparser.NewListArg(bvName), nil) var childWhereExpr sqlparser.Expr = compExpr - var childOp ops.Operator - var err error + // In the case of non-literal updates, we need to assign bindvariables for storing the updated value of the columns + // coming from the SELECT query. + if len(nonLiteralUpdateInfo) > 0 { + for idx, info := range nonLiteralUpdateInfo { + info.UpdateExprBvName = ctx.ReservedVars.ReserveVariable(foreignKeyUpdateExpr) + nonLiteralUpdateInfo[idx] = info + } + } + + var childOp Operator switch fk.OnUpdate { case sqlparser.Cascade: - childOp, err = buildChildUpdOpForCascade(ctx, fk, updStmt, childWhereExpr, updatedTable) + childOp = buildChildUpdOpForCascade(ctx, fk, childWhereExpr, nonLiteralUpdateInfo, updatedTable) case sqlparser.SetNull: - childOp, err = buildChildUpdOpForSetNull(ctx, fk, updStmt, childWhereExpr) + childOp = buildChildUpdOpForSetNull(ctx, fk, childWhereExpr, nonLiteralUpdateInfo, updatedTable) case sqlparser.SetDefault: - return nil, vterrors.VT09016() - } - if err != nil { - return nil, err + panic(vterrors.VT09016()) } return &FkChild{ - BVName: bvName, - Cols: cols, - Op: childOp, - }, nil + BVName: bvName, + Cols: selectOffsets, + Op: childOp, + NonLiteralInfo: nonLiteralUpdateInfo, + } } // buildChildUpdOpForCascade builds the child update statement operator for the CASCADE type foreign key constraint. // The query looks like this - // // `UPDATE SET WHERE IN ()` -func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr, updatedTable *vindexes.Table) (ops.Operator, error) { +func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, childWhereExpr sqlparser.Expr, nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo, updatedTable *vindexes.Table) Operator { // The update expressions are the same as the update expressions in the parent update query // with the column names replaced with the child column names. var childUpdateExprs sqlparser.UpdateExprs - for _, updateExpr := range updStmt.Exprs { + for idx, updateExpr := range ctx.SemTable.GetUpdateExpressionsForFk(fk.String(updatedTable)) { colIdx := fk.ParentColumns.FindColumn(updateExpr.Name.Name) if colIdx == -1 { continue @@ -344,17 +527,19 @@ func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.Chi // The where condition is the same as the comparison expression above // with the column names replaced with the child column names. + childUpdateExpr := updateExpr.Expr + if len(nonLiteralUpdateInfo) > 0 && nonLiteralUpdateInfo[idx].UpdateExprBvName != "" { + childUpdateExpr = sqlparser.NewArgument(nonLiteralUpdateInfo[idx].UpdateExprBvName) + } childUpdateExprs = append(childUpdateExprs, &sqlparser.UpdateExpr{ Name: sqlparser.NewColName(fk.ChildColumns[colIdx].String()), - Expr: updateExpr.Expr, + Expr: childUpdateExpr, }) } // Because we could be updating the child to a non-null value, // We have to run with foreign key checks OFF because the parent isn't guaranteed to have // the data being updated to. - parsedComments := sqlparser.Comments{ - "/*+ SET_VAR(foreign_key_checks=OFF) */", - }.Parsed() + parsedComments := (&sqlparser.ParsedComments{}).SetMySQLSetVarValue(sysvars.ForeignKeyChecks, "OFF").Parsed() childUpdStmt := &sqlparser.Update{ Comments: parsedComments, Exprs: childUpdateExprs, @@ -373,7 +558,13 @@ func buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.Chi // `UPDATE SET // WHERE IN () // [AND ({ IS NULL OR}... NOT IN ())]` -func buildChildUpdOpForSetNull(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, updStmt *sqlparser.Update, childWhereExpr sqlparser.Expr) (ops.Operator, error) { +func buildChildUpdOpForSetNull( + ctx *plancontext.PlanningContext, + fk vindexes.ChildFKInfo, + childWhereExpr sqlparser.Expr, + nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo, + updatedTable *vindexes.Table, +) Operator { // For the SET NULL type constraint, we need to set all the child columns to NULL. var childUpdateExprs sqlparser.UpdateExprs for _, column := range fk.ChildColumns { @@ -392,34 +583,57 @@ func buildChildUpdOpForSetNull(ctx *plancontext.PlanningContext, fk vindexes.Chi // For example, if we are setting `update parent cola = :v1 and colb = :v2`, then on the child, the where condition would look something like this - // `:v1 IS NULL OR :v2 IS NULL OR (child_cola, child_colb) NOT IN ((:v1,:v2))` // So, if either of :v1 or :v2 is NULL, then the entire condition is true (which is the same as not having the condition when :v1 or :v2 is NULL). - compExpr := nullSafeNotInComparison(updStmt.Exprs, fk) + updateExprs := ctx.SemTable.GetUpdateExpressionsForFk(fk.String(updatedTable)) + compExpr := nullSafeNotInComparison(ctx, + updatedTable, + updateExprs, fk, updatedTable.GetTableName(), nonLiteralUpdateInfo, false /* appendQualifier */) if compExpr != nil { childWhereExpr = &sqlparser.AndExpr{ Left: childWhereExpr, Right: compExpr, } } + parsedComments := getParsedCommentsForFkChecks(ctx) childUpdStmt := &sqlparser.Update{ Exprs: childUpdateExprs, + Comments: parsedComments, TableExprs: []sqlparser.TableExpr{sqlparser.NewAliasedTableExpr(fk.Table.GetTableName(), "")}, Where: &sqlparser.Where{Type: sqlparser.WhereClause, Expr: childWhereExpr}, } return createOpFromStmt(ctx, childUpdStmt, false, "") } +// getParsedCommentsForFkChecks gets the parsed comments to be set on a child query related to foreign_key_checks session variable. +// We only use this function if foreign key checks are either unspecified or on. +// If foreign key checks are explicity turned on, then we should add the set_var parsed comment too +// since underlying MySQL might have foreign_key_checks as off. +// We run with foreign key checks on because the DML might still fail on MySQL due to a child table +// with RESTRICT constraints. +func getParsedCommentsForFkChecks(ctx *plancontext.PlanningContext) (parsedComments *sqlparser.ParsedComments) { + fkState := ctx.VSchema.GetForeignKeyChecksState() + if fkState != nil && *fkState { + parsedComments = parsedComments.SetMySQLSetVarValue(sysvars.ForeignKeyChecks, "ON").Parsed() + } + return parsedComments +} + // createFKVerifyOp creates the verify operator for the parent foreign key constraints. -func createFKVerifyOp(ctx *plancontext.PlanningContext, childOp ops.Operator, updStmt *sqlparser.Update, parentFks []vindexes.ParentFKInfo, restrictChildFks []vindexes.ChildFKInfo) (ops.Operator, error) { +func createFKVerifyOp( + ctx *plancontext.PlanningContext, + childOp Operator, + updStmt *sqlparser.Update, + parentFks []vindexes.ParentFKInfo, + restrictChildFks []vindexes.ChildFKInfo, + updatedTable *vindexes.Table, +) Operator { if len(parentFks) == 0 && len(restrictChildFks) == 0 { - return childOp, nil + return childOp } var Verify []*VerifyOp // This validates that new values exists on the parent table. for _, fk := range parentFks { - op, err := createFkVerifyOpForParentFKForUpdate(ctx, updStmt, fk) - if err != nil { - return nil, err - } + op := createFkVerifyOpForParentFKForUpdate(ctx, updatedTable, updStmt, fk) Verify = append(Verify, &VerifyOp{ Op: op, Typ: engine.ParentVerify, @@ -427,10 +641,8 @@ func createFKVerifyOp(ctx *plancontext.PlanningContext, childOp ops.Operator, up } // This validates that the old values don't exist on the child table. for _, fk := range restrictChildFks { - op, err := createFkVerifyOpForChildFKForUpdate(ctx, updStmt, fk) - if err != nil { - return nil, err - } + op := createFkVerifyOpForChildFKForUpdate(ctx, updatedTable, updStmt, fk) + Verify = append(Verify, &VerifyOp{ Op: op, Typ: engine.ChildVerify, @@ -440,29 +652,31 @@ func createFKVerifyOp(ctx *plancontext.PlanningContext, childOp ops.Operator, up return &FkVerify{ Verify: Verify, Input: childOp, - }, nil + } } // Each parent foreign key constraint is verified by an anti join query of the form: // select 1 from child_tbl left join parent_tbl on -// where and and limit 1 +// where and and and and limit 1 // E.g: // Child (c1, c2) references Parent (p1, p2) -// update Child set c1 = 1 where id = 1 +// update Child set c1 = c2 + 1 where id = 1 // verify query: -// select 1 from Child left join Parent on Parent.p1 = 1 and Parent.p2 = Child.c2 -// where Parent.p1 is null and Parent.p2 is null and Child.id = 1 -// and Child.c2 is not null +// select 1 from Child left join Parent on Parent.p1 = Child.c2 + 1 and Parent.p2 = Child.c2 +// where Parent.p1 is null and Parent.p2 is null and Child.id = 1 and Child.c2 + 1 is not null +// and Child.c2 is not null and not ((Child.c1) <=> (Child.c2 + 1)) // limit 1 -func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, pFK vindexes.ParentFKInfo) (ops.Operator, error) { +func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updatedTable *vindexes.Table, updStmt *sqlparser.Update, pFK vindexes.ParentFKInfo) Operator { childTblExpr := updStmt.TableExprs[0].(*sqlparser.AliasedTableExpr) childTbl, err := childTblExpr.TableName() if err != nil { - return nil, err + panic(err) } parentTbl := pFK.Table.GetTableName() var whereCond sqlparser.Expr var joinCond sqlparser.Expr + var notEqualColNames sqlparser.ValTuple + var notEqualExprs sqlparser.ValTuple for idx, column := range pFK.ChildColumns { var matchedExpr *sqlparser.UpdateExpr for _, updateExpr := range updStmt.Exprs { @@ -479,7 +693,7 @@ func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updS var joinExpr sqlparser.Expr if matchedExpr == nil { predicate = &sqlparser.AndExpr{ - Left: parentIsNullExpr, + Left: predicate, Right: &sqlparser.IsExpr{ Left: sqlparser.NewColNameWithQualifier(pFK.ChildColumns[idx].String(), childTbl), Right: sqlparser.IsNotNullOp, @@ -491,10 +705,20 @@ func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updS Right: sqlparser.NewColNameWithQualifier(pFK.ChildColumns[idx].String(), childTbl), } } else { + notEqualColNames = append(notEqualColNames, prefixColNames(ctx, childTbl, matchedExpr.Name)) + prefixedMatchExpr := prefixColNames(ctx, childTbl, getCastedUpdateExpression(updatedTable, matchedExpr)) + notEqualExprs = append(notEqualExprs, prefixedMatchExpr) joinExpr = &sqlparser.ComparisonExpr{ Operator: sqlparser.EqualOp, Left: sqlparser.NewColNameWithQualifier(pFK.ParentColumns[idx].String(), parentTbl), - Right: prefixColNames(childTbl, matchedExpr.Expr), + Right: prefixedMatchExpr, + } + predicate = &sqlparser.AndExpr{ + Left: predicate, + Right: &sqlparser.IsExpr{ + Left: prefixedMatchExpr, + Right: sqlparser.IsNotNullOp, + }, } } @@ -505,9 +729,19 @@ func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updS joinCond = &sqlparser.AndExpr{Left: joinCond, Right: joinExpr} whereCond = &sqlparser.AndExpr{Left: whereCond, Right: predicate} } + whereCond = &sqlparser.AndExpr{ + Left: whereCond, + Right: &sqlparser.NotExpr{ + Expr: &sqlparser.ComparisonExpr{ + Operator: sqlparser.NullSafeEqualOp, + Left: notEqualColNames, + Right: notEqualExprs, + }, + }, + } // add existing where condition on the update statement if updStmt.Where != nil { - whereCond = &sqlparser.AndExpr{Left: whereCond, Right: prefixColNames(childTbl, updStmt.Where.Expr)} + whereCond = &sqlparser.AndExpr{Left: whereCond, Right: prefixColNames(ctx, childTbl, updStmt.Where.Expr)} } return createSelectionOp(ctx, sqlparser.SelectExprs{sqlparser.NewAliasedExpr(sqlparser.NewIntLiteral("1"), "")}, @@ -519,28 +753,43 @@ func createFkVerifyOpForParentFKForUpdate(ctx *plancontext.PlanningContext, updS sqlparser.NewJoinCondition(joinCond, nil)), }, sqlparser.NewWhere(sqlparser.WhereClause, whereCond), + nil, sqlparser.NewLimitWithoutOffset(1), - sqlparser.ShareModeLock) + getVerifyLock(updatedTable)) +} + +func getVerifyLock(vTbl *vindexes.Table) sqlparser.Lock { + if len(vTbl.UniqueKeys) > 0 { + return sqlparser.ForShareLockNoWait + } + return sqlparser.ForShareLock +} + +func getUpdateLock(vTbl *vindexes.Table) sqlparser.Lock { + if len(vTbl.UniqueKeys) > 0 { + return sqlparser.ForUpdateLockNoWait + } + return sqlparser.ForUpdateLock } // Each child foreign key constraint is verified by a join query of the form: // select 1 from child_tbl join parent_tbl on where [AND ({ IS NULL OR}... NOT IN ())] limit 1 // E.g: // Child (c1, c2) references Parent (p1, p2) -// update Parent set p1 = 1 where id = 1 +// update Parent set p1 = col + 1 where id = 1 // verify query: // select 1 from Child join Parent on Parent.p1 = Child.c1 and Parent.p2 = Child.c2 -// where Parent.id = 1 and (1 IS NULL OR (child.c1) NOT IN ((1))) limit 1 -func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, cFk vindexes.ChildFKInfo) (ops.Operator, error) { +// where Parent.id = 1 and ((Parent.col + 1) IS NULL OR (child.c1) NOT IN ((Parent.col + 1))) limit 1 +func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updatedTable *vindexes.Table, updStmt *sqlparser.Update, cFk vindexes.ChildFKInfo) Operator { // ON UPDATE RESTRICT foreign keys that require validation, should only be allowed in the case where we // are verifying all the FKs on vtgate level. if !ctx.VerifyAllFKs { - return nil, vterrors.VT12002() + panic(vterrors.VT12002()) } parentTblExpr := updStmt.TableExprs[0].(*sqlparser.AliasedTableExpr) parentTbl, err := parentTblExpr.TableName() if err != nil { - return nil, err + panic(err) } childTbl := cFk.Table.GetTableName() var joinCond sqlparser.Expr @@ -561,7 +810,7 @@ func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updSt var whereCond sqlparser.Expr // add existing where condition on the update statement if updStmt.Where != nil { - whereCond = prefixColNames(parentTbl, updStmt.Where.Expr) + whereCond = prefixColNames(ctx, parentTbl, updStmt.Where.Expr) } // We don't want to fail the RESTRICT for the case where the parent columns remains unchanged on the update. @@ -573,7 +822,7 @@ func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updSt // For example, if we are setting `update child cola = :v1 and colb = :v2`, then on the parent, the where condition would look something like this - // `:v1 IS NULL OR :v2 IS NULL OR (cola, colb) NOT IN ((:v1,:v2))` // So, if either of :v1 or :v2 is NULL, then the entire condition is true (which is the same as not having the condition when :v1 or :v2 is NULL). - compExpr := nullSafeNotInComparison(updStmt.Exprs, cFk) + compExpr := nullSafeNotInComparison(ctx, updatedTable, updStmt.Exprs, cFk, parentTbl, nil /* nonLiteralUpdateInfo */, true /* appendQualifier */) if compExpr != nil { whereCond = sqlparser.AndExpressions(whereCond, compExpr) } @@ -588,31 +837,38 @@ func createFkVerifyOpForChildFKForUpdate(ctx *plancontext.PlanningContext, updSt sqlparser.NewJoinCondition(joinCond, nil)), }, sqlparser.NewWhere(sqlparser.WhereClause, whereCond), + nil, sqlparser.NewLimitWithoutOffset(1), - sqlparser.ShareModeLock) + getVerifyLock(updatedTable)) } // nullSafeNotInComparison is used to compare the child columns in the foreign key constraint aren't the same as the updateExpressions exactly. -// This comparison has to be null safe so we create an expression which looks like the following for a query like `update child cola = :v1 and colb = :v2` - +// This comparison has to be null safe, so we create an expression which looks like the following for a query like `update child cola = :v1 and colb = :v2` - // `:v1 IS NULL OR :v2 IS NULL OR (cola, colb) NOT IN ((:v1,:v2))` // So, if either of :v1 or :v2 is NULL, then the entire condition is true (which is the same as not having the condition when :v1 or :v2 is NULL) // This expression is used in cascading SET NULLs and in verifying whether an update should be restricted. -func nullSafeNotInComparison(updateExprs sqlparser.UpdateExprs, cFk vindexes.ChildFKInfo) sqlparser.Expr { +func nullSafeNotInComparison(ctx *plancontext.PlanningContext, updatedTable *vindexes.Table, updateExprs sqlparser.UpdateExprs, cFk vindexes.ChildFKInfo, parentTbl sqlparser.TableName, nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo, appendQualifier bool) sqlparser.Expr { + var valTuple sqlparser.ValTuple var updateValues sqlparser.ValTuple - for _, updateExpr := range updateExprs { + for idx, updateExpr := range updateExprs { colIdx := cFk.ParentColumns.FindColumn(updateExpr.Name.Name) if colIdx >= 0 { if sqlparser.IsNull(updateExpr.Expr) { return nil } - updateValues = append(updateValues, updateExpr.Expr) + childUpdateExpr := prefixColNames(ctx, parentTbl, getCastedUpdateExpression(updatedTable, updateExpr)) + if len(nonLiteralUpdateInfo) > 0 && nonLiteralUpdateInfo[idx].UpdateExprBvName != "" { + childUpdateExpr = sqlparser.NewArgument(nonLiteralUpdateInfo[idx].UpdateExprBvName) + } + updateValues = append(updateValues, childUpdateExpr) + if appendQualifier { + valTuple = append(valTuple, sqlparser.NewColNameWithQualifier(cFk.ChildColumns[colIdx].String(), cFk.Table.GetTableName())) + } else { + valTuple = append(valTuple, sqlparser.NewColName(cFk.ChildColumns[colIdx].String())) + } } } - // Create a ValTuple of child column names - var valTuple sqlparser.ValTuple - for _, column := range cFk.ChildColumns { - valTuple = append(valTuple, sqlparser.NewColNameWithQualifier(column.String(), cFk.Table.GetTableName())) - } + var finalExpr sqlparser.Expr = sqlparser.NewComparisonExpr(sqlparser.NotInOp, valTuple, sqlparser.ValTuple{updateValues}, nil) for _, value := range updateValues { finalExpr = &sqlparser.OrExpr{ diff --git a/go/vt/vtgate/planbuilder/operators/update_test.go b/go/vt/vtgate/planbuilder/operators/update_test.go new file mode 100644 index 00000000000..6cdf7be3f7d --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/update_test.go @@ -0,0 +1,140 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "testing" + + "github.com/stretchr/testify/require" + + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +// TestGetCastTypeForColumn tests that we get the correct string value to use in a CAST function based on the type of the column. +func TestGetCastTypeForColumn(t *testing.T) { + tests := []struct { + name string + typ querypb.Type + want string + }{ + { + name: "VARCHAR column", + typ: querypb.Type_VARCHAR, + want: "CHAR", + }, + { + name: "CHAR column", + typ: querypb.Type_CHAR, + want: "CHAR", + }, + { + name: "VARBINARY column", + typ: querypb.Type_VARBINARY, + want: "BINARY", + }, + { + name: "BINARY column", + typ: querypb.Type_BINARY, + want: "BINARY", + }, + { + name: "UINT16 column", + typ: querypb.Type_UINT16, + want: "UNSIGNED", + }, + { + name: "UINT24 column", + typ: querypb.Type_UINT24, + want: "UNSIGNED", + }, + { + name: "UINT32 column", + typ: querypb.Type_UINT32, + want: "UNSIGNED", + }, + { + name: "UINT64 column", + typ: querypb.Type_UINT64, + want: "UNSIGNED", + }, + { + name: "INT16 column", + typ: querypb.Type_INT16, + want: "SIGNED", + }, + { + name: "INT24 column", + typ: querypb.Type_INT24, + want: "SIGNED", + }, + { + name: "INT32 column", + typ: querypb.Type_INT32, + want: "SIGNED", + }, + { + name: "INT64 column", + typ: querypb.Type_INT64, + want: "SIGNED", + }, + { + name: "FLOAT32 column", + typ: querypb.Type_FLOAT32, + want: "FLOAT", + }, + { + name: "FLOAT64 column", + typ: querypb.Type_FLOAT64, + want: "FLOAT", + }, + { + name: "DECIMAL column", + typ: querypb.Type_DECIMAL, + want: "DECIMAL", + }, + { + name: "DATETIME column", + typ: querypb.Type_DATETIME, + want: "DATETIME", + }, + { + name: "NULL column", + typ: querypb.Type_NULL_TYPE, + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + updExpr := &sqlparser.UpdateExpr{ + Name: sqlparser.NewColName("col"), + } + updatedTable := &vindexes.Table{ + Columns: []vindexes.Column{ + { + Name: sqlparser.NewIdentifierCI("col"), + Type: tt.typ, + }, + }, + } + tyStr := getCastTypeForColumn(updatedTable, updExpr) + require.EqualValues(t, tt.want, tyStr) + }) + } +} diff --git a/go/vt/vtgate/planbuilder/operators/upsert.go b/go/vt/vtgate/planbuilder/operators/upsert.go new file mode 100644 index 00000000000..8f028a790b2 --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/upsert.go @@ -0,0 +1,143 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +var _ Operator = (*Upsert)(nil) + +// Upsert represents an insert on duplicate key operation on a table. +type Upsert struct { + Sources []UpsertSource + + noColumns + noPredicates +} + +type UpsertSource struct { + Insert Operator + Update Operator +} + +func (u *Upsert) Clone(inputs []Operator) Operator { + up := &Upsert{} + up.setInputs(inputs) + return up +} + +func (u *Upsert) setInputs(inputs []Operator) { + for i := 0; i < len(inputs); i += 2 { + u.Sources = append(u.Sources, UpsertSource{ + Insert: inputs[i], + Update: inputs[i+1], + }) + } +} + +func (u *Upsert) Inputs() []Operator { + var inputs []Operator + for _, source := range u.Sources { + inputs = append(inputs, source.Insert, source.Update) + } + return inputs +} + +func (u *Upsert) SetInputs(inputs []Operator) { + u.Sources = nil + u.setInputs(inputs) +} + +func (u *Upsert) ShortDescription() string { + return "" +} + +func (u *Upsert) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { + return nil +} + +func createUpsertOperator(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, insOp Operator, rows sqlparser.Values, vTbl *vindexes.Table) Operator { + if len(vTbl.UniqueKeys) != 0 { + panic(vterrors.VT12001("ON DUPLICATE KEY UPDATE with foreign keys with unique keys")) + } + + pIndexes, _ := findPKIndexes(vTbl, ins) + if len(pIndexes) == 0 { + // nothing to compare for update. + // Hence, only perform insert. + return insOp + } + + upsert := &Upsert{} + for _, row := range rows { + var comparisons []sqlparser.Expr + for _, pIdx := range pIndexes { + var expr sqlparser.Expr + if pIdx.idx == -1 { + expr = pIdx.def + } else { + expr = row[pIdx.idx] + } + comparisons = append(comparisons, + sqlparser.NewComparisonExpr(sqlparser.EqualOp, sqlparser.NewColName(pIdx.col.String()), expr, nil)) + } + whereExpr := sqlparser.AndExpressions(comparisons...) + + var updExprs sqlparser.UpdateExprs + for _, ue := range ins.OnDup { + expr := sqlparser.CopyOnRewrite(ue.Expr, nil, func(cursor *sqlparser.CopyOnWriteCursor) { + vfExpr, ok := cursor.Node().(*sqlparser.ValuesFuncExpr) + if !ok { + return + } + idx := ins.Columns.FindColumn(vfExpr.Name.Name) + if idx == -1 { + panic(vterrors.VT03014(sqlparser.String(vfExpr.Name), "field list")) + } + cursor.Replace(row[idx]) + }, nil).(sqlparser.Expr) + updExprs = append(updExprs, &sqlparser.UpdateExpr{ + Name: ue.Name, + Expr: expr, + }) + } + + upd := &sqlparser.Update{ + Comments: ins.Comments, + TableExprs: sqlparser.TableExprs{ins.Table}, + Exprs: updExprs, + Where: sqlparser.NewWhere(sqlparser.WhereClause, whereExpr), + } + updOp := createOpFromStmt(ctx, upd, false, "") + + // replan insert statement without on duplicate key update. + newInsert := sqlparser.CloneRefOfInsert(ins) + newInsert.OnDup = nil + newInsert.Rows = sqlparser.Values{row} + insOp = createOpFromStmt(ctx, newInsert, false, "") + upsert.Sources = append(upsert.Sources, UpsertSource{ + Insert: insOp, + Update: updOp, + }) + } + + return upsert +} diff --git a/go/vt/vtgate/planbuilder/operators/utils_test.go b/go/vt/vtgate/planbuilder/operators/utils_test.go new file mode 100644 index 00000000000..596489150da --- /dev/null +++ b/go/vt/vtgate/planbuilder/operators/utils_test.go @@ -0,0 +1,89 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operators + +import ( + "slices" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" +) + +type fakeOp struct { + id semantics.TableSet + inputs []Operator + cols []*sqlparser.AliasedExpr +} + +var _ Operator = (*fakeOp)(nil) + +func (f *fakeOp) Clone(inputs []Operator) Operator { + return f +} + +func (f *fakeOp) Inputs() []Operator { + return f.inputs +} + +func (f *fakeOp) SetInputs(operators []Operator) { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) AddColumn(ctx *plancontext.PlanningContext, reuseExisting bool, _ bool, expr *sqlparser.AliasedExpr) int { + if offset := f.FindCol(ctx, expr.Expr, false); reuseExisting && offset >= 0 { + return offset + } + f.cols = append(f.cols, expr) + return len(f.cols) - 1 +} + +func (f *fakeOp) FindCol(ctx *plancontext.PlanningContext, a sqlparser.Expr, underRoute bool) int { + return slices.IndexFunc(f.cols, func(b *sqlparser.AliasedExpr) bool { + return a == b.Expr + }) +} + +func (f *fakeOp) GetColumns(ctx *plancontext.PlanningContext) []*sqlparser.AliasedExpr { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.SelectExprs { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) ShortDescription() string { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy { + // TODO implement me + panic("implement me") +} + +func (f *fakeOp) introducesTableID() semantics.TableSet { + return f.id +} diff --git a/go/vt/vtgate/planbuilder/operators/vindex.go b/go/vt/vtgate/planbuilder/operators/vindex.go index 2fe2bf4d3e5..c16944e7c49 100644 --- a/go/vt/vtgate/planbuilder/operators/vindex.go +++ b/go/vt/vtgate/planbuilder/operators/vindex.go @@ -21,7 +21,6 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -57,7 +56,7 @@ func (v *Vindex) introducesTableID() semantics.TableSet { } // Clone implements the Operator interface -func (v *Vindex) Clone([]ops.Operator) ops.Operator { +func (v *Vindex) Clone([]Operator) Operator { clone := *v return &clone } @@ -101,7 +100,7 @@ func (v *Vindex) GetSelectExprs(ctx *plancontext.PlanningContext) sqlparser.Sele return transformColumnsToSelectExprs(ctx, v) } -func (v *Vindex) GetOrdering(*plancontext.PlanningContext) []ops.OrderBy { +func (v *Vindex) GetOrdering(*plancontext.PlanningContext) []OrderBy { return nil } @@ -113,15 +112,13 @@ func (v *Vindex) AddCol(col *sqlparser.ColName) { v.Columns = append(v.Columns, col) } -func (v *Vindex) CheckValid() error { +func (v *Vindex) CheckValid() { if len(v.Table.Predicates) == 0 { - return vterrors.VT09018(wrongWhereCond + " (where clause missing)") + panic(vterrors.VT09018(wrongWhereCond + " (where clause missing)")) } - - return nil } -func (v *Vindex) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) ops.Operator { +func (v *Vindex) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) Operator { for _, e := range sqlparser.SplitAndExpression(nil, expr) { deps := ctx.SemTable.RecursiveDeps(e) if deps.NumberOfTables() > 1 { @@ -149,15 +146,12 @@ func (v *Vindex) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.E } // check RHS - var err error if sqlparser.IsValue(comparison.Right) || sqlparser.IsSimpleTuple(comparison.Right) { v.Value = comparison.Right } else { panic(vterrors.VT09018(wrongWhereCond + " (rhs is not a value)")) } - if err != nil { - panic(vterrors.VT09018(wrongWhereCond+": %v", err)) - } + v.OpCode = engine.VindexMap v.Table.Predicates = append(v.Table.Predicates, e) } diff --git a/go/vt/vtgate/planbuilder/ordered_aggregate.go b/go/vt/vtgate/planbuilder/ordered_aggregate.go index 34646fa3dea..c6a37c8decb 100644 --- a/go/vt/vtgate/planbuilder/ordered_aggregate.go +++ b/go/vt/vtgate/planbuilder/ordered_aggregate.go @@ -17,6 +17,7 @@ limitations under the License. package planbuilder import ( + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/vtgate/engine" ) @@ -60,6 +61,8 @@ type orderedAggregate struct { groupByKeys []*engine.GroupByParams truncateColumnCount int + + collationEnv *collations.Environment } // Primitive implements the logicalPlan interface @@ -78,6 +81,7 @@ func (oa *orderedAggregate) Primitive() engine.Primitive { GroupByKeys: oa.groupByKeys, TruncateColumnCount: oa.truncateColumnCount, Input: input, + CollationEnv: oa.collationEnv, } } diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 472648828ef..e7c18af1d4c 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -36,12 +36,12 @@ import ( "vitess.io/vitess/go/test/vschemawrapper" "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/engine" - oprewriters "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite" + "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -60,8 +60,10 @@ func TestPlan(t *testing.T) { TabletType_: topodatapb.TabletType_PRIMARY, SysVarEnabled: true, TestBuilder: TestBuilder, + Env: vtenv.NewTestEnv(), } testOutputTempDir := makeTestOutput(t) + addPKs(t, vschemaWrapper.V, "user", []string{"user", "music"}) // You will notice that some tests expect user.Id instead of user.id. // This is because we now pre-create vindex columns in the symbol @@ -106,6 +108,7 @@ func TestForeignKeyPlanning(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: vschema, TestBuilder: TestBuilder, + Env: vtenv.NewTestEnv(), } testOutputTempDir := makeTestOutput(t) @@ -113,6 +116,40 @@ func TestForeignKeyPlanning(t *testing.T) { testFile(t, "foreignkey_cases.json", testOutputTempDir, vschemaWrapper, false) } +// TestForeignKeyChecksOn tests the planning when the session variable for foreign_key_checks is set to ON. +func TestForeignKeyChecksOn(t *testing.T) { + vschema := loadSchema(t, "vschemas/schema.json", true) + setFks(t, vschema) + fkChecksState := true + vschemaWrapper := &vschemawrapper.VSchemaWrapper{ + V: vschema, + TestBuilder: TestBuilder, + ForeignKeyChecksState: &fkChecksState, + Env: vtenv.NewTestEnv(), + } + + testOutputTempDir := makeTestOutput(t) + + testFile(t, "foreignkey_checks_on_cases.json", testOutputTempDir, vschemaWrapper, false) +} + +// TestForeignKeyChecksOff tests the planning when the session variable for foreign_key_checks is set to OFF. +func TestForeignKeyChecksOff(t *testing.T) { + vschema := loadSchema(t, "vschemas/schema.json", true) + setFks(t, vschema) + fkChecksState := false + vschemaWrapper := &vschemawrapper.VSchemaWrapper{ + V: vschema, + TestBuilder: TestBuilder, + ForeignKeyChecksState: &fkChecksState, + Env: vtenv.NewTestEnv(), + } + + testOutputTempDir := makeTestOutput(t) + + testFile(t, "foreignkey_checks_off_cases.json", testOutputTempDir, vschemaWrapper, false) +} + func setFks(t *testing.T, vschema *vindexes.VSchema) { if vschema.Keyspaces["sharded_fk_allow"] != nil { // FK from multicol_tbl2 referencing multicol_tbl1 that is shard scoped. @@ -146,6 +183,10 @@ func setFks(t *testing.T, vschema *vindexes.VSchema) { // FK from tblrefDef referencing tbl20 that is shard scoped of SET-Default types. _ = vschema.AddForeignKey("sharded_fk_allow", "tblrefDef", createFkDefinition([]string{"ref"}, "tbl20", []string{"col2"}, sqlparser.SetDefault, sqlparser.SetDefault)) + // FK from tbl_auth referencing tbl20 that is shard scoped of CASCADE types. + _ = vschema.AddForeignKey("sharded_fk_allow", "tbl_auth", createFkDefinition([]string{"id"}, "tbl20", []string{"col2"}, sqlparser.Cascade, sqlparser.Cascade)) + addPKs(t, vschema, "sharded_fk_allow", []string{"tbl1", "tbl2", "tbl3", "tbl4", "tbl5", "tbl6", "tbl7", "tbl9", "tbl10", + "multicol_tbl1", "multicol_tbl2", "tbl_auth", "tblrefDef", "tbl20"}) } if vschema.Keyspaces["unsharded_fk_allow"] != nil { // u_tbl2(col2) -> u_tbl1(col1) Cascade. @@ -168,24 +209,45 @@ func setFks(t *testing.T, vschema *vindexes.VSchema) { _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl4", createFkDefinition([]string{"col4"}, "u_tbl3", []string{"col3"}, sqlparser.Restrict, sqlparser.Restrict)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl6", createFkDefinition([]string{"col6"}, "u_tbl5", []string{"col5"}, sqlparser.DefaultAction, sqlparser.DefaultAction)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl9", []string{"col9"}, sqlparser.SetNull, sqlparser.SetNull)) - _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl6", []string{"col6"}, sqlparser.Cascade, sqlparser.CASCADE)) + _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl6", []string{"col6"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl4", createFkDefinition([]string{"col4"}, "u_tbl7", []string{"col7"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl9", createFkDefinition([]string{"col9"}, "u_tbl4", []string{"col4"}, sqlparser.Restrict, sqlparser.Restrict)) + _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl11", createFkDefinition([]string{"col"}, "u_tbl10", []string{"col"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl", createFkDefinition([]string{"col"}, "sharded_fk_allow.s_tbl", []string{"col"}, sqlparser.Restrict, sqlparser.Restrict)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_multicol_tbl2", createFkDefinition([]string{"cola", "colb"}, "u_multicol_tbl1", []string{"cola", "colb"}, sqlparser.SetNull, sqlparser.SetNull)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_multicol_tbl3", createFkDefinition([]string{"cola", "colb"}, "u_multicol_tbl2", []string{"cola", "colb"}, sqlparser.Cascade, sqlparser.Cascade)) + + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{sqlparser.NewColName("col9")}) + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{&sqlparser.BinaryExpr{Operator: sqlparser.MultOp, Left: sqlparser.NewColName("col9"), Right: sqlparser.NewColName("foo")}}) + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{sqlparser.NewColName("col9"), sqlparser.NewColName("foo")}) + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{sqlparser.NewColName("foo"), sqlparser.NewColName("bar")}) + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{sqlparser.NewColName("bar"), sqlparser.NewColName("col9")}) + _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl8", sqlparser.Exprs{sqlparser.NewColName("col8")}) + + addPKs(t, vschema, "unsharded_fk_allow", []string{"u_tbl1", "u_tbl2", "u_tbl3", "u_tbl4", "u_tbl5", "u_tbl6", "u_tbl7", "u_tbl8", "u_tbl9", "u_tbl10", "u_tbl11", + "u_multicol_tbl1", "u_multicol_tbl2", "u_multicol_tbl3"}) + } + +} + +func addPKs(t *testing.T, vschema *vindexes.VSchema, ks string, tbls []string) { + for _, tbl := range tbls { + require.NoError(t, + vschema.AddPrimaryKey(ks, tbl, []string{"id"})) } } func TestSystemTables57(t *testing.T) { // first we move everything to use 5.7 logic - oldVer := servenv.MySQLServerVersion() - servenv.SetMySQLServerVersionForTest("5.7") - defer func() { - servenv.SetMySQLServerVersionForTest(oldVer) - }() - vschemaWrapper := &vschemawrapper.VSchemaWrapper{V: loadSchema(t, "vschemas/schema.json", true)} + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: "5.7.9", + }) + require.NoError(t, err) + vschemaWrapper := &vschemawrapper.VSchemaWrapper{ + V: loadSchema(t, "vschemas/schema.json", true), + Env: env, + } testOutputTempDir := makeTestOutput(t) testFile(t, "info_schema57_cases.json", testOutputTempDir, vschemaWrapper, false) } @@ -194,6 +256,7 @@ func TestSysVarSetDisabled(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), SysVarEnabled: false, + Env: vtenv.NewTestEnv(), } testFile(t, "set_sysvar_disabled_cases.json", makeTestOutput(t), vschemaWrapper, false) @@ -203,88 +266,110 @@ func TestViews(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), EnableViews: true, + Env: vtenv.NewTestEnv(), } testFile(t, "view_cases.json", makeTestOutput(t), vschemaWrapper, false) } func TestOne(t *testing.T) { - reset := oprewriters.EnableDebugPrinting() + reset := operators.EnableDebugPrinting() defer reset() lv := loadSchema(t, "vschemas/schema.json", true) setFks(t, lv) + addPKs(t, lv, "user", []string{"user", "music"}) + addPKs(t, lv, "main", []string{"unsharded"}) vschema := &vschemawrapper.VSchemaWrapper{ V: lv, TestBuilder: TestBuilder, + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneTPCC(t *testing.T) { + reset := operators.EnableDebugPrinting() + defer reset() + vschema := &vschemawrapper.VSchemaWrapper{ - V: loadSchema(t, "vschemas/tpcc_schema.json", true), + V: loadSchema(t, "vschemas/tpcc_schema.json", true), + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneWithMainAsDefault(t *testing.T) { + reset := operators.EnableDebugPrinting() + defer reset() vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Keyspace: &vindexes.Keyspace{ Name: "main", Sharded: false, }, + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneWithSecondUserAsDefault(t *testing.T) { + reset := operators.EnableDebugPrinting() + defer reset() vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Keyspace: &vindexes.Keyspace{ Name: "second_user", Sharded: true, }, + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneWithUserAsDefault(t *testing.T) { + reset := operators.EnableDebugPrinting() + defer reset() vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Keyspace: &vindexes.Keyspace{ Name: "user", Sharded: true, }, + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneWithTPCHVSchema(t *testing.T) { - reset := oprewriters.EnableDebugPrinting() + reset := operators.EnableDebugPrinting() defer reset() vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/tpch_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testFile(t, "onecase.json", "", vschema, false) } func TestOneWith57Version(t *testing.T) { + reset := operators.EnableDebugPrinting() + defer reset() // first we move everything to use 5.7 logic - oldVer := servenv.MySQLServerVersion() - servenv.SetMySQLServerVersionForTest("5.7") - defer func() { - servenv.SetMySQLServerVersionForTest(oldVer) - }() - vschema := &vschemawrapper.VSchemaWrapper{V: loadSchema(t, "vschemas/schema.json", true)} + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: "5.7.9", + }) + require.NoError(t, err) + vschema := &vschemawrapper.VSchemaWrapper{ + V: loadSchema(t, "vschemas/schema.json", true), + Env: env, + } testFile(t, "onecase.json", "", vschema, false) } @@ -293,6 +378,7 @@ func TestRubyOnRailsQueries(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/rails_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testFile(t, "rails_cases.json", makeTestOutput(t), vschemaWrapper, false) @@ -302,6 +388,7 @@ func TestOLTP(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/oltp_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testFile(t, "oltp_cases.json", makeTestOutput(t), vschemaWrapper, false) @@ -311,6 +398,7 @@ func TestTPCC(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/tpcc_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testFile(t, "tpcc_cases.json", makeTestOutput(t), vschemaWrapper, false) @@ -320,6 +408,7 @@ func TestTPCH(t *testing.T) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/tpch_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testFile(t, "tpch_cases.json", makeTestOutput(t), vschemaWrapper, false) @@ -341,6 +430,7 @@ func benchmarkWorkload(b *testing.B, name string) { vschemaWrapper := &vschemawrapper.VSchemaWrapper{ V: loadSchema(b, "vschemas/"+name+"_schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } testCases := readJSONTests(name + "_cases.json") @@ -360,7 +450,9 @@ func TestBypassPlanningShardTargetFromFile(t *testing.T) { Sharded: false, }, TabletType_: topodatapb.TabletType_PRIMARY, - Dest: key.DestinationShard("-80")} + Dest: key.DestinationShard("-80"), + Env: vtenv.NewTestEnv(), + } testFile(t, "bypass_shard_cases.json", makeTestOutput(t), vschema, false) } @@ -376,6 +468,7 @@ func TestBypassPlanningKeyrangeTargetFromFile(t *testing.T) { }, TabletType_: topodatapb.TabletType_PRIMARY, Dest: key.DestinationExactKeyRange{KeyRange: keyRange[0]}, + Env: vtenv.NewTestEnv(), } testFile(t, "bypass_keyrange_cases.json", makeTestOutput(t), vschema, false) @@ -392,6 +485,7 @@ func TestWithDefaultKeyspaceFromFile(t *testing.T) { Sharded: false, }, TabletType_: topodatapb.TabletType_PRIMARY, + Env: vtenv.NewTestEnv(), } ts := memorytopo.NewServer(ctx, "cell1") ts.CreateKeyspace(ctx, "main", &topodatapb.Keyspace{}) @@ -425,6 +519,7 @@ func TestWithDefaultKeyspaceFromFileSharded(t *testing.T) { Sharded: true, }, TabletType_: topodatapb.TabletType_PRIMARY, + Env: vtenv.NewTestEnv(), } testOutputTempDir := makeTestOutput(t) @@ -440,6 +535,7 @@ func TestWithUserDefaultKeyspaceFromFileSharded(t *testing.T) { Sharded: true, }, TabletType_: topodatapb.TabletType_PRIMARY, + Env: vtenv.NewTestEnv(), } testOutputTempDir := makeTestOutput(t) @@ -452,6 +548,7 @@ func TestWithSystemSchemaAsDefaultKeyspace(t *testing.T) { V: loadSchema(t, "vschemas/schema.json", true), Keyspace: &vindexes.Keyspace{Name: "information_schema"}, TabletType_: topodatapb.TabletType_PRIMARY, + Env: vtenv.NewTestEnv(), } testFile(t, "sysschema_default.json", makeTestOutput(t), vschema, false) @@ -466,6 +563,7 @@ func TestOtherPlanningFromFile(t *testing.T) { Sharded: false, }, TabletType_: topodatapb.TabletType_PRIMARY, + Env: vtenv.NewTestEnv(), } testOutputTempDir := makeTestOutput(t) @@ -478,7 +576,7 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch if err != nil { t.Fatal(err) } - vschema := vindexes.BuildVSchema(formal) + vschema := vindexes.BuildVSchema(formal, sqlparser.NewTestParser()) if err != nil { t.Fatal(err) } @@ -489,9 +587,7 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch // adding view in user keyspace if ks.Keyspace.Name == "user" { - if err = vschema.AddView(ks.Keyspace.Name, - "user_details_view", - "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id"); err != nil { + if err = vschema.AddView(ks.Keyspace.Name, "user_details_view", "select user.id, user_extra.col from user join user_extra on user.id = user_extra.user_id", sqlparser.NewTestParser()); err != nil { t.Fatal(err) } } @@ -502,7 +598,7 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch if setCollation { for _, table := range ks.Tables { for i, col := range table.Columns { - if sqltypes.IsText(col.Type) { + if sqltypes.IsText(col.Type) && col.CollationName == "" { table.Columns[i].CollationName = "latin1_swedish_ci" } } @@ -514,7 +610,7 @@ func loadSchema(t testing.TB, filename string, setCollation bool) *vindexes.VSch // createFkDefinition is a helper function to create a Foreign key definition struct from the columns used in it provided as list of strings. func createFkDefinition(childCols []string, parentTableName string, parentCols []string, onUpdate, onDelete sqlparser.ReferenceAction) *sqlparser.ForeignKeyDefinition { - pKs, pTbl, _ := sqlparser.ParseTable(parentTableName) + pKs, pTbl, _ := sqlparser.NewTestParser().ParseTable(parentTableName) return &sqlparser.ForeignKeyDefinition{ Source: sqlparser.MakeColumns(childCols...), ReferenceDefinition: &sqlparser.ReferenceDefinition{ @@ -598,6 +694,7 @@ func readJSONTests(filename string) []planTest { panic(err) } dec := json.NewDecoder(file) + dec.DisallowUnknownFields() err = dec.Decode(&output) if err != nil { panic(err) @@ -646,6 +743,7 @@ func BenchmarkPlanner(b *testing.B) { vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(b, "vschemas/schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } for _, filename := range benchMarkFiles { testCases := readJSONTests(filename) @@ -662,6 +760,7 @@ func BenchmarkSemAnalysis(b *testing.B) { vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(b, "vschemas/schema.json", true), SysVarEnabled: true, + Env: vtenv.NewTestEnv(), } for i := 0; i < b.N; i++ { @@ -679,7 +778,7 @@ func exerciseAnalyzer(query, database string, s semantics.SchemaInformation) { recover() }() - ast, err := sqlparser.Parse(query) + ast, err := sqlparser.NewTestParser().Parse(query) if err != nil { return } @@ -696,6 +795,7 @@ func BenchmarkSelectVsDML(b *testing.B) { V: loadSchema(b, "vschemas/schema.json", true), SysVarEnabled: true, Version: Gen4, + Env: vtenv.NewTestEnv(), } dmlCases := readJSONTests("dml_cases.json") diff --git a/go/vt/vtgate/planbuilder/plan_test_vindex.go b/go/vt/vtgate/planbuilder/plan_test_vindex.go index 432ef7b8479..30d72f8c03a 100644 --- a/go/vt/vtgate/planbuilder/plan_test_vindex.go +++ b/go/vt/vtgate/planbuilder/plan_test_vindex.go @@ -72,7 +72,10 @@ func newLookupIndex(name string, _ map[string]string) (vindexes.Vindex, error) { var _ vindexes.Lookup = (*lookupIndex)(nil) // nameLkpIndex satisfies Lookup, NonUnique. -type nameLkpIndex struct{ name string } +type nameLkpIndex struct { + name string + inBackfill bool +} func (v *nameLkpIndex) String() string { return v.name } func (*nameLkpIndex) Cost() int { return 3 } @@ -102,13 +105,21 @@ func (*nameLkpIndex) Query() (string, []string) { func (*nameLkpIndex) MapResult([]sqltypes.Value, []*sqltypes.Result) ([]key.Destination, error) { return nil, nil } -func newNameLkpIndex(name string, _ map[string]string) (vindexes.Vindex, error) { - return &nameLkpIndex{name: name}, nil + +func (v *nameLkpIndex) IsBackfilling() bool { return v.inBackfill } + +func newNameLkpIndex(name string, m map[string]string) (vindexes.Vindex, error) { + vdx := &nameLkpIndex{name: name} + if val, ok := m["write_only"]; ok { + vdx.inBackfill = val == "true" + } + return vdx, nil } var _ vindexes.Vindex = (*nameLkpIndex)(nil) var _ vindexes.Lookup = (*nameLkpIndex)(nil) var _ vindexes.LookupPlanable = (*nameLkpIndex)(nil) +var _ vindexes.LookupBackfill = (*nameLkpIndex)(nil) // costlyIndex satisfies Lookup, NonUnique. type costlyIndex struct{ name string } diff --git a/go/vt/vtgate/planbuilder/plancontext/planning_context.go b/go/vt/vtgate/planbuilder/plancontext/planning_context.go index 68ccc95b9fd..49039ddd347 100644 --- a/go/vt/vtgate/planbuilder/plancontext/planning_context.go +++ b/go/vt/vtgate/planbuilder/plancontext/planning_context.go @@ -19,6 +19,7 @@ package plancontext import ( querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -27,12 +28,16 @@ type PlanningContext struct { SemTable *semantics.SemTable VSchema VSchema - // here we add all predicates that were created because of a join condition - // e.g. [FROM tblA JOIN tblB ON a.colA = b.colB] will be rewritten to [FROM tblB WHERE :a_colA = b.colB], - // if we assume that tblB is on the RHS of the join. This last predicate in the WHERE clause is added to the - // map below - JoinPredicates map[sqlparser.Expr][]sqlparser.Expr - SkipPredicates map[sqlparser.Expr]any + // joinPredicates maps each original join predicate (key) to a slice of + // variations of the RHS predicates (value). This map is used to handle + // different scenarios in join planning, where the RHS predicates are + // modified to accommodate dependencies from the LHS, represented as Arguments. + joinPredicates map[sqlparser.Expr][]sqlparser.Expr + + // skipPredicates tracks predicates that should be skipped, typically when + // a join predicate is reverted to its original form during planning. + skipPredicates map[sqlparser.Expr]any + PlannerVersion querypb.ExecuteOptions_PlannerVersion // If we during planning have turned this expression into an argument name, @@ -49,11 +54,17 @@ type PlanningContext struct { // CurrentPhase keeps track of how far we've gone in the planning process // The type should be operators.Phase, but depending on that would lead to circular dependencies CurrentPhase int + + // Statement contains the originally parsed statement + Statement sqlparser.Statement } +// CreatePlanningContext initializes a new PlanningContext with the given parameters. +// It analyzes the SQL statement within the given virtual schema context, +// handling default keyspace settings and semantic analysis. +// Returns an error if semantic analysis fails. func CreatePlanningContext(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, - vschema VSchema, version querypb.ExecuteOptions_PlannerVersion, ) (*PlanningContext, error) { @@ -74,13 +85,17 @@ func CreatePlanningContext(stmt sqlparser.Statement, ReservedVars: reservedVars, SemTable: semTable, VSchema: vschema, - JoinPredicates: map[sqlparser.Expr][]sqlparser.Expr{}, - SkipPredicates: map[sqlparser.Expr]any{}, + joinPredicates: map[sqlparser.Expr][]sqlparser.Expr{}, + skipPredicates: map[sqlparser.Expr]any{}, PlannerVersion: version, ReservedArguments: map[sqlparser.Expr]string{}, + Statement: stmt, }, nil } +// GetReservedArgumentFor retrieves a reserved argument name for a given expression. +// If the expression already has a reserved argument, it returns that name; +// otherwise, it reserves a new name based on the expression type. func (ctx *PlanningContext) GetReservedArgumentFor(expr sqlparser.Expr) string { for key, name := range ctx.ReservedArguments { if ctx.SemTable.EqualsExpr(key, expr) { @@ -101,13 +116,75 @@ func (ctx *PlanningContext) GetReservedArgumentFor(expr sqlparser.Expr) string { return bvName } -func (ctx *PlanningContext) GetArgumentFor(expr sqlparser.Expr, f func() string) string { - for key, name := range ctx.ReservedArguments { - if ctx.SemTable.EqualsExpr(key, expr) { - return name +// ShouldSkip determines if a given expression should be ignored in the SQL output building. +// It checks against expressions that have been marked to be excluded from further processing. +func (ctx *PlanningContext) ShouldSkip(expr sqlparser.Expr) bool { + for k := range ctx.skipPredicates { + if ctx.SemTable.EqualsExpr(expr, k) { + return true } } - bvName := f() - ctx.ReservedArguments[expr] = bvName - return bvName + return false +} + +// AddJoinPredicates associates additional RHS predicates with an existing join predicate. +// This is used to dynamically adjust the RHS predicates based on evolving join conditions. +func (ctx *PlanningContext) AddJoinPredicates(joinPred sqlparser.Expr, predicates ...sqlparser.Expr) { + fn := func(original sqlparser.Expr, rhsExprs []sqlparser.Expr) { + ctx.joinPredicates[original] = append(rhsExprs, predicates...) + } + if ctx.execOnJoinPredicateEqual(joinPred, fn) { + return + } + + // we didn't find an existing entry + ctx.joinPredicates[joinPred] = predicates +} + +// SkipJoinPredicates marks the predicates related to a specific join predicate as irrelevant +// for the current planning stage. This is used when a join has been pushed under a route and +// the original predicate will be used. +func (ctx *PlanningContext) SkipJoinPredicates(joinPred sqlparser.Expr) error { + fn := func(_ sqlparser.Expr, rhsExprs []sqlparser.Expr) { + ctx.skipThesePredicates(rhsExprs...) + } + if ctx.execOnJoinPredicateEqual(joinPred, fn) { + return nil + } + return vterrors.VT13001("predicate does not exist: " + sqlparser.String(joinPred)) +} + +// KeepPredicateInfo transfers join predicate information from another context. +// This is useful when nesting queries, ensuring consistent predicate handling across contexts. +func (ctx *PlanningContext) KeepPredicateInfo(other *PlanningContext) { + for k, v := range other.joinPredicates { + ctx.AddJoinPredicates(k, v...) + } + for expr := range other.skipPredicates { + ctx.skipThesePredicates(expr) + } +} + +// skipThesePredicates is a utility function to exclude certain predicates from SQL building +func (ctx *PlanningContext) skipThesePredicates(preds ...sqlparser.Expr) { +outer: + for _, expr := range preds { + for k := range ctx.skipPredicates { + if ctx.SemTable.EqualsExpr(expr, k) { + // already skipped + continue outer + } + } + ctx.skipPredicates[expr] = nil + } +} + +func (ctx *PlanningContext) execOnJoinPredicateEqual(joinPred sqlparser.Expr, fn func(original sqlparser.Expr, rhsExprs []sqlparser.Expr)) bool { + for key, values := range ctx.joinPredicates { + if ctx.SemTable.EqualsExpr(joinPred, key) { + fn(key, values) + return true + } + } + return false } diff --git a/go/vt/vtgate/planbuilder/plancontext/vschema.go b/go/vt/vtgate/planbuilder/plancontext/vschema.go index 9dde6dee31c..1bca80cbc94 100644 --- a/go/vt/vtgate/planbuilder/plancontext/vschema.go +++ b/go/vt/vtgate/planbuilder/plancontext/vschema.go @@ -6,6 +6,7 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/mysql/collations" @@ -41,6 +42,7 @@ type VSchema interface { Planner() PlannerVersion SetPlannerVersion(pv PlannerVersion) ConnCollation() collations.ID + Environment() *vtenv.Environment // ErrorIfShardedF will return an error if the keyspace is sharded, // and produce a warning if the vtgate if configured to do so @@ -60,6 +62,8 @@ type VSchema interface { // KeyspaceError returns any error in the keyspace vschema. KeyspaceError(keyspace string) error + GetForeignKeyChecksState() *bool + // GetVSchema returns the latest cached vindexes.VSchema GetVSchema() *vindexes.VSchema diff --git a/go/vt/vtgate/planbuilder/planner_test.go b/go/vt/vtgate/planbuilder/planner_test.go index 38c579502fe..2601615522f 100644 --- a/go/vt/vtgate/planbuilder/planner_test.go +++ b/go/vt/vtgate/planbuilder/planner_test.go @@ -58,7 +58,7 @@ func TestBindingSubquery(t *testing.T) { } for _, testcase := range testcases { t.Run(testcase.query, func(t *testing.T) { - parse, err := sqlparser.Parse(testcase.query) + parse, err := sqlparser.NewTestParser().Parse(testcase.query) require.NoError(t, err) selStmt := parse.(*sqlparser.Select) semTable, err := semantics.Analyze(selStmt, "d", &semantics.FakeSI{ diff --git a/go/vt/vtgate/planbuilder/predicate_rewrite_test.go b/go/vt/vtgate/planbuilder/predicate_rewrite_test.go index 369a99bf5d3..ba1d60ff234 100644 --- a/go/vt/vtgate/planbuilder/predicate_rewrite_test.go +++ b/go/vt/vtgate/planbuilder/predicate_rewrite_test.go @@ -30,6 +30,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -90,6 +91,7 @@ func TestFuzzRewriting(t *testing.T) { // values - trying TRUE, FALSE and NULL. If the two expressions do not return the same value, // this is considered a test failure. + venv := vtenv.NewTestEnv() start := time.Now() for time.Since(start) < 1*time.Second { tc := testCase{ @@ -103,17 +105,19 @@ func TestFuzzRewriting(t *testing.T) { simplified := sqlparser.RewritePredicate(predicate) original, err := evalengine.Translate(predicate, &evalengine.Config{ - Collation: collations.Default(), + Environment: venv, + Collation: collations.MySQL8().DefaultConnectionCharset(), ResolveColumn: resolveForFuzz, }) require.NoError(t, err) simpler, err := evalengine.Translate(simplified.(sqlparser.Expr), &evalengine.Config{ - Collation: collations.Default(), + Environment: venv, + Collation: collations.MySQL8().DefaultConnectionCharset(), ResolveColumn: resolveForFuzz, }) require.NoError(t, err) - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(venv) env.Row = make([]sqltypes.Value, tc.nodes) for i := range env.Row { env.Row[i] = sqltypes.NewInt32(1) @@ -139,7 +143,7 @@ func testValues(t *testing.T, env *evalengine.ExpressionEnv, i int, original, si require.NoError(t, err) v2, err := env.Evaluate(simpler) require.NoError(t, err) - assert.Equal(t, v1.Value(collations.Default()), v2.Value(collations.Default())) + assert.Equal(t, v1.Value(collations.MySQL8().DefaultConnectionCharset()), v2.Value(collations.MySQL8().DefaultConnectionCharset())) if len(env.Row) > i+1 { testValues(t, env, i+1, original, simpler) } diff --git a/go/vt/vtgate/planbuilder/rewrite.go b/go/vt/vtgate/planbuilder/rewrite.go index f59441c77ac..915b5e753cd 100644 --- a/go/vt/vtgate/planbuilder/rewrite.go +++ b/go/vt/vtgate/planbuilder/rewrite.go @@ -18,14 +18,12 @@ package planbuilder import ( "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vtgate/semantics" ) type rewriter struct { semTable *semantics.SemTable reservedVars *sqlparser.ReservedVars - inSubquery int err error } @@ -34,26 +32,16 @@ func queryRewrite(semTable *semantics.SemTable, reservedVars *sqlparser.Reserved semTable: semTable, reservedVars: reservedVars, } - sqlparser.Rewrite(statement, r.rewriteDown, r.rewriteUp) + sqlparser.Rewrite(statement, r.rewriteDown, nil) return nil } -func (r *rewriter) rewriteUp(cursor *sqlparser.Cursor) bool { - _, ok := cursor.Node().(*sqlparser.Subquery) - if ok { - r.inSubquery-- - } - return true -} - func (r *rewriter) rewriteDown(cursor *sqlparser.Cursor) bool { switch node := cursor.Node().(type) { case *sqlparser.Select: rewriteHavingClause(node) case *sqlparser.AliasedTableExpr: - // rewrite names of the routed tables for the subquery - // We only need to do this for non-derived tables and if they are in a subquery - if _, isDerived := node.Expr.(*sqlparser.DerivedTable); isDerived || r.inSubquery == 0 { + if _, isDerived := node.Expr.(*sqlparser.DerivedTable); isDerived { break } // find the tableSet and tableInfo that this table points to @@ -82,10 +70,9 @@ func (r *rewriter) rewriteDown(cursor *sqlparser.Cursor) bool { node.As = tableName.Name } // replace the table name with the original table + tableName.Qualifier = sqlparser.IdentifierCS{} tableName.Name = vindexTable.Name node.Expr = tableName - case *sqlparser.Subquery: - r.inSubquery++ } return true } @@ -95,15 +82,6 @@ func rewriteHavingClause(node *sqlparser.Select) { return } - selectExprMap := map[string]sqlparser.Expr{} - for _, selectExpr := range node.SelectExprs { - aliasedExpr, isAliased := selectExpr.(*sqlparser.AliasedExpr) - if !isAliased || aliasedExpr.As.IsEmpty() { - continue - } - selectExprMap[aliasedExpr.As.Lowered()] = aliasedExpr.Expr - } - // for each expression in the having clause, we check if it contains aggregation. // if it does, we keep the expression in the having clause ; and if it does not // and the expression is in the select list, we replace the expression by the one @@ -111,40 +89,10 @@ func rewriteHavingClause(node *sqlparser.Select) { exprs := sqlparser.SplitAndExpression(nil, node.Having.Expr) node.Having = nil for _, expr := range exprs { - hasAggr := sqlparser.ContainsAggregation(expr) - if !hasAggr { - sqlparser.Rewrite(expr, func(cursor *sqlparser.Cursor) bool { - visitColName(cursor.Node(), selectExprMap, func(original sqlparser.Expr) { - if sqlparser.ContainsAggregation(original) { - hasAggr = true - } - }) - return true - }, nil) - } - if hasAggr { + if sqlparser.ContainsAggregation(expr) { node.AddHaving(expr) } else { - sqlparser.Rewrite(expr, func(cursor *sqlparser.Cursor) bool { - visitColName(cursor.Node(), selectExprMap, func(original sqlparser.Expr) { - cursor.Replace(original) - }) - return true - }, nil) node.AddWhere(expr) } } } -func visitColName(cursor sqlparser.SQLNode, selectExprMap map[string]sqlparser.Expr, f func(original sqlparser.Expr)) { - switch x := cursor.(type) { - case *sqlparser.ColName: - if !x.Qualifier.IsEmpty() { - return - } - originalExpr, isInMap := selectExprMap[x.Name.Lowered()] - if isInMap { - f(originalExpr) - } - return - } -} diff --git a/go/vt/vtgate/planbuilder/rewrite_test.go b/go/vt/vtgate/planbuilder/rewrite_test.go index 292c94f448a..87c8985fd63 100644 --- a/go/vt/vtgate/planbuilder/rewrite_test.go +++ b/go/vt/vtgate/planbuilder/rewrite_test.go @@ -82,7 +82,7 @@ func TestHavingRewrite(t *testing.T) { } func prepTest(t *testing.T, sql string) (*semantics.SemTable, *sqlparser.ReservedVars, *sqlparser.Select) { - ast, vars, err := sqlparser.Parse2(sql) + ast, vars, err := sqlparser.NewTestParser().Parse2(sql) require.NoError(t, err) sel, isSelectStatement := ast.(*sqlparser.Select) diff --git a/go/vt/vtgate/planbuilder/route.go b/go/vt/vtgate/planbuilder/route.go index 63f6d0ea612..d4f72f0ff3a 100644 --- a/go/vt/vtgate/planbuilder/route.go +++ b/go/vt/vtgate/planbuilder/route.go @@ -73,7 +73,7 @@ func (rb *route) Wireup(ctx *plancontext.PlanningContext) error { } query, args := planableVindex.Query() - stmt, reserved, err := sqlparser.Parse2(query) + stmt, reserved, err := ctx.VSchema.Environment().Parser().Parse2(query) if err != nil { return err } diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go index 44976815bd2..13671e7efa0 100644 --- a/go/vt/vtgate/planbuilder/select.go +++ b/go/vt/vtgate/planbuilder/select.go @@ -26,7 +26,6 @@ import ( "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" ) @@ -131,7 +130,7 @@ func buildSQLCalcFoundRowsPlan( return nil, nil, err } - statement2, reserved2, err := sqlparser.Parse2(originalQuery) + statement2, reserved2, err := vschema.Environment().Parser().Parse2(originalQuery) if err != nil { return nil, nil, err } @@ -225,7 +224,7 @@ func newBuildSelectPlan( return plan, operators.TablesUsed(op), nil } -func createSelectOperator(ctx *plancontext.PlanningContext, selStmt sqlparser.SelectStatement, reservedVars *sqlparser.ReservedVars) (ops.Operator, error) { +func createSelectOperator(ctx *plancontext.PlanningContext, selStmt sqlparser.SelectStatement, reservedVars *sqlparser.ReservedVars) (operators.Operator, error) { err := queryRewrite(ctx.SemTable, reservedVars, selStmt) if err != nil { return nil, err @@ -290,7 +289,10 @@ func handleDualSelects(sel *sqlparser.Select, vschema plancontext.VSchema) (engi if isLFunc { elem := &engine.LockFunc{Typ: expr.Expr.(*sqlparser.LockingFunc)} if lFunc.Name != nil { - n, err := evalengine.Translate(lFunc.Name, nil) + n, err := evalengine.Translate(lFunc.Name, &evalengine.Config{ + Collation: vschema.ConnCollation(), + Environment: vschema.Environment(), + }) if err != nil { return nil, err } @@ -302,7 +304,10 @@ func handleDualSelects(sel *sqlparser.Select, vschema plancontext.VSchema) (engi if len(lockFunctions) > 0 { return nil, vterrors.VT12001(fmt.Sprintf("LOCK function and other expression: [%s] in same select query", sqlparser.String(expr))) } - exprs[i], err = evalengine.Translate(expr.Expr, &evalengine.Config{Collation: vschema.ConnCollation()}) + exprs[i], err = evalengine.Translate(expr.Expr, &evalengine.Config{ + Collation: vschema.ConnCollation(), + Environment: vschema.Environment(), + }) if err != nil { return nil, nil } diff --git a/go/vt/vtgate/planbuilder/sequential.go b/go/vt/vtgate/planbuilder/sequential.go new file mode 100644 index 00000000000..ff6abacb437 --- /dev/null +++ b/go/vt/vtgate/planbuilder/sequential.go @@ -0,0 +1,36 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package planbuilder + +import ( + "vitess.io/vitess/go/vt/vtgate/engine" +) + +type sequential struct { + sources []logicalPlan +} + +var _ logicalPlan = (*sequential)(nil) + +// Primitive implements the logicalPlan interface +func (s *sequential) Primitive() engine.Primitive { + var sources []engine.Primitive + for _, source := range s.sources { + sources = append(sources, source.Primitive()) + } + return engine.NewSequential(sources) +} diff --git a/go/vt/vtgate/planbuilder/set.go b/go/vt/vtgate/planbuilder/set.go index 7b1e584132d..bf6820b7489 100644 --- a/go/vt/vtgate/planbuilder/set.go +++ b/go/vt/vtgate/planbuilder/set.go @@ -55,12 +55,15 @@ func buildSetPlan(stmt *sqlparser.Set, vschema plancontext.VSchema) (*planResult var setOps []engine.SetOp var err error - ec := new(expressionConverter) + ec := &expressionConverter{ + env: vschema.Environment(), + collation: vschema.ConnCollation(), + } for _, expr := range stmt.Exprs { // AST struct has been prepared before getting here, so no scope here means that // we have a UDV. If the original query didn't explicitly specify the scope, it - // would have been explictly set to sqlparser.SessionStr before reaching this + // would have been explicitly set to sqlparser.SessionStr before reaching this // phase of planning switch expr.Var.Scope { case sqlparser.GlobalScope: @@ -80,7 +83,7 @@ func buildSetPlan(stmt *sqlparser.Set, vschema plancontext.VSchema) (*planResult } setOps = append(setOps, setOp) case sqlparser.NextTxScope, sqlparser.SessionScope: - planFunc, err := sysvarPlanningFuncs.Get(expr) + planFunc, err := sysvarPlanningFuncs.Get(vschema.Environment(), expr) if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/show.go b/go/vt/vtgate/planbuilder/show.go index aba5b1a9016..1830f197fa8 100644 --- a/go/vt/vtgate/planbuilder/show.go +++ b/go/vt/vtgate/planbuilder/show.go @@ -20,6 +20,7 @@ import ( "fmt" "regexp" "sort" + "strconv" "strings" "sync" @@ -116,6 +117,8 @@ func buildShowBasicPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) return buildShowTargetPlan(vschema) case sqlparser.VschemaTables: return buildVschemaTablesPlan(vschema) + case sqlparser.VschemaKeyspaces: + return buildVschemaKeyspacesPlan(vschema) case sqlparser.VschemaVindexes: return buildVschemaVindexesPlan(show, vschema) } @@ -164,7 +167,7 @@ func buildVariablePlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) ( } func buildShowTblPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) { - if !show.DbName.IsEmpty() { + if show.DbName.NotEmpty() { show.Tbl.Qualifier = sqlparser.NewIdentifierCS(show.DbName.String()) // Remove Database Name from the query. show.DbName = sqlparser.NewIdentifierCS("") @@ -174,7 +177,7 @@ func buildShowTblPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (e var ks *vindexes.Keyspace var err error - if !show.Tbl.Qualifier.IsEmpty() && sqlparser.SystemSchema(show.Tbl.Qualifier.String()) { + if show.Tbl.Qualifier.NotEmpty() && sqlparser.SystemSchema(show.Tbl.Qualifier.String()) { ks, err = vschema.AnyKeyspace() if err != nil { return nil, err @@ -486,7 +489,7 @@ func buildCreateTblPlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) var ks *vindexes.Keyspace var err error - if !show.Op.Qualifier.IsEmpty() && sqlparser.SystemSchema(show.Op.Qualifier.String()) { + if show.Op.Qualifier.NotEmpty() && sqlparser.SystemSchema(show.Op.Qualifier.String()) { ks, err = vschema.AnyKeyspace() if err != nil { return nil, err @@ -519,7 +522,7 @@ func buildCreateTblPlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) func buildCreatePlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) (engine.Primitive, error) { dbName := "" - if !show.Op.Qualifier.IsEmpty() { + if show.Op.Qualifier.NotEmpty() { dbName = show.Op.Qualifier.String() } @@ -558,16 +561,17 @@ func buildShowVGtidPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) } return &engine.OrderedAggregate{ Aggregates: []*engine.AggregateParams{ - engine.NewAggregateParam(popcode.AggregateGtid, 1, "global vgtid_executed"), + engine.NewAggregateParam(popcode.AggregateGtid, 1, "global vgtid_executed", vschema.Environment().CollationEnv()), }, TruncateColumnCount: 2, Input: send, + CollationEnv: vschema.Environment().CollationEnv(), }, nil } func buildShowGtidPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) { dbName := "" - if !show.DbName.IsEmpty() { + if show.DbName.NotEmpty() { dbName = show.DbName.String() } dest, ks, _, err := vschema.TargetDestination(dbName) @@ -641,6 +645,26 @@ func buildEnginesPlan() (engine.Primitive, error) { buildVarCharFields("Engine", "Support", "Comment", "Transactions", "XA", "Savepoints")), nil } +func buildVschemaKeyspacesPlan(vschema plancontext.VSchema) (engine.Primitive, error) { + vs := vschema.GetVSchema() + var rows [][]sqltypes.Value + for ksName, ks := range vs.Keyspaces { + var row []sqltypes.Value + row = append(row, sqltypes.NewVarChar(ksName)) + row = append(row, sqltypes.NewVarChar(strconv.FormatBool(ks.Keyspace.Sharded))) + fkMode, _ := vschema.ForeignKeyMode(ksName) + row = append(row, sqltypes.NewVarChar(fkMode.String())) + ksError := "" + if ks.Error != nil { + ksError = ks.Error.Error() + } + row = append(row, sqltypes.NewVarChar(ksError)) + rows = append(rows, row) + } + + return engine.NewRowsPrimitive(rows, buildVarCharFields("Keyspace", "Sharded", "Foreign Key", "Comment")), nil +} + func buildVschemaTablesPlan(vschema plancontext.VSchema) (engine.Primitive, error) { vs := vschema.GetVSchema() ks, err := vschema.DefaultKeyspace() diff --git a/go/vt/vtgate/planbuilder/show_test.go b/go/vt/vtgate/planbuilder/show_test.go index b36133bb1c7..931c914149d 100644 --- a/go/vt/vtgate/planbuilder/show_test.go +++ b/go/vt/vtgate/planbuilder/show_test.go @@ -22,6 +22,7 @@ import ( "testing" "vitess.io/vitess/go/test/vschemawrapper" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/require" @@ -35,6 +36,7 @@ import ( func TestBuildDBPlan(t *testing.T) { vschema := &vschemawrapper.VSchemaWrapper{ Keyspace: &vindexes.Keyspace{Name: "main"}, + Env: vtenv.NewTestEnv(), } testCases := []struct { @@ -50,7 +52,7 @@ func TestBuildDBPlan(t *testing.T) { for _, s := range testCases { t.Run(s.query, func(t *testing.T) { - parserOut, err := sqlparser.Parse(s.query) + parserOut, err := sqlparser.NewTestParser().Parse(s.query) require.NoError(t, err) show := parserOut.(*sqlparser.Show) @@ -76,7 +78,7 @@ func TestGenerateCharsetRows(t *testing.T) { append(buildVarCharRow( "utf8mb4", "UTF-8 Unicode", - collations.Local().LookupName(collations.Default())), + collations.MySQL8().LookupName(collations.MySQL8().DefaultConnectionCharset())), sqltypes.NewUint32(4)), } rows2 := [][]sqltypes.Value{ @@ -88,7 +90,7 @@ func TestGenerateCharsetRows(t *testing.T) { append(buildVarCharRow( "utf8mb4", "UTF-8 Unicode", - collations.Local().LookupName(collations.Default())), + collations.MySQL8().LookupName(collations.MySQL8().DefaultConnectionCharset())), sqltypes.NewUint32(4)), } @@ -110,7 +112,7 @@ func TestGenerateCharsetRows(t *testing.T) { for _, tc := range testcases { t.Run(tc.input, func(t *testing.T) { - stmt, err := sqlparser.Parse(tc.input) + stmt, err := sqlparser.NewTestParser().Parse(tc.input) require.NoError(t, err) match := stmt.(*sqlparser.Show).Internal.(*sqlparser.ShowBasic) filter := match.Filter diff --git a/go/vt/vtgate/planbuilder/simplifier_test.go b/go/vt/vtgate/planbuilder/simplifier_test.go index 1e106adacc0..61ed220bd44 100644 --- a/go/vt/vtgate/planbuilder/simplifier_test.go +++ b/go/vt/vtgate/planbuilder/simplifier_test.go @@ -21,19 +21,15 @@ import ( "fmt" "testing" - "vitess.io/vitess/go/test/vschemawrapper" - - "vitess.io/vitess/go/vt/vterrors" - "github.com/stretchr/testify/assert" - - "vitess.io/vitess/go/vt/vtgate/simplifier" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/vschemawrapper" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/simplifier" ) // TestSimplifyBuggyQuery should be used to whenever we get a planner bug reported @@ -45,10 +41,11 @@ func TestSimplifyBuggyQuery(t *testing.T) { vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Version: Gen4, + Env: vtenv.NewTestEnv(), } - stmt, reserved, err := sqlparser.Parse2(query) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( @@ -67,10 +64,11 @@ func TestSimplifyPanic(t *testing.T) { vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Version: Gen4, + Env: vtenv.NewTestEnv(), } - stmt, reserved, err := sqlparser.Parse2(query) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(query) require.NoError(t, err) - rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil) + rewritten, _ := sqlparser.RewriteAST(sqlparser.CloneStatement(stmt), vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) reservedVars := sqlparser.NewReservedVars("vtg", reserved) simplified := simplifier.SimplifyStatement( @@ -88,19 +86,20 @@ func TestUnsupportedFile(t *testing.T) { vschema := &vschemawrapper.VSchemaWrapper{ V: loadSchema(t, "vschemas/schema.json", true), Version: Gen4, + Env: vtenv.NewTestEnv(), } fmt.Println(vschema) for _, tcase := range readJSONTests("unsupported_cases.txt") { t.Run(tcase.Query, func(t *testing.T) { log.Errorf("unsupported_cases.txt - %s", tcase.Query) - stmt, reserved, err := sqlparser.Parse2(tcase.Query) + stmt, reserved, err := sqlparser.NewTestParser().Parse2(tcase.Query) require.NoError(t, err) _, ok := stmt.(sqlparser.SelectStatement) if !ok { t.Skip() return } - rewritten, err := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil) + rewritten, err := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) if err != nil { t.Skip() } @@ -109,7 +108,7 @@ func TestUnsupportedFile(t *testing.T) { reservedVars := sqlparser.NewReservedVars("vtg", reserved) ast := rewritten.AST origQuery := sqlparser.String(ast) - stmt, _, _ = sqlparser.Parse2(tcase.Query) + stmt, _, _ = sqlparser.NewTestParser().Parse2(tcase.Query) simplified := simplifier.SimplifyStatement( stmt.(sqlparser.SelectStatement), vschema.CurrentDb(), @@ -130,11 +129,11 @@ func TestUnsupportedFile(t *testing.T) { } func keepSameError(query string, reservedVars *sqlparser.ReservedVars, vschema *vschemawrapper.VSchemaWrapper, needs *sqlparser.BindVarNeeds) func(statement sqlparser.SelectStatement) bool { - stmt, _, err := sqlparser.Parse2(query) + stmt, _, err := sqlparser.NewTestParser().Parse2(query) if err != nil { panic(err) } - rewritten, _ := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil) + rewritten, _ := sqlparser.RewriteAST(stmt, vschema.CurrentDb(), sqlparser.SQLSelectLimitUnset, "", nil, nil, nil) ast := rewritten.AST _, expected := BuildFromStmt(context.Background(), query, ast, reservedVars, vschema, rewritten.BindVarNeeds, true, true) if expected == nil { @@ -169,7 +168,7 @@ func keepPanicking(query string, reservedVars *sqlparser.ReservedVars, vschema * return false } - stmt, _, err := sqlparser.Parse2(query) + stmt, _, err := sqlparser.NewTestParser().Parse2(query) if err != nil { panic(err.Error()) } diff --git a/go/vt/vtgate/planbuilder/single_sharded_shortcut.go b/go/vt/vtgate/planbuilder/single_sharded_shortcut.go index daf19ced859..dea4e7bb595 100644 --- a/go/vt/vtgate/planbuilder/single_sharded_shortcut.go +++ b/go/vt/vtgate/planbuilder/single_sharded_shortcut.go @@ -20,11 +20,10 @@ import ( "sort" "strings" - "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -106,7 +105,7 @@ func getTableNames(semTable *semantics.SemTable) ([]sqlparser.TableName, error) func removeKeyspaceFromSelectExpr(expr sqlparser.SelectExpr) { switch expr := expr.(type) { case *sqlparser.AliasedExpr: - sqlparser.RemoveKeyspaceFromColName(expr.Expr) + sqlparser.RemoveKeyspaceInCol(expr.Expr) case *sqlparser.StarExpr: expr.TableName.Qualifier = sqlparser.NewIdentifierCS("") } diff --git a/go/vt/vtgate/planbuilder/system_variables.go b/go/vt/vtgate/planbuilder/system_variables.go index eccb263c65a..8ef968a2ac8 100644 --- a/go/vt/vtgate/planbuilder/system_variables.go +++ b/go/vt/vtgate/planbuilder/system_variables.go @@ -22,6 +22,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/sysvars" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -29,6 +30,7 @@ import ( type sysvarPlanCache struct { funcs map[string]planFunc once sync.Once + env *vtenv.Environment } func (pc *sysvarPlanCache) initForSettings(systemVariables []sysvars.SystemVariable, f func(setting) planFunc) { @@ -53,21 +55,25 @@ func (pc *sysvarPlanCache) initForSettings(systemVariables []sysvars.SystemVaria } func (pc *sysvarPlanCache) parseAndBuildDefaultValue(sysvar sysvars.SystemVariable) evalengine.Expr { - stmt, err := sqlparser.Parse(fmt.Sprintf("select %s", sysvar.Default)) + stmt, err := pc.env.Parser().Parse(fmt.Sprintf("select %s", sysvar.Default)) if err != nil { panic(fmt.Sprintf("bug in set plan init - default value for %s not parsable: %s", sysvar.Name, sysvar.Default)) } sel := stmt.(*sqlparser.Select) aliasedExpr := sel.SelectExprs[0].(*sqlparser.AliasedExpr) - def, err := evalengine.Translate(aliasedExpr.Expr, nil) + def, err := evalengine.Translate(aliasedExpr.Expr, &evalengine.Config{ + Collation: pc.env.CollationEnv().DefaultConnectionCharset(), + Environment: pc.env, + }) if err != nil { panic(fmt.Sprintf("bug in set plan init - default value for %s not able to convert to evalengine.Expr: %s", sysvar.Name, sysvar.Default)) } return def } -func (pc *sysvarPlanCache) init() { +func (pc *sysvarPlanCache) init(env *vtenv.Environment) { pc.once.Do(func() { + pc.env = env pc.funcs = make(map[string]planFunc) pc.initForSettings(sysvars.ReadOnly, buildSetOpReadOnly) pc.initForSettings(sysvars.IgnoreThese, buildSetOpIgnore) @@ -80,8 +86,8 @@ func (pc *sysvarPlanCache) init() { var sysvarPlanningFuncs sysvarPlanCache -func (pc *sysvarPlanCache) Get(expr *sqlparser.SetExpr) (planFunc, error) { - pc.init() +func (pc *sysvarPlanCache) Get(env *vtenv.Environment, expr *sqlparser.SetExpr) (planFunc, error) { + pc.init(env) pf, ok := pc.funcs[expr.Var.Name.Lowered()] if !ok { return nil, vterrors.VT05006(sqlparser.String(expr)) diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json index e47b17cd2ae..5acbfbe61bc 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json @@ -808,7 +808,7 @@ { "comment": "scatter aggregate group by invalid column number", "query": "select col from user group by 2", - "plan": "Unknown column '2' in 'group statement'" + "plan": "Unknown column '2' in 'group clause'" }, { "comment": "scatter aggregate order by null", @@ -1054,8 +1054,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `user`.col1 as a from `user` where 1 != 1 group by a collate utf8_general_ci", - "Query": "select `user`.col1 as a from `user` where `user`.id = 5 group by a collate utf8_general_ci", + "FieldQuery": "select `user`.col1 as a from `user` where 1 != 1 group by `user`.col1 collate utf8_general_ci", + "Query": "select `user`.col1 as a from `user` where `user`.id = 5 group by `user`.col1 collate utf8_general_ci", "Table": "`user`", "Values": [ "5" @@ -1207,7 +1207,7 @@ { "comment": "Group by out of range column number (code is duplicated from symab).", "query": "select id from user group by 2", - "plan": "Unknown column '2' in 'group statement'" + "plan": "Unknown column '2' in 'group clause'" }, { "comment": "here it is safe to remove the order by on the derived table since it will not influence the output of the count(*)", @@ -1602,9 +1602,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select lower(textcol1) as v, count(*), weight_string(lower(textcol1)) from `user` where 1 != 1 group by v, weight_string(lower(textcol1))", + "FieldQuery": "select lower(textcol1) as v, count(*), weight_string(lower(textcol1)) from `user` where 1 != 1 group by lower(textcol1), weight_string(lower(textcol1))", "OrderBy": "(0|2) ASC", - "Query": "select lower(textcol1) as v, count(*), weight_string(lower(textcol1)) from `user` group by v, weight_string(lower(textcol1)) order by v asc", + "Query": "select lower(textcol1) as v, count(*), weight_string(lower(textcol1)) from `user` group by lower(textcol1), weight_string(lower(textcol1)) order by lower(textcol1) asc", "Table": "`user`" } ] @@ -1634,9 +1634,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select char_length(texcol1) as a, count(*), weight_string(char_length(texcol1)) from `user` where 1 != 1 group by a, weight_string(char_length(texcol1))", + "FieldQuery": "select char_length(texcol1) as a, count(*), weight_string(char_length(texcol1)) from `user` where 1 != 1 group by char_length(texcol1), weight_string(char_length(texcol1))", "OrderBy": "(0|2) ASC", - "Query": "select char_length(texcol1) as a, count(*), weight_string(char_length(texcol1)) from `user` group by a, weight_string(char_length(texcol1)) order by a asc", + "Query": "select char_length(texcol1) as a, count(*), weight_string(char_length(texcol1)) from `user` group by char_length(texcol1), weight_string(char_length(texcol1)) order by char_length(texcol1) asc", "Table": "`user`" } ] @@ -2638,9 +2638,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` where 1 != 1 group by a, weight_string(ascii(val1))", + "FieldQuery": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` where 1 != 1 group by ascii(val1), weight_string(ascii(val1))", "OrderBy": "(0|2) ASC", - "Query": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` group by a, weight_string(ascii(val1)) order by a asc", + "Query": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` group by ascii(val1), weight_string(ascii(val1)) order by ascii(val1) asc", "Table": "`user`" } ] @@ -2843,11 +2843,6 @@ ] } }, - { - "comment": "select count(distinct user_id, name) from user", - "query": "select count(distinct user_id, name) from user", - "plan": "VT03001: aggregate functions take a single argument 'count(distinct user_id, `name`)'" - }, { "comment": "select sum(col) from (select user.col as col, 32 from user join user_extra) t", "query": "select sum(col) from (select user.col as col, 32 from user join user_extra) t", @@ -3356,9 +3351,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `user`.id, weight_string(`user`.id) from `user` where 1 != 1 group by id, weight_string(`user`.id)", + "FieldQuery": "select `user`.id, weight_string(`user`.id) from `user` where 1 != 1 group by `user`.id, weight_string(`user`.id)", "OrderBy": "(0|1) ASC", - "Query": "select `user`.id, weight_string(`user`.id) from `user` group by id, weight_string(`user`.id) order by id asc", + "Query": "select `user`.id, weight_string(`user`.id) from `user` group by `user`.id, weight_string(`user`.id) order by `user`.id asc", "Table": "`user`" }, { @@ -3410,8 +3405,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select phone, id, city from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", - "Query": "select phone, id, city from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "FieldQuery": "select x.phone, x.id, x.city from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.phone, x.id, x.city from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", "Table": "`user`" } ] @@ -3453,8 +3448,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select phone, id, city, 1 from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", - "Query": "select phone, id, city, 1 from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "FieldQuery": "select x.phone, x.id, x.city, 1 from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.phone, x.id, x.city, 1 from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", "Table": "`user`" } ] @@ -3558,9 +3553,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", + "FieldQuery": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", "OrderBy": "(1|3) ASC", - "Query": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where val2 < 4) as x order by val1 asc limit :__upper_limit", + "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by val1 asc limit :__upper_limit", "Table": "`user`" } ] @@ -4845,7 +4840,8 @@ "Instructions": { "OperatorType": "Aggregate", "Variant": "Scalar", - "Aggregates": "min(0 COLLATE latin1_swedish_ci) AS min(textcol1), max(1 COLLATE latin1_swedish_ci) AS max(textcol2), sum_distinct(2 COLLATE latin1_swedish_ci) AS sum(distinct textcol1), count_distinct(3 COLLATE latin1_swedish_ci) AS count(distinct textcol1)", + "Aggregates": "min(0 COLLATE latin1_swedish_ci) AS min(textcol1), max(1|4) AS max(textcol2), sum_distinct(2 COLLATE latin1_swedish_ci) AS sum(distinct textcol1), count_distinct(3 COLLATE latin1_swedish_ci) AS count(distinct textcol1)", + "ResultColumns": 4, "Inputs": [ { "OperatorType": "Route", @@ -4854,9 +4850,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select min(textcol1), max(textcol2), textcol1, textcol1 from `user` where 1 != 1 group by textcol1", + "FieldQuery": "select min(textcol1), max(textcol2), textcol1, textcol1, weight_string(textcol2) from `user` where 1 != 1 group by textcol1, weight_string(textcol2)", "OrderBy": "2 ASC COLLATE latin1_swedish_ci", - "Query": "select min(textcol1), max(textcol2), textcol1, textcol1 from `user` group by textcol1 order by textcol1 asc", + "Query": "select min(textcol1), max(textcol2), textcol1, textcol1, weight_string(textcol2) from `user` group by textcol1, weight_string(textcol2) order by textcol1 asc", "Table": "`user`" } ] @@ -4875,8 +4871,9 @@ "Instructions": { "OperatorType": "Aggregate", "Variant": "Ordered", - "Aggregates": "min(1 COLLATE latin1_swedish_ci) AS min(textcol1), max(2 COLLATE latin1_swedish_ci) AS max(textcol2), sum_distinct(3 COLLATE latin1_swedish_ci) AS sum(distinct textcol1), count_distinct(4 COLLATE latin1_swedish_ci) AS count(distinct textcol1)", + "Aggregates": "min(1 COLLATE latin1_swedish_ci) AS min(textcol1), max(2|5) AS max(textcol2), sum_distinct(3 COLLATE latin1_swedish_ci) AS sum(distinct textcol1), count_distinct(4 COLLATE latin1_swedish_ci) AS count(distinct textcol1)", "GroupBy": "0", + "ResultColumns": 5, "Inputs": [ { "OperatorType": "Route", @@ -4885,9 +4882,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select col, min(textcol1), max(textcol2), textcol1, textcol1 from `user` where 1 != 1 group by col, textcol1", + "FieldQuery": "select col, min(textcol1), max(textcol2), textcol1, textcol1, weight_string(textcol2) from `user` where 1 != 1 group by col, textcol1, weight_string(textcol2)", "OrderBy": "0 ASC, 3 ASC COLLATE latin1_swedish_ci", - "Query": "select col, min(textcol1), max(textcol2), textcol1, textcol1 from `user` group by col, textcol1 order by col asc, textcol1 asc", + "Query": "select col, min(textcol1), max(textcol2), textcol1, textcol1, weight_string(textcol2) from `user` group by col, textcol1, weight_string(textcol2) order by col asc, textcol1 asc", "Table": "`user`" } ] @@ -5861,8 +5858,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, val2 from (select id, val2 from `user` where 1 != 1) as x where 1 != 1", - "Query": "select id, val2 from (select id, val2 from `user` where val2 is null) as x limit :__upper_limit", + "FieldQuery": "select x.id, x.val2 from (select id, val2 from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.id, x.val2 from (select id, val2 from `user` where val2 is null) as x limit :__upper_limit", "Table": "`user`" } ] @@ -6038,5 +6035,908 @@ "user.user_extra" ] } + }, + { + "comment": "avg function on scatter query", + "query": "select avg(id) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select avg(id) from user", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "sum(id) / count(id) as avg(id)" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS avg(id), sum_count(1) AS count(id)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select sum(id), count(id) from `user` where 1 != 1", + "Query": "select sum(id), count(id) from `user`", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "avg function on scatter query deep inside the output expression", + "query": "select avg(id)+count(foo)+bar from user group by bar", + "plan": { + "QueryType": "SELECT", + "Original": "select avg(id)+count(foo)+bar from user group by bar", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "avg(id) + count(foo) + bar as avg(id) + count(foo) + bar" + ], + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + ":0 as bar", + "sum(id) / count(id) as avg(id)", + ":2 as count(foo)" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum(1) AS avg(id), sum_count(2) AS count(foo), sum_count(3) AS count(id)", + "GroupBy": "(0|4)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select bar, sum(id), count(foo), count(id), weight_string(bar) from `user` where 1 != 1 group by bar, weight_string(bar)", + "OrderBy": "(0|4) ASC", + "Query": "select bar, sum(id), count(foo), count(id), weight_string(bar) from `user` group by bar, weight_string(bar) order by bar asc", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "avg function on scatter query deep inside the output expression", + "query": "select avg(id)+count(foo)+bar from user group by bar", + "plan": { + "QueryType": "SELECT", + "Original": "select avg(id)+count(foo)+bar from user group by bar", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "avg(id) + count(foo) + bar as avg(id) + count(foo) + bar" + ], + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + ":0 as bar", + "sum(id) / count(id) as avg(id)", + ":2 as count(foo)" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum(1) AS avg(id), sum_count(2) AS count(foo), sum_count(3) AS count(id)", + "GroupBy": "(0|4)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select bar, sum(id), count(foo), count(id), weight_string(bar) from `user` where 1 != 1 group by bar, weight_string(bar)", + "OrderBy": "(0|4) ASC", + "Query": "select bar, sum(id), count(foo), count(id), weight_string(bar) from `user` group by bar, weight_string(bar) order by bar asc", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "two avg aggregations", + "query": "select avg(foo), avg(bar) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select avg(foo), avg(bar) from user", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "sum(foo) / count(foo) as avg(foo)", + "sum(bar) / count(bar) as avg(bar)" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS avg(foo), sum(1) AS avg(bar), sum_count(2) AS count(foo), sum_count(3) AS count(bar)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select sum(foo), sum(bar), count(foo), count(bar) from `user` where 1 != 1", + "Query": "select sum(foo), sum(bar), count(foo), count(bar) from `user`", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "avg and count on the same argument", + "query": "select avg(foo), count(foo) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select avg(foo), count(foo) from user", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "sum(foo) / count(foo) as avg(foo)", + ":1 as count(foo)" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS avg(foo), sum_count(1) AS count(foo), sum_count(2) AS count(foo)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select sum(foo), count(foo), count(foo) from `user` where 1 != 1", + "Query": "select sum(foo), count(foo), count(foo) from `user`", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "GROUP BY inside derived table on the RHS should not be a problem", + "query": "SELECT c.column_name FROM user c JOIN (SELECT table_name FROM user WHERE id = 143 GROUP BY 1) AS tables ON tables.table_name = c.table_name", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT c.column_name FROM user c JOIN (SELECT table_name FROM user WHERE id = 143 GROUP BY 1) AS tables ON tables.table_name = c.table_name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "tables_table_name": 0 + }, + "TableName": "`user`_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select table_name from (select table_name from `user` where 1 != 1 group by table_name) as `tables` where 1 != 1", + "Query": "select table_name from (select table_name from `user` where id = 143 group by table_name) as `tables`", + "Table": "`user`", + "Values": [ + "143" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select c.column_name from `user` as c where 1 != 1", + "Query": "select c.column_name from `user` as c where c.table_name = :tables_table_name", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Group by aggregated column should not be a problem", + "query": "SELECT b.col FROM music AS b JOIN (SELECT MIN(bb.id) AS min_id, MAX(bb.id) AS max_id FROM user AS bb) AS foobars WHERE b.id > foobars.min_id GROUP BY b.col", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT b.col FROM music AS b JOIN (SELECT MIN(bb.id) AS min_id, MAX(bb.id) AS max_id FROM user AS bb) AS foobars WHERE b.id > foobars.min_id GROUP BY b.col", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "GroupBy": "(0|1)", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Sort", + "Variant": "Memory", + "OrderBy": "(0|1) ASC", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0,R:1", + "JoinVars": { + "foobars_min_id": 0 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "GroupBy": "0 COLLATE utf8mb4_0900_ai_ci", + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "min(0|2) AS min_id, max(1|2) AS max_id", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select min(bb.id) as min_id, max(bb.id) as max_id, weight_string(bb.id) from `user` as bb where 1 != 1 group by weight_string(bb.id)", + "OrderBy": "0 ASC COLLATE utf8mb4_0900_ai_ci", + "Query": "select min(bb.id) as min_id, max(bb.id) as max_id, weight_string(bb.id) from `user` as bb group by weight_string(bb.id) order by min(bb.id) asc", + "Table": "`user`" + } + ] + } + ] + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select b.col, weight_string(b.col) from music as b where 1 != 1 group by b.col, weight_string(b.col)", + "Query": "select b.col, weight_string(b.col) from music as b where b.id > :foobars_min_id group by b.col, weight_string(b.col)", + "Table": "music" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "Group by aliases on both sides of a join", + "query": "select count(*), cast(user.foo as datetime) as f1, cast(music.foo as datetime) as f2 from user join music group by f1, f2", + "plan": { + "QueryType": "SELECT", + "Original": "select count(*), cast(user.foo as datetime) as f1, cast(music.foo as datetime) as f2 from user join music group by f1, f2", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum_count_star(0) AS count(*)", + "GroupBy": "(1|3), (2|4)", + "ResultColumns": 3, + "Inputs": [ + { + "OperatorType": "Sort", + "Variant": "Memory", + "OrderBy": "(1|3) ASC, (2|4) ASC", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "count(*) * count(*) as count(*)", + ":2 as f1", + ":3 as f2", + ":4 as weight_string(cast(`user`.foo as datetime))", + ":5 as weight_string(cast(music.foo as datetime))" + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,R:0,L:1,R:1,L:2,R:2", + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*), cast(`user`.foo as datetime) as f1, weight_string(cast(`user`.foo as datetime)) from `user` where 1 != 1 group by cast(`user`.foo as datetime), weight_string(cast(`user`.foo as datetime))", + "Query": "select count(*), cast(`user`.foo as datetime) as f1, weight_string(cast(`user`.foo as datetime)) from `user` group by cast(`user`.foo as datetime), weight_string(cast(`user`.foo as datetime))", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*), cast(music.foo as datetime) as f2, weight_string(cast(music.foo as datetime)) from music where 1 != 1 group by cast(music.foo as datetime), weight_string(cast(music.foo as datetime))", + "Query": "select count(*), cast(music.foo as datetime) as f2, weight_string(cast(music.foo as datetime)) from music group by cast(music.foo as datetime), weight_string(cast(music.foo as datetime))", + "Table": "music" + } + ] + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "count(*) push down through left hash join", + "query": "select count(*) from user left join (select col, bar from user_extra limit 10) ue on user.col = ue.col group by user.foo, ue.bar", + "plan": { + "QueryType": "SELECT", + "Original": "select count(*) from user left join (select col, bar from user_extra limit 10) ue on user.col = ue.col group by user.foo, ue.bar", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum_count_star(0) AS count(*)", + "GroupBy": "(1|2), (3|4)", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "count(*) * coalesce(count(*), 1) as count(*)", + ":4 as foo", + ":6 as weight_string(`user`.foo)", + ":5 as bar", + ":7 as weight_string(ue.bar)" + ], + "Inputs": [ + { + "OperatorType": "Sort", + "Variant": "Memory", + "OrderBy": "(4|6) ASC, (5|7) ASC", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "HashLeftJoin", + "Collation": "binary", + "ComparisonType": "INT16", + "JoinColumnIndexes": "-1,1,-2,2,-3,3", + "Predicate": "`user`.col = ue.col", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*), `user`.col, `user`.foo from `user` where 1 != 1 group by `user`.col, `user`.foo", + "Query": "select count(*), `user`.col, `user`.foo from `user` group by `user`.col, `user`.foo", + "Table": "`user`" + }, + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "count_star(0)", + "GroupBy": "1, (2|3)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 2, + 0, + 1, + 3 + ], + "Inputs": [ + { + "OperatorType": "Sort", + "Variant": "Memory", + "OrderBy": "0 ASC, (1|3) ASC", + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select ue.col, ue.bar, 1, weight_string(ue.bar) from (select col, bar from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select ue.col, ue.bar, 1, weight_string(ue.bar) from (select col, bar from user_extra) as ue limit :__upper_limit", + "Table": "user_extra" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "sharded subquery inside aggregation function on a dual table", + "query": "select max((select min(col) from user where id = 1))", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select min(col) from user where id = 1))", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max((select min(col) from `user` where 1 != 1)) from dual where 1 != 1", + "Query": "select max((select min(col) from `user` where id = 1)) from dual", + "Table": "dual", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "main.dual", + "user.user" + ] + } + }, + { + "comment": "unsharded subquery inside aggregation function on a sharded table", + "query": "select max((select min(col) from unsharded)) from user where id = 1", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select min(col) from unsharded)) from user where id = 1", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "max(0|1) AS max((select min(col) from unsharded))", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select min(col) from unsharded where 1 != 1", + "Query": "select min(col) from unsharded", + "Table": "unsharded" + }, + { + "InputName": "Outer", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max(:__sq1), weight_string(:__sq1) from `user` where 1 != 1 group by weight_string(:__sq1)", + "Query": "select max(:__sq1), weight_string(:__sq1) from `user` where id = 1 group by weight_string(:__sq1)", + "Table": "`user`", + "Values": [ + "1" + ], + "Vindex": "user_index" + } + ] + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "sharded subquery inside aggregation function on a sharded table on different vindex value", + "query": "select max((select min(col) from user where id = 1)) from user where id = 2", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select min(col) from user where id = 1)) from user where id = 2", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "max(0|1) AS max((select min(col) from `user` where id = 1))", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select min(col) from `user` where 1 != 1", + "Query": "select min(col) from `user` where id = 1", + "Table": "`user`", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + { + "InputName": "Outer", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max(:__sq1), weight_string(:__sq1) from `user` where 1 != 1 group by weight_string(:__sq1)", + "Query": "select max(:__sq1), weight_string(:__sq1) from `user` where id = 2 group by weight_string(:__sq1)", + "Table": "`user`", + "Values": [ + "2" + ], + "Vindex": "user_index" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "sharded subquery inside group_concat multi-column aggregation function on a dual table", + "query": "select max((select group_concat(col1, col2) from user where id = 1))", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select group_concat(col1, col2) from user where id = 1))", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max((select group_concat(col1, col2) from `user` where 1 != 1)) from dual where 1 != 1", + "Query": "select max((select group_concat(col1, col2) from `user` where id = 1)) from dual", + "Table": "dual", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "main.dual", + "user.user" + ] + } + }, + { + "comment": "sharded subquery inside group_concat multi-column aggregation function on a sharded table on same vindex value", + "query": "select max((select group_concat(col1, col2) from user where id = 1)) from user where id = 1", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select group_concat(col1, col2) from user where id = 1)) from user where id = 1", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max((select group_concat(col1, col2) from `user` where 1 != 1)) from `user` where 1 != 1", + "Query": "select max((select group_concat(col1, col2) from `user` where id = 1)) from `user` where id = 1", + "Table": "`user`", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "sharded subquery inside group_concat multi-column aggregation function on a sharded table", + "query": "select max((select group_concat(col1, col2) from user where id = 1)) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select group_concat(col1, col2) from user where id = 1)) from user", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "max(0|1) AS max((select group_concat(col1, col2) from `user` where id = 1))", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select group_concat(col1, col2) from `user` where 1 != 1", + "Query": "select group_concat(col1, col2) from `user` where id = 1", + "Table": "`user`", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + { + "InputName": "Outer", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max(:__sq1), weight_string(:__sq1) from `user` where 1 != 1 group by weight_string(:__sq1)", + "Query": "select max(:__sq1), weight_string(:__sq1) from `user` group by weight_string(:__sq1)", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "sharded correlated subquery inside aggregation function on a sharded table on same vindex", + "query": "select max((select max(col2) from user u1 where u1.id = u2.id)) from user u2", + "plan": { + "QueryType": "SELECT", + "Original": "select max((select max(col2) from user u1 where u1.id = u2.id)) from user u2", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "max(0|1) AS max((select max(col2) from `user` as u1 where u1.id = u2.id))", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select max((select max(col2) from `user` as u1 where 1 != 1)), weight_string((select max(col2) from `user` as u1 where 1 != 1)) from `user` as u2 where 1 != 1 group by weight_string((select max(col2) from `user` as u1 where 1 != 1))", + "Query": "select max((select max(col2) from `user` as u1 where u1.id = u2.id)), weight_string((select max(col2) from `user` as u1 where u1.id = u2.id)) from `user` as u2 group by weight_string((select max(col2) from `user` as u1 where u1.id = u2.id))", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Multi-value aggregates pushed as function without splitting", + "query": "select count(a,b) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select count(a,b) from user", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count(0) AS count(a, b)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(a, b) from `user` where 1 != 1", + "Query": "select count(a, b) from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "group_concat with multi column - pushed without splitting", + "query": "select group_concat(col1, col2) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select group_concat(col1, col2) from user", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "group_concat(0) AS group_concat(col1, col2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select group_concat(col1, col2) from `user` where 1 != 1", + "Query": "select group_concat(col1, col2) from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "select count(distinct name, id) from user", + "query": "select count(distinct name, id) from user", + "plan": { + "QueryType": "SELECT", + "Original": "select count(distinct name, id) from user", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count_distinct(0) AS count(distinct `name`, id)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(distinct `name`, id) from `user` where 1 != 1", + "Query": "select count(distinct `name`, id) from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "valid but slightly confusing query should work - col in the order by should not get expanded to the column alias col", + "query": "select id, from_unixtime(min(col)) as col from user group by id order by min(col)", + "plan": { + "QueryType": "SELECT", + "Original": "select id, from_unixtime(min(col)) as col from user group by id order by min(col)", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, from_unixtime(min(col)) as col, min(col) from `user` where 1 != 1 group by id", + "OrderBy": "2 ASC COLLATE utf8mb4_0900_ai_ci", + "Query": "select id, from_unixtime(min(col)) as col, min(col) from `user` group by id order by min(col) asc", + "ResultColumns": 2, + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/cte_cases.json b/go/vt/vtgate/planbuilder/testdata/cte_cases.json index e41aa27ce1b..e43b6320340 100644 --- a/go/vt/vtgate/planbuilder/testdata/cte_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/cte_cases.json @@ -198,8 +198,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select phone, id, city from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", - "Query": "select phone, id, city from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "FieldQuery": "select x.phone, x.id, x.city from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.phone, x.id, x.city from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", "Table": "`user`" } ] @@ -241,8 +241,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select phone, id, city, 1 from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", - "Query": "select phone, id, city, 1 from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "FieldQuery": "select x.phone, x.id, x.city, 1 from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.phone, x.id, x.city, 1 from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", "Table": "`user`" } ] @@ -346,9 +346,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", + "FieldQuery": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", "OrderBy": "(1|3) ASC", - "Query": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where val2 < 4) as x order by val1 asc limit :__upper_limit", + "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by val1 asc limit :__upper_limit", "Table": "`user`" } ] @@ -691,8 +691,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, val2 from (select id, val2 from `user` where 1 != 1) as x where 1 != 1", - "Query": "select id, val2 from (select id, val2 from `user` where val2 is null) as x limit :__upper_limit", + "FieldQuery": "select x.id, x.val2 from (select id, val2 from `user` where 1 != 1) as x where 1 != 1", + "Query": "select x.id, x.val2 from (select id, val2 from `user` where val2 is null) as x limit :__upper_limit", "Table": "`user`" } ] @@ -1369,8 +1369,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select u.* from (select * from unsharded where 1 != 1) as u where 1 != 1", - "Query": "select u.* from (select * from unsharded) as u", + "FieldQuery": "with u as (select * from unsharded where 1 != 1) select u.* from u where 1 != 1", + "Query": "with u as (select * from unsharded) select u.* from u", "Table": "unsharded" }, "TablesUsed": [ @@ -1709,8 +1709,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select col from (select col from unsharded join unsharded_b where 1 != 1) as u join unsharded_a as ua where 1 != 1", - "Query": "select col from (select col from unsharded join unsharded_b) as u join unsharded_a as ua limit 1", + "FieldQuery": "with u as (select col from unsharded join unsharded_b where 1 != 1) select col from u join unsharded_a as ua where 1 != 1", + "Query": "with u as (select col from unsharded join unsharded_b) select col from u join unsharded_a as ua limit 1", "Table": "unsharded, unsharded_a, unsharded_b" }, "TablesUsed": [ @@ -1830,8 +1830,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1 union select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1) as dt where 1 != 1", - "Query": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user`) as x union select id, foo from (select id, foo from `user`) as x) as dt", + "FieldQuery": "select dt.id, dt.foo, weight_string(dt.id), weight_string(dt.foo) from (select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1 union select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1) as dt where 1 != 1", + "Query": "select dt.id, dt.foo, weight_string(dt.id), weight_string(dt.foo) from (select id, foo from (select id, foo from `user`) as x union select id, foo from (select id, foo from `user`) as x) as dt", "Table": "`user`" } ] @@ -1840,5 +1840,28 @@ "user.user" ] } + }, + { + "comment": "recursive WITH against an unsharded database", + "query": "WITH RECURSIVE cte (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5 ) SELECT cte.n FROM unsharded join cte on unsharded.id = cte.n ", + "plan": { + "QueryType": "SELECT", + "Original": "WITH RECURSIVE cte (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5 ) SELECT cte.n FROM unsharded join cte on unsharded.id = cte.n ", + "Instructions": { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "with recursive cte(n) as (select 1 from dual where 1 != 1 union all select n + 1 from cte where 1 != 1) select cte.n from unsharded join cte on unsharded.id = cte.n where 1 != 1", + "Query": "with recursive cte(n) as (select 1 from dual union all select n + 1 from cte where n < 5) select cte.n from unsharded join cte on unsharded.id = cte.n", + "Table": "dual, unsharded" + }, + "TablesUsed": [ + "main.dual", + "main.unsharded" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/ddl_cases.json b/go/vt/vtgate/planbuilder/testdata/ddl_cases.json index e31cc3e29e1..41aded18c5d 100644 --- a/go/vt/vtgate/planbuilder/testdata/ddl_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/ddl_cases.json @@ -260,7 +260,7 @@ "Name": "main", "Sharded": false }, - "Query": "create view view_a as select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a" + "Query": "create view view_a as select * from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a" }, "TablesUsed": [ "main.view_a" diff --git a/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json b/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json index c2e1b1ed94e..f9943319c2d 100644 --- a/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json +++ b/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json @@ -472,7 +472,7 @@ "Name": "user", "Sharded": true }, - "Query": "create view view_a as select user0_.col as col0_ from `user` as user0_ where id = 1 order by col0_ desc" + "Query": "create view view_a as select user0_.col as col0_ from `user` as user0_ where id = 1 order by user0_.col desc" }, "TablesUsed": [ "user.view_a" diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.json b/go/vt/vtgate/planbuilder/testdata/dml_cases.json index eebcf63edf3..6fbc31eb84d 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.json @@ -1207,7 +1207,7 @@ { "comment": "insert with mimatched column list", "query": "insert into user(id) values (1, 2)", - "plan": "VT03006: column count does not match value count at row 1" + "plan": "VT03006: column count does not match value count with the row" }, { "comment": "insert no column list for sharded authoritative table", @@ -3759,17 +3759,17 @@ { "comment": "insert using select with more columns in insert", "query": "insert into music(id, user_id) select 1", - "plan": "VT03006: column count does not match value count at row 1" + "plan": "VT03006: column count does not match value count with the row" }, { "comment": "insert using select with more columns in select", "query": "insert into music(id, user_id) select id, count(user_id), sum(user_id) from user group by id", - "plan": "VT03006: column count does not match value count at row 1" + "plan": "VT03006: column count does not match value count with the row" }, { "comment": "insert using select with more columns in select after accounting for star column", "query": "insert into music(id, user_id) select id, *, 2 from user", - "plan": "VT03006: column count does not match value count at row 1" + "plan": "VT03006: column count does not match value count with the row" }, { "comment": "insert using select with auto-inc column using vitess sequence, sequence column not present", @@ -4084,7 +4084,7 @@ "Original": "insert into unsharded(col) select col from unsharded_tab", "Instructions": { "OperatorType": "Insert", - "Variant": "Unsharded", + "Variant": "Select", "Keyspace": { "Name": "main", "Sharded": false @@ -4119,7 +4119,7 @@ "Original": "insert into unsharded(col) select col from t1", "Instructions": { "OperatorType": "Insert", - "Variant": "Unsharded", + "Variant": "Select", "Keyspace": { "Name": "main", "Sharded": false @@ -4410,8 +4410,8 @@ } }, { - "comment": "explain dml without any directive should fail", - "query": "explain format=vtexplain delete from user", + "comment": "vexplain all dml without any directive should fail", + "query": "vexplain all delete from user", "plan": "VT09008: vexplain queries/all will actually run queries" }, { @@ -4889,5 +4889,1304 @@ "comment": "Unsupported update statement with a replica target destination", "query": "update `user[-]@replica`.user_metadata set id=2", "plan": "VT09002: update statement with a replica target" + }, + { + "comment": "insert row values smaller than number of columns", + "query": "insert into user(one, two, three, four) values (1, 2, 3)", + "plan": "VT03006: column count does not match value count with the row" + }, + { + "comment": "insert row values greater than number of columns", + "query": "insert into user(one, two, three) values (1, 2, 3, 4)", + "plan": "VT03006: column count does not match value count with the row" + }, + { + "comment": "insert on duplicate key update with database qualifier", + "query": "insert into user.music(id, user_id, col) values (1, 2, 3) on duplicate key update user.music.col = 5", + "plan": { + "QueryType": "INSERT", + "Original": "insert into user.music(id, user_id, col) values (1, 2, 3) on duplicate key update user.music.col = 5", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "InsertIgnore": true, + "Query": "insert into music(id, user_id, col) values (:_id_0, :_user_id_0, 3) on duplicate key update music.col = 5", + "TableName": "music", + "VindexValues": { + "music_user_map": "1", + "user_index": "2" + } + }, + "TablesUsed": [ + "user.music" + ] + } + }, + { + "comment": "delete from reference table - query send to source table", + "query": "delete from user.ref_with_source where col = 1", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user.ref_with_source where col = 1", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from source_of_ref where col = 1", + "Table": "source_of_ref" + }, + "TablesUsed": [ + "main.source_of_ref" + ] + } + }, + { + "comment": "delete from reference table - no source", + "query": "delete from user.ref", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user.ref", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Reference", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from ref", + "Table": "ref" + }, + "TablesUsed": [ + "user.ref" + ] + } + }, + { + "comment": "delete by target destination with limit", + "query": "delete from `user[-]`.`user` limit 20", + "plan": { + "QueryType": "DELETE", + "Original": "delete from `user[-]`.`user` limit 20", + "Instructions": { + "OperatorType": "Delete", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly from `user` limit 20 for update", + "Query": "delete from `user` limit 20", + "Table": "user" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "delete sharded table with join with reference table", + "query": "delete u from user u join ref_with_source r on u.col = r.col", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from user u join ref_with_source r on u.col = r.col", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, ref_with_source as r where u.col = r.col for update", + "Query": "delete u from `user` as u, ref_with_source as r where u.col = r.col", + "Table": "user" + }, + "TablesUsed": [ + "user.ref_with_source", + "user.user" + ] + } + }, + { + "comment": "delete sharded table with join with another sharded table on vindex column", + "query": "delete u from user u join music m on u.id = m.user_id", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from user u join music m on u.id = m.user_id", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, music as m where u.id = m.user_id for update", + "Query": "delete u from `user` as u, music as m where u.id = m.user_id", + "Table": "user" + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "multi delete multi table", + "query": "delete user from user join user_extra on user.id = user_extra.id where user.name = 'foo'", + "plan": { + "QueryType": "DELETE", + "Original": "delete user from user join user_extra on user.id = user_extra.id where user.name = 'foo'", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "user_extra_id": 0 + }, + "TableName": "user_extra_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select user_extra.id from user_extra where 1 != 1", + "Query": "select user_extra.id from user_extra", + "Table": "user_extra" + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user` where `user`.`name` = 'foo' and `user`.id = :user_extra_id", + "Table": "`user`", + "Values": [ + ":user_extra_id" + ], + "Vindex": "user_index" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select `user`.Id, `user`.`Name`, `user`.Costly from `user` where `user`.id in ::dml_vals for update", + "Query": "delete from `user` where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "multi delete multi table with alias", + "query": "delete u from user u join music m on u.col = m.col", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from user u join music m on u.col = m.col", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "u_col": 1 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.id, u.col from `user` as u where 1 != 1", + "Query": "select u.id, u.col from `user` as u", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as m where 1 != 1", + "Query": "select 1 from music as m where m.col = :u_col", + "Table": "music" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u where u.id in ::dml_vals for update", + "Query": "delete from `user` as u where u.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "reverse the join order for delete", + "query": "delete u from music m join user u where u.col = m.col and m.foo = 42", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from music m join user u where u.col = m.col and m.foo = 42", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "m_col": 0 + }, + "TableName": "music_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select m.col from music as m where 1 != 1", + "Query": "select m.col from music as m where m.foo = 42", + "Table": "music" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.id from `user` as u where 1 != 1", + "Query": "select u.id from `user` as u where u.col = :m_col", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u where u.id in ::dml_vals for update", + "Query": "delete from `user` as u where u.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "multi table delete with join on vindex column", + "query": "delete u from user u join music m where u.id = m.user_id and m.foo = 42", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from user u join music m where u.id = m.user_id and m.foo = 42", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, music as m where m.foo = 42 and u.id = m.user_id for update", + "Query": "delete u from `user` as u, music as m where m.foo = 42 and u.id = m.user_id", + "Table": "user" + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "delete 3 way join with sharding key and primary key same", + "query": "delete u from user u join music m on u.col = m.col join user_extra ue on m.user_id = ue.user_id where ue.foo = 20 and u.col = 30 and m.bar = 40", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from user u join music m on u.col = m.col join user_extra ue on m.user_id = ue.user_id where ue.foo = 20 and u.col = 30 and m.bar = 40", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "u_col": 1 + }, + "TableName": "`user`_music, user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.id, u.col from `user` as u where 1 != 1", + "Query": "select u.id, u.col from `user` as u where u.col = 30", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as m, user_extra as ue where 1 != 1", + "Query": "select 1 from music as m, user_extra as ue where m.bar = 40 and m.col = :u_col and ue.foo = 20 and m.user_id = ue.user_id", + "Table": "music, user_extra" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u where u.id in ::dml_vals for update", + "Query": "delete from `user` as u where u.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "delete 3 way join with sharding key and primary key different", + "query": "delete m from user u join music m on u.col = m.col join user_extra ue on m.user_id = ue.user_id where ue.foo = 20 and u.col = 30 and m.bar = 40", + "plan": { + "QueryType": "DELETE", + "Original": "delete m from user u join music m on u.col = m.col join user_extra ue on m.user_id = ue.user_id where ue.foo = 20 and u.col = 30 and m.bar = 40", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "u_col": 0 + }, + "TableName": "`user`_music, user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.col from `user` as u where 1 != 1", + "Query": "select u.col from `user` as u where u.col = 30", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select m.id from music as m, user_extra as ue where 1 != 1", + "Query": "select m.id from music as m, user_extra as ue where m.bar = 40 and m.col = :u_col and ue.foo = 20 and m.user_id = ue.user_id", + "Table": "music, user_extra" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select m.user_id, m.id from music as m where m.id in ::dml_vals for update", + "Query": "delete from music as m where m.id in ::dml_vals", + "Table": "music", + "Values": [ + "::dml_vals" + ], + "Vindex": "music_user_map" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "sharded delete with limit clause", + "query": "delete from user limit 10", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user limit 10", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user` limit :__upper_limit", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where `user`.id in ::dml_vals for update", + "Query": "delete from `user` where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "sharded delete with order by and limit clause", + "query": "delete from user order by name, col limit 5", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user order by name, col limit 5", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "5", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id, `name`, weight_string(`name`), col from `user` where 1 != 1", + "OrderBy": "(1|2) ASC, 3 ASC", + "Query": "select `user`.id, `name`, weight_string(`name`), col from `user` order by `name` asc, col asc limit :__upper_limit", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Delete", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where `user`.id in ::dml_vals for update", + "Query": "delete from `user` where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "update with limit clause", + "query": "update user set val = 1 where (name = 'foo' or id = 1) limit 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update user set val = 1 where (name = 'foo' or id = 1) limit 1", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user` where `name` = 'foo' or id = 1 limit :__upper_limit lock in share mode", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Update", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update `user` set val = 1 where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "update a vindex column with limit", + "query": "update user set name = 'abc' where id > 10 limit 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update user set name = 'abc' where id > 10 limit 1", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user` where id > 10 limit :__upper_limit lock in share mode", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Update", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "ChangedVindexValues": [ + "name_user_map:3" + ], + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly, `name` = 'abc' from `user` where `user`.id in ::dml_vals for update", + "Query": "update `user` set `name` = 'abc' where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "update with multi table join with single target", + "query": "update user as u, user_extra as ue set u.name = 'foo' where u.id = ue.id", + "plan": { + "QueryType": "UPDATE", + "Original": "update user as u, user_extra as ue set u.name = 'foo' where u.id = ue.id", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "ue_id": 0 + }, + "TableName": "user_extra_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select ue.id from user_extra as ue where 1 != 1", + "Query": "select ue.id from user_extra as ue lock in share mode", + "Table": "user_extra" + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.id from `user` as u where 1 != 1", + "Query": "select u.id from `user` as u where u.id = :ue_id lock in share mode", + "Table": "`user`", + "Values": [ + ":ue_id" + ], + "Vindex": "user_index" + } + ] + }, + { + "OperatorType": "Update", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "ChangedVindexValues": [ + "name_user_map:3" + ], + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly, u.`name` = 'foo' from `user` as u where u.id in ::dml_vals for update", + "Query": "update `user` as u set u.`name` = 'foo' where u.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "update with multi table join with single target modifying lookup vindex", + "query": "update user join user_extra on user.id = user_extra.id set user.name = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update user join user_extra on user.id = user_extra.id set user.name = 'foo'", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "user_extra_id": 0 + }, + "TableName": "user_extra_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select user_extra.id from user_extra where 1 != 1", + "Query": "select user_extra.id from user_extra lock in share mode", + "Table": "user_extra" + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user` where `user`.id = :user_extra_id lock in share mode", + "Table": "`user`", + "Values": [ + ":user_extra_id" + ], + "Vindex": "user_index" + } + ] + }, + { + "OperatorType": "Update", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "ChangedVindexValues": [ + "name_user_map:3" + ], + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly, `user`.`name` = 'foo' from `user` where `user`.id in ::dml_vals for update", + "Query": "update `user` set `user`.`name` = 'foo' where `user`.id in ::dml_vals", + "Table": "user", + "Values": [ + "::dml_vals" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "update with multi table reference with multi target update on a derived table", + "query": "update ignore (select foo, col, bar from user) u, music m set u.foo = 21, u.bar = 'abc' where u.col = m.col", + "plan": "VT03031: the target table (select foo, col, bar from `user`) as u of the UPDATE is not updatable" + }, + { + "comment": "update with derived table", + "query": "update (select id from user) as u set id = 4", + "plan": "VT03031: the target table (select id from `user`) as u of the UPDATE is not updatable" + }, + { + "comment": "Delete with routed table on music", + "query": "delete from second_user.bar", + "plan": { + "QueryType": "DELETE", + "Original": "delete from second_user.bar", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select user_id, id from music as bar for update", + "Query": "delete from music as bar", + "Table": "music" + }, + "TablesUsed": [ + "user.music" + ] + } + }, + { + "comment": "Update with routed table on music", + "query": "update second_user.bar set col = 23", + "plan": { + "QueryType": "UPDATE", + "Original": "update second_user.bar set col = 23", + "Instructions": { + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update music as bar set col = 23", + "Table": "music" + }, + "TablesUsed": [ + "user.music" + ] + } + }, + { + "comment": "Insert with routed table on music", + "query": "insert into second_user.bar(id) values (2)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into second_user.bar(id) values (2)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert into music(id, user_id) values (:_id_0, :_user_id_0)", + "TableName": "music", + "VindexValues": { + "music_user_map": "2", + "user_index": "null" + } + }, + "TablesUsed": [ + "user.music" + ] + } + }, + { + "comment": "sharded subquery in sharded delete", + "query": "delete from user where id = (select id from music where user_id = 1)", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user where id = (select id from music where user_id = 1)", + "Instructions": { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from music where 1 != 1", + "Query": "select id from music where user_id = 1", + "Table": "music", + "Values": [ + "1" + ], + "Vindex": "user_index" + }, + { + "InputName": "Outer", + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where id = :__sq1 for update", + "Query": "delete from `user` where id = :__sq1", + "Table": "user", + "Values": [ + ":__sq1" + ], + "Vindex": "user_index" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "unsharded subquery in sharded delete", + "query": "delete from user where col = (select id from unsharded)", + "plan": { + "QueryType": "DELETE", + "Original": "delete from user where col = (select id from unsharded)", + "Instructions": { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select id from unsharded where 1 != 1", + "Query": "select id from unsharded", + "Table": "unsharded" + }, + { + "InputName": "Outer", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "KsidLength": 1, + "KsidVindex": "user_index", + "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where col = :__sq1 for update", + "Query": "delete from `user` where col = :__sq1", + "Table": "user" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "sharded subquery in unsharded delete", + "query": "delete from unsharded where col = (select id from user)", + "plan": { + "QueryType": "DELETE", + "Original": "delete from unsharded where col = (select id from user)", + "Instructions": { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from `user` where 1 != 1", + "Query": "select id from `user`", + "Table": "`user`" + }, + { + "InputName": "Outer", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from unsharded where col = :__sq1", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "sharded subquery in unsharded subquery in unsharded delete", + "query": "delete from unsharded where col = (select id from unsharded where id = (select id from user))", + "plan": { + "QueryType": "DELETE", + "Original": "delete from unsharded where col = (select id from unsharded where id = (select id from user))", + "Instructions": { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq2" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from `user` where 1 != 1", + "Query": "select id from `user`", + "Table": "`user`" + }, + { + "InputName": "Outer", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select id from unsharded where 1 != 1", + "Query": "select id from unsharded where id = :__sq2", + "Table": "unsharded" + } + ] + }, + { + "InputName": "Outer", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from unsharded where col = :__sq1", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "sharded join unsharded subquery in unsharded delete", + "query": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)", + "plan": { + "QueryType": "DELETE", + "Original": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)", + "Instructions": { + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], + "Inputs": [ + { + "InputName": "SubQuery", + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "unsharded_id": 0 + }, + "TableName": "unsharded_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select unsharded.id from unsharded where 1 != 1", + "Query": "select unsharded.id from unsharded", + "Table": "unsharded" + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from `user` where 1 != 1", + "Query": "select id from `user` where `user`.id = :unsharded_id", + "Table": "`user`", + "Values": [ + ":unsharded_id" + ], + "Vindex": "user_index" + } + ] + }, + { + "InputName": "Outer", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from unsharded where col = :__sq1", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.json b/go/vt/vtgate/planbuilder/testdata/filter_cases.json index 83e675d89f6..4353f31fd48 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.json @@ -1966,7 +1966,7 @@ "Sharded": true }, "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user` where not :__sq_has_values and id not in ::__sq1", + "Query": "select id from `user` where not :__sq_has_values or id not in ::__sq1", "Table": "`user`" } ] @@ -2503,7 +2503,7 @@ "Sharded": true }, "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user` where not :__sq_has_values and id not in ::__sq1 and :__sq_has_values1 and id in ::__vals", + "Query": "select id from `user` where (not :__sq_has_values or id not in ::__sq1) and :__sq_has_values1 and id in ::__vals", "Table": "`user`", "Values": [ "::__sq2" @@ -2950,7 +2950,7 @@ "Sharded": true }, "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user` where id = 5 and id in (select user_extra.col from user_extra where user_extra.user_id = 5) and not :__sq_has_values and id not in ::__sq1", + "Query": "select id from `user` where id = 5 and id in (select user_extra.col from user_extra where user_extra.user_id = 5) and (not :__sq_has_values or id not in ::__sq1)", "Table": "`user`", "Values": [ "5" @@ -4026,6 +4026,30 @@ ] } }, + { + "comment": "Order by aliases are expanded", + "query": "select a+2 as a from user order by a", + "plan": { + "QueryType": "SELECT", + "Original": "select a+2 as a from user order by a", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select a + 2 as a, weight_string(a + 2) from `user` where 1 != 1", + "OrderBy": "(0|1) ASC", + "Query": "select a + 2 as a, weight_string(a + 2) from `user` order by a + 2 asc", + "ResultColumns": 1, + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, { "comment": "HAVING predicates that use table columns are safe to rewrite if we can move them to the WHERE clause", "query": "select user.col + 2 as a from user having a = 42", @@ -4110,7 +4134,7 @@ "Sharded": true }, "FieldQuery": "select id from `user` where 1 != 1", - "Query": "select id from `user` where id = 5 and `name` = 'foo' or id = 12 and `name` = 'bar'", + "Query": "select id from `user` where id in ::__vals and (id = 5 or `name` = 'bar') and (`name` = 'foo' or id = 12) and `name` in ('foo', 'bar')", "Table": "`user`", "Values": [ "(5, 12)" @@ -4402,5 +4426,164 @@ "user.user_extra" ] } + }, + { + "comment": "list args: single column vindex", + "query": "select 1 from user where (id, col) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from user where (id, col) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1", + "Query": "select 1 from `user` where (id, col) in ::vals", + "Table": "`user`", + "Values": [ + "vals:0" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "list args: single column vindex on non-zero offset", + "query": "select 1 from user where (col, id) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from user where (col, id) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1", + "Query": "select 1 from `user` where (col, id) in ::vals", + "Table": "`user`", + "Values": [ + "vals:1" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "list args: multi column vindex", + "query": "select 1 from multicol_tbl where (cola, colb) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from multicol_tbl where (cola, colb) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from multicol_tbl where 1 != 1", + "Query": "select 1 from multicol_tbl where (cola, colb) in ::vals", + "Table": "multicol_tbl", + "Values": [ + "vals:0", + "vals:1" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "user.multicol_tbl" + ] + } + }, + { + "comment": "list args: multi column vindex - subshard", + "query": "select 1 from multicol_tbl where (cola) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from multicol_tbl where (cola) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from multicol_tbl where 1 != 1", + "Query": "select 1 from multicol_tbl where cola in ::__vals0", + "Table": "multicol_tbl", + "Values": [ + "::vals" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "user.multicol_tbl" + ] + } + }, + { + "comment": "list args: multi column vindex - more columns", + "query": "select 1 from multicol_tbl where (cola, colx, colb) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from multicol_tbl where (cola, colx, colb) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from multicol_tbl where 1 != 1", + "Query": "select 1 from multicol_tbl where (cola, colx, colb) in ::vals", + "Table": "multicol_tbl", + "Values": [ + "vals:0", + "vals:2" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "user.multicol_tbl" + ] + } + }, + { + "comment": "list args: multi column vindex - columns rearranged", + "query": "select 1 from multicol_tbl where (colb, colx, cola) in ::vals", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from multicol_tbl where (colb, colx, cola) in ::vals", + "Instructions": { + "OperatorType": "Route", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from multicol_tbl where 1 != 1", + "Query": "select 1 from multicol_tbl where (colb, colx, cola) in ::vals", + "Table": "multicol_tbl", + "Values": [ + "vals:2", + "vals:0" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "user.multicol_tbl" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/flush_cases.json b/go/vt/vtgate/planbuilder/testdata/flush_cases.json index 8298c6de649..26a1f218c8d 100644 --- a/go/vt/vtgate/planbuilder/testdata/flush_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/flush_cases.json @@ -33,7 +33,8 @@ "Sharded": false }, "TargetDestination": "AllShards()", - "Query": "flush local tables with read lock" + "Query": "flush local tables with read lock", + "ReservedConnectionNeeded": true } } }, @@ -53,5 +54,42 @@ "Query": "flush local hosts, logs" } } + }, + { + "comment": "Flush statement with multiple tables in different keyspace with read lock", + "query": "flush tables user.music, main.unsharded with read lock", + "plan": { + "QueryType": "FLUSH", + "Original": "flush tables user.music, main.unsharded with read lock", + "Instructions": { + "OperatorType": "Concatenate", + "Inputs": [ + { + "OperatorType": "Send", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "TargetDestination": "AllShards()", + "Query": "flush tables unsharded with read lock", + "ReservedConnectionNeeded": true + }, + { + "OperatorType": "Send", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetDestination": "AllShards()", + "Query": "flush tables music with read lock", + "ReservedConnectionNeeded": true + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.music" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/flush_cases_no_default_keyspace.json b/go/vt/vtgate/planbuilder/testdata/flush_cases_no_default_keyspace.json index a3370a74f5d..7afd090ba21 100644 --- a/go/vt/vtgate/planbuilder/testdata/flush_cases_no_default_keyspace.json +++ b/go/vt/vtgate/planbuilder/testdata/flush_cases_no_default_keyspace.json @@ -15,7 +15,8 @@ "Sharded": false }, "TargetDestination": "AllShards()", - "Query": "flush local tables unsharded_a with read lock" + "Query": "flush local tables unsharded_a with read lock", + "ReservedConnectionNeeded": true }, { "OperatorType": "Send", @@ -24,7 +25,8 @@ "Sharded": true }, "TargetDestination": "AllShards()", - "Query": "flush local tables `user`, user_extra with read lock" + "Query": "flush local tables `user`, user_extra with read lock", + "ReservedConnectionNeeded": true } ] }, @@ -105,7 +107,8 @@ "Sharded": false }, "TargetDestination": "AllShards()", - "Query": "flush tables a with read lock" + "Query": "flush tables a with read lock", + "ReservedConnectionNeeded": true }, "TablesUsed": [ "main.a" @@ -128,7 +131,8 @@ "Sharded": false }, "TargetDestination": "AllShards()", - "Query": "flush local tables unsharded_a with read lock" + "Query": "flush local tables unsharded_a with read lock", + "ReservedConnectionNeeded": true }, { "OperatorType": "Send", @@ -137,7 +141,8 @@ "Sharded": false }, "TargetDestination": "AllShards()", - "Query": "flush local tables unsharded_tab with read lock" + "Query": "flush local tables unsharded_tab with read lock", + "ReservedConnectionNeeded": true }, { "OperatorType": "Send", @@ -146,7 +151,8 @@ "Sharded": true }, "TargetDestination": "AllShards()", - "Query": "flush local tables `user`, user_extra with read lock" + "Query": "flush local tables `user`, user_extra with read lock", + "ReservedConnectionNeeded": true } ] }, diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json index 065691d2356..c8f059b7b88 100644 --- a/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json @@ -81,8 +81,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select colb, cola, y, colc, x from multicol_tbl1 where 1 != 1", - "Query": "select colb, cola, y, colc, x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", "Table": "multicol_tbl1", "Values": [ "1", @@ -94,7 +94,7 @@ { "InputName": "CascadeChild-1", "OperatorType": "Delete", - "Variant": "Scatter", + "Variant": "MultiEqual", "Keyspace": { "Name": "sharded_fk_allow", "Sharded": true @@ -109,7 +109,13 @@ 4 ], "Query": "delete from multicol_tbl2 where (colb, cola, x, colc, y) in ::fkc_vals", - "Table": "multicol_tbl2" + "Table": "multicol_tbl2", + "Values": [ + "fkc_vals:1", + "fkc_vals:0", + "fkc_vals:3" + ], + "Vindex": "multicolIdx" }, { "InputName": "Parent", @@ -154,14 +160,14 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { "InputName": "CascadeChild-1", "OperatorType": "Delete", - "Variant": "Scatter", + "Variant": "MultiEqual", "Keyspace": { "Name": "sharded_fk_allow", "Sharded": true @@ -172,7 +178,11 @@ 0 ], "Query": "delete from tbl4 where (col4) in ::fkc_vals", - "Table": "tbl4" + "Table": "tbl4", + "Values": [ + "fkc_vals:0" + ], + "Vindex": "hash_vin" }, { "InputName": "CascadeChild-2", @@ -232,8 +242,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where col9 = 5 for update", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where col9 = 5 for update nowait", "Table": "u_tbl9" }, { @@ -311,8 +321,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -328,7 +338,7 @@ "Cols": [ 0 ], - "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (u_tbl3.col3) not in (('bar'))", + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (col3) not in ((cast('bar' as CHAR)))", "Table": "u_tbl3" }, { @@ -422,8 +432,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select t5col5 from tbl5 where 1 != 1", - "Query": "select t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -439,7 +449,7 @@ "Cols": [ 0 ], - "Query": "update tbl4 set t4col4 = null where (t4col4) in ::fkc_vals and (tbl4.t4col4) not in (('foo'))", + "Query": "update tbl4 set t4col4 = null where (t4col4) in ::fkc_vals and (t4col4) not in (('foo'))", "Table": "tbl4" }, { @@ -527,7 +537,7 @@ "Sharded": true }, "FieldQuery": "select 1 from tbl10 where 1 != 1", - "Query": "select 1 from tbl10 lock in share mode", + "Query": "select 1 from tbl10 where not (tbl10.col) <=> ('foo') for share", "Table": "tbl10" }, { @@ -538,7 +548,7 @@ "Sharded": true }, "FieldQuery": "select tbl3.col from tbl3 where 1 != 1", - "Query": "select tbl3.col from tbl3 where tbl3.col = 'foo' lock in share mode", + "Query": "select tbl3.col from tbl3 where tbl3.col = 'foo' for share", "Table": "tbl3" } ] @@ -591,8 +601,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col9 from tbl9 where 1 != 1", - "Query": "select col9 from tbl9 where col9 = 34 for update", + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", "Table": "tbl9", "Values": [ "34" @@ -656,8 +666,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1, col1 from u_tbl1 where 1 != 1", - "Query": "select col1, col1 from u_tbl1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 for update", "Table": "u_tbl1" }, { @@ -676,8 +686,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -693,7 +703,7 @@ "Cols": [ 0 ], - "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (u_tbl3.col3) not in (('foo'))", + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast('foo' as CHAR)))", "Table": "u_tbl3" }, { @@ -715,7 +725,7 @@ "OperatorType": "FkCascade", "BvName": "fkc_vals2", "Cols": [ - 1 + 0 ], "Inputs": [ { @@ -726,8 +736,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (u_tbl9.col9) not in (('foo')) for update", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -755,7 +765,7 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (u_tbl9.col9) not in (('foo'))", + "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR)))", "Table": "u_tbl9" } ] @@ -789,78 +799,94 @@ "plan": "VT12001: unsupported: update with limit with foreign key constraints" }, { - "comment": "update in a table with non-literal value - set null fail due to child update where condition", + "comment": "update in a table with non-literal value - set null", "query": "update u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", - "plan": "VT12001: unsupported: update expression with non-literal values with foreign key constraints" - }, - { - "comment": "update in a table with non-literal value - with cascade fail as the cascade value is not known", - "query": "update u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", - "plan": "VT12001: unsupported: update expression with non-literal values with foreign key constraints" - }, - { - "comment": "update in a table with set null, non-literal value on non-foreign key column - allowed", - "query": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", "plan": { "QueryType": "UPDATE", - "Original": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", + "Original": "update u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", "Instructions": { - "OperatorType": "FkCascade", + "OperatorType": "FKVerify", "Inputs": [ { - "InputName": "Selection", + "InputName": "VerifyParent-1", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", - "Table": "u_tbl2" - }, - { - "InputName": "CascadeChild-1", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "BvName": "fkc_vals", - "Cols": [ - 0 - ], - "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (u_tbl3.col3) not in ((2))", - "Table": "u_tbl3" + "FieldQuery": "select 1 from u_tbl2 left join u_tbl1 on u_tbl1.col1 = cast(u_tbl2.col1 + 'bar' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl2 left join u_tbl1 on u_tbl1.col1 = cast(u_tbl2.col1 + 'bar' as CHAR) where u_tbl1.col1 is null and cast(u_tbl2.col1 + 'bar' as CHAR) is not null and not (u_tbl2.col2) <=> (cast(u_tbl2.col1 + 'bar' as CHAR)) and u_tbl2.id = 1 limit 1 for share", + "Table": "u_tbl1, u_tbl2" }, { - "InputName": "Parent", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", - "Table": "u_tbl2" + "InputName": "PostVerify", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd" + } + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (:fkc_upd is null or (col3) not in ((:fkc_upd)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", + "Table": "u_tbl2" + } + ] } ] }, "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", "unsharded_fk_allow.u_tbl2", "unsharded_fk_allow.u_tbl3" ] } }, { - "comment": "update in a table with cascade, non-literal value on non-foreign key column - allowed", - "query": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "comment": "update in a table with non-literal value - with cascade", + "query": "update u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", "plan": { "QueryType": "UPDATE", - "Original": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "Original": "update u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", "Instructions": { "OperatorType": "FkCascade", "Inputs": [ @@ -872,8 +898,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1, col1 from u_tbl1 where 1 != 1", - "Query": "select col1, col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -883,6 +909,13 @@ "Cols": [ 0 ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd" + } + ], "Inputs": [ { "InputName": "Selection", @@ -892,8 +925,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -909,7 +942,7 @@ "Cols": [ 0 ], - "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (u_tbl3.col3) not in ((2))", + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (cast(:fkc_upd as CHAR) is null or (col3) not in ((cast(:fkc_upd as CHAR))))", "Table": "u_tbl3" }, { @@ -921,7 +954,7 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 2 where (col2) in ::fkc_vals", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = :fkc_upd where (col2) in ::fkc_vals", "Table": "u_tbl2" } ] @@ -931,7 +964,14 @@ "OperatorType": "FkCascade", "BvName": "fkc_vals2", "Cols": [ - 1 + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd1" + } ], "Inputs": [ { @@ -942,8 +982,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (u_tbl9.col9) not in ((2)) for update", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", "Table": "u_tbl9" }, { @@ -971,7 +1011,7 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (u_tbl9.col9) not in ((2))", + "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1)))", "Table": "u_tbl9" } ] @@ -985,7 +1025,7 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", "Table": "u_tbl1" } ] @@ -1000,106 +1040,11 @@ } }, { - "comment": "update in a table with a child table having SET DEFAULT constraint - disallowed", - "query": "update tbl20 set col2 = 'bar'", - "plan": "VT09016: Cannot delete or update a parent row: a foreign key constraint fails" - }, - { - "comment": "delete in a table with limit - disallowed", - "query": "delete from u_tbl2 limit 2", - "plan": "VT12001: unsupported: foreign keys management at vitess with limit" - }, - { - "comment": "update with fk on cross-shard with a where condition on non-literal value - disallowed", - "query": "update tbl3 set coly = colx + 10 where coly = 10", - "plan": "VT12001: unsupported: update expression with non-literal values with foreign key constraints" - }, - { - "comment": "update with fk on cross-shard with a where condition", - "query": "update tbl3 set coly = 20 where coly = 10", - "plan": { - "QueryType": "UPDATE", - "Original": "update tbl3 set coly = 20 where coly = 10", - "Instructions": { - "OperatorType": "FKVerify", - "Inputs": [ - { - "InputName": "VerifyParent-1", - "OperatorType": "Limit", - "Count": "1", - "Inputs": [ - { - "OperatorType": "Projection", - "Expressions": [ - "1 as 1" - ], - "Inputs": [ - { - "OperatorType": "Filter", - "Predicate": "tbl1.t1col1 is null", - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "LeftJoin", - "JoinColumnIndexes": "R:0,R:0", - "TableName": "tbl3_tbl1", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true - }, - "FieldQuery": "select 1 from tbl3 where 1 != 1", - "Query": "select 1 from tbl3 where tbl3.coly = 10 lock in share mode", - "Table": "tbl3" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true - }, - "FieldQuery": "select tbl1.t1col1 from tbl1 where 1 != 1", - "Query": "select tbl1.t1col1 from tbl1 where tbl1.t1col1 = 20 lock in share mode", - "Table": "tbl1" - } - ] - } - ] - } - ] - } - ] - }, - { - "InputName": "PostVerify", - "OperatorType": "Update", - "Variant": "Scatter", - "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true - }, - "TargetTabletType": "PRIMARY", - "Query": "update tbl3 set coly = 20 where tbl3.coly = 10", - "Table": "tbl3" - } - ] - }, - "TablesUsed": [ - "sharded_fk_allow.tbl1", - "sharded_fk_allow.tbl3" - ] - } - }, - { - "comment": "Update in a table with shard-scoped foreign keys with cascade that requires a validation of a different parent foreign key", - "query": "update u_tbl6 set col6 = 'foo'", + "comment": "update in a table with set null, non-literal value on non-foreign key column", + "query": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", "plan": { "QueryType": "UPDATE", - "Original": "update u_tbl6 set col6 = 'foo'", + "Original": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", "Instructions": { "OperatorType": "FkCascade", "Inputs": [ @@ -1111,43 +1056,25 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col6 from u_tbl6 where 1 != 1", - "Query": "select col6 from u_tbl6 for update", - "Table": "u_tbl6" + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", + "Table": "u_tbl2" }, { "InputName": "CascadeChild-1", - "OperatorType": "FKVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", "BvName": "fkc_vals", "Cols": [ 0 ], - "Inputs": [ - { - "InputName": "VerifyParent-1", - "OperatorType": "Route", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "FieldQuery": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = 'foo' where 1 != 1", - "Query": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = 'foo' where (u_tbl8.col8) in ::fkc_vals and u_tbl9.col9 is null limit 1 lock in share mode", - "Table": "u_tbl8, u_tbl9" - }, - { - "InputName": "PostVerify", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl8 set col8 = 'foo' where (u_tbl8.col8) in ::fkc_vals", - "Table": "u_tbl8" - } - ] + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (col3) not in ((cast(2 as CHAR)))", + "Table": "u_tbl3" }, { "InputName": "Parent", @@ -1158,24 +1085,23 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_tbl6 set col6 = 'foo'", - "Table": "u_tbl6" + "Query": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", + "Table": "u_tbl2" } ] }, "TablesUsed": [ - "unsharded_fk_allow.u_tbl6", - "unsharded_fk_allow.u_tbl8", - "unsharded_fk_allow.u_tbl9" + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" ] } }, { - "comment": "Update that cascades and requires parent fk and restrict child fk verification", - "query": "update u_tbl7 set col7 = 'foo'", + "comment": "update in a table with cascade, non-literal value on non-foreign key column", + "query": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", "plan": { "QueryType": "UPDATE", - "Original": "update u_tbl7 set col7 = 'foo'", + "Original": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", "Instructions": { "OperatorType": "FkCascade", "Inputs": [ @@ -1187,44 +1113,48 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", - "Table": "u_tbl7" + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" }, { "InputName": "CascadeChild-1", - "OperatorType": "FKVerify", + "OperatorType": "FkCascade", "BvName": "fkc_vals", "Cols": [ 0 ], "Inputs": [ { - "InputName": "VerifyParent-1", + "InputName": "Selection", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = 'foo' where 1 != 1", - "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = 'foo' where (u_tbl4.col4) in ::fkc_vals and u_tbl3.col3 is null limit 1 lock in share mode", - "Table": "u_tbl3, u_tbl4" + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" }, { - "InputName": "VerifyChild-2", - "OperatorType": "Route", + "InputName": "CascadeChild-1", + "OperatorType": "Update", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", - "Query": "select 1 from u_tbl4, u_tbl9 where (u_tbl4.col4) in ::fkc_vals and (u_tbl9.col9) not in (('foo')) and u_tbl4.col4 = u_tbl9.col9 limit 1 lock in share mode", - "Table": "u_tbl4, u_tbl9" + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast(2 as CHAR)))", + "Table": "u_tbl3" }, { - "InputName": "PostVerify", + "InputName": "Parent", "OperatorType": "Update", "Variant": "Unsharded", "Keyspace": { @@ -1232,8 +1162,58 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = 'foo' where (u_tbl4.col4) in ::fkc_vals", - "Table": "u_tbl4" + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR)))", + "Table": "u_tbl9" } ] }, @@ -1246,206 +1226,2324 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_tbl7 set col7 = 'foo'", - "Table": "u_tbl7" + "Query": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "Table": "u_tbl1" } ] }, "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", "unsharded_fk_allow.u_tbl3", - "unsharded_fk_allow.u_tbl4", - "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl8", "unsharded_fk_allow.u_tbl9" ] } }, { - "comment": "Update that cascades and requires parent fk and restrict child fk verification - bindVariable", - "query": "update u_tbl7 set col7 = :v1", + "comment": "update in a table with a child table having SET DEFAULT constraint - disallowed", + "query": "update tbl20 set col2 = 'bar'", + "plan": "VT09016: Cannot delete or update a parent row: a foreign key constraint fails" + }, + { + "comment": "delete in a table with limit", + "query": "delete from u_tbl2 limit 2", "plan": { - "QueryType": "UPDATE", - "Original": "update u_tbl7 set col7 = :v1", + "QueryType": "DELETE", + "Original": "delete from u_tbl2 limit 2", "Instructions": { - "OperatorType": "FkCascade", + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], "Inputs": [ { - "InputName": "Selection", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", - "Table": "u_tbl7" + "FieldQuery": "select u_tbl2.id from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.id from u_tbl2 limit 2 for update", + "Table": "u_tbl2" }, { - "InputName": "CascadeChild-1", - "OperatorType": "FKVerify", - "BvName": "fkc_vals", - "Cols": [ - 0 - ], + "OperatorType": "FkCascade", "Inputs": [ { - "InputName": "VerifyParent-1", + "InputName": "Selection", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = :v1 where 1 != 1", - "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = :v1 where (u_tbl4.col4) in ::fkc_vals and u_tbl3.col3 is null limit 1 lock in share mode", - "Table": "u_tbl3, u_tbl4" + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where u_tbl2.id in ::dml_vals for update", + "Table": "u_tbl2" }, { - "InputName": "VerifyChild-2", + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl2 where u_tbl2.id in ::dml_vals", + "Table": "u_tbl2" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update with fk on cross-shard with a update condition on non-literal value", + "query": "update tbl3 set coly = colx + 10 where coly = 10", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl3 set coly = colx + 10 where coly = 10", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Filter", + "Predicate": "tbl1.t1col1 is null", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0,R:0", + "JoinVars": { + "tbl3_colx": 0 + }, + "TableName": "tbl3_tbl1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl3.colx from tbl3 where 1 != 1", + "Query": "select tbl3.colx from tbl3 where tbl3.colx + 10 is not null and not (tbl3.coly) <=> (tbl3.colx + 10) and tbl3.coly = 10 for share", + "Table": "tbl3" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl1.t1col1 from tbl1 where 1 != 1", + "Query": "select tbl1.t1col1 from tbl1 where tbl1.t1col1 = :tbl3_colx + 10 for share", + "Table": "tbl1" + } + ] + } + ] + } + ] + } + ] + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ tbl3 set coly = colx + 10 where coly = 10", + "Table": "tbl3" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1", + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "update with fk on cross-shard with a where condition", + "query": "update tbl3 set coly = 20 where coly = 10", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl3 set coly = 20 where coly = 10", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Filter", + "Predicate": "tbl1.t1col1 is null", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0,R:0", + "TableName": "tbl3_tbl1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select 1 from tbl3 where 1 != 1", + "Query": "select 1 from tbl3 where not (tbl3.coly) <=> (20) and tbl3.coly = 10 for share", + "Table": "tbl3" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl1.t1col1 from tbl1 where 1 != 1", + "Query": "select tbl1.t1col1 from tbl1 where tbl1.t1col1 = 20 for share", + "Table": "tbl1" + } + ] + } + ] + } + ] + } + ] + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update tbl3 set coly = 20 where coly = 10", + "Table": "tbl3" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1", + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "Update in a table with shard-scoped foreign keys with cascade that requires a validation of a different parent foreign key", + "query": "update u_tbl6 set col6 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl6 set col6 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.col6 from u_tbl6 where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 for update", + "Table": "u_tbl6" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = cast('foo' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = cast('foo' as CHAR) where u_tbl9.col9 is null and cast('foo' as CHAR) is not null and not (u_tbl8.col8) <=> (cast('foo' as CHAR)) and (u_tbl8.col8) in ::fkc_vals limit 1 for share nowait", + "Table": "u_tbl8, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl8 set col8 = 'foo' where (col8) in ::fkc_vals", + "Table": "u_tbl8" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl6 set col6 = 'foo'", + "Table": "u_tbl6" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl6", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Update that cascades and requires parent fk and restrict child fk verification", + "query": "update u_tbl7 set col7 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl7 set col7 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", + "Table": "u_tbl7" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast('foo' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast('foo' as CHAR) where u_tbl3.col3 is null and cast('foo' as CHAR) is not null and not (u_tbl4.col4) <=> (cast('foo' as CHAR)) and (u_tbl4.col4) in ::fkc_vals limit 1 for share", + "Table": "u_tbl3, u_tbl4" + }, + { + "InputName": "VerifyChild-2", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", + "Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (cast('foo' as CHAR) is null or (u_tbl9.col9) not in ((cast('foo' as CHAR)))) limit 1 for share", + "Table": "u_tbl4, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = 'foo' where (col4) in ::fkc_vals", + "Table": "u_tbl4" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl7 set col7 = 'foo'", + "Table": "u_tbl7" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl4", + "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Update that cascades and requires parent fk and restrict child fk verification - bindVariable", + "query": "update u_tbl7 set col7 = :v1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl7 set col7 = :v1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", + "Table": "u_tbl7" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:v1 as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:v1 as CHAR) where u_tbl3.col3 is null and cast(:v1 as CHAR) is not null and not (u_tbl4.col4) <=> (cast(:v1 as CHAR)) and (u_tbl4.col4) in ::fkc_vals limit 1 for share", + "Table": "u_tbl3, u_tbl4" + }, + { + "InputName": "VerifyChild-2", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", + "Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (cast(:v1 as CHAR) is null or (u_tbl9.col9) not in ((cast(:v1 as CHAR)))) limit 1 for share", + "Table": "u_tbl4, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = :v1 where (col4) in ::fkc_vals", + "Table": "u_tbl4" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl7 set col7 = :v1", + "Table": "u_tbl7" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl4", + "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Insert with on duplicate key update - foreign key with new value", + "query": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = 5", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = 5", + "Instructions": { + "OperatorType": "Upsert", + "TargetTabletType": "PRIMARY", + "Inputs": [ + { + "InputName": "Insert-1", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl1(id, col1) values (1, 3)", + "TableName": "u_tbl1" + }, + { + "InputName": "Update-1", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast(5 as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 5 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl1 set col1 = 5 where id = 1", + "Table": "u_tbl1" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Insert with on duplicate key update - foreign keys not on update column - allowed", + "query": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "insert into u_tbl1(id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "TableName": "u_tbl1" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1" + ] + } + }, + { + "comment": "Insert with unsharded table having fk reference in sharded table", + "query": "insert into u_tbl (id, col) values (1, 2)", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "replace into with table having primary key", + "query": "replace into u_tbl1 (id, col1) values (1, 2)", + "plan": { + "QueryType": "INSERT", + "Original": "replace into u_tbl1 (id, col1) values (1, 2)", + "Instructions": { + "OperatorType": "Sequential", + "Inputs": [ + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where (id) in ((1)) for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl1 where (id) in ((1))", + "Table": "u_tbl1" + } + ] + }, + { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl1(id, col1) values (1, 2)", + "TableName": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update on a multicol foreign key that set nulls and then cascades", + "query": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = 3 for update", + "Table": "u_multicol_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0, + 1 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2))", + "Table": "u_multicol_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Table": "u_multicol_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "update on a multicol foreign key that set nulls and then cascades - bindVariables", + "query": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = :v3 for update", + "Table": "u_multicol_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0, + 1 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2))))", + "Table": "u_multicol_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "Table": "u_multicol_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "Cascaded delete run from prepared statement", + "query": "execute prep_delete using @foo", + "plan": { + "QueryType": "EXECUTE", + "Original": "execute prep_delete using @foo", + "Instructions": { + "OperatorType": "EXECUTE", + "Parameters": [ + "foo" + ], + "Inputs": [ + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 where id = :v1 for update", + "Table": "tbl5" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "delete from tbl4 where (col4) in ::fkc_vals", + "Table": "tbl4", + "Values": [ + "fkc_vals:0" + ], + "Vindex": "hash_vin" + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 1 + ], + "Query": "delete from tbl4 where (t4col4) in ::fkc_vals1", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from tbl5 where id = :v1", + "Table": "tbl5" + } + ] + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl5" + ] + } + }, + { + "comment": "foreign key column updated by using a column which is also getting updated", + "query": "update u_tbl1 set foo = 100, col1 = baz + 1 + foo where bar = 42", + "plan": "VT12001: unsupported: foo column referenced in foreign key column col1 is itself updated" + }, + { + "comment": "foreign key column updated by using a column which is also getting updated - self reference column is allowed", + "query": "update u_tbl7 set foo = 100, col7 = baz + 1 + col7 where bar = 42", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl7 set foo = 100, col7 = baz + 1 + col7 where bar = 42", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl7.col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where bar = 42 for update", + "Table": "u_tbl7" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd" + } + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:fkc_upd as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:fkc_upd as CHAR) where u_tbl3.col3 is null and cast(:fkc_upd as CHAR) is not null and not (u_tbl4.col4) <=> (cast(:fkc_upd as CHAR)) and (u_tbl4.col4) in ::fkc_vals limit 1 for share", + "Table": "u_tbl3, u_tbl4" + }, + { + "InputName": "VerifyChild-2", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", + "Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (cast(:fkc_upd as CHAR) is null or (u_tbl9.col9) not in ((cast(:fkc_upd as CHAR)))) limit 1 for share", + "Table": "u_tbl4, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = :fkc_upd where (col4) in ::fkc_vals", + "Table": "u_tbl4" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl7 set foo = 100, col7 = baz + 1 + col7 where bar = 42", + "Table": "u_tbl7" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl4", + "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Single column updated in a multi-col table", + "query": "update u_multicol_tbl1 set cola = cola + 3 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_multicol_tbl1 set cola = cola + 3 where id = 3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where id = 3 for update", + "Table": "u_multicol_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 2, + "UpdateExprCol": 3, + "UpdateExprBvName": "fkc_upd" + } + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:fkc_upd is null or (cola) not in ((:fkc_upd))) for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0, + 1 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (:fkc_upd is null or (cola) not in ((:fkc_upd)))", + "Table": "u_multicol_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl1 set cola = cola + 3 where id = 3", + "Table": "u_multicol_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "updating multiple columns of a fk constraint such that one uses the other", + "query": "update u_multicol_tbl3 set cola = id, colb = 5 * (cola + (1 - (cola))) where id = 2", + "plan": "VT12001: unsupported: cola column referenced in foreign key column colb is itself updated" + }, + { + "comment": "multicol foreign key updates with one literal and one non-literal update", + "query": "update u_multicol_tbl2 set cola = 2, colb = colc - (2) where id = 7", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_multicol_tbl2 set cola = 2, colb = colc - (2) where id = 7", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_multicol_tbl2 left join u_multicol_tbl1 on u_multicol_tbl1.cola = 2 and u_multicol_tbl1.colb = u_multicol_tbl2.colc - 2 where 1 != 1", + "Query": "select 1 from u_multicol_tbl2 left join u_multicol_tbl1 on u_multicol_tbl1.cola = 2 and u_multicol_tbl1.colb = u_multicol_tbl2.colc - 2 where u_multicol_tbl1.cola is null and 2 is not null and u_multicol_tbl1.colb is null and u_multicol_tbl2.colc - 2 is not null and not (u_multicol_tbl2.cola, u_multicol_tbl2.colb) <=> (2, u_multicol_tbl2.colc - 2) and u_multicol_tbl2.id = 7 limit 1 for share", + "Table": "u_multicol_tbl1, u_multicol_tbl2" + }, + { + "InputName": "PostVerify", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where id = 7 for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 2, + "UpdateExprCol": 3, + "UpdateExprBvName": "fkc_upd" + }, + { + "CompExprCol": 4, + "UpdateExprCol": 5, + "UpdateExprBvName": "fkc_upd1" + } + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = :fkc_upd, colb = :fkc_upd1 where (cola, colb) in ::fkc_vals", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl2 set cola = 2, colb = colc - 2 where id = 7", + "Table": "u_multicol_tbl2" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "replace into with table having unique key and primary key", + "query": "replace into u_tbl9(id, col9) values (1, 10),(2, 20),(3, 30)", + "plan": { + "QueryType": "INSERT", + "Original": "replace into u_tbl9(id, col9) values (1, 10),(2, 20),(3, 30)", + "Instructions": { + "OperatorType": "Sequential", + "Inputs": [ + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ((10), (20), (30)) or (col9 * foo) in ((10 * null), (20 * null), (30 * null)) or (bar, col9) in ((1, 10), (1, 20), (1, 30)) or (id) in ((1), (2), (3)) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update u_tbl8 set col8 = null where (col8) in ::fkc_vals", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl9 where (col9) in ((10), (20), (30)) or (col9 * foo) in ((10 * null), (20 * null), (30 * null)) or (bar, col9) in ((1, 10), (1, 20), (1, 30)) or (id) in ((1), (2), (3))", + "Table": "u_tbl9" + } + ] + }, + { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl9(id, col9) values (1, 10), (2, 20), (3, 30)", + "TableName": "u_tbl9" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Delete with foreign key checks off", + "query": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "plan": { + "QueryType": "DELETE", + "Original": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Instructions": { + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl1" + ] + } + }, + { + "comment": "Update with foreign key checks off", + "query": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=Off) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Table": "u_multicol_tbl1" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1" + ] + } + }, + { + "comment": "Insert with cross shard foreign keys and foreign key checks off", + "query": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into tbl3(col3, coly) values (:_col3_0, 3)", + "TableName": "tbl3", + "VindexValues": { + "hash_vin": "1" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "Insert with on duplicate key update - foreign key with values function", + "query": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = values(col1)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = values(col1)", + "Instructions": { + "OperatorType": "Upsert", + "TargetTabletType": "PRIMARY", + "Inputs": [ + { + "InputName": "Insert-1", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl1(id, col1) values (1, 3)", + "TableName": "u_tbl1" + }, + { + "InputName": "Update-1", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast(3 as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 3 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(3 as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast(3 as CHAR)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl1 set col1 = 3 where id = 1", + "Table": "u_tbl1" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "insert with on duplicate key update with multiple rows", + "query": "insert into u_tbl2 (id, col2) values (:v1, :v2),(:v3, :v4), (:v5, :v6) on duplicate key update col2 = values(col2)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl2 (id, col2) values (:v1, :v2),(:v3, :v4), (:v5, :v6) on duplicate key update col2 = values(col2)", + "Instructions": { + "OperatorType": "Upsert", + "TargetTabletType": "PRIMARY", + "Inputs": [ + { + "InputName": "Insert-1", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl2(id, col2) values (:v1, :v2)", + "TableName": "u_tbl2" + }, + { + "InputName": "Update-1", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v1 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals and (cast(:v2 as CHAR) is null or (col3) not in ((cast(:v2 as CHAR))))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl2 set col2 = :v2 where id = :v1", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Insert-2", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl2(id, col2) values (:v3, :v4)", + "TableName": "u_tbl2" + }, + { + "InputName": "Update-2", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v3 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (cast(:v4 as CHAR) is null or (col3) not in ((cast(:v4 as CHAR))))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl2 set col2 = :v4 where id = :v3", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Insert-3", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert into u_tbl2(id, col2) values (:v5, :v6)", + "TableName": "u_tbl2" + }, + { + "InputName": "Update-3", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v5 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals2 and (cast(:v6 as CHAR) is null or (col3) not in ((cast(:v6 as CHAR))))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update u_tbl2 set col2 = :v6 where id = :v5", + "Table": "u_tbl2" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "Unknown update column in foreign keys", + "query": "update tbl_auth set unknown_col = 'verified' where id = 1", + "plan": "column 'unknown_col' not found in table 'tbl_auth'" + }, + { + "comment": "Unsharded multi-table delete with foreign keys", + "query": "delete u from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.id from u_tbl6 as u, u_tbl5 as m where 1 != 1", + "Query": "select u_tbl6.id from u_tbl6 as u, u_tbl5 as m where u.col2 = 4 and m.col3 = 6 and u.col = m.col for update", + "Table": "u_tbl5, u_tbl6" + }, + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.col6 from u_tbl6 as u where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 as u where u_tbl6.id in ::dml_vals for update", + "Table": "u_tbl6" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "delete from u_tbl8 where (col8) in ::fkc_vals", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl6 as u where u_tbl6.id in ::dml_vals", + "Table": "u_tbl6" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl5", + "unsharded_fk_allow.u_tbl6", + "unsharded_fk_allow.u_tbl8" + ] + } + }, + { + "comment": "Multi table delete with using", + "query": "delete u_tbl10 from u_tbl10 join u_tbl11 using (id) where id = 5", + "plan": { + "QueryType": "DELETE", + "Original": "delete u_tbl10 from u_tbl10 join u_tbl11 using (id) where id = 5", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl10.id from u_tbl10, u_tbl11 where 1 != 1", + "Query": "select u_tbl10.id from u_tbl10, u_tbl11 where u_tbl10.id = 5 and u_tbl10.id = u_tbl11.id for update", + "Table": "u_tbl10, u_tbl11" + }, + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl10.col from u_tbl10 where 1 != 1", + "Query": "select u_tbl10.col from u_tbl10 where u_tbl10.id in ::dml_vals for update", + "Table": "u_tbl10" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "delete from u_tbl11 where (col) in ::fkc_vals", + "Table": "u_tbl11" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl10 where u_tbl10.id in ::dml_vals", + "Table": "u_tbl10" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl10", + "unsharded_fk_allow.u_tbl11" + ] + } + }, + { + "comment": "Multi table delete with unrelated tables", + "query": "delete u_tbl1 from u_tbl10 join u_tbl1 on u_tbl10.col = u_tbl1.col", + "plan": { + "QueryType": "DELETE", + "Original": "delete u_tbl1 from u_tbl10 join u_tbl1 on u_tbl10.col = u_tbl1.col", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.id from u_tbl10, u_tbl1 where 1 != 1", + "Query": "select u_tbl1.id from u_tbl10, u_tbl1 where u_tbl10.col = u_tbl1.col for update", + "Table": "u_tbl1, u_tbl10" + }, + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where u_tbl1.id in ::dml_vals for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl1 where u_tbl1.id in ::dml_vals", + "Table": "u_tbl1" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl10", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "Delete with limit", + "query": "delete from u_tbl1 order by id limit 1", + "plan": { + "QueryType": "DELETE", + "Original": "delete from u_tbl1 order by id limit 1", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.id from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.id from u_tbl1 order by id asc limit 1 for update", + "Table": "u_tbl1" + }, + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", - "Query": "select 1 from u_tbl4, u_tbl9 where (u_tbl4.col4) in ::fkc_vals and (:v1 is null or (u_tbl9.col9) not in ((:v1))) and u_tbl4.col4 = u_tbl9.col9 limit 1 lock in share mode", - "Table": "u_tbl4, u_tbl9" + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where u_tbl1.id in ::dml_vals for update", + "Table": "u_tbl1" }, { - "InputName": "PostVerify", - "OperatorType": "Update", + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Delete", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = :v1 where (u_tbl4.col4) in ::fkc_vals", - "Table": "u_tbl4" + "Query": "delete from u_tbl1 where u_tbl1.id in ::dml_vals", + "Table": "u_tbl1" } ] - }, - { - "InputName": "Parent", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "update u_tbl7 set col7 = :v1", - "Table": "u_tbl7" } ] }, "TablesUsed": [ - "unsharded_fk_allow.u_tbl3", - "unsharded_fk_allow.u_tbl4", - "unsharded_fk_allow.u_tbl7", - "unsharded_fk_allow.u_tbl9" - ] - } - }, - { - "comment": "Insert with on duplicate key update - foreign keys disallowed", - "query": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = 5", - "plan": "VT12001: unsupported: ON DUPLICATE KEY UPDATE with foreign keys" - }, - { - "comment": "Insert with on duplicate key update - foreign keys not on update column - allowed", - "query": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", - "plan": { - "QueryType": "INSERT", - "Original": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", - "Instructions": { - "OperatorType": "Insert", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "insert into u_tbl1(id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", - "TableName": "u_tbl1" - }, - "TablesUsed": [ - "unsharded_fk_allow.u_tbl1" + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" ] } }, { - "comment": "Insert with unsharded table having fk reference in sharded table", - "query": "insert into u_tbl (id, col) values (1, 2)", - "plan": "VT12002: unsupported: cross-shard foreign keys" - }, - { - "comment": "replace with fk reference unsupported", - "query": "replace into u_tbl1 (id, col1) values (1, 2)", - "plan": "VT12001: unsupported: REPLACE INTO with foreign keys" - }, - { - "comment": "update on a multicol foreign key that set nulls and then cascades", - "query": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "comment": "update query with an uncorrelated subquery", + "query": "update u_tbl4 set col41 = (select col14 from u_tbl1 where x = 2 and y = 4) where col4 = 3", "plan": { "QueryType": "UPDATE", - "Original": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Original": "update u_tbl4 set col41 = (select col14 from u_tbl1 where x = 2 and y = 4) where col4 = 3", "Instructions": { - "OperatorType": "FkCascade", + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], "Inputs": [ { - "InputName": "Selection", + "InputName": "SubQuery", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = 3 for update", - "Table": "u_multicol_tbl1" + "FieldQuery": "select col14 from u_tbl1 where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ col14 from u_tbl1 where x = 2 and y = 4 lock in share mode", + "Table": "u_tbl1" }, { - "InputName": "CascadeChild-1", - "OperatorType": "FkCascade", - "BvName": "fkc_vals", - "Cols": [ - 0, - 1 - ], + "InputName": "Outer", + "OperatorType": "FKVerify", "Inputs": [ { - "InputName": "Selection", + "InputName": "VerifyParent-1", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (u_multicol_tbl2.cola, u_multicol_tbl2.colb) not in ((1, 2)) for update", - "Table": "u_multicol_tbl2" - }, - { - "InputName": "CascadeChild-1", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "BvName": "fkc_vals1", - "Cols": [ - 0, - 1 - ], - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", - "Table": "u_multicol_tbl3" + "FieldQuery": "select 1 from u_tbl4 left join u_tbl1 on u_tbl1.col14 = cast(:__sq1 as SIGNED) where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ 1 from u_tbl4 left join u_tbl1 on u_tbl1.col14 = cast(:__sq1 as SIGNED) where u_tbl1.col14 is null and cast(:__sq1 as SIGNED) is not null and not (u_tbl4.col41) <=> (cast(:__sq1 as SIGNED)) and u_tbl4.col4 = 3 limit 1 lock in share mode", + "Table": "u_tbl1, u_tbl4" }, { - "InputName": "Parent", + "InputName": "PostVerify", "OperatorType": "Update", "Variant": "Unsharded", "Keyspace": { @@ -1453,61 +3551,47 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (u_multicol_tbl2.cola, u_multicol_tbl2.colb) not in ((1, 2))", - "Table": "u_multicol_tbl2" + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col41 = :__sq1 where col4 = 3", + "Table": "u_tbl4" } ] - }, - { - "InputName": "Parent", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", - "Table": "u_multicol_tbl1" } ] }, "TablesUsed": [ - "unsharded_fk_allow.u_multicol_tbl1", - "unsharded_fk_allow.u_multicol_tbl2", - "unsharded_fk_allow.u_multicol_tbl3" + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl4" ] } }, { - "comment": "update on a multicol foreign key that set nulls and then cascades - bindVariables", - "query": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "comment": "update with a subquery", + "query": "update u_tbl1 set col1 = (select foo from u_tbl1 where id = 1) order by id desc", "plan": { "QueryType": "UPDATE", - "Original": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "Original": "update u_tbl1 set col1 = (select foo from u_tbl1 where id = 1) order by id desc", "Instructions": { - "OperatorType": "FkCascade", + "OperatorType": "UncorrelatedSubquery", + "Variant": "PulloutValue", + "PulloutVars": [ + "__sq1" + ], "Inputs": [ { - "InputName": "Selection", + "InputName": "SubQuery", "OperatorType": "Route", "Variant": "Unsharded", "Keyspace": { "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = :v3 for update", - "Table": "u_multicol_tbl1" + "FieldQuery": "select foo from u_tbl1 where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ foo from u_tbl1 where id = 1 lock in share mode", + "Table": "u_tbl1" }, { - "InputName": "CascadeChild-1", + "InputName": "Outer", "OperatorType": "FkCascade", - "BvName": "fkc_vals", - "Cols": [ - 0, - 1 - ], "Inputs": [ { "InputName": "Selection", @@ -1517,26 +3601,109 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (u_multicol_tbl2.cola, u_multicol_tbl2.colb) not in ((:v1, :v2)))) for update", - "Table": "u_multicol_tbl2" + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl1.col1 from u_tbl1 order by id desc lock in share mode", + "Table": "u_tbl1" }, { "InputName": "CascadeChild-1", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "BvName": "fkc_vals1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", "Cols": [ - 0, - 1 + 0 ], - "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", - "Table": "u_multicol_tbl3" + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals lock in share mode", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (cast(:__sq1 as CHAR) is null or (col3) not in ((cast(:__sq1 as CHAR))))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = :__sq1 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (cast(:__sq1 as CHAR) is null or (col9) not in ((cast(:__sq1 as CHAR)))) lock in share mode", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (cast(:__sq1 as CHAR) is null or (col9) not in ((cast(:__sq1 as CHAR))))", + "Table": "u_tbl9" + } + ] }, { "InputName": "Parent", @@ -1547,110 +3714,96 @@ "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "update u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (u_multicol_tbl2.cola, u_multicol_tbl2.colb) not in ((:v1, :v2))))", - "Table": "u_multicol_tbl2" + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl1 set col1 = :__sq1 order by id desc", + "Table": "u_tbl1" } ] - }, - { - "InputName": "Parent", - "OperatorType": "Update", - "Variant": "Unsharded", - "Keyspace": { - "Name": "unsharded_fk_allow", - "Sharded": false - }, - "TargetTabletType": "PRIMARY", - "Query": "update u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", - "Table": "u_multicol_tbl1" } ] }, "TablesUsed": [ - "unsharded_fk_allow.u_multicol_tbl1", - "unsharded_fk_allow.u_multicol_tbl2", - "unsharded_fk_allow.u_multicol_tbl3" + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" ] } }, { - "comment": "Cascaded delete run from prepared statement", - "query": "execute prep_delete using @foo", + "comment": "Multi table delete such that the two tables are foreign key related", + "query": "delete u_tbl6 from u_tbl6 join u_tbl8 on u_tbl6.id = u_tbl8.id where u_tbl6.id = 4", "plan": { - "QueryType": "EXECUTE", - "Original": "execute prep_delete using @foo", + "QueryType": "DELETE", + "Original": "delete u_tbl6 from u_tbl6 join u_tbl8 on u_tbl6.id = u_tbl8.id where u_tbl6.id = 4", "Instructions": { - "OperatorType": "EXECUTE", - "Parameters": [ - "foo" + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 ], "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.id from u_tbl6, u_tbl8 where 1 != 1", + "Query": "select u_tbl6.id from u_tbl6, u_tbl8 where u_tbl6.id = 4 and u_tbl6.id = u_tbl8.id for update", + "Table": "u_tbl6, u_tbl8" + }, { "OperatorType": "FkCascade", "Inputs": [ { "InputName": "Selection", "OperatorType": "Route", - "Variant": "Scatter", + "Variant": "Unsharded", "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true + "Name": "unsharded_fk_allow", + "Sharded": false }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 where id = :v1 for update", - "Table": "tbl5" + "FieldQuery": "select u_tbl6.col6 from u_tbl6 where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 where u_tbl6.id in ::dml_vals for update", + "Table": "u_tbl6" }, { "InputName": "CascadeChild-1", "OperatorType": "Delete", - "Variant": "Scatter", + "Variant": "Unsharded", "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true + "Name": "unsharded_fk_allow", + "Sharded": false }, "TargetTabletType": "PRIMARY", "BvName": "fkc_vals", "Cols": [ 0 ], - "Query": "delete from tbl4 where (col4) in ::fkc_vals", - "Table": "tbl4" - }, - { - "InputName": "CascadeChild-2", - "OperatorType": "Delete", - "Variant": "Scatter", - "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true - }, - "TargetTabletType": "PRIMARY", - "BvName": "fkc_vals1", - "Cols": [ - 1 - ], - "Query": "delete from tbl4 where (t4col4) in ::fkc_vals1", - "Table": "tbl4" + "Query": "delete from u_tbl8 where (col8) in ::fkc_vals", + "Table": "u_tbl8" }, { "InputName": "Parent", "OperatorType": "Delete", - "Variant": "Scatter", + "Variant": "Unsharded", "Keyspace": { - "Name": "sharded_fk_allow", - "Sharded": true + "Name": "unsharded_fk_allow", + "Sharded": false }, "TargetTabletType": "PRIMARY", - "Query": "delete from tbl5 where id = :v1", - "Table": "tbl5" + "Query": "delete from u_tbl6 where u_tbl6.id in ::dml_vals", + "Table": "u_tbl6" } ] } ] }, "TablesUsed": [ - "sharded_fk_allow.tbl4", - "sharded_fk_allow.tbl5" + "unsharded_fk_allow.u_tbl6", + "unsharded_fk_allow.u_tbl8" ] } } diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json new file mode 100644 index 00000000000..264311696a3 --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json @@ -0,0 +1,497 @@ +[ + { + "comment": "Insertion in a table with cross-shard foreign keys works with foreign_key_checks off", + "query": "insert into tbl3 (col3, coly) values (1, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into tbl3 (col3, coly) values (1, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into tbl3(col3, coly) values (:_col3_0, 3)", + "TableName": "tbl3", + "VindexValues": { + "hash_vin": "1" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "Insertion in a table with shard-scoped multiple column foreign key is allowed", + "query": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into multicol_tbl2(cola, colb, colc) values (:_cola_0, :_colb_0, :_colc_0)", + "TableName": "multicol_tbl2", + "VindexValues": { + "multicolIdx": "1, 2, 3" + } + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl2" + ] + } + }, + { + "comment": "Delete in a table with cross-shard foreign key works with foreign_key_checks off ", + "query": "delete from tbl1", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl1", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from tbl1", + "Table": "tbl1" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1" + ] + } + }, + { + "comment": "Delete in a table with not all column shard-scoped foreign keys works with foreign_key_checks off", + "query": "delete from tbl7", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl7", + "Instructions": { + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from tbl7", + "Table": "tbl7" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl7" + ] + } + }, + { + "comment": "Delete in a table with shard-scoped multiple column foreign key with cascade with foreign key checks on", + "query": "delete /*+ SET_VAR(foreign_key_checks=1) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "plan": { + "QueryType": "DELETE", + "Original": "delete /*+ SET_VAR(foreign_key_checks=1) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1, + 2, + 3, + 4 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from multicol_tbl2 where (colb, cola, x, colc, y) in ::fkc_vals", + "Table": "multicol_tbl2", + "Values": [ + "fkc_vals:1", + "fkc_vals:0", + "fkc_vals:3" + ], + "Vindex": "multicolIdx" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl1", + "sharded_fk_allow.multicol_tbl2" + ] + } + }, + { + "comment": "Delete in a table with shard-scoped foreign keys with SET NULL", + "query": "delete from tbl8 where col8 = 1", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl8 where col8 = 1", + "Instructions": { + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from tbl8 where col8 = 1", + "Table": "tbl8", + "Values": [ + "1" + ], + "Vindex": "hash_vin" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl8" + ] + } + }, + { + "comment": "Update in a table with cross-shard foreign keys works with foreign_key_checks off", + "query": "update tbl1 set t1col1 = 'foo' where col1 = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl1 set t1col1 = 'foo' where col1 = 1", + "Instructions": { + "OperatorType": "Update", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=Off) */ tbl1 set t1col1 = 'foo' where col1 = 1", + "Table": "tbl1", + "Values": [ + "1" + ], + "Vindex": "hash_vin" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1" + ] + } + }, + { + "comment": "Update in a table with column modified not shard-scoped foreign key whereas other column referencing same table is works with foreign_key_checks off", + "query": "update tbl7 set t7col7 = 'foo', t7col72 = 42", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl7 set t7col7 = 'foo', t7col72 = 42", + "Instructions": { + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=Off) */ tbl7 set t7col7 = 'foo', t7col72 = 42", + "Table": "tbl7" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl7" + ] + } + }, + { + "comment": "Update in a table with shard-scoped foreign keys with cascade", + "query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl5 set t5col5 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl5 set t5col5 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", + "Table": "tbl5" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ tbl4 set t4col4 = null where (t4col4) in ::fkc_vals and (t4col4) not in (('foo'))", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl5 set t5col5 = 'foo'", + "Table": "tbl5" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl5" + ] + } + }, + { + "comment": "Insertion in a table with 2 foreign keys constraint with same table on different columns - both are not shard scoped - works with foreign_key_checks off", + "query": "insert into tbl6 (col6, t6col6) values (100, 'foo')", + "plan": { + "QueryType": "INSERT", + "Original": "insert into tbl6 (col6, t6col6) values (100, 'foo')", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into tbl6(col6, t6col6) values (:_col6_0, 'foo')", + "TableName": "tbl6", + "VindexValues": { + "hash_vin": "100" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl6" + ] + } + }, + { + "comment": "delete table with shard scoped foreign key set default works with foreign_key_checks off", + "query": "delete from tbl20 where col = 'bar'", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl20 where col = 'bar'", + "Instructions": { + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from tbl20 where col = 'bar'", + "Table": "tbl20", + "Values": [ + "'bar'" + ], + "Vindex": "hash_vin" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl20" + ] + } + }, + { + "comment": "Delete table with cross-shard foreign key with set null - should be eventually allowed", + "query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl9 where col9 = 34", + "plan": { + "QueryType": "DELETE", + "Original": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl9 where col9 = 34", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", + "Table": "tbl9", + "Values": [ + "34" + ], + "Vindex": "hash_vin" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ tbl4 set col_ref = null where (col_ref) in ::fkc_vals", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl9 where col9 = 34", + "Table": "tbl9", + "Values": [ + "34" + ], + "Vindex": "hash_vin" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl9" + ] + } + }, + { + "comment": "Delete with foreign key checks off", + "query": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "plan": { + "QueryType": "DELETE", + "Original": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Instructions": { + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl1" + ] + } + }, + { + "comment": "Update with foreign key checks off", + "query": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=Off) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Table": "u_multicol_tbl1" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1" + ] + } + }, + { + "comment": "Insert with cross shard foreign keys and foreign key checks off", + "query": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into tbl3(col3, coly) values (:_col3_0, 3)", + "TableName": "tbl3", + "VindexValues": { + "hash_vin": "1" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl3" + ] + } + } +] diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json new file mode 100644 index 00000000000..311ff6adbff --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json @@ -0,0 +1,2390 @@ +[ + { + "comment": "Insertion in a table with cross-shard foreign keys disallowed", + "query": "insert into tbl3 (col3, coly) values (1, 3)", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Insertion in a table with shard-scoped foreign keys is allowed", + "query": "insert into tbl2 (col2, coly) values (1, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into tbl2 (col2, coly) values (1, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=On) */ into tbl2(col2, coly) values (:_col2_0, 3)", + "TableName": "tbl2", + "VindexValues": { + "hash_vin": "1" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl2" + ] + } + }, + { + "comment": "Insertion in a table with shard-scoped multiple column foreign key is allowed", + "query": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert into multicol_tbl2 (cola, colb, colc) values (1, 2, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=On) */ into multicol_tbl2(cola, colb, colc) values (:_cola_0, :_colb_0, :_colc_0)", + "TableName": "multicol_tbl2", + "VindexValues": { + "multicolIdx": "1, 2, 3" + } + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl2" + ] + } + }, + { + "comment": "Delete in a table with cross-shard foreign keys disallowed", + "query": "delete from tbl1", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Delete in a table with not all column shard-scoped foreign keys - disallowed", + "query": "delete from tbl7", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Delete in a table with shard-scoped multiple column foreign key with cascade", + "query": "delete from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "plan": { + "QueryType": "DELETE", + "Original": "delete from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1, + 2, + 3, + 4 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from multicol_tbl2 where (colb, cola, x, colc, y) in ::fkc_vals", + "Table": "multicol_tbl2", + "Values": [ + "fkc_vals:1", + "fkc_vals:0", + "fkc_vals:3" + ], + "Vindex": "multicolIdx" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl1", + "sharded_fk_allow.multicol_tbl2" + ] + } + }, + { + "comment": "Delete in a table with shard-scoped foreign keys with cascade", + "query": "delete from tbl5", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl5", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 for update", + "Table": "tbl5" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from tbl4 where (col4) in ::fkc_vals", + "Table": "tbl4", + "Values": [ + "fkc_vals:0" + ], + "Vindex": "hash_vin" + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 1 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from tbl4 where (t4col4) in ::fkc_vals1", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl5", + "Table": "tbl5" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl5" + ] + } + }, + { + "comment": "Delete in a table with shard-scoped foreign keys with SET NULL", + "query": "delete from tbl8 where col8 = 1", + "plan": "VT12001: unsupported: you cannot UPDATE primary vindex columns; invalid update on vindex: hash_vin" + }, + { + "comment": "Delete in a table with unsharded foreign key with SET NULL", + "query": "delete from u_tbl9 where col9 = 5", + "plan": { + "QueryType": "DELETE", + "Original": "delete from u_tbl9 where col9 = 5", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where col9 = 5 for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from u_tbl9 where col9 = 5", + "Table": "u_tbl9" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "update in unsharded table with restrict", + "query": "update u_tbl5 set col5 = 'foo' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl5 set col5 = 'foo' where id = 1", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl5 set col5 = 'foo' where id = 1", + "Table": "u_tbl5" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl5" + ] + } + }, + { + "comment": "update in unsharded table with cascade", + "query": "update u_tbl2 set col2 = 'bar' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl2 set col2 = 'bar' where id = 1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals and (col3) not in ((cast('bar' as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl2 set col2 = 'bar' where id = 1", + "Table": "u_tbl2" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update in unsharded table with cascade - on non-referenced column", + "query": "update u_tbl2 set col_no_ref = 'baz' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl2 set col_no_ref = 'baz' where id = 1", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl2 set col_no_ref = 'baz' where id = 1", + "Table": "u_tbl2" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2" + ] + } + }, + { + "comment": "Update in a table with cross-shard foreign keys disallowed", + "query": "update tbl1 set t1col1 = 'foo' where col1 = 1", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Update in a table with cross-shard foreign keys, column not in update expression - allowed", + "query": "update tbl1 set not_ref_col = 'foo' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl1 set not_ref_col = 'foo' where id = 1", + "Instructions": { + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl1 set not_ref_col = 'foo' where id = 1", + "Table": "tbl1" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1" + ] + } + }, + { + "comment": "Update in a table with column modified not shard-scoped foreign key whereas other column referencing same table is - disallowed", + "query": "update tbl7 set t7col7 = 'foo', t7col72 = 42", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Update in a table with shard-scoped foreign keys with cascade", + "query": "update tbl5 set t5col5 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl5 set t5col5 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", + "Table": "tbl5" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ tbl4 set t4col4 = null where (t4col4) in ::fkc_vals and (t4col4) not in (('foo'))", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl5 set t5col5 = 'foo'", + "Table": "tbl5" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl5" + ] + } + }, + { + "comment": "Insertion in a table with 2 foreign keys constraint with same table on different columns - both are not shard scoped - disallowed", + "query": "insert into tbl6 (col6, t6col6) values (100, 'foo')", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "Update a table with parent and child foreign keys - shard scoped", + "query": "update tbl2 set col = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl2 set col = 'foo'", + "Instructions": { + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl2 set col = 'foo'", + "Table": "tbl2" + }, + "TablesUsed": [ + "sharded_fk_allow.tbl2" + ] + } + }, + { + "comment": "update table with column's parent foreign key cross shard", + "query": "update tbl10 set col = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl10 set col = 'foo'", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Filter", + "Predicate": "tbl3.col is null", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0,R:0", + "TableName": "tbl10_tbl3", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select 1 from tbl10 where 1 != 1", + "Query": "select 1 from tbl10 where not (tbl10.col) <=> ('foo') for share", + "Table": "tbl10" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl3.col from tbl3 where 1 != 1", + "Query": "select tbl3.col from tbl3 where tbl3.col = 'foo' for share", + "Table": "tbl3" + } + ] + } + ] + } + ] + } + ] + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl10 set col = 'foo'", + "Table": "tbl10" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl10", + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "delete table with shard scoped foreign key set default - disallowed", + "query": "delete from tbl20 where col = 'bar'", + "plan": "VT09016: Cannot delete or update a parent row: a foreign key constraint fails" + }, + { + "comment": "Delete table with cross-shard foreign key with set null - should be eventually allowed", + "query": "delete from tbl9 where col9 = 34", + "plan": { + "QueryType": "DELETE", + "Original": "delete from tbl9 where col9 = 34", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", + "Table": "tbl9", + "Values": [ + "34" + ], + "Vindex": "hash_vin" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ tbl4 set col_ref = null where (col_ref) in ::fkc_vals", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl9 where col9 = 34", + "Table": "tbl9", + "Values": [ + "34" + ], + "Vindex": "hash_vin" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl9" + ] + } + }, + { + "comment": "update table with same column having reference to different tables, one with on update cascade other with on update set null - child table have further reference", + "query": "update u_tbl1 set col1 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl1 set col1 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast('foo' as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 'foo' where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl1 set col1 = 'foo'", + "Table": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "update in a table with limit - disallowed", + "query": "update u_tbl2 set col2 = 'bar' limit 2", + "plan": "VT12001: unsupported: update with limit with foreign key constraints" + }, + { + "comment": "update in a table with non-literal value - set null fail due to child update where condition", + "query": "update u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl2 left join u_tbl1 on u_tbl1.col1 = cast(u_tbl2.col1 + 'bar' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl2 left join u_tbl1 on u_tbl1.col1 = cast(u_tbl2.col1 + 'bar' as CHAR) where u_tbl1.col1 is null and cast(u_tbl2.col1 + 'bar' as CHAR) is not null and not (u_tbl2.col2) <=> (cast(u_tbl2.col1 + 'bar' as CHAR)) and u_tbl2.id = 1 limit 1 for share", + "Table": "u_tbl1, u_tbl2" + }, + { + "InputName": "PostVerify", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd" + } + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals and (:fkc_upd is null or (col3) not in ((:fkc_upd)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set m = 2, col2 = col1 + 'bar' where id = 1", + "Table": "u_tbl2" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update in a table with non-literal value - with cascade fail as the cascade value is not known", + "query": "update u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd" + } + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (cast(:fkc_upd as CHAR) is null or (col3) not in ((cast(:fkc_upd as CHAR))))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = :fkc_upd where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "NonLiteralUpdateInfo": [ + { + "CompExprCol": 1, + "UpdateExprCol": 2, + "UpdateExprBvName": "fkc_upd1" + } + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl1 set m = 2, col1 = x + 'bar' where id = 1", + "Table": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "update in a table with set null, non-literal value on non-foreign key column - allowed", + "query": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals and (col3) not in ((cast(2 as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl2 set m = col1 + 'bar', col2 = 2 where id = 1", + "Table": "u_tbl2" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update in a table with cascade, non-literal value on non-foreign key column - allowed", + "query": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast(2 as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl1 set m = x + 'bar', col1 = 2 where id = 1", + "Table": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "update in a table with a child table having SET DEFAULT constraint - disallowed", + "query": "update tbl20 set col2 = 'bar'", + "plan": "VT09016: Cannot delete or update a parent row: a foreign key constraint fails" + }, + { + "comment": "delete in a table with limit", + "query": "delete from u_tbl2 limit 2", + "plan": { + "QueryType": "DELETE", + "Original": "delete from u_tbl2 limit 2", + "Instructions": { + "OperatorType": "DMLWithInput", + "TargetTabletType": "PRIMARY", + "Offset": [ + 0 + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.id from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.id from u_tbl2 limit 2 for update", + "Table": "u_tbl2" + }, + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where u_tbl2.id in ::dml_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from u_tbl2 where u_tbl2.id in ::dml_vals", + "Table": "u_tbl2" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update with fk on cross-shard with a where condition on non-literal value - disallowed", + "query": "update tbl3 set coly = colx + 10 where coly = 10", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl3 set coly = colx + 10 where coly = 10", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Filter", + "Predicate": "tbl1.t1col1 is null", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0,R:0", + "JoinVars": { + "tbl3_colx": 0 + }, + "TableName": "tbl3_tbl1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl3.colx from tbl3 where 1 != 1", + "Query": "select tbl3.colx from tbl3 where tbl3.colx + 10 is not null and not (tbl3.coly) <=> (tbl3.colx + 10) and tbl3.coly = 10 for share", + "Table": "tbl3" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl1.t1col1 from tbl1 where 1 != 1", + "Query": "select tbl1.t1col1 from tbl1 where tbl1.t1col1 = :tbl3_colx + 10 for share", + "Table": "tbl1" + } + ] + } + ] + } + ] + } + ] + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ tbl3 set coly = colx + 10 where coly = 10", + "Table": "tbl3" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1", + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "update with fk on cross-shard with a where condition", + "query": "update tbl3 set coly = 20 where coly = 10", + "plan": { + "QueryType": "UPDATE", + "Original": "update tbl3 set coly = 20 where coly = 10", + "Instructions": { + "OperatorType": "FKVerify", + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Limit", + "Count": "1", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Filter", + "Predicate": "tbl1.t1col1 is null", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0,R:0", + "TableName": "tbl3_tbl1", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select 1 from tbl3 where 1 != 1", + "Query": "select 1 from tbl3 where not (tbl3.coly) <=> (20) and tbl3.coly = 10 for share", + "Table": "tbl3" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl1.t1col1 from tbl1 where 1 != 1", + "Query": "select tbl1.t1col1 from tbl1 where tbl1.t1col1 = 20 for share", + "Table": "tbl1" + } + ] + } + ] + } + ] + } + ] + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ tbl3 set coly = 20 where coly = 10", + "Table": "tbl3" + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl1", + "sharded_fk_allow.tbl3" + ] + } + }, + { + "comment": "Update in a table with shard-scoped foreign keys with cascade that requires a validation of a different parent foreign key", + "query": "update u_tbl6 set col6 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl6 set col6 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.col6 from u_tbl6 where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 for update", + "Table": "u_tbl6" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = cast('foo' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = cast('foo' as CHAR) where u_tbl9.col9 is null and cast('foo' as CHAR) is not null and not (u_tbl8.col8) <=> (cast('foo' as CHAR)) and (u_tbl8.col8) in ::fkc_vals limit 1 for share nowait", + "Table": "u_tbl8, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl8 set col8 = 'foo' where (col8) in ::fkc_vals", + "Table": "u_tbl8" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl6 set col6 = 'foo'", + "Table": "u_tbl6" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl6", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Update that cascades and requires parent fk and restrict child fk verification", + "query": "update u_tbl7 set col7 = 'foo'", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl7 set col7 = 'foo'", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", + "Table": "u_tbl7" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast('foo' as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast('foo' as CHAR) where u_tbl3.col3 is null and cast('foo' as CHAR) is not null and not (u_tbl4.col4) <=> (cast('foo' as CHAR)) and (u_tbl4.col4) in ::fkc_vals limit 1 for share", + "Table": "u_tbl3, u_tbl4" + }, + { + "InputName": "VerifyChild-2", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", + "Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (cast('foo' as CHAR) is null or (u_tbl9.col9) not in ((cast('foo' as CHAR)))) limit 1 for share", + "Table": "u_tbl4, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = 'foo' where (col4) in ::fkc_vals", + "Table": "u_tbl4" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl7 set col7 = 'foo'", + "Table": "u_tbl7" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl4", + "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Update that cascades and requires parent fk and restrict child fk verification - bindVariable", + "query": "update u_tbl7 set col7 = :v1", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_tbl7 set col7 = :v1", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", + "Table": "u_tbl7" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FKVerify", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "VerifyParent-1", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:v1 as CHAR) where 1 != 1", + "Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = cast(:v1 as CHAR) where u_tbl3.col3 is null and cast(:v1 as CHAR) is not null and not (u_tbl4.col4) <=> (cast(:v1 as CHAR)) and (u_tbl4.col4) in ::fkc_vals limit 1 for share", + "Table": "u_tbl3, u_tbl4" + }, + { + "InputName": "VerifyChild-2", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1", + "Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (cast(:v1 as CHAR) is null or (u_tbl9.col9) not in ((cast(:v1 as CHAR)))) limit 1 for share", + "Table": "u_tbl4, u_tbl9" + }, + { + "InputName": "PostVerify", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl4 set col4 = :v1 where (col4) in ::fkc_vals", + "Table": "u_tbl4" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl7 set col7 = :v1", + "Table": "u_tbl7" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl4", + "unsharded_fk_allow.u_tbl7", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Insert with on duplicate key update - foreign keys disallowed", + "query": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = 5", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl1 (id, col1) values (1, 3) on duplicate key update col1 = 5", + "Instructions": { + "OperatorType": "Upsert", + "TargetTabletType": "PRIMARY", + "Inputs": [ + { + "InputName": "Insert-1", + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert /*+ SET_VAR(foreign_key_checks=On) */ into u_tbl1(id, col1) values (1, 3)", + "TableName": "u_tbl1" + }, + { + "InputName": "Update-1", + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1 and (col3) not in ((cast(5 as CHAR)))", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_tbl2 set col2 = 5 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "FkCascade", + "BvName": "fkc_vals2", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", + "Table": "u_tbl9" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals3", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl8 set col8 = null where (col8) in ::fkc_vals3", + "Table": "u_tbl8" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl9 set col9 = null where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR)))", + "Table": "u_tbl9" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_tbl1 set col1 = 5 where id = 1", + "Table": "u_tbl1" + } + ] + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3", + "unsharded_fk_allow.u_tbl8", + "unsharded_fk_allow.u_tbl9" + ] + } + }, + { + "comment": "Insert with on duplicate key update - foreign keys not on update column - allowed", + "query": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "plan": { + "QueryType": "INSERT", + "Original": "insert into u_tbl1 (id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=On) */ into u_tbl1(id, col1, foo) values (1, 3, 'bar') on duplicate key update foo = 'baz'", + "TableName": "u_tbl1" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1" + ] + } + }, + { + "comment": "Insert with unsharded table having fk reference in sharded table", + "query": "insert into u_tbl (id, col) values (1, 2)", + "plan": "VT12002: unsupported: cross-shard foreign keys" + }, + { + "comment": "replace with fk reference unsupported", + "query": "replace into u_tbl1 (id, col1) values (1, 2)", + "plan": { + "QueryType": "INSERT", + "Original": "replace into u_tbl1 (id, col1) values (1, 2)", + "Instructions": { + "OperatorType": "Sequential", + "Inputs": [ + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where (id) in ((1)) for update", + "Table": "u_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_tbl3 set col3 = null where (col3) in ::fkc_vals1", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from u_tbl2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from u_tbl1 where (id) in ((1))", + "Table": "u_tbl1" + } + ] + }, + { + "OperatorType": "Insert", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "NoAutoCommit": true, + "Query": "insert /*+ SET_VAR(foreign_key_checks=On) */ into u_tbl1(id, col1) values (1, 2)", + "TableName": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } + }, + { + "comment": "update on a multicol foreign key that set nulls and then cascades", + "query": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = 3 for update", + "Table": "u_multicol_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0, + 1 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2))", + "Table": "u_multicol_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Table": "u_multicol_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "update on a multicol foreign key that set nulls and then cascades - bindVariables", + "query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "plan": { + "QueryType": "UPDATE", + "Original": "update /*+ SET_VAR(foreign_key_checks=On) */ u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = :v3 for update", + "Table": "u_multicol_tbl1" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0, + 1 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", + "Table": "u_multicol_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0, + 1 + ], + "Query": "update /*+ SET_VAR(foreign_key_checks=OFF) */ u_multicol_tbl3 set cola = null, colb = null where (cola, colb) in ::fkc_vals1", + "Table": "u_multicol_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=ON) */ u_multicol_tbl2 set cola = null, colb = null where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2))))", + "Table": "u_multicol_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=On) */ u_multicol_tbl1 set cola = :v1, colb = :v2 where id = :v3", + "Table": "u_multicol_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1", + "unsharded_fk_allow.u_multicol_tbl2", + "unsharded_fk_allow.u_multicol_tbl3" + ] + } + }, + { + "comment": "Cascaded delete run from prepared statement", + "query": "execute prep_delete using @foo", + "plan": { + "QueryType": "EXECUTE", + "Original": "execute prep_delete using @foo", + "Instructions": { + "OperatorType": "EXECUTE", + "Parameters": [ + "foo" + ], + "Inputs": [ + { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 where id = :v1 for update", + "Table": "tbl5" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Delete", + "Variant": "MultiEqual", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from tbl4 where (col4) in ::fkc_vals", + "Table": "tbl4", + "Values": [ + "fkc_vals:0" + ], + "Vindex": "hash_vin" + }, + { + "InputName": "CascadeChild-2", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 1 + ], + "Query": "delete /*+ SET_VAR(foreign_key_checks=ON) */ from tbl4 where (t4col4) in ::fkc_vals1", + "Table": "tbl4" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Scatter", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=On) */ from tbl5 where id = :v1", + "Table": "tbl5" + } + ] + } + ] + }, + "TablesUsed": [ + "sharded_fk_allow.tbl4", + "sharded_fk_allow.tbl5" + ] + } + }, + { + "comment": "Delete with foreign key checks off", + "query": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "plan": { + "QueryType": "DELETE", + "Original": "delete /*+ SET_VAR(foreign_key_checks=off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Instructions": { + "OperatorType": "Delete", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "delete /*+ SET_VAR(foreign_key_checks=Off) */ from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3", + "Table": "multicol_tbl1", + "Values": [ + "1", + "2", + "3" + ], + "Vindex": "multicolIdx" + }, + "TablesUsed": [ + "sharded_fk_allow.multicol_tbl1" + ] + } + }, + { + "comment": "Update with foreign key checks off", + "query": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "plan": { + "QueryType": "UPDATE", + "Original": "update /*+ SET_VAR(foreign_key_checks=0) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Instructions": { + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "update /*+ SET_VAR(foreign_key_checks=Off) */ u_multicol_tbl1 set cola = 1, colb = 2 where id = 3", + "Table": "u_multicol_tbl1" + }, + "TablesUsed": [ + "unsharded_fk_allow.u_multicol_tbl1" + ] + } + }, + { + "comment": "Insert with cross shard foreign keys and foreign key checks off", + "query": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "plan": { + "QueryType": "INSERT", + "Original": "insert /*+ SET_VAR(foreign_key_checks=0) */ into tbl3 (col3, coly) values (1, 3)", + "Instructions": { + "OperatorType": "Insert", + "Variant": "Sharded", + "Keyspace": { + "Name": "sharded_fk_allow", + "Sharded": true + }, + "TargetTabletType": "PRIMARY", + "Query": "insert /*+ SET_VAR(foreign_key_checks=Off) */ into tbl3(col3, coly) values (:_col3_0, 3)", + "TableName": "tbl3", + "VindexValues": { + "hash_vin": "1" + } + }, + "TablesUsed": [ + "sharded_fk_allow.tbl3" + ] + } + } +] diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 7a3c13c3635..044036a4590 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -711,8 +711,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select m1.col from unsharded as m1 join unsharded as m2 where 1 != 1", - "Query": "select m1.col from unsharded as m1 join unsharded as m2", + "FieldQuery": "select m1.col from unsharded as m1 straight_join unsharded as m2 where 1 != 1", + "Query": "select m1.col from unsharded as m1 straight_join unsharded as m2", "Table": "unsharded" }, "TablesUsed": [ @@ -3989,8 +3989,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1 where 1 != 1", - "Query": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1", + "FieldQuery": "select * from unsharded_authoritative as A left join unsharded_authoritative as B using (col1) where 1 != 1", + "Query": "select * from unsharded_authoritative as A left join unsharded_authoritative as B using (col1)", "Table": "unsharded_authoritative" }, "TablesUsed": [ @@ -4050,5 +4050,375 @@ "comment": "select with a target destination", "query": "select * from `user[-]`.user_metadata", "plan": "VT09017: SELECT with a target destination is not allowed" + }, + { + "comment": "query that needs a hash join", + "query": "select id from user left join (select col from user_extra limit 10) ue on user.col = ue.col", + "plan": { + "QueryType": "SELECT", + "Original": "select id from user left join (select col from user_extra limit 10) ue on user.col = ue.col", + "Instructions": { + "OperatorType": "Join", + "Variant": "HashLeftJoin", + "Collation": "binary", + "ComparisonType": "INT16", + "JoinColumnIndexes": "-2", + "Predicate": "`user`.col = ue.col", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.col, id from `user` where 1 != 1", + "Query": "select `user`.col, id from `user`", + "Table": "`user`" + }, + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select ue.col from (select col from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select ue.col from (select col from user_extra) as ue limit :__upper_limit", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "query that needs a hash join - both sides have limits", + "query": "select id, user_id from (select id, col from user limit 10) u join (select col, user_id from user_extra limit 10) ue on u.col = ue.col", + "plan": { + "QueryType": "SELECT", + "Original": "select id, user_id from (select id, col from user limit 10) u join (select col, user_id from user_extra limit 10) ue on u.col = ue.col", + "Instructions": { + "OperatorType": "Join", + "Variant": "HashJoin", + "Collation": "binary", + "ComparisonType": "INT16", + "JoinColumnIndexes": "-1,2", + "Predicate": "u.col = ue.col", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.id, u.col from (select id, col from `user` where 1 != 1) as u where 1 != 1", + "Query": "select u.id, u.col from (select id, col from `user`) as u limit :__upper_limit", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select ue.col, ue.user_id from (select col, user_id from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select ue.col, ue.user_id from (select col, user_id from user_extra) as ue limit :__upper_limit", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "query that needs a hash join - both sides have limits. check that it can be merged even with the hash join", + "query": "select id, user_id from (select id, col from user where id = 17 limit 10) u join (select col, user_id from user_extra where user_id = 17 limit 10) ue on u.col = ue.col", + "plan": { + "QueryType": "SELECT", + "Original": "select id, user_id from (select id, col from user where id = 17 limit 10) u join (select col, user_id from user_extra where user_id = 17 limit 10) ue on u.col = ue.col", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, user_id from (select id, col from `user` where 1 != 1) as u, (select col, user_id from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select id, user_id from (select id, col from `user` where id = 17 limit 10) as u, (select col, user_id from user_extra where user_id = 17 limit 10) as ue where u.col = ue.col", + "Table": "`user`, user_extra", + "Values": [ + "17" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "query that needs a hash join - outer side has LIMIT. distinct should be pushed down", + "query": "select distinct id, user_id from (select id, col from user) u left join (select col, user_id from user_extra limit 10) ue on u.col = ue.col", + "plan": { + "QueryType": "SELECT", + "Original": "select distinct id, user_id from (select id, col from user) u left join (select col, user_id from user_extra limit 10) ue on u.col = ue.col", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:2)", + "(1:3)" + ], + "ResultColumns": 2, + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "HashLeftJoin", + "Collation": "binary", + "ComparisonType": "INT16", + "JoinColumnIndexes": "-1,2", + "Predicate": "u.col = ue.col", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, col from (select id, col from `user` where 1 != 1) as u where 1 != 1", + "Query": "select distinct id, col from (select id, col from `user`) as u", + "Table": "`user`" + }, + { + "OperatorType": "Limit", + "Count": "10", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select ue.col, ue.user_id from (select col, user_id from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select ue.col, ue.user_id from (select col, user_id from user_extra) as ue limit :__upper_limit", + "Table": "user_extra" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "unexpanded columns are fine if we can push down into single route", + "query": "select x from (select t.*, 1 as x from unsharded t union select t.*, 1 as x from unsharded t) as x", + "plan": { + "QueryType": "SELECT", + "Original": "select x from (select t.*, 1 as x from unsharded t union select t.*, 1 as x from unsharded t) as x", + "Instructions": { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select x from (select t.*, 1 as x from unsharded as t where 1 != 1 union select t.*, 1 as x from unsharded as t where 1 != 1) as x where 1 != 1", + "Query": "select x from (select t.*, 1 as x from unsharded as t union select t.*, 1 as x from unsharded as t) as x", + "Table": "unsharded" + }, + "TablesUsed": [ + "main.unsharded" + ] + } + }, + { + "comment": "pushing derived projection under the join should not cause problems", + "query": "SELECT count(*) FROM (SELECT DISTINCT u.user_id FROM user u JOIN user_extra ue ON u.id = ue.user_id JOIN music m ON m.id = u.id) subquery_for_count", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT count(*) FROM (SELECT DISTINCT u.user_id FROM user u JOIN user_extra ue ON u.id = ue.user_id JOIN music m ON m.id = u.id) subquery_for_count", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count_star(0) AS count(*)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 1 + ], + "Inputs": [ + { + "OperatorType": "Distinct", + "Collations": [ + "(0:2)", + "1" + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0,L:1,R:1", + "JoinVars": { + "m_id": 0 + }, + "TableName": "music_`user`, user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select subquery_for_count.`m.id`, 1 from (select m.id as `m.id` from music as m where 1 != 1) as subquery_for_count where 1 != 1", + "Query": "select distinct subquery_for_count.`m.id`, 1 from (select m.id as `m.id` from music as m) as subquery_for_count", + "Table": "music" + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select subquery_for_count.user_id, weight_string(subquery_for_count.user_id) from (select u.user_id from `user` as u, user_extra as ue where 1 != 1) as subquery_for_count where 1 != 1", + "Query": "select distinct subquery_for_count.user_id, weight_string(subquery_for_count.user_id) from (select u.user_id from `user` as u, user_extra as ue where u.id = :m_id and u.id = ue.user_id) as subquery_for_count", + "Table": "`user`, user_extra", + "Values": [ + ":m_id" + ], + "Vindex": "user_index" + } + ] + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "join table influencing vindex selection with ignore and use vindex syntax", + "query": "select u.intcol, u.id from user u use vindex (name_user_map) join music m ignore vindex(user_index) on u.col = m.col where u.name = 'bb' and u.id = 3 and m.user_id = 5 and m.id = 20", + "plan": { + "QueryType": "SELECT", + "Original": "select u.intcol, u.id from user u use vindex (name_user_map) join music m ignore vindex(user_index) on u.col = m.col where u.name = 'bb' and u.id = 3 and m.user_id = 5 and m.id = 20", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,L:1", + "JoinVars": { + "u_col": 2 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "'bb'" + ], + "Vindex": "name_user_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, keyspace_id from name_user_vdx where 1 != 1", + "Query": "select `name`, keyspace_id from name_user_vdx where `name` in ::__vals", + "Table": "name_user_vdx", + "Values": [ + "::name" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.intcol, u.id, u.col from `user` as u where 1 != 1", + "Query": "select u.intcol, u.id, u.col from `user` as u where u.`name` = 'bb' and u.id = 3", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as m where 1 != 1", + "Query": "select 1 from music as m where m.user_id = 5 and m.id = 20 and m.col = :u_col", + "Table": "music", + "Values": [ + "20" + ], + "Vindex": "music_user_map" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/hash_joins.txt b/go/vt/vtgate/planbuilder/testdata/hash_joins.txt deleted file mode 100644 index afc175a581c..00000000000 --- a/go/vt/vtgate/planbuilder/testdata/hash_joins.txt +++ /dev/null @@ -1,531 +0,0 @@ -# Test cases in this file are currently turned off -# Multi-route unique vindex constraint (with hash join) -"select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where user.id = 5" -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where user.id = 5", - "Instructions": { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "1", - "JoinVars": { - "user_col": 0 - }, - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.col from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where `user`.id = 5", - "Table": "`user`", - "Values": [ - "INT64(5)" - ], - "Vindex": "user_index" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select user_extra.id from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user_extra where user_extra.col = :user_col", - "Table": "user_extra" - } - ] - } -} -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where user.id = 5", - "Instructions": { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT16", - "JoinColumnIndexes": "2", - "Predicate": "`user`.col = user_extra.col", - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.col from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where `user`.id = 5", - "Table": "`user`", - "Values": [ - "INT64(5)" - ], - "Vindex": "user_index" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select user_extra.col, user_extra.id from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.col, user_extra.id from user_extra", - "Table": "user_extra" - } - ] - } -} - - -# Multi-route with non-route constraint, should use first route. -"select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where 1 = 1" -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where 1 = 1", - "Instructions": { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "1", - "JoinVars": { - "user_col": 0 - }, - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.col from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where 1 = 1", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select user_extra.id from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user_extra where user_extra.col = :user_col", - "Table": "user_extra" - } - ] - } -} -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where 1 = 1", - "Instructions": { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT16", - "JoinColumnIndexes": "2", - "Predicate": "`user`.col = user_extra.col", - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.col from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where 1 = 1", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select user_extra.col, user_extra.id from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.col, user_extra.id from user_extra where 1 = 1", - "Table": "user_extra" - } - ] - } -} - -# wire-up on within cross-shard derived table (hash-join version) -"select /*vt+ ALLOW_HASH_JOIN */ t.id from (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) as t" -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ t.id from (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) as t", - "Instructions": { - "OperatorType": "SimpleProjection", - "Columns": [ - 0 - ], - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1,-2", - "JoinVars": { - "user_col": 2 - }, - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.id, `user`.col1, `user`.col from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.id, `user`.col1, `user`.col from `user`", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select 1 from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from user_extra where user_extra.col = :user_col", - "Table": "user_extra" - } - ] - } - ] - } -} -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ t.id from (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) as t", - "Instructions": { - "OperatorType": "SimpleProjection", - "Columns": [ - 0 - ], - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT16", - "JoinColumnIndexes": "-2,-3", - "Predicate": "user_extra.col = `user`.col", - "TableName": "`user`_user_extra", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select `user`.col, `user`.id, `user`.col1 from `user` where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col, `user`.id, `user`.col1 from `user`", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select user_extra.col from user_extra where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ user_extra.col from user_extra", - "Table": "user_extra" - } - ] - } - ] - } -} - -# hash join on int columns -"select /*vt+ ALLOW_HASH_JOIN */ u.id from user as u join user as uu on u.intcol = uu.intcol" -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ u.id from user as u join user as uu on u.intcol = uu.intcol", - "Instructions": { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1", - "JoinVars": { - "u_intcol": 1 - }, - "TableName": "`user`_`user`", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select u.id, u.intcol from `user` as u where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ u.id, u.intcol from `user` as u", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select 1 from `user` as uu where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from `user` as uu where uu.intcol = :u_intcol", - "Table": "`user`" - } - ] - } -} -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ u.id from user as u join user as uu on u.intcol = uu.intcol", - "Instructions": { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT16", - "JoinColumnIndexes": "-2", - "Predicate": "u.intcol = uu.intcol", - "TableName": "`user`_`user`", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select u.intcol, u.id from `user` as u where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ u.intcol, u.id from `user` as u", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select uu.intcol from `user` as uu where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ uu.intcol from `user` as uu", - "Table": "`user`" - } - ] - } -} - -# Author5.joins(books: [{orders: :customer}, :supplier]) (with hash join) -"select /*vt+ ALLOW_HASH_JOIN */ author5s.* from author5s join book6s on book6s.author5_id = author5s.id join book6s_order2s on book6s_order2s.book6_id = book6s.id join order2s on order2s.id = book6s_order2s.order2_id join customer2s on customer2s.id = order2s.customer2_id join supplier5s on supplier5s.id = book6s.supplier5_id" -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ author5s.* from author5s join book6s on book6s.author5_id = author5s.id join book6s_order2s on book6s_order2s.book6_id = book6s.id join order2s on order2s.id = book6s_order2s.order2_id join customer2s on customer2s.id = order2s.customer2_id join supplier5s on supplier5s.id = book6s.supplier5_id", - "Instructions": { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1,-2,-3,-4", - "JoinVars": { - "book6s_supplier5_id": 4 - }, - "TableName": "author5s, book6s_book6s_order2s_order2s_customer2s_supplier5s", - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1,-2,-3,-4,-5", - "JoinVars": { - "order2s_customer2_id": 5 - }, - "TableName": "author5s, book6s_book6s_order2s_order2s_customer2s", - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1,-2,-3,-4,-5,1", - "JoinVars": { - "book6s_order2s_order2_id": 5 - }, - "TableName": "author5s, book6s_book6s_order2s_order2s", - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "-1,-2,-3,-4,-5,1", - "JoinVars": { - "book6s_id": 5 - }, - "TableName": "author5s, book6s_book6s_order2s", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select author5s.id, author5s.`name`, author5s.created_at, author5s.updated_at, book6s.supplier5_id, book6s.id from author5s join book6s on book6s.author5_id = author5s.id where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ author5s.id, author5s.`name`, author5s.created_at, author5s.updated_at, book6s.supplier5_id, book6s.id from author5s join book6s on book6s.author5_id = author5s.id", - "Table": "author5s, book6s" - }, - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select book6s_order2s.order2_id from book6s_order2s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ book6s_order2s.order2_id from book6s_order2s where book6s_order2s.book6_id = :book6s_id", - "Table": "book6s_order2s", - "Values": [ - ":book6s_id" - ], - "Vindex": "binary_md5" - } - ] - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select order2s.customer2_id from order2s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ order2s.customer2_id from order2s where order2s.id = :book6s_order2s_order2_id", - "Table": "order2s" - } - ] - }, - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select 1 from customer2s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from customer2s where customer2s.id = :order2s_customer2_id", - "Table": "customer2s", - "Values": [ - ":order2s_customer2_id" - ], - "Vindex": "binary_md5" - } - ] - }, - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select 1 from supplier5s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from supplier5s where supplier5s.id = :book6s_supplier5_id", - "Table": "supplier5s", - "Values": [ - ":book6s_supplier5_id" - ], - "Vindex": "binary_md5" - } - ] - } -} -{ - "QueryType": "SELECT", - "Original": "select /*vt+ ALLOW_HASH_JOIN */ author5s.* from author5s join book6s on book6s.author5_id = author5s.id join book6s_order2s on book6s_order2s.book6_id = book6s.id join order2s on order2s.id = book6s_order2s.order2_id join customer2s on customer2s.id = order2s.customer2_id join supplier5s on supplier5s.id = book6s.supplier5_id", - "Instructions": { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT64", - "JoinColumnIndexes": "2,3,4,5", - "Predicate": "order2s.id = book6s_order2s.order2_id", - "TableName": "customer2s, order2s_author5s, book6s_book6s_order2s_supplier5s", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select order2s.id from order2s, customer2s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ order2s.id from order2s, customer2s where customer2s.id = order2s.customer2_id", - "Table": "customer2s, order2s" - }, - { - "OperatorType": "Join", - "Variant": "HashJoin", - "ComparisonType": "INT64", - "JoinColumnIndexes": "-1,-2,-3,-4,-5", - "Predicate": "supplier5s.id = book6s.supplier5_id", - "TableName": "author5s, book6s_book6s_order2s_supplier5s", - "Inputs": [ - { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "1,-3,-4,-5,-6", - "JoinVars": { - "book6s_id": 0 - }, - "Predicate": "book6s_order2s.book6_id = book6s.id", - "TableName": "author5s, book6s_book6s_order2s", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select book6s.id, book6s.supplier5_id, author5s.id as id, author5s.`name` as `name`, author5s.created_at as created_at, author5s.updated_at as updated_at from author5s, book6s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ book6s.id, book6s.supplier5_id, author5s.id as id, author5s.`name` as `name`, author5s.created_at as created_at, author5s.updated_at as updated_at from author5s, book6s where book6s.author5_id = author5s.id", - "Table": "author5s, book6s" - }, - { - "OperatorType": "Route", - "Variant": "EqualUnique", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select book6s_order2s.order2_id from book6s_order2s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ book6s_order2s.order2_id from book6s_order2s where book6s_order2s.book6_id = :book6s_id", - "Table": "book6s_order2s", - "Values": [ - ":book6s_id" - ], - "Vindex": "binary_md5" - } - ] - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select supplier5s.id from supplier5s where 1 != 1", - "Query": "select /*vt+ ALLOW_HASH_JOIN */ supplier5s.id from supplier5s", - "Table": "supplier5s" - } - ] - } - ] - } -} diff --git a/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json index 50234d5ed73..84b6f551b73 100644 --- a/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json @@ -103,7 +103,7 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -146,13 +146,13 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci", - "1: utf8mb4_0900_ai_ci", - "2: utf8mb4_0900_ai_ci", - "3: utf8mb4_0900_ai_ci", - "4: utf8mb4_0900_ai_ci", + "0: utf8mb3_general_ci", + "1: utf8mb3_general_ci", + "2: utf8mb3_general_ci", + "3: utf8mb3_general_ci", + "4: utf8mb3_general_ci", "5", - "6: utf8mb4_0900_ai_ci", + "6: utf8mb3_general_ci", "7", "8", "9", @@ -163,10 +163,10 @@ "14", "15", "16", - "17: utf8mb4_0900_ai_ci", + "17: utf8mb3_general_ci", "18", - "19: utf8mb4_0900_ai_ci", - "20: utf8mb4_0900_ai_ci" + "19: utf8mb3_general_ci", + "20: utf8mb3_general_ci" ], "Inputs": [ { @@ -307,7 +307,7 @@ "Sharded": false }, "FieldQuery": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where 1 != 1", - "Query": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.referenced_column_name is not null order by ordinal_position asc", + "Query": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.referenced_column_name is not null order by kcu.ordinal_position asc", "SysTableTableSchema": "[:v1]", "Table": "information_schema.key_column_usage" }, @@ -1000,7 +1000,7 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -1037,10 +1037,10 @@ }, { "comment": "merge even one side have schema name in subquery", - "query": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `COLUMN_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", + "query": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `TABLE_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", "plan": { "QueryType": "SELECT", - "Original": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `COLUMN_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", + "Original": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `TABLE_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", "Instructions": { "OperatorType": "UncorrelatedSubquery", "Variant": "PulloutIn", @@ -1053,7 +1053,7 @@ "InputName": "SubQuery", "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -1066,8 +1066,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select :COLUMN_NAME from information_schema.`tables` as t where 1 != 1", - "Query": "select distinct :COLUMN_NAME from information_schema.`tables` as t where t.TABLE_SCHEMA = :__vtschemaname /* VARCHAR */", + "FieldQuery": "select TABLE_NAME from information_schema.`tables` as t where 1 != 1", + "Query": "select distinct TABLE_NAME from information_schema.`tables` as t where t.TABLE_SCHEMA = :__vtschemaname /* VARCHAR */", "SysTableTableSchema": "['a']", "Table": "information_schema.`tables`" }, @@ -1139,5 +1139,47 @@ "Table": "information_schema.apa" } } + }, + { + "comment": "LIMIT 1 inside derived table on the RHS should not be a problem", + "query": "SELECT c.column_name FROM information_schema.columns c JOIN ( SELECT table_name FROM information_schema.tables WHERE table_schema != 'information_schema' LIMIT 1 ) AS tables ON tables.table_name = c.table_name", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT c.column_name FROM information_schema.columns c JOIN ( SELECT table_name FROM information_schema.tables WHERE table_schema != 'information_schema' LIMIT 1 ) AS tables ON tables.table_name = c.table_name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "tables_table_name": 0 + }, + "TableName": "information_schema.`tables`_information_schema.`columns`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select table_name from (select table_name from information_schema.`tables` where 1 != 1) as `tables` where 1 != 1", + "Query": "select table_name from (select table_name from information_schema.`tables` where table_schema != 'information_schema' limit 1) as `tables`", + "Table": "information_schema.`tables`" + }, + { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select c.column_name from information_schema.`columns` as c where 1 != 1", + "Query": "select c.column_name from information_schema.`columns` as c where c.table_name = :c_table_name /* VARCHAR */", + "SysTableTableName": "[c_table_name::tables_table_name]", + "Table": "information_schema.`columns`" + } + ] + } + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json index b37804b9584..ce870bf2437 100644 --- a/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json @@ -103,7 +103,7 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -146,11 +146,11 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci", - "1: utf8mb4_0900_ai_ci", - "2: utf8mb4_0900_ai_ci", + "0: utf8mb3_general_ci", + "1: utf8mb3_general_ci", + "2: utf8mb3_general_ci", "3", - "4: utf8mb4_0900_ai_ci", + "4: utf8mb3_general_ci", "5", "6", "7", @@ -163,10 +163,10 @@ "14", "15", "16", - "17: utf8mb4_0900_ai_ci", + "17: utf8mb3_general_ci", "18", - "19: utf8mb4_0900_ai_ci", - "20: utf8mb4_0900_ai_ci" + "19: utf8mb3_general_ci", + "20: utf8mb3_general_ci" ], "Inputs": [ { @@ -307,7 +307,7 @@ "Sharded": false }, "FieldQuery": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where 1 != 1", - "Query": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.referenced_column_name is not null order by ordinal_position asc", + "Query": "select kcu.constraint_name as constraint_name, kcu.column_name as column_name, kcu.referenced_table_name as referenced_table_name, kcu.referenced_column_name as referenced_column_name, kcu.ordinal_position as ordinal_position, kcu.table_name as table_name from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.referenced_column_name is not null order by kcu.ordinal_position asc", "SysTableTableSchema": "[:v1]", "Table": "information_schema.key_column_usage" }, @@ -1065,7 +1065,7 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -1102,10 +1102,10 @@ }, { "comment": "merge even one side have schema name in subquery", - "query": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `COLUMN_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", + "query": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `TABLE_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", "plan": { "QueryType": "SELECT", - "Original": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `COLUMN_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", + "Original": "select `COLLATION_NAME` from information_schema.`COLUMNS` t where `COLUMN_NAME` in (select `TABLE_NAME` from information_schema.tables t where t.TABLE_SCHEMA = 'a' union select `COLUMN_NAME` from information_schema.columns)", "Instructions": { "OperatorType": "UncorrelatedSubquery", "Variant": "PulloutIn", @@ -1118,7 +1118,7 @@ "InputName": "SubQuery", "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -1131,8 +1131,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select :COLUMN_NAME from information_schema.`tables` as t where 1 != 1", - "Query": "select distinct :COLUMN_NAME from information_schema.`tables` as t where t.TABLE_SCHEMA = :__vtschemaname /* VARCHAR */", + "FieldQuery": "select TABLE_NAME from information_schema.`tables` as t where 1 != 1", + "Query": "select distinct TABLE_NAME from information_schema.`tables` as t where t.TABLE_SCHEMA = :__vtschemaname /* VARCHAR */", "SysTableTableSchema": "['a']", "Table": "information_schema.`tables`" }, @@ -1261,5 +1261,47 @@ "Table": "information_schema.apa" } } + }, + { + "comment": "LIMIT 1 inside derived table on the RHS should not be a problem", + "query": "SELECT c.column_name FROM information_schema.columns c JOIN ( SELECT table_name FROM information_schema.tables WHERE table_schema != 'information_schema' LIMIT 1 ) AS tables ON tables.table_name = c.table_name", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT c.column_name FROM information_schema.columns c JOIN ( SELECT table_name FROM information_schema.tables WHERE table_schema != 'information_schema' LIMIT 1 ) AS tables ON tables.table_name = c.table_name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "tables_table_name": 0 + }, + "TableName": "information_schema.`tables`_information_schema.`columns`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select table_name from (select table_name from information_schema.`tables` where 1 != 1) as `tables` where 1 != 1", + "Query": "select table_name from (select table_name from information_schema.`tables` where table_schema != 'information_schema' limit 1) as `tables`", + "Table": "information_schema.`tables`" + }, + { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select c.column_name from information_schema.`columns` as c where 1 != 1", + "Query": "select c.column_name from information_schema.`columns` as c where c.table_name = :c_table_name /* VARCHAR */", + "SysTableTableName": "[c_table_name::tables_table_name]", + "Table": "information_schema.`columns`" + } + ] + } + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/large_union_cases.json b/go/vt/vtgate/planbuilder/testdata/large_union_cases.json index 2d66bc62d42..89adb07335a 100644 --- a/go/vt/vtgate/planbuilder/testdata/large_union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/large_union_cases.json @@ -23,8 +23,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select content, user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where 1 != 1) union (select content, user_id from music where 1 != 1)) as dt where 1 != 1", - "Query": "select content, user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where user_id = 1270698330 order by created_at asc, id asc limit 11) union (select content, user_id from music where user_id = 1270698330 order by created_at asc, id asc limit 11)) as dt", + "FieldQuery": "select dt.content, dt.user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where 1 != 1) union (select content, user_id from music where 1 != 1)) as dt where 1 != 1", + "Query": "select dt.content, dt.user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where user_id = 1270698330 order by created_at asc, id asc limit 11) union (select content, user_id from music where user_id = 1270698330 order by created_at asc, id asc limit 11)) as dt", "Table": "music", "Values": [ "1270698330" @@ -38,8 +38,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select content, user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where 1 != 1) union (select content, user_id from music where 1 != 1)) as dt where 1 != 1", - "Query": "select content, user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where user_id = 1270699497 order by created_at asc, id asc limit 11) union (select content, user_id from music where user_id = 1270699497 order by created_at asc, id asc limit 11)) as dt", + "FieldQuery": "select dt.content, dt.user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where 1 != 1) union (select content, user_id from music where 1 != 1)) as dt where 1 != 1", + "Query": "select dt.content, dt.user_id, weight_string(content), weight_string(user_id) from ((select content, user_id from music where user_id = 1270699497 order by created_at asc, id asc limit 11) union (select content, user_id from music where user_id = 1270699497 order by created_at asc, id asc limit 11)) as dt", "Table": "music", "Values": [ "1270699497" diff --git a/go/vt/vtgate/planbuilder/testdata/lock_cases.json b/go/vt/vtgate/planbuilder/testdata/lock_cases.json index c14ba026869..2490424a1ec 100644 --- a/go/vt/vtgate/planbuilder/testdata/lock_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/lock_cases.json @@ -97,7 +97,7 @@ "QueryType": "UNLOCK_TABLES", "Original": "unlock tables", "Instructions": { - "OperatorType": "Rows" + "OperatorType": "UnlockTables" } } }, @@ -124,5 +124,95 @@ "main.dual" ] } + }, + { + "comment": "select nowait", + "query": "select u.col, u.bar from user u join music m on u.foo = m.foo for update nowait", + "plan": { + "QueryType": "SELECT", + "Original": "select u.col, u.bar from user u join music m on u.foo = m.foo for update nowait", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,L:1", + "JoinVars": { + "u_foo": 2 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.col, u.bar, u.foo from `user` as u where 1 != 1", + "Query": "select u.col, u.bar, u.foo from `user` as u for update nowait", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as m where 1 != 1", + "Query": "select 1 from music as m where m.foo = :u_foo for update nowait", + "Table": "music" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "select skip locked", + "query": "select u.col, u.bar from user u join music m on u.foo = m.foo for share skip locked", + "plan": { + "QueryType": "SELECT", + "Original": "select u.col, u.bar from user u join music m on u.foo = m.foo for share skip locked", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,L:1", + "JoinVars": { + "u_foo": 2 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.col, u.bar, u.foo from `user` as u where 1 != 1", + "Query": "select u.col, u.bar, u.foo from `user` as u for share skip locked", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as m where 1 != 1", + "Query": "select 1 from music as m where m.foo = :u_foo for share skip locked", + "Table": "music" + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json index cc09c95282f..3ca7e1059e4 100644 --- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.json @@ -585,9 +585,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select u.a, convert(a, binary), weight_string(convert(a, binary)) from `user` as u where 1 != 1", + "FieldQuery": "select u.a, convert(u.a, binary), weight_string(convert(u.a, binary)) from `user` as u where 1 != 1", "OrderBy": "(1|2) DESC", - "Query": "select u.a, convert(a, binary), weight_string(convert(a, binary)) from `user` as u order by convert(a, binary) desc", + "Query": "select u.a, convert(u.a, binary), weight_string(convert(u.a, binary)) from `user` as u order by convert(u.a, binary) desc", "Table": "`user`" }, { diff --git a/go/vt/vtgate/planbuilder/testdata/other_read_cases.json b/go/vt/vtgate/planbuilder/testdata/other_read_cases.json index 92c8d132eda..8b67623186a 100644 --- a/go/vt/vtgate/planbuilder/testdata/other_read_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/other_read_cases.json @@ -14,22 +14,10 @@ "TargetDestination": "AnyShard()", "Query": "explain select * from `user`", "SingleShardOnly": true - } - } - }, - { - "comment": "Explain Vitess statement", - "query": "explain format=vitess select * from user", - "plan": { - "QueryType": "EXPLAIN", - "Original": "explain format=vitess select * from user", - "Instructions": { - "OperatorType": "Rows", - "Fields": { - "JSON": "VARCHAR" - }, - "RowCount": 1 - } + }, + "TablesUsed": [ + "main.user" + ] } }, { @@ -67,7 +55,10 @@ "TargetDestination": "AnyShard()", "Query": "explain select * from t", "SingleShardOnly": true - } + }, + "TablesUsed": [ + "main.t" + ] } }, { @@ -85,7 +76,75 @@ "TargetDestination": "AnyShard()", "Query": "explain select * from t", "SingleShardOnly": true - } + }, + "TablesUsed": [ + "main.t" + ] + } + }, + { + "comment": "explain - routed table with same name", + "query": "explain select 1, second_user.user.id from second_user.user", + "plan": { + "QueryType": "EXPLAIN", + "Original": "explain select 1, second_user.user.id from second_user.user", + "Instructions": { + "OperatorType": "Send", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetDestination": "AnyShard()", + "Query": "explain select 1, `user`.id from `user`", + "SingleShardOnly": true + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "explain - routed table with different name", + "query": "explain select 1, second_user.foo.id, foo.col from second_user.foo", + "plan": { + "QueryType": "EXPLAIN", + "Original": "explain select 1, second_user.foo.id, foo.col from second_user.foo", + "Instructions": { + "OperatorType": "Send", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetDestination": "AnyShard()", + "Query": "explain select 1, foo.id, foo.col from `user` as foo", + "SingleShardOnly": true + }, + "TablesUsed": [ + "user.foo" + ] + } + }, + { + "comment": "explain - routed table with join on different table on routed keyspace", + "query": "explain select 1, second_user.foo.id, foo.col from second_user.foo join user.user join user.music", + "plan": { + "QueryType": "EXPLAIN", + "Original": "explain select 1, second_user.foo.id, foo.col from second_user.foo join user.user join user.music", + "Instructions": { + "OperatorType": "Send", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "TargetDestination": "AnyShard()", + "Query": "explain select 1, foo.id, foo.col from `user` as foo join `user` join music", + "SingleShardOnly": true + }, + "TablesUsed": [ + "user.foo", + "user.user", + "user.music" + ] } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json index cad8e9be1eb..9093fe2f390 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.json @@ -88,7 +88,7 @@ "Sharded": true }, "FieldQuery": "select `user`.col1 as a, `user`.col2 from `user` where 1 != 1", - "Query": "select `user`.col1 as a, `user`.col2 from `user` where `user`.col1 = 1 and `user`.col1 = `user`.col2 and 1 = 1", + "Query": "select `user`.col1 as a, `user`.col2 from `user` where `user`.col1 = 1 and `user`.col1 = `user`.col2", "Table": "`user`" }, { @@ -99,7 +99,7 @@ "Sharded": true }, "FieldQuery": "select user_extra.col3 from user_extra where 1 != 1", - "Query": "select user_extra.col3 from user_extra where user_extra.col3 = 1 and 1 = 1", + "Query": "select user_extra.col3 from user_extra where user_extra.col3 = 1", "Table": "user_extra" } ] @@ -320,7 +320,7 @@ }, "FieldQuery": "select a, `user`.textcol1, b, weight_string(a), weight_string(b) from `user` where 1 != 1", "OrderBy": "(0|3) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|4) ASC", - "Query": "select a, `user`.textcol1, b, weight_string(a), weight_string(b) from `user` order by a asc, textcol1 asc, b asc", + "Query": "select a, `user`.textcol1, b, weight_string(a), weight_string(b) from `user` order by a asc, `user`.textcol1 asc, b asc", "ResultColumns": 3, "Table": "`user`" }, @@ -342,9 +342,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b) from `user` where 1 != 1", - "OrderBy": "(0|4) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|5) ASC, 3 ASC COLLATE latin1_swedish_ci", - "Query": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b) from `user` order by a asc, textcol1 asc, b asc, textcol2 asc", + "FieldQuery": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b), weight_string(textcol2) from `user` where 1 != 1", + "OrderBy": "(0|4) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|5) ASC, (3|6) ASC COLLATE ", + "Query": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b), weight_string(textcol2) from `user` order by a asc, textcol1 asc, b asc, textcol2 asc", "ResultColumns": 4, "Table": "`user`" }, @@ -373,7 +373,7 @@ }, "FieldQuery": "select id as foo, weight_string(id) from music where 1 != 1", "OrderBy": "(0|1) ASC", - "Query": "select id as foo, weight_string(id) from music order by foo asc", + "Query": "select id as foo, weight_string(id) from music order by id asc", "ResultColumns": 1, "Table": "music" }, @@ -526,7 +526,7 @@ "Sharded": true }, "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1", - "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by a asc", + "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by `user`.col1 asc", "Table": "`user`", "Values": [ "1" @@ -579,7 +579,7 @@ "Sharded": true }, "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1", - "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by a asc", + "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by `user`.col1 asc", "Table": "`user`", "Values": [ "1" @@ -1365,7 +1365,7 @@ }, "FieldQuery": "select id as foo, weight_string(id) from music where 1 != 1", "OrderBy": "(0|1) ASC", - "Query": "select id as foo, weight_string(id) from music order by foo asc", + "Query": "select id as foo, weight_string(id) from music order by id asc", "ResultColumns": 1, "Table": "music" }, @@ -1389,7 +1389,7 @@ }, "FieldQuery": "select id as foo, id2 as id, weight_string(id2) from music where 1 != 1", "OrderBy": "(1|2) ASC", - "Query": "select id as foo, id2 as id, weight_string(id2) from music order by id asc", + "Query": "select id as foo, id2 as id, weight_string(id2) from music order by id2 asc", "ResultColumns": 2, "Table": "music" }, @@ -1902,9 +1902,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `user`.col1 as a, a collate utf8_general_ci, weight_string(a collate utf8_general_ci) from `user` where 1 != 1", + "FieldQuery": "select `user`.col1 as a, `user`.col1 collate utf8_general_ci, weight_string(`user`.col1 collate utf8_general_ci) from `user` where 1 != 1", "OrderBy": "(1|2) ASC", - "Query": "select `user`.col1 as a, a collate utf8_general_ci, weight_string(a collate utf8_general_ci) from `user` order by a collate utf8_general_ci asc", + "Query": "select `user`.col1 as a, `user`.col1 collate utf8_general_ci, weight_string(`user`.col1 collate utf8_general_ci) from `user` order by `user`.col1 collate utf8_general_ci asc", "ResultColumns": 1, "Table": "`user`" }, @@ -1950,9 +1950,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `user`.col1 as a, a collate utf8_general_ci, weight_string(a collate utf8_general_ci) from `user` where 1 != 1", + "FieldQuery": "select `user`.col1 as a, `user`.col1 collate utf8_general_ci, weight_string(`user`.col1 collate utf8_general_ci) from `user` where 1 != 1", "OrderBy": "(1|2) ASC", - "Query": "select `user`.col1 as a, a collate utf8_general_ci, weight_string(a collate utf8_general_ci) from `user` order by a collate utf8_general_ci asc", + "Query": "select `user`.col1 as a, `user`.col1 collate utf8_general_ci, weight_string(`user`.col1 collate utf8_general_ci) from `user` order by `user`.col1 collate utf8_general_ci asc", "ResultColumns": 1, "Table": "`user`" }, @@ -2021,54 +2021,47 @@ "QueryType": "SELECT", "Original": "select a.tcol1 from user a join music b where a.tcol1 = b.tcol2 group by a.tcol1 having repeat(a.tcol1,min(a.id)) like \"A\\%B\" order by a.tcol1", "Instructions": { - "OperatorType": "Sort", - "Variant": "Memory", - "OrderBy": "(0|2) ASC", + "OperatorType": "Filter", + "Predicate": "repeat(a.tcol1, min(a.id)) like 'A\\%B'", "ResultColumns": 1, "Inputs": [ { - "OperatorType": "Filter", - "Predicate": "repeat(a.tcol1, min(a.id)) like 'A\\%B'", + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "min(1|3) AS min(a.id)", + "GroupBy": "(0|2)", "Inputs": [ { - "OperatorType": "Aggregate", - "Variant": "Ordered", - "Aggregates": "min(1|3) AS min(a.id)", - "GroupBy": "(0|2)", + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:1,L:0,L:2,L:3", + "JoinVars": { + "a_tcol1": 1 + }, + "TableName": "`user`_music", "Inputs": [ { - "OperatorType": "Join", - "Variant": "Join", - "JoinColumnIndexes": "L:1,L:0,L:2,L:3", - "JoinVars": { - "a_tcol1": 1 + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true }, - "TableName": "`user`_music", - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select min(a.id), a.tcol1, weight_string(a.tcol1), weight_string(a.id) from `user` as a where 1 != 1 group by a.tcol1, weight_string(a.tcol1), weight_string(a.id)", - "OrderBy": "(1|2) ASC", - "Query": "select min(a.id), a.tcol1, weight_string(a.tcol1), weight_string(a.id) from `user` as a group by a.tcol1, weight_string(a.tcol1), weight_string(a.id) order by a.tcol1 asc", - "Table": "`user`" - }, - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select 1 from music as b where 1 != 1 group by .0", - "Query": "select 1 from music as b where b.tcol2 = :a_tcol1 group by .0", - "Table": "music" - } - ] + "FieldQuery": "select min(a.id), a.tcol1, weight_string(a.tcol1), weight_string(a.id) from `user` as a where 1 != 1 group by a.tcol1, weight_string(a.tcol1), weight_string(a.id)", + "OrderBy": "(1|2) ASC", + "Query": "select min(a.id), a.tcol1, weight_string(a.tcol1), weight_string(a.id) from `user` as a group by a.tcol1, weight_string(a.tcol1), weight_string(a.id) order by a.tcol1 asc", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music as b where 1 != 1 group by .0", + "Query": "select 1 from music as b where b.tcol2 = :a_tcol1 group by .0", + "Table": "music" } ] } @@ -2181,5 +2174,66 @@ "user.user" ] } + }, + { + "comment": "DISTINCT on an unsupported collation should fall back on weightstrings", + "query": "select distinct textcol2 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select distinct textcol2 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1): " + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select textcol2, weight_string(textcol2) from `user` where 1 != 1", + "Query": "select distinct textcol2, weight_string(textcol2) from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "No weightstrings or derived table needed", + "query": "select textcol1 from user union select textcol1 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select textcol1 from user union select textcol1 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "0: latin1_swedish_ci" + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select textcol1 from `user` where 1 != 1 union select textcol1 from `user` where 1 != 1", + "Query": "select textcol1 from `user` union select textcol1 from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index f26cfc4f065..0ef10b5247f 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -980,6 +980,84 @@ ] } }, + { + "comment": "Selection but explicitly ignore a vindex", + "query": "select * from user ignore vindex (user_index) where id = 1", + "plan": { + "QueryType": "SELECT", + "Original": "select * from user ignore vindex (user_index) where id = 1", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from `user` where 1 != 1", + "Query": "select * from `user` where id = 1", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Selection but make the planner explicitly use a vindex", + "query": "select intcol, id from user use vindex (name_user_map) where costly = 'aa' and name = 'bb' and id = 3", + "plan": { + "QueryType": "SELECT", + "Original": "select intcol, id from user use vindex (name_user_map) where costly = 'aa' and name = 'bb' and id = 3", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "'bb'" + ], + "Vindex": "name_user_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, keyspace_id from name_user_vdx where 1 != 1", + "Query": "select `name`, keyspace_id from name_user_vdx where `name` in ::__vals", + "Table": "name_user_vdx", + "Values": [ + "::name" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select intcol, id from `user` where 1 != 1", + "Query": "select intcol, id from `user` where costly = 'aa' and `name` = 'bb' and id = 3", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Vindex hint on a non-existing vindex", + "query": "select * from user use vindex (does_not_exist) where id = 1", + "plan": "VT09021: Vindex 'does_not_exist' does not exist in table 'user.user'" + }, { "comment": "sharded limit offset", "query": "select user_id from music order by user_id limit 10, 20", @@ -1129,7 +1207,7 @@ "Sharded": true }, "FieldQuery": "select user0_.col as col0_ from `user` as user0_ where 1 != 1", - "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by col0_ desc limit 3", + "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by user0_.col desc limit 3", "Table": "`user`", "Values": [ "1" @@ -1428,8 +1506,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select col1, col2 from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1", - "Query": "select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a", + "FieldQuery": "select * from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1", + "Query": "select * from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a", "Table": "unsharded" }, "TablesUsed": [ @@ -1573,6 +1651,28 @@ ] } }, + { + "comment": "routing table on music", + "query": "select * from second_user.bar where id > 2", + "plan": { + "QueryType": "SELECT", + "Original": "select * from second_user.bar where id > 2", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from music as bar where 1 != 1", + "Query": "select * from music as bar where id > 2", + "Table": "music" + }, + "TablesUsed": [ + "user.music" + ] + } + }, { "comment": "testing SingleRow Projection", "query": "select 42", @@ -2021,11 +2121,11 @@ } }, { - "comment": "select (select col from user limit 1) as a from user join user_extra order by a", - "query": "select (select col from user limit 1) as a from user join user_extra order by a", + "comment": "subquery in select expression of derived table", + "query": "select t.a from (select (select col from user limit 1) as a from user join user_extra) t", "plan": { "QueryType": "SELECT", - "Original": "select (select col from user limit 1) as a from user join user_extra order by a", + "Original": "select t.a from (select (select col from user limit 1) as a from user join user_extra) t", "Instructions": { "OperatorType": "Join", "Variant": "Join", @@ -2065,9 +2165,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select :__sq1 as a, weight_string(:__sq1) from `user` where 1 != 1", - "OrderBy": "(0|1) ASC", - "Query": "select :__sq1 as a, weight_string(:__sq1) from `user` order by a asc", + "FieldQuery": "select t.a from (select :__sq1 as a from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.a from (select :__sq1 as a from `user`) as t", "Table": "`user`" } ] @@ -2092,11 +2191,11 @@ } }, { - "comment": "select t.a from (select (select col from user limit 1) as a from user join user_extra) t", - "query": "select t.a from (select (select col from user limit 1) as a from user join user_extra) t", + "comment": "select (select col from user limit 1) as a from user join user_extra order by a", + "query": "select (select col from user limit 1) as a from user join user_extra order by a", "plan": { "QueryType": "SELECT", - "Original": "select t.a from (select (select col from user limit 1) as a from user join user_extra) t", + "Original": "select (select col from user limit 1) as a from user join user_extra order by a", "Instructions": { "OperatorType": "Join", "Variant": "Join", @@ -2136,8 +2235,9 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select t.a from (select :__sq1 as a from `user` where 1 != 1) as t where 1 != 1", - "Query": "select t.a from (select :__sq1 as a from `user`) as t", + "FieldQuery": "select :__sq1 as __sq1, weight_string(:__sq1) from `user` where 1 != 1", + "OrderBy": "(0|1) ASC", + "Query": "select :__sq1 as __sq1, weight_string(:__sq1) from `user` order by __sq1 asc", "Table": "`user`" } ] @@ -2615,7 +2715,7 @@ "Sharded": false }, "FieldQuery": "select 1 from (select col, count(*) as a from unsharded where 1 != 1 group by col) as f left join unsharded as u on f.col = u.id where 1 != 1", - "Query": "select 1 from (select col, count(*) as a from unsharded group by col having count(*) > 0 limit 0, 12) as f left join unsharded as u on f.col = u.id", + "Query": "select 1 from (select col, count(*) as a from unsharded group by col having a > 0 limit 0, 12) as f left join unsharded as u on f.col = u.id", "Table": "unsharded" }, "TablesUsed": [ @@ -3048,15 +3148,15 @@ "QueryType": "SELECT", "Original": "select insert('Quadratic', 3, 4, 'What')", "Instructions": { - "OperatorType": "Route", - "Variant": "Reference", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "FieldQuery": "select insert('Quadratic', 3, 4, 'What') from dual where 1 != 1", - "Query": "select insert('Quadratic', 3, 4, 'What') from dual", - "Table": "dual" + "OperatorType": "Projection", + "Expressions": [ + "'QuWhattic' as insert('Quadratic', 3, 4, 'What')" + ], + "Inputs": [ + { + "OperatorType": "SingleRow" + } + ] }, "TablesUsed": [ "main.dual" @@ -3966,7 +4066,7 @@ "Sharded": true }, "FieldQuery": "select id from (select id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", - "Query": "select id from (select id from (select music.id from music where music.user_id in ::__vals) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", + "Query": "select id from (select id from (select music.id from music where music.user_id in ::__vals) as subquery_for_limit limit :__upper_limit) as subquery_for_limit", "Table": "music", "Values": [ "(5, 6)" @@ -4025,7 +4125,7 @@ "Sharded": true }, "FieldQuery": "select id from (select id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", - "Query": "select id from (select id from (select music.id from music) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", + "Query": "select id from (select id from (select music.id from music) as subquery_for_limit limit :__upper_limit) as subquery_for_limit", "Table": "music" } ] @@ -4223,8 +4323,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select user_id from (select user_id from user_extra where 1 != 1) as ue where 1 != 1", - "Query": "select user_id from (select user_id from user_extra) as ue limit :__upper_limit", + "FieldQuery": "select ue.user_id from (select user_id from user_extra where 1 != 1) as ue where 1 != 1", + "Query": "select ue.user_id from (select user_id from user_extra) as ue limit :__upper_limit", "Table": "user_extra" } ] @@ -4664,6 +4764,28 @@ ] } }, + { + "comment": "name is in backfill vindex - not selected for vindex lookup", + "query": "select * from customer where name = 'x'", + "plan": { + "QueryType": "SELECT", + "Original": "select * from customer where name = 'x'", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where `name` = 'x'", + "Table": "customer" + }, + "TablesUsed": [ + "user.customer" + ] + } + }, { "comment": "email vindex is costly than phone vindex - but phone vindex is backfiling hence ignored", "query": "select * from customer where email = 'a@mail.com' and phone = 123456", @@ -4809,7 +4931,7 @@ }, "FieldQuery": "select u.foo, weight_string(u.foo) from `user` as u where 1 != 1", "OrderBy": "(0|1) ASC", - "Query": "select u.foo, weight_string(u.foo) from `user` as u order by foo asc", + "Query": "select u.foo, weight_string(u.foo) from `user` as u order by u.foo asc", "Table": "`user`" }, { @@ -4830,5 +4952,50 @@ "user.user_extra" ] } + }, + { + "comment": "Derived tables going to a single shard still need to expand derived table columns", + "query": "SELECT c.column_name FROM user c JOIN (SELECT table_name FROM unsharded LIMIT 1) AS tables ON tables.table_name = c.table_name", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT c.column_name FROM user c JOIN (SELECT table_name FROM unsharded LIMIT 1) AS tables ON tables.table_name = c.table_name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "tables_table_name": 0 + }, + "TableName": "unsharded_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select table_name from (select table_name from unsharded where 1 != 1) as `tables` where 1 != 1", + "Query": "select table_name from (select table_name from unsharded limit 1) as `tables`", + "Table": "unsharded" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select c.column_name from `user` as c where 1 != 1", + "Query": "select c.column_name from `user` as c where c.table_name = :tables_table_name", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/show_cases.json b/go/vt/vtgate/planbuilder/testdata/show_cases.json index c20a1c79f5a..896f762819e 100644 --- a/go/vt/vtgate/planbuilder/testdata/show_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/show_cases.json @@ -720,6 +720,24 @@ } } }, + { + "comment": "show vschema keyspaces", + "query": "show vschema keyspaces", + "plan": { + "QueryType": "SHOW", + "Original": "show vschema keyspaces", + "Instructions": { + "OperatorType": "Rows", + "Fields": { + "Comment": "VARCHAR", + "Foreign Key": "VARCHAR", + "Keyspace": "VARCHAR", + "Sharded": "VARCHAR" + }, + "RowCount": 7 + } + } + }, { "comment": "show vschema vindexes", "query": "show vschema vindexes", diff --git a/go/vt/vtgate/planbuilder/testdata/tpch_cases.json b/go/vt/vtgate/planbuilder/testdata/tpch_cases.json index f40ea961334..2d225808992 100644 --- a/go/vt/vtgate/planbuilder/testdata/tpch_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/tpch_cases.json @@ -2,7 +2,50 @@ { "comment": "TPC-H query 1", "query": "select l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, avg(l_quantity) as avg_qty, avg(l_extendedprice) as avg_price, avg(l_discount) as avg_disc, count(*) as count_order from lineitem where l_shipdate <= '1998-12-01' - interval '108' day group by l_returnflag, l_linestatus order by l_returnflag, l_linestatus", - "plan": "VT12001: unsupported: in scatter query: aggregation function 'avg(l_quantity) as avg_qty'" + "plan": { + "QueryType": "SELECT", + "Original": "select l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, avg(l_quantity) as avg_qty, avg(l_extendedprice) as avg_price, avg(l_discount) as avg_disc, count(*) as count_order from lineitem where l_shipdate <= '1998-12-01' - interval '108' day group by l_returnflag, l_linestatus order by l_returnflag, l_linestatus", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + ":0 as l_returnflag", + ":1 as l_linestatus", + ":2 as sum_qty", + ":3 as sum_base_price", + ":4 as sum_disc_price", + ":5 as sum_charge", + "sum(l_quantity) / count(l_quantity) as avg_qty", + "sum(l_extendedprice) / count(l_extendedprice) as avg_price", + "sum(l_discount) / count(l_discount) as avg_disc", + ":9 as count_order" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum(2) AS sum_qty, sum(3) AS sum_base_price, sum(4) AS sum_disc_price, sum(5) AS sum_charge, sum(6) AS avg_qty, sum(7) AS avg_price, sum(8) AS avg_disc, sum_count_star(9) AS count_order, sum_count(10) AS count(l_quantity), sum_count(11) AS count(l_extendedprice), sum_count(12) AS count(l_discount)", + "GroupBy": "(0|13), (1|14)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "main", + "Sharded": true + }, + "FieldQuery": "select l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, sum(l_quantity) as avg_qty, sum(l_extendedprice) as avg_price, sum(l_discount) as avg_disc, count(*) as count_order, count(l_quantity), count(l_extendedprice), count(l_discount), weight_string(l_returnflag), weight_string(l_linestatus) from lineitem where 1 != 1 group by l_returnflag, l_linestatus, weight_string(l_returnflag), weight_string(l_linestatus)", + "OrderBy": "(0|13) ASC, (1|14) ASC", + "Query": "select l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, sum(l_quantity) as avg_qty, sum(l_extendedprice) as avg_price, sum(l_discount) as avg_disc, count(*) as count_order, count(l_quantity), count(l_extendedprice), count(l_discount), weight_string(l_returnflag), weight_string(l_linestatus) from lineitem where l_shipdate <= '1998-12-01' - interval '108' day group by l_returnflag, l_linestatus, weight_string(l_returnflag), weight_string(l_linestatus) order by l_returnflag asc, l_linestatus asc", + "Table": "lineitem" + } + ] + } + ] + }, + "TablesUsed": [ + "main.lineitem" + ] + } }, { "comment": "TPC-H query 2", @@ -535,9 +578,9 @@ { "OperatorType": "Join", "Variant": "Join", - "JoinColumnIndexes": "L:0,R:0,L:1,R:1,L:2,L:5,R:2,L:6", + "JoinColumnIndexes": "L:0,R:0,L:1,R:1,L:2,L:4,R:2,L:5", "JoinVars": { - "n1_n_name": 4, + "n1_n_name": 1, "o_custkey": 3 }, "TableName": "lineitem_orders_supplier_nation_customer_nation", @@ -548,18 +591,17 @@ "sum(volume) * count(*) as revenue", ":2 as supp_nation", ":3 as l_year", - ":4 as orders.o_custkey", - ":5 as n1.n_name", - ":6 as weight_string(supp_nation)", - ":7 as weight_string(l_year)" + ":4 as o_custkey", + ":5 as weight_string(supp_nation)", + ":6 as weight_string(l_year)" ], "Inputs": [ { "OperatorType": "Join", "Variant": "Join", - "JoinColumnIndexes": "L:0,R:0,R:1,L:1,L:2,L:3,R:2,L:5", + "JoinColumnIndexes": "L:0,R:0,R:1,L:1,L:2,R:2,L:4", "JoinVars": { - "l_suppkey": 4 + "l_suppkey": 3 }, "TableName": "lineitem_orders_supplier_nation", "Inputs": [ @@ -568,18 +610,17 @@ "Expressions": [ "sum(volume) * count(*) as revenue", ":2 as l_year", - ":3 as orders.o_custkey", - ":4 as n1.n_name", - ":5 as lineitem.l_suppkey", - ":6 as weight_string(l_year)" + ":3 as o_custkey", + ":4 as l_suppkey", + ":5 as weight_string(l_year)" ], "Inputs": [ { "OperatorType": "Join", "Variant": "Join", - "JoinColumnIndexes": "L:0,R:0,L:1,L:2,L:3,L:4,L:6", + "JoinColumnIndexes": "L:0,R:0,L:1,R:1,L:2,L:4", "JoinVars": { - "l_orderkey": 5 + "l_orderkey": 3 }, "TableName": "lineitem_orders", "Inputs": [ @@ -590,9 +631,9 @@ "Name": "main", "Sharded": true }, - "FieldQuery": "select sum(volume) as revenue, l_year, shipping.`orders.o_custkey`, shipping.`n1.n_name`, shipping.`lineitem.l_suppkey`, shipping.`lineitem.l_orderkey`, weight_string(l_year), supp_nation, weight_string(supp_nation), cust_nation, weight_string(cust_nation) from (select extract(year from l_shipdate) as l_year, l_extendedprice * (1 - l_discount) as volume, orders.o_custkey as `orders.o_custkey`, lineitem.l_suppkey as `lineitem.l_suppkey`, lineitem.l_orderkey as `lineitem.l_orderkey` from lineitem where 1 != 1) as shipping where 1 != 1 group by l_year, shipping.`orders.o_custkey`, shipping.`n1.n_name`, shipping.`lineitem.l_suppkey`, shipping.`lineitem.l_orderkey`, weight_string(l_year)", - "OrderBy": "(7|8) ASC, (9|10) ASC, (1|6) ASC", - "Query": "select sum(volume) as revenue, l_year, shipping.`orders.o_custkey`, shipping.`n1.n_name`, shipping.`lineitem.l_suppkey`, shipping.`lineitem.l_orderkey`, weight_string(l_year), supp_nation, weight_string(supp_nation), cust_nation, weight_string(cust_nation) from (select extract(year from l_shipdate) as l_year, l_extendedprice * (1 - l_discount) as volume, orders.o_custkey as `orders.o_custkey`, lineitem.l_suppkey as `lineitem.l_suppkey`, lineitem.l_orderkey as `lineitem.l_orderkey` from lineitem where l_shipdate between date('1995-01-01') and date('1996-12-31')) as shipping group by l_year, shipping.`orders.o_custkey`, shipping.`n1.n_name`, shipping.`lineitem.l_suppkey`, shipping.`lineitem.l_orderkey`, weight_string(l_year) order by supp_nation asc, cust_nation asc, l_year asc", + "FieldQuery": "select sum(volume) as revenue, l_year, shipping.l_suppkey, shipping.l_orderkey, weight_string(l_year), supp_nation, weight_string(supp_nation), cust_nation, weight_string(cust_nation) from (select extract(year from l_shipdate) as l_year, l_extendedprice * (1 - l_discount) as volume, l_suppkey as l_suppkey, l_orderkey as l_orderkey from lineitem where 1 != 1) as shipping where 1 != 1 group by l_year, shipping.l_suppkey, shipping.l_orderkey, weight_string(l_year)", + "OrderBy": "(5|6) ASC, (7|8) ASC, (1|4) ASC", + "Query": "select sum(volume) as revenue, l_year, shipping.l_suppkey, shipping.l_orderkey, weight_string(l_year), supp_nation, weight_string(supp_nation), cust_nation, weight_string(cust_nation) from (select extract(year from l_shipdate) as l_year, l_extendedprice * (1 - l_discount) as volume, l_suppkey as l_suppkey, l_orderkey as l_orderkey from lineitem where l_shipdate between date('1995-01-01') and date('1996-12-31')) as shipping group by l_year, shipping.l_suppkey, shipping.l_orderkey, weight_string(l_year) order by supp_nation asc, cust_nation asc, l_year asc", "Table": "lineitem" }, { @@ -602,8 +643,8 @@ "Name": "main", "Sharded": true }, - "FieldQuery": "select count(*) from orders where 1 != 1 group by .0", - "Query": "select count(*) from orders where o_orderkey = :l_orderkey group by .0", + "FieldQuery": "select count(*), shipping.o_custkey from (select o_custkey as o_custkey from orders where 1 != 1) as shipping where 1 != 1 group by shipping.o_custkey", + "Query": "select count(*), shipping.o_custkey from (select o_custkey as o_custkey from orders where o_orderkey = :l_orderkey) as shipping group by shipping.o_custkey", "Table": "orders", "Values": [ ":l_orderkey" @@ -638,8 +679,8 @@ "Name": "main", "Sharded": true }, - "FieldQuery": "select count(*), shipping.`supplier.s_nationkey` from (select supplier.s_nationkey as `supplier.s_nationkey` from supplier where 1 != 1) as shipping where 1 != 1 group by shipping.`supplier.s_nationkey`", - "Query": "select count(*), shipping.`supplier.s_nationkey` from (select supplier.s_nationkey as `supplier.s_nationkey` from supplier where s_suppkey = :l_suppkey) as shipping group by shipping.`supplier.s_nationkey`", + "FieldQuery": "select count(*), shipping.s_nationkey from (select s_nationkey as s_nationkey from supplier where 1 != 1) as shipping where 1 != 1 group by shipping.s_nationkey", + "Query": "select count(*), shipping.s_nationkey from (select s_nationkey as s_nationkey from supplier where s_suppkey = :l_suppkey) as shipping group by shipping.s_nationkey", "Table": "supplier", "Values": [ ":l_suppkey" @@ -653,8 +694,8 @@ "Name": "main", "Sharded": true }, - "FieldQuery": "select count(*), supp_nation, weight_string(supp_nation) from (select n1.n_name as supp_nation from nation as n1 where 1 != 1) as shipping where 1 != 1 group by supp_nation, weight_string(supp_nation)", - "Query": "select count(*), supp_nation, weight_string(supp_nation) from (select n1.n_name as supp_nation from nation as n1 where n1.n_nationkey = :s_nationkey) as shipping group by supp_nation, weight_string(supp_nation)", + "FieldQuery": "select count(*), supp_nation, weight_string(supp_nation) from (select n1.n_name as supp_nation, n1.n_name = 'FRANCE' as `n1.n_name = 'FRANCE'`, n1.n_name = 'GERMANY' as `n1.n_name = 'GERMANY'` from nation as n1 where 1 != 1) as shipping where 1 != 1 group by supp_nation, weight_string(supp_nation)", + "Query": "select count(*), supp_nation, weight_string(supp_nation) from (select n1.n_name as supp_nation, n1.n_name = 'FRANCE' as `n1.n_name = 'FRANCE'`, n1.n_name = 'GERMANY' as `n1.n_name = 'GERMANY'` from nation as n1 where n1.n_nationkey = :s_nationkey) as shipping group by supp_nation, weight_string(supp_nation)", "Table": "nation", "Values": [ ":s_nationkey" @@ -693,8 +734,8 @@ "Name": "main", "Sharded": true }, - "FieldQuery": "select count(*), shipping.`customer.c_nationkey` from (select customer.c_nationkey as `customer.c_nationkey` from customer where 1 != 1) as shipping where 1 != 1 group by shipping.`customer.c_nationkey`", - "Query": "select count(*), shipping.`customer.c_nationkey` from (select customer.c_nationkey as `customer.c_nationkey` from customer where c_custkey = :o_custkey) as shipping group by shipping.`customer.c_nationkey`", + "FieldQuery": "select count(*), shipping.c_nationkey from (select c_nationkey as c_nationkey from customer where 1 != 1) as shipping where 1 != 1 group by shipping.c_nationkey", + "Query": "select count(*), shipping.c_nationkey from (select c_nationkey as c_nationkey from customer where c_custkey = :o_custkey) as shipping group by shipping.c_nationkey", "Table": "customer", "Values": [ ":o_custkey" @@ -1551,7 +1592,7 @@ "Sharded": true }, "FieldQuery": "select ps_suppkey, weight_string(ps_suppkey), ps_partkey from partsupp where 1 != 1", - "Query": "select ps_suppkey, weight_string(ps_suppkey), ps_partkey from partsupp where not :__sq_has_values and ps_suppkey not in ::__sq1", + "Query": "select ps_suppkey, weight_string(ps_suppkey), ps_partkey from partsupp where not :__sq_has_values or ps_suppkey not in ::__sq1", "Table": "partsupp" } ] diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.json b/go/vt/vtgate/planbuilder/testdata/union_cases.json index 9c1f376b652..9ac8db73be7 100644 --- a/go/vt/vtgate/planbuilder/testdata/union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/union_cases.json @@ -42,8 +42,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select id from music) as dt", + "FieldQuery": "select dt.id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1) as dt where 1 != 1", + "Query": "select dt.id, weight_string(dt.id) from (select id from `user` union select id from music) as dt", "Table": "`user`, music" } ] @@ -322,7 +322,7 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -384,8 +384,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1 union select 1 from dual where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select id from music union select 1 from dual) as dt", + "FieldQuery": "select dt.id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1 union select 1 from dual where 1 != 1) as dt where 1 != 1", + "Query": "select dt.id, weight_string(dt.id) from (select id from `user` union select id from music union select 1 from dual) as dt", "Table": "`user`, dual, music" } ] @@ -503,8 +503,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from ((select id from `user` where 1 != 1) union (select id from `user` where 1 != 1)) as dt where 1 != 1", - "Query": "select id, weight_string(id) from ((select id from `user` order by id desc) union (select id from `user` order by id asc)) as dt", + "FieldQuery": "select dt.id, weight_string(dt.id) from ((select id from `user` where 1 != 1) union (select id from `user` where 1 != 1)) as dt where 1 != 1", + "Query": "select dt.id, weight_string(dt.id) from ((select id from `user` order by id desc) union (select id from `user` order by id asc)) as dt", "Table": "`user`" } ] @@ -523,9 +523,8 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "(0:1)" + "0" ], - "ResultColumns": 1, "Inputs": [ { "OperatorType": "Route", @@ -534,8 +533,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `1`, weight_string(`1`) from (select 1 from dual where 1 != 1 union select null from dual where 1 != 1 union select 1.0 from dual where 1 != 1 union select '1' from dual where 1 != 1 union select 2 from dual where 1 != 1 union select 2.0 from `user` where 1 != 1) as dt where 1 != 1", - "Query": "select `1`, weight_string(`1`) from (select 1 from dual union select null from dual union select 1.0 from dual union select '1' from dual union select 2 from dual union select 2.0 from `user`) as dt", + "FieldQuery": "select 1 from dual where 1 != 1 union select null from dual where 1 != 1 union select 1.0 from dual where 1 != 1 union select '1' from dual where 1 != 1 union select 2 from dual where 1 != 1 union select 2.0 from `user` where 1 != 1", + "Query": "select 1 from dual union select null from dual union select 1.0 from dual union select '1' from dual union select 2 from dual union select 2.0 from `user`", "Table": "`user`, dual" } ] @@ -762,8 +761,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select id + 1 from `user` where 1 != 1 union select user_id from user_extra where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select id + 1 from `user` union select user_id from user_extra) as dt", + "FieldQuery": "select dt.id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select id + 1 from `user` where 1 != 1 union select user_id from user_extra where 1 != 1) as dt where 1 != 1", + "Query": "select dt.id, weight_string(dt.id) from (select id from `user` union select id + 1 from `user` union select user_id from user_extra) as dt", "Table": "`user`, user_extra" } ] @@ -797,8 +796,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select id from music) as dt", + "FieldQuery": "select id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select id from music where 1 != 1) as dt where 1 != 1", + "Query": "select id, weight_string(dt.id) from (select id from `user` union select id from music) as dt", "Table": "`user`, music" }, { @@ -847,8 +846,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select 3 from dual where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select 3 from dual limit :__upper_limit) as dt", + "FieldQuery": "select id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select 3 from dual where 1 != 1) as dt where 1 != 1", + "Query": "select id, weight_string(dt.id) from (select id from `user` union select 3 from dual limit :__upper_limit) as dt", "Table": "`user`, dual" } ] @@ -906,8 +905,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select col, weight_string(col) from (select col from unsharded where 1 != 1 union select col2 from unsharded where 1 != 1) as dt where 1 != 1", - "Query": "select col, weight_string(col) from (select col from unsharded union select col2 from unsharded) as dt", + "FieldQuery": "select dt.col, weight_string(col) from (select col from unsharded where 1 != 1 union select col2 from unsharded where 1 != 1) as dt where 1 != 1", + "Query": "select dt.col, weight_string(col) from (select col from unsharded union select col2 from unsharded) as dt", "Table": "unsharded" }, { @@ -1056,8 +1055,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from (select id from `user` where 1 != 1 union select 3 from dual where 1 != 1) as dt where 1 != 1", - "Query": "select id, weight_string(id) from (select id from `user` union select 3 from dual) as dt", + "FieldQuery": "select dt.id, weight_string(dt.id) from (select id from `user` where 1 != 1 union select 3 from dual where 1 != 1) as dt where 1 != 1", + "Query": "select dt.id, weight_string(dt.id) from (select id from `user` union select 3 from dual) as dt", "Table": "`user`, dual" } ] @@ -1138,7 +1137,7 @@ { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci" + "0: utf8mb3_general_ci" ], "Inputs": [ { @@ -1218,18 +1217,18 @@ { "OperatorType": "Distinct", "Collations": [ - "0: utf8mb4_0900_ai_ci", - "1: utf8mb4_0900_ai_ci", - "2: utf8mb4_0900_ai_ci", - "3: utf8mb4_0900_ai_ci", - "4: utf8mb4_0900_ai_ci", - "5: utf8mb4_0900_ai_ci", - "6: utf8mb4_0900_ai_ci", + "0: utf8mb3_general_ci", + "1: utf8mb3_general_ci", + "2: utf8mb3_general_ci", + "3: utf8mb3_general_ci", + "4: utf8mb3_general_ci", + "5: utf8mb3_general_ci", + "6: utf8mb3_general_ci", "7", "8", - "9: utf8mb4_0900_ai_ci", - "10: utf8mb4_0900_ai_ci", - "11: utf8mb4_0900_ai_ci" + "9: utf8mb3_general_ci", + "10: utf8mb3_general_ci", + "11: utf8mb3_general_ci" ], "Inputs": [ { @@ -1429,8 +1428,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select bar, baz, toto, weight_string(bar), weight_string(baz), weight_string(toto) from (select bar, baz, toto from music where 1 != 1 union select foo, foo, foo from `user` where 1 != 1) as dt where 1 != 1", - "Query": "select bar, baz, toto, weight_string(bar), weight_string(baz), weight_string(toto) from (select bar, baz, toto from music union select foo, foo, foo from `user`) as dt", + "FieldQuery": "select dt.bar, dt.baz, dt.toto, weight_string(dt.bar), weight_string(dt.baz), weight_string(dt.toto) from (select bar, baz, toto from music where 1 != 1 union select foo, foo, foo from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.bar, dt.baz, dt.toto, weight_string(dt.bar), weight_string(dt.baz), weight_string(dt.toto) from (select bar, baz, toto from music union select foo, foo, foo from `user`) as dt", "Table": "`user`, music" } ] @@ -1463,8 +1462,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select foo, foo, foo, weight_string(foo) from (select foo, foo, foo from `user` where 1 != 1 union select bar, baz, toto from music where 1 != 1) as dt where 1 != 1", - "Query": "select foo, foo, foo, weight_string(foo) from (select foo, foo, foo from `user` union select bar, baz, toto from music) as dt", + "FieldQuery": "select dt.foo, dt.foo, dt.foo, weight_string(dt.foo) from (select foo, foo, foo from `user` where 1 != 1 union select bar, baz, toto from music where 1 != 1) as dt where 1 != 1", + "Query": "select dt.foo, dt.foo, dt.foo, weight_string(dt.foo) from (select foo, foo, foo from `user` union select bar, baz, toto from music) as dt", "Table": "`user`, music" } ] @@ -1503,8 +1502,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select foo, weight_string(foo) from (select foo from `user` where 1 != 1 union select foo from `user` where 1 != 1) as dt where 1 != 1", - "Query": "select foo, weight_string(foo) from (select foo from `user` where bar = 12 union select foo from `user` where bar = 134) as dt", + "FieldQuery": "select dt.foo, weight_string(dt.foo) from (select foo from `user` where 1 != 1 union select foo from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.foo, weight_string(dt.foo) from (select foo from `user` where bar = 12 union select foo from `user` where bar = 134) as dt", "Table": "`user`" } ] @@ -1522,8 +1521,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select bar, weight_string(bar) from (select bar from music where 1 != 1 union select bar from music where 1 != 1) as dt where 1 != 1", - "Query": "select bar, weight_string(bar) from (select bar from music where foo = 12 and bar = :t1_foo union select bar from music where foo = 1234 and bar = :t1_foo) as dt", + "FieldQuery": "select dt.bar, weight_string(dt.bar) from (select bar from music where 1 != 1 union select bar from music where 1 != 1) as dt where 1 != 1", + "Query": "select dt.bar, weight_string(dt.bar) from (select bar from music where foo = 12 and bar = :t1_foo union select bar from music where foo = 1234 and bar = :t1_foo) as dt", "Table": "music" } ] @@ -1535,5 +1534,190 @@ "user.user" ] } + }, + { + "comment": "Select literals from table union Select literals from table", + "query": "SELECT 1 from user UNION SELECT 2 from user", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT 1 from user UNION SELECT 2 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "0" + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1 union select 2 from `user` where 1 != 1", + "Query": "select 1 from `user` union select 2 from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select column from table union Select literals from table", + "query": "select col1 from user union select 3 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select col1 from user union select 3 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select dt.col1, weight_string(dt.col1) from (select col1 from `user` where 1 != 1 union select 3 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.col1, weight_string(dt.col1) from (select col1 from `user` union select 3 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select literals from table union Select column from table", + "query": "select 3 from user union select col1 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select 3 from user union select col1 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select dt.`3`, weight_string(dt.`3`) from (select 3 from `user` where 1 != 1 union select col1 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.`3`, weight_string(dt.`3`) from (select 3 from `user` union select col1 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select literals from table union Select now() from table", + "query": "select 3 from user union select now() from user", + "plan": { + "QueryType": "SELECT", + "Original": "select 3 from user union select now() from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select dt.`3`, weight_string(dt.`3`) from (select 3 from `user` where 1 != 1 union select now() from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.`3`, weight_string(dt.`3`) from (select 3 from `user` union select now() from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select now() from table union Select literals from table", + "query": "select now() from user union select 3 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select now() from user union select 3 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select dt.`now()`, weight_string(dt.`now()`) from (select now() from `user` where 1 != 1 union select 3 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.`now()`, weight_string(dt.`now()`) from (select now() from `user` union select 3 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select now() from table union Select column from table", + "query": "select now() from user union select id from user", + "plan": { + "QueryType": "SELECT", + "Original": "select now() from user union select id from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select dt.`now()`, weight_string(dt.`now()`) from (select now() from `user` where 1 != 1 union select id from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select dt.`now()`, weight_string(dt.`now()`) from (select now() from `user` union select id from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/unknown_schema_cases.json b/go/vt/vtgate/planbuilder/testdata/unknown_schema_cases.json index 7bbc4b5b509..888127f11c4 100644 --- a/go/vt/vtgate/planbuilder/testdata/unknown_schema_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unknown_schema_cases.json @@ -52,7 +52,7 @@ { "comment": "Group by column number, used with non-aliased expression (duplicated code)", "query": "select * from user group by 1", - "plan": "cannot use column offsets in group statement when using `*`" + "plan": "cannot use column offsets in group clause when using `*`" }, { "comment": "create view with Cannot auto-resolve for cross-shard joins", diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index 923e7804782..5c8bcad0c57 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -14,51 +14,11 @@ "query": "select * from user natural right join user_extra", "plan": "VT12001: unsupported: natural right join" }, - { - "comment": "Multi-value aggregates not supported", - "query": "select count(a,b) from user", - "plan": "VT03001: aggregate functions take a single argument 'count(a, b)'" - }, { "comment": "subqueries not supported in group by", "query": "select id from user group by id, (select id from user_extra)", "plan": "VT12001: unsupported: subqueries in GROUP BY" }, - { - "comment": "subqueries in delete", - "query": "delete from user where col = (select id from unsharded)", - "plan": "VT12001: unsupported: subqueries in DML" - }, - { - "comment": "sharded subqueries in unsharded delete", - "query": "delete from unsharded where col = (select id from user)", - "plan": "VT12001: unsupported: subqueries in DML" - }, - { - "comment": "sharded delete with limit clasue", - "query": "delete from user_extra limit 10", - "plan": "VT12001: unsupported: multi shard DELETE with LIMIT" - }, - { - "comment": "sharded subquery in unsharded subquery in unsharded delete", - "query": "delete from unsharded where col = (select id from unsharded where id = (select id from user))", - "plan": "VT12001: unsupported: subqueries in DML" - }, - { - "comment": "sharded join unsharded subqueries in unsharded delete", - "query": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)", - "plan": "VT12001: unsupported: subqueries in DML" - }, - { - "comment": "scatter update with limit clause", - "query": "update user_extra set val = 1 where (name = 'foo' or id = 1) limit 1", - "plan": "VT12001: unsupported: multi shard UPDATE with LIMIT" - }, - { - "comment": "multi delete multi table", - "query": "delete user from user join user_extra on user.id = user_extra.id where user.name = 'foo'", - "plan": "VT12001: unsupported: multi-shard or vindex write statement" - }, { "comment": "update changes primary vindex column", "query": "update user set id = 1 where id = 1", @@ -82,27 +42,12 @@ { "comment": "update by primary keyspace id, changing one vindex column, limit without order clause", "query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1 limit 10", - "plan": "VT12001: unsupported: you need to provide the ORDER BY clause when using LIMIT; invalid update on vindex: email_user_map" - }, - { - "comment": "update with derived table", - "query": "update (select id from user) as u set id = 4", - "plan": "The target table u of the UPDATE is not updatable" - }, - { - "comment": "join in update tables", - "query": "update user join user_extra on user.id = user_extra.id set user.name = 'foo'", - "plan": "VT12001: unsupported: unaliased multiple tables in update" - }, - { - "comment": "multiple tables in update", - "query": "update user as u, user_extra as ue set u.name = 'foo' where u.id = ue.id", - "plan": "VT12001: unsupported: multiple (2) tables in update" + "plan": "VT12001: unsupported: Vindex update should have ORDER BY clause when using LIMIT" }, { "comment": "unsharded insert, col list does not match values", "query": "insert into unsharded_auto(id, val) values(1)", - "plan": "VT03006: column count does not match value count at row 1" + "plan": "VT03006: column count does not match value count with the row" }, { "comment": "sharded upsert can't change vindex", @@ -162,7 +107,7 @@ { "comment": "delete with multi-table targets", "query": "delete music,user from music inner join user where music.id = user.id", - "plan": "VT12001: unsupported: multi-shard or vindex write statement" + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" }, { "comment": "select get_lock with non-dual table", @@ -224,11 +169,6 @@ "query": "create view main.view_a as select * from user.user_extra", "plan": "VT12001: unsupported: Select query does not belong to the same keyspace as the view statement" }, - { - "comment": "avg function on scatter query", - "query": "select avg(id) from user", - "plan": "VT12001: unsupported: in scatter query: aggregation function 'avg(id)'" - }, { "comment": "outer and inner subquery route reference the same \"uu.id\" name\n# but they refer to different things. The first reference is to the outermost query,\n# and the second reference is to the innermost 'from' subquery.\n# This query will never work as the inner derived table is only selecting one of the column", "query": "select id2 from user uu where id in (select id from user where id = uu.id and user.col in (select col from (select id from user_extra where user_id = 5) uu where uu.user_id = uu.id))", @@ -299,6 +239,11 @@ "query": "delete from user where x = (@val := 42)", "plan": "VT12001: unsupported: Assignment expression" }, + { + "comment": "explain - routed table with join on different keyspace table", + "query": "explain select 1, second_user.foo.id, foo.col from second_user.foo join user.user join main.unsharded", + "plan": "VT03031: EXPLAIN is only supported for single keyspace" + }, { "comment": "extremum on input from both sides", "query": "insert into music(user_id, id) select foo, bar from music on duplicate key update id = id+1", @@ -335,14 +280,9 @@ "plan": "VT12001: unsupported: unmergable subquery can not be inside complex expression" }, { - "comment": "cant switch sides for outer joins", - "query": "select id from user left join (select user_id from user_extra limit 10) ue on user.id = ue.user_id", - "plan": "VT12001: unsupported: LEFT JOIN with derived tables" - }, - { - "comment": "limit on both sides means that we can't evaluate this at all", + "comment": "this query needs better type information to be able to use the hash join", "query": "select id from (select id from user limit 10) u join (select user_id from user_extra limit 10) ue on u.id = ue.user_id", - "plan": "VT12001: unsupported: JOIN between derived tables" + "plan": "VT12001: unsupported: missing type information for [u.id, ue.user_id]" }, { "comment": "multi-shard union", @@ -388,5 +328,55 @@ "comment": "Alias cannot clash with base tables", "query": "WITH user AS (SELECT col FROM user) SELECT * FROM user", "plan": "VT12001: unsupported: do not support CTE that use the CTE alias inside the CTE query" + }, + { + "comment": "correlated subqueries in select expressions are unsupported", + "query": "SELECT (SELECT sum(user.name) FROM music LIMIT 1) FROM user", + "plan": "VT12001: unsupported: correlated subquery is only supported for EXISTS" + }, + { + "comment": "We need schema tracking to allow unexpanded columns inside UNION", + "query": "select x from (select t.*, 0 as x from user t union select t.*, 1 as x from user_extra t) AS t", + "plan": "VT09015: schema tracking required" + }, + { + "comment": "multi table delete with 2 sharded tables join on vindex column", + "query": "delete u, m from user u join music m on u.id = m.user_id", + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" + }, + { + "comment": "multi table delete with 2 sharded tables join on non-vindex column", + "query": "delete u, m from user u join music m on u.col = m.col", + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" + }, + { + "comment": "multi table delete with 1 sharded and 1 reference table", + "query": "delete u, r from user u join ref_with_source r on u.col = r.col", + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" + }, + { + "comment": "reference table delete with join", + "query": "delete r from user u join ref_with_source r on u.col = r.col", + "plan": "VT12001: unsupported: DELETE on reference table with join" + }, + { + "comment": "group_concat unsupported when needs full evaluation at vtgate with more than 1 column", + "query": "select group_concat(user.col1, music.col2) x from user join music on user.col = music.col order by x", + "plan": "VT12001: unsupported: group_concat with more than 1 column" + }, + { + "comment": "count aggregation function having multiple column", + "query": "select count(distinct user_id, name) from user", + "plan": "VT12001: unsupported: distinct aggregation function with multiple expressions 'count(distinct user_id, `name`)'" + }, + { + "comment": "count and sum distinct on different columns", + "query": "SELECT COUNT(DISTINCT col), SUM(DISTINCT id) FROM user", + "plan": "VT12001: unsupported: only one DISTINCT aggregation is allowed in a SELECT: sum(distinct id)" + }, + { + "comment": "update with multi table reference with multi target update", + "query": "update ignore user u, music m set u.foo = 21, m.bar = 'abc' where u.col = m.col", + "plan": "VT12001: unsupported: multi-table UPDATE statement with multi-target column update" } ] diff --git a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json index a7824126c98..7aaa2648388 100644 --- a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json +++ b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json @@ -25,6 +25,12 @@ "user.user" ] }, + { + "from_table": "second_user.bar", + "to_tables": [ + "user.music" + ] + }, { "from_table": "primary_redirect@primary", "to_tables": [ @@ -166,6 +172,16 @@ "to": "keyspace_id", "cost": "300" } + }, + "lkp_bf_vdx": { + "type": "name_lkp_test", + "owner": "customer", + "params": { + "table": "lkp_shard_vdx", + "from": " ", + "to": "keyspace_id", + "write_only": "true" + } } }, "tables": { @@ -209,7 +225,8 @@ }, { "name": "textcol2", - "type": "VARCHAR" + "type": "VARCHAR", + "collation_name": "big5_bin" } ] }, @@ -476,6 +493,10 @@ { "column": "phone", "name": "unq_lkp_bf_vdx" + }, + { + "column": "name", + "name": "lkp_bf_vdx" } ] }, @@ -493,8 +514,7 @@ "sharded": true, "vindexes": { "hash_dup": { - "type": "hash_test", - "owner": "user" + "type": "hash_test" } }, "tables": { @@ -723,6 +743,20 @@ } ] }, + "tbl_auth": { + "columns": [ + { + "name": "id" + } + ], + "column_vindexes": [ + { + "column": "id", + "name": "hash_vin" + } + ], + "column_list_authoritative": true + }, "tblrefDef": { "column_vindexes": [ { @@ -752,15 +786,110 @@ "unsharded_fk_allow": { "foreignKeyMode": "managed", "tables": { - "u_tbl1": {}, - "u_tbl2": {}, - "u_tbl3": {}, - "u_tbl4": {}, - "u_tbl5": {}, - "u_tbl6": {}, - "u_tbl7": {}, - "u_tbl8": {}, - "u_tbl9": {}, + "u_tbl1": { + "columns": [ + { + "name": "col1", + "type": "VARCHAR" + }, + { + "name": "col14", + "type": "INT16" + } + ] + }, + "u_tbl2": { + "columns": [ + { + "name": "col2", + "type": "VARCHAR" + } + ] + }, + "u_tbl3": { + "columns": [ + { + "name": "col3", + "type": "VARCHAR" + } + ] + }, + "u_tbl4": { + "columns": [ + { + "name": "col41", + "type": "INT16" + }, + { + "name": "col4", + "type": "VARCHAR" + } + ] + }, + "u_tbl5": { + "columns": [ + { + "name": "col5", + "type": "VARCHAR" + } + ] + }, + "u_tbl6": { + "columns": [ + { + "name": "col6", + "type": "VARCHAR" + } + ] + }, + "u_tbl7": { + "columns": [ + { + "name": "col7", + "type": "VARCHAR" + } + ] + }, + "u_tbl8": { + "columns": [ + { + "name": "col8", + "type": "VARCHAR" + } + ] + }, + "u_tbl9": { + "columns": [ + { + "name": "col9", + "type": "VARCHAR" + }, + {"name": "foo"}, + {"name": "bar", "default": "1"} + ] + }, + "u_tbl10": { + "columns": [ + { + "name": "col10", + "type": "VARCHAR" + }, + {"name": "col"}, + {"name": "id"} + ], + "column_list_authoritative": true + }, + "u_tbl11": { + "columns": [ + { + "name": "col11", + "type": "VARCHAR" + }, + {"name": "col"}, + {"name": "id"} + ], + "column_list_authoritative": true + }, "u_tbl": {}, "u_multicol_tbl1": {}, "u_multicol_tbl2": {}, diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index eced4251ab3..eb21546224c 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -19,6 +19,7 @@ package planbuilder import ( querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/sysvars" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" @@ -41,11 +42,18 @@ func gen4UpdateStmtPlanner( return nil, err } - err = rewriteRoutedTables(updStmt, vschema) + err = queryRewrite(ctx.SemTable, reservedVars, updStmt) if err != nil { return nil, err } + // If there are non-literal foreign key updates, we have to run the query with foreign key checks off. + if ctx.SemTable.HasNonLiteralForeignKeyUpdate(updStmt.Exprs) { + // Since we are running the query with foreign key checks off, we have to verify all the foreign keys validity on vtgate. + ctx.VerifyAllFKs = true + updStmt.SetComments(updStmt.GetParsedComments().SetMySQLSetVarValue(sysvars.ForeignKeyChecks, "OFF")) + } + // Remove all the foreign keys that don't require any handling. err = ctx.SemTable.RemoveNonRequiredForeignKeys(ctx.VerifyAllFKs, vindexes.UpdateAction) if err != nil { @@ -63,11 +71,6 @@ func gen4UpdateStmtPlanner( return nil, ctx.SemTable.NotUnshardedErr } - err = queryRewrite(ctx.SemTable, reservedVars, updStmt) - if err != nil { - return nil, err - } - op, err := operators.PlanQuery(ctx, updStmt) if err != nil { return nil, err diff --git a/go/vt/vtgate/planbuilder/upsert.go b/go/vt/vtgate/planbuilder/upsert.go new file mode 100644 index 00000000000..cd9c127635c --- /dev/null +++ b/go/vt/vtgate/planbuilder/upsert.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package planbuilder + +import ( + "vitess.io/vitess/go/vt/vtgate/engine" +) + +type upsert struct { + insert []logicalPlan + update []logicalPlan +} + +var _ logicalPlan = (*upsert)(nil) + +// Primitive implements the logicalPlan interface +func (u *upsert) Primitive() engine.Primitive { + up := &engine.Upsert{} + for i := 0; i < len(u.insert); i++ { + up.AddUpsert(u.insert[i].Primitive(), u.update[i].Primitive()) + } + return up +} diff --git a/go/vt/vtgate/planbuilder/vexplain.go b/go/vt/vtgate/planbuilder/vexplain.go index 5c99ab87a95..7b200fb2e09 100644 --- a/go/vt/vtgate/planbuilder/vexplain.go +++ b/go/vt/vtgate/planbuilder/vexplain.go @@ -19,9 +19,6 @@ package planbuilder import ( "context" "encoding/json" - "fmt" - - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" @@ -30,28 +27,10 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) -// Builds an explain-plan for the given Primitive -func buildExplainPlan(ctx context.Context, stmt sqlparser.Explain, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { - switch explain := stmt.(type) { - case *sqlparser.ExplainTab: - return explainTabPlan(explain, vschema) - case *sqlparser.ExplainStmt: - switch explain.Type { - case sqlparser.VitessType: - vschema.PlannerWarning("EXPLAIN FORMAT = VITESS is deprecated, please use VEXPLAIN PLAN instead.") - return buildVExplainVtgatePlan(ctx, explain.Statement, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) - case sqlparser.VTExplainType: - vschema.PlannerWarning("EXPLAIN FORMAT = VTEXPLAIN is deprecated, please use VEXPLAIN QUERIES instead.") - return buildVExplainLoggingPlan(ctx, &sqlparser.VExplainStmt{Type: sqlparser.QueriesVExplainType, Statement: explain.Statement, Comments: explain.Comments}, reservedVars, vschema, enableOnlineDDL, enableDirectDDL) - default: - return buildOtherReadAndAdmin(sqlparser.String(explain), vschema) - } - } - return nil, vterrors.VT13001(fmt.Sprintf("unexpected explain type: %T", stmt)) -} - func buildVExplainPlan(ctx context.Context, vexplainStmt *sqlparser.VExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, enableOnlineDDL, enableDirectDDL bool) (*planResult, error) { switch vexplainStmt.Type { case sqlparser.QueriesVExplainType, sqlparser.AllVExplainType: @@ -125,3 +104,52 @@ func buildVExplainLoggingPlan(ctx context.Context, explain *sqlparser.VExplainSt return &planResult{primitive: &engine.VExplain{Input: input.primitive, Type: explain.Type}, tables: input.tables}, nil } + +// buildExplainStmtPlan takes an EXPLAIN query and if possible sends the whole query to a single shard +func buildExplainStmtPlan(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { + explain := stmt.(*sqlparser.ExplainStmt) + switch explain.Statement.(type) { + case sqlparser.SelectStatement, *sqlparser.Update, *sqlparser.Delete, *sqlparser.Insert: + return explainPlan(explain, reservedVars, vschema) + default: + return buildOtherReadAndAdmin(sqlparser.String(explain), vschema) + } + +} + +func explainPlan(explain *sqlparser.ExplainStmt, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { + ctx, err := plancontext.CreatePlanningContext(explain.Statement, reservedVars, vschema, Gen4) + if err != nil { + return nil, err + } + + ks := ctx.SemTable.SingleKeyspace() + if ks == nil { + return nil, vterrors.VT03031() + } + + if err = queryRewrite(ctx.SemTable, reservedVars, explain.Statement); err != nil { + return nil, err + } + + // Remove keyspace qualifier from columns and tables. + sqlparser.RemoveKeyspace(explain.Statement) + + var tables []string + for _, table := range ctx.SemTable.Tables { + name, err := table.Name() + if err != nil { + // this is just for reporting which tables we are touching + // it's OK to ignore errors here + continue + } + tables = append(tables, operators.QualifiedString(ks, name.Name.String())) + } + + return newPlanResult(&engine.Send{ + Keyspace: ks, + TargetDestination: key.DestinationAnyShard{}, + Query: sqlparser.String(explain), + SingleShardOnly: true, + }, tables...), nil +} diff --git a/go/vt/vtgate/planbuilder/vindex_op.go b/go/vt/vtgate/planbuilder/vindex_op.go index c439dec1701..ffd304aa06d 100644 --- a/go/vt/vtgate/planbuilder/vindex_op.go +++ b/go/vt/vtgate/planbuilder/vindex_op.go @@ -35,6 +35,7 @@ func transformVindexPlan(ctx *plancontext.PlanningContext, op *operators.Vindex) expr, err := evalengine.Translate(op.Value, &evalengine.Config{ Collation: ctx.SemTable.Collation, ResolveType: ctx.SemTable.TypeForExpr, + Environment: ctx.VSchema.Environment(), }) if err != nil { return nil, err diff --git a/go/vt/vtgate/plugin_mysql_server.go b/go/vt/vtgate/plugin_mysql_server.go index 273592b5bf7..c0b2fe38b56 100644 --- a/go/vt/vtgate/plugin_mysql_server.go +++ b/go/vt/vtgate/plugin_mysql_server.go @@ -45,6 +45,7 @@ import ( vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttls" ) @@ -74,6 +75,8 @@ var ( mysqlDefaultWorkloadName = "OLTP" mysqlDefaultWorkload int32 + + mysqlServerFlushDelay = 100 * time.Millisecond ) func registerPluginFlags(fs *pflag.FlagSet) { @@ -97,6 +100,7 @@ func registerPluginFlags(fs *pflag.FlagSet) { fs.DurationVar(&mysqlQueryTimeout, "mysql_server_query_timeout", mysqlQueryTimeout, "mysql query timeout") fs.BoolVar(&mysqlConnBufferPooling, "mysql-server-pool-conn-read-buffers", mysqlConnBufferPooling, "If set, the server will pool incoming connection read buffers") fs.DurationVar(&mysqlKeepAlivePeriod, "mysql-server-keepalive-period", mysqlKeepAlivePeriod, "TCP period between keep-alives") + fs.DurationVar(&mysqlServerFlushDelay, "mysql_server_flush_delay", mysqlServerFlushDelay, "Delay after which buffered response will be flushed to the client.") fs.StringVar(&mysqlDefaultWorkloadName, "mysql_default_workload", mysqlDefaultWorkloadName, "Default session workload (OLTP, OLAP, DBA)") } @@ -417,6 +421,10 @@ func (vh *vtgateHandler) KillQuery(connectionID uint32) error { return nil } +func (vh *vtgateHandler) Env() *vtenv.Environment { + return vh.vtg.executor.env +} + func (vh *vtgateHandler) session(c *mysql.Conn) *vtgatepb.Session { session, _ := c.ClientData.(*vtgatepb.Session) if session == nil { @@ -526,11 +534,11 @@ func initMySQLProtocol(vtgate *VTGate) *mysqlServer { mysqlProxyProtocol, mysqlConnBufferPooling, mysqlKeepAlivePeriod, + mysqlServerFlushDelay, ) if err != nil { log.Exitf("mysql.NewListener failed: %v", err) } - srv.tcpListener.ServerVersion = servenv.MySQLServerVersion() if mysqlSslCert != "" && mysqlSslKey != "" { tlsVersion, err := vttls.TLSVersionToNumber(mysqlTLSMinVersion) if err != nil { @@ -550,17 +558,10 @@ func initMySQLProtocol(vtgate *VTGate) *mysqlServer { } if mysqlServerSocketPath != "" { - // Let's create this unix socket with permissions to all users. In this way, - // clients can connect to vtgate mysql server without being vtgate user - oldMask := syscall.Umask(000) - srv.unixListener, err = newMysqlUnixSocket(mysqlServerSocketPath, authServer, srv.vtgateHandle) - _ = syscall.Umask(oldMask) + err = setupUnixSocket(srv, authServer, mysqlServerSocketPath) if err != nil { log.Exitf("mysql.NewListener failed: %v", err) - return nil } - // Listen for unix socket - go srv.unixListener.Accept() } return srv } @@ -578,6 +579,7 @@ func newMysqlUnixSocket(address string, authServer mysql.AuthServer, handler mys false, mysqlConnBufferPooling, mysqlKeepAlivePeriod, + mysqlServerFlushDelay, ) switch err := err.(type) { @@ -610,6 +612,7 @@ func newMysqlUnixSocket(address string, authServer mysql.AuthServer, handler mys false, mysqlConnBufferPooling, mysqlKeepAlivePeriod, + mysqlServerFlushDelay, ) return listener, listenerErr default: diff --git a/go/vt/vtgate/plugin_mysql_server_test.go b/go/vt/vtgate/plugin_mysql_server_test.go index 1aa201b5d4c..5da79b9fe17 100644 --- a/go/vt/vtgate/plugin_mysql_server_test.go +++ b/go/vt/vtgate/plugin_mysql_server_test.go @@ -30,15 +30,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/utils" - - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/trace" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/tlstest" + "vitess.io/vitess/go/vt/vtenv" ) type testHandler struct { @@ -83,6 +82,10 @@ func (th *testHandler) WarningCount(c *mysql.Conn) uint16 { return 0 } +func (th *testHandler) Env() *vtenv.Environment { + return vtenv.NewTestEnv() +} + func TestConnectionUnixSocket(t *testing.T) { th := &testHandler{} @@ -348,7 +351,7 @@ func TestGracefulShutdown(t *testing.T) { vh := newVtgateHandler(&VTGate{executor: executor, timings: timings, rowsReturned: rowsReturned, rowsAffected: rowsAffected}) th := &testHandler{} - listener, err := mysql.NewListener("tcp", "127.0.0.1:", mysql.NewAuthServerNone(), th, 0, 0, false, false, 0) + listener, err := mysql.NewListener("tcp", "127.0.0.1:", mysql.NewAuthServerNone(), th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer listener.Close() @@ -378,7 +381,7 @@ func TestGracefulShutdownWithTransaction(t *testing.T) { vh := newVtgateHandler(&VTGate{executor: executor, timings: timings, rowsReturned: rowsReturned, rowsAffected: rowsAffected}) th := &testHandler{} - listener, err := mysql.NewListener("tcp", "127.0.0.1:", mysql.NewAuthServerNone(), th, 0, 0, false, false, 0) + listener, err := mysql.NewListener("tcp", "127.0.0.1:", mysql.NewAuthServerNone(), th, 0, 0, false, false, 0, 0) require.NoError(t, err) defer listener.Close() diff --git a/go/vt/vtgate/plugin_mysql_server_unix.go b/go/vt/vtgate/plugin_mysql_server_unix.go new file mode 100644 index 00000000000..95c9731fccc --- /dev/null +++ b/go/vt/vtgate/plugin_mysql_server_unix.go @@ -0,0 +1,40 @@ +//go:build !windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "syscall" + + "vitess.io/vitess/go/mysql" +) + +func setupUnixSocket(srv *mysqlServer, authServer mysql.AuthServer, path string) error { + // Let's create this unix socket with permissions to all users. In this way, + // clients can connect to vtgate mysql server without being vtgate user + var err error + oldMask := syscall.Umask(000) + srv.unixListener, err = newMysqlUnixSocket(path, authServer, srv.vtgateHandle) + _ = syscall.Umask(oldMask) + if err != nil { + return err + } + // Listen for unix socket + go srv.unixListener.Accept() + return nil +} diff --git a/go/vt/vtgate/plugin_mysql_server_windows.go b/go/vt/vtgate/plugin_mysql_server_windows.go new file mode 100644 index 00000000000..0502cadf863 --- /dev/null +++ b/go/vt/vtgate/plugin_mysql_server_windows.go @@ -0,0 +1,29 @@ +//go:build windows + +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "errors" + + "vitess.io/vitess/go/mysql" +) + +func setupUnixSocket(srv *mysqlServer, authServer mysql.AuthServer, path string) error { + return errors.New("unix sockets are not supported on windows") +} diff --git a/go/vt/vtgate/querylog.go b/go/vt/vtgate/querylog.go index 7425f2feba9..bddc799363d 100644 --- a/go/vt/vtgate/querylog.go +++ b/go/vt/vtgate/querylog.go @@ -42,7 +42,7 @@ func (e *Executor) defaultQueryLogger() error { servenv.HTTPHandleFunc(QueryLogzHandler, func(w http.ResponseWriter, r *http.Request) { ch := queryLogger.Subscribe("querylogz") defer queryLogger.Unsubscribe(ch) - querylogzHandler(ch, w, r) + querylogzHandler(ch, w, r, e.env.Parser()) }) servenv.HTTPHandleFunc(QueryzHandler, func(w http.ResponseWriter, r *http.Request) { diff --git a/go/vt/vtgate/querylogz.go b/go/vt/vtgate/querylogz.go index acfb970df5a..7c72e950d4a 100644 --- a/go/vt/vtgate/querylogz.go +++ b/go/vt/vtgate/querylogz.go @@ -55,10 +55,9 @@ var ( `) querylogzFuncMap = template.FuncMap{ - "stampMicro": func(t time.Time) string { return t.Format(time.StampMicro) }, - "cssWrappable": logz.Wrappable, - "truncateQuery": sqlparser.TruncateForUI, - "unquote": func(s string) string { return strings.Trim(s, "\"") }, + "stampMicro": func(t time.Time) string { return t.Format(time.StampMicro) }, + "cssWrappable": logz.Wrappable, + "unquote": func(s string) string { return strings.Trim(s, "\"") }, } querylogzTmpl = template.Must(template.New("example").Funcs(querylogzFuncMap).Parse(` @@ -74,7 +73,7 @@ var ( - + @@ -84,7 +83,7 @@ var ( // querylogzHandler serves a human readable snapshot of the // current query log. -func querylogzHandler(ch chan *logstats.LogStats, w http.ResponseWriter, r *http.Request) { +func querylogzHandler(ch chan *logstats.LogStats, w http.ResponseWriter, r *http.Request, parser *sqlparser.Parser) { if err := acl.CheckAccessHTTP(r, acl.DEBUGGING); err != nil { acl.SendError(w, err) return @@ -115,7 +114,8 @@ func querylogzHandler(ch chan *logstats.LogStats, w http.ResponseWriter, r *http tmplData := struct { *logstats.LogStats ColorLevel string - }{stats, level} + Parser *sqlparser.Parser + }{stats, level, parser} if err := querylogzTmpl.Execute(w, tmplData); err != nil { log.Errorf("querylogz: couldn't execute template: %v", err) } diff --git a/go/vt/vtgate/querylogz_test.go b/go/vt/vtgate/querylogz_test.go index ce0f4d4311b..3cecb983b3f 100644 --- a/go/vt/vtgate/querylogz_test.go +++ b/go/vt/vtgate/querylogz_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/logstats" "vitess.io/vitess/go/streamlog" @@ -73,7 +74,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { response := httptest.NewRecorder() ch := make(chan *logstats.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ := io.ReadAll(response.Body) checkQuerylogzHasStats(t, fastQueryPattern, logStats, body) @@ -103,7 +104,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { response = httptest.NewRecorder() ch = make(chan *logstats.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, mediumQueryPattern, logStats, body) @@ -132,7 +133,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { logStats.EndTime = logStats.StartTime.Add(500 * time.Millisecond) ch = make(chan *logstats.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, slowQueryPattern, logStats, body) @@ -142,7 +143,7 @@ func TestQuerylogzHandlerFormatting(t *testing.T) { defer func() { streamlog.SetQueryLogFilterTag("") }() ch = make(chan *logstats.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, slowQueryPattern, logStats, body) diff --git a/go/vt/vtgate/queryz.go b/go/vt/vtgate/queryz.go index e546fc68c6f..540b014e11c 100644 --- a/go/vt/vtgate/queryz.go +++ b/go/vt/vtgate/queryz.go @@ -27,7 +27,6 @@ import ( "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" ) @@ -145,7 +144,7 @@ func queryzHandler(e *Executor, w http.ResponseWriter, r *http.Request) { e.ForEachPlan(func(plan *engine.Plan) bool { Value := &queryzRow{ - Query: logz.Wrappable(sqlparser.TruncateForUI(plan.Original)), + Query: logz.Wrappable(e.env.Parser().TruncateForUI(plan.Original)), } Value.Count, Value.tm, Value.ShardQueries, Value.RowsAffected, Value.RowsReturned, Value.Errors = plan.Stats() var timepq time.Duration diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/safe_session.go index e2f3c235c94..45fff46f629 100644 --- a/go/vt/vtgate/safe_session.go +++ b/go/vt/vtgate/safe_session.go @@ -73,6 +73,7 @@ type ( mu sync.Mutex entries []engine.ExecuteEntry lastID int + parser *sqlparser.Parser } // autocommitState keeps track of whether a single round-trip @@ -435,7 +436,7 @@ func (session *SafeSession) AppendOrUpdate(shardSession *vtgatepb.Session_ShardS if session.queryFromVindex { break } - // isSingle is enforced only for normmal commit order operations. + // isSingle is enforced only for normal commit order operations. if session.isSingleDB(txMode) && len(session.ShardSessions) > 1 { count := actualNoOfShardSession(session.ShardSessions) if count <= 1 { @@ -572,6 +573,26 @@ func (session *SafeSession) TimeZone() *time.Location { return loc } +// ForeignKeyChecks returns the foreign_key_checks stored in system_variables map in the session. +func (session *SafeSession) ForeignKeyChecks() *bool { + session.mu.Lock() + fkVal, ok := session.SystemVariables[sysvars.ForeignKeyChecks] + session.mu.Unlock() + + if !ok { + return nil + } + switch strings.ToLower(fkVal) { + case "off", "0": + fkCheckBool := false + return &fkCheckBool + case "on", "1": + fkCheckBool := true + return &fkCheckBool + } + return nil +} + // SetOptions sets the options func (session *SafeSession) SetOptions(options *querypb.ExecuteOptions) { session.mu.Lock() @@ -921,11 +942,13 @@ func (session *SafeSession) ClearAdvisoryLock() { session.AdvisoryLock = nil } -func (session *SafeSession) EnableLogging() { +func (session *SafeSession) EnableLogging(parser *sqlparser.Parser) { session.mu.Lock() defer session.mu.Unlock() - session.logging = &executeLogger{} + session.logging = &executeLogger{ + parser: parser, + } } // GetUDV returns the bind variable value for the user defined variable. @@ -978,7 +1001,7 @@ func (l *executeLogger) log(primitive engine.Primitive, target *querypb.Target, FiredFrom: primitive, }) } - ast, err := sqlparser.Parse(query) + ast, err := l.parser.Parse(query) if err != nil { panic("query not able to parse. this should not happen") } diff --git a/go/vt/vtgate/sandbox_test.go b/go/vt/vtgate/sandbox_test.go index 1629e9a4faa..27be6442cfe 100644 --- a/go/vt/vtgate/sandbox_test.go +++ b/go/vt/vtgate/sandbox_test.go @@ -41,10 +41,9 @@ import ( // sandbox_test.go provides a sandbox for unit testing VTGate. const ( - KsTestSharded = "TestExecutor" - KsTestUnsharded = "TestUnsharded" - KsTestUnshardedServedFrom = "TestUnshardedServedFrom" - KsTestBadVSchema = "TestXBadVSchema" + KsTestSharded = "TestExecutor" + KsTestUnsharded = "TestUnsharded" + KsTestBadVSchema = "TestXBadVSchema" ) func init() { @@ -172,18 +171,6 @@ func createShardedSrvKeyspace(shardSpec, servedFromKeyspace string) (*topodatapb }, }, } - if servedFromKeyspace != "" { - shardedSrvKeyspace.ServedFrom = []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Keyspace: servedFromKeyspace, - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: servedFromKeyspace, - }, - } - } return shardedSrvKeyspace, nil } @@ -259,27 +246,11 @@ func (sct *sandboxTopo) GetSrvKeyspace(ctx context.Context, cell, keyspace strin return nil, fmt.Errorf("topo error GetSrvKeyspace") } switch keyspace { - case KsTestUnshardedServedFrom: - servedFromKeyspace, err := createUnshardedKeyspace() - if err != nil { - return nil, err - } - servedFromKeyspace.ServedFrom = []*topodatapb.SrvKeyspace_ServedFrom{ - { - TabletType: topodatapb.TabletType_RDONLY, - Keyspace: KsTestUnsharded, - }, - { - TabletType: topodatapb.TabletType_PRIMARY, - Keyspace: KsTestUnsharded, - }, - } - return servedFromKeyspace, nil case KsTestUnsharded: return createUnshardedKeyspace() + default: + return createShardedSrvKeyspace(sand.ShardSpec, sand.KeyspaceServedFrom) } - - return createShardedSrvKeyspace(sand.ShardSpec, sand.KeyspaceServedFrom) } func (sct *sandboxTopo) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) { @@ -310,7 +281,10 @@ func (sct *sandboxTopo) WatchSrvVSchema(ctx context.Context, cell string, callba } sct.topoServer.UpdateSrvVSchema(ctx, cell, srvVSchema) - current, updateChan, _ := sct.topoServer.WatchSrvVSchema(ctx, cell) + current, updateChan, err := sct.topoServer.WatchSrvVSchema(ctx, cell) + if err != nil { + panic(fmt.Sprintf("sandboxTopo WatchSrvVSchema returned an error: %v", err)) + } if !callback(current.Value, nil) { panic("sandboxTopo callback returned false") } diff --git a/go/vt/vtgate/schema/tracker.go b/go/vt/vtgate/schema/tracker.go index 369ab178986..f7b46521b68 100644 --- a/go/vt/vtgate/schema/tracker.go +++ b/go/vt/vtgate/schema/tracker.go @@ -23,6 +23,7 @@ import ( "sync" "time" + "vitess.io/vitess/go/ptr" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" @@ -50,6 +51,8 @@ type ( // map of keyspace currently tracked tracked map[keyspaceStr]*updateController consumeDelay time.Duration + + parser *sqlparser.Parser } ) @@ -57,17 +60,18 @@ type ( const defaultConsumeDelay = 1 * time.Second // NewTracker creates the tracker object. -func NewTracker(ch chan *discovery.TabletHealth, enableViews bool) *Tracker { +func NewTracker(ch chan *discovery.TabletHealth, enableViews bool, parser *sqlparser.Parser) *Tracker { t := &Tracker{ ctx: context.Background(), ch: ch, tables: &tableMap{m: make(map[keyspaceStr]map[tableNameStr]*vindexes.TableInfo)}, tracked: map[keyspaceStr]*updateController{}, consumeDelay: defaultConsumeDelay, + parser: parser, } if enableViews { - t.views = &viewMap{m: map[keyspaceStr]map[viewNameStr]sqlparser.SelectStatement{}} + t.views = &viewMap{m: map[keyspaceStr]map[viewNameStr]sqlparser.SelectStatement{}, parser: parser} } return t } @@ -216,6 +220,15 @@ func (t *Tracker) GetForeignKeys(ks string, tbl string) []*sqlparser.ForeignKeyD return tblInfo.ForeignKeys } +// GetIndexes returns the indexes for table in the given keyspace. +func (t *Tracker) GetIndexes(ks string, tbl string) []*sqlparser.IndexDefinition { + t.mu.Lock() + defer t.mu.Unlock() + + tblInfo := t.tables.get(ks, tbl) + return tblInfo.Indexes +} + // Tables returns a map with the columns for all known tables in the keyspace func (t *Tracker) Tables(ks string) map[string]*vindexes.TableInfo { t.mu.Lock() @@ -280,7 +293,7 @@ func (t *Tracker) updatedTableSchema(th *discovery.TabletHealth) bool { func (t *Tracker) updateTables(keyspace string, res map[string]string) { for tableName, tableDef := range res { - stmt, err := sqlparser.Parse(tableDef) + stmt, err := t.parser.Parse(tableDef) if err != nil { log.Warningf("error parsing table definition for %s: %v", tableName, err) continue @@ -293,7 +306,7 @@ func (t *Tracker) updateTables(keyspace string, res map[string]string) { cols := getColumns(ddl.TableSpec) fks := getForeignKeys(ddl.TableSpec) - t.tables.set(keyspace, tableName, cols, fks) + t.tables.set(keyspace, tableName, cols, fks, ddl.TableSpec.Indexes) } } @@ -302,12 +315,20 @@ func getColumns(tblSpec *sqlparser.TableSpec) []vindexes.Column { cols := make([]vindexes.Column, 0, len(tblSpec.Columns)) for _, column := range tblSpec.Columns { colCollation := getColumnCollation(tblCollation, column) + size := ptr.Unwrap(column.Type.Length, 0) + scale := ptr.Unwrap(column.Type.Scale, 0) + nullable := ptr.Unwrap(column.Type.Options.Null, true) cols = append(cols, vindexes.Column{ Name: column.Name, Type: column.Type.SQLType(), CollationName: colCollation, + Default: column.Type.Options.Default, Invisible: column.Type.Invisible(), + Size: int32(size), + Scale: int32(scale), + Nullable: nullable, + Values: column.Type.EnumValues, }) } return cols @@ -343,7 +364,13 @@ func getTableCollation(tblSpec *sqlparser.TableSpec) string { func getColumnCollation(defaultCollation string, column *sqlparser.ColumnDefinition) string { if column.Type.Options == nil || column.Type.Options.Collate == "" { - return defaultCollation + switch strings.ToLower(column.Type.Type) { + case "enum", "set", "text", "tinytext", "mediumtext", "longtext", "varchar", "char": + return defaultCollation + case "json": + return "utf8mb4_bin" + } + return "binary" } return column.Type.Options.Collate } @@ -403,13 +430,13 @@ type tableMap struct { m map[keyspaceStr]map[tableNameStr]*vindexes.TableInfo } -func (tm *tableMap) set(ks, tbl string, cols []vindexes.Column, fks []*sqlparser.ForeignKeyDefinition) { +func (tm *tableMap) set(ks, tbl string, cols []vindexes.Column, fks []*sqlparser.ForeignKeyDefinition, indexes []*sqlparser.IndexDefinition) { m := tm.m[ks] if m == nil { m = make(map[tableNameStr]*vindexes.TableInfo) tm.m[ks] = m } - m[tbl] = &vindexes.TableInfo{Columns: cols, ForeignKeys: fks} + m[tbl] = &vindexes.TableInfo{Columns: cols, ForeignKeys: fks, Indexes: indexes} } func (tm *tableMap) get(ks, tbl string) *vindexes.TableInfo { @@ -438,7 +465,8 @@ func (t *Tracker) clearKeyspaceTables(ks string) { } type viewMap struct { - m map[keyspaceStr]map[viewNameStr]sqlparser.SelectStatement + m map[keyspaceStr]map[viewNameStr]sqlparser.SelectStatement + parser *sqlparser.Parser } func (vm *viewMap) set(ks, tbl, sql string) { @@ -447,7 +475,7 @@ func (vm *viewMap) set(ks, tbl, sql string) { m = make(map[tableNameStr]sqlparser.SelectStatement) vm.m[ks] = m } - stmt, err := sqlparser.Parse(sql) + stmt, err := vm.parser.Parse(sql) if err != nil { log.Warningf("ignoring view '%s', parsing error in view definition: '%s'", tbl, sql) return diff --git a/go/vt/vtgate/schema/tracker_test.go b/go/vt/vtgate/schema/tracker_test.go index 4f514fec101..1ee1aee6a0f 100644 --- a/go/vt/vtgate/schema/tracker_test.go +++ b/go/vt/vtgate/schema/tracker_test.go @@ -81,7 +81,7 @@ func TestTrackingUnHealthyTablet(t *testing.T) { sbc := sandboxconn.NewSandboxConn(tablet) ch := make(chan *discovery.TabletHealth) - tracker := NewTracker(ch, false) + tracker := NewTracker(ch, false, sqlparser.NewTestParser()) tracker.consumeDelay = 1 * time.Millisecond tracker.Start() defer tracker.Stop() @@ -173,10 +173,10 @@ func TestTableTracking(t *testing.T) { }, { // initial load of view - kept empty }, { - "t1": "create table t1(id bigint primary key, name varchar(50))", - "t2": "create table t2(id varchar(50) primary key)", + "t1": "create table t1(id bigint primary key, name varchar(50), email varchar(50) not null default 'a@b.com')", + "T1": "create table T1(id varchar(50) primary key)", }, { - "t2": "create table t2(id varchar(50) primary key, name varchar(50))", + "T1": "create table T1(id varchar(50) primary key, name varchar(50))", "t3": "create table t3(id datetime primary key)", }, { "t4": "create table t4(name varchar(50) primary key)", @@ -185,32 +185,32 @@ func TestTableTracking(t *testing.T) { testcases := []testCases{{ testName: "initial table load", expTbl: map[string][]vindexes.Column{ - "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, + "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32, CollationName: "binary", Nullable: true}}, }, }, { testName: "new tables", - updTbl: []string{"t1", "t2"}, + updTbl: []string{"t1", "T1"}, expTbl: map[string][]vindexes.Column{ - "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}}, + "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32, CollationName: "binary", Nullable: true}}, + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: true}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}, {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: false, Default: &sqlparser.Literal{Val: "a@b.com"}}}, + "T1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}}, }, }, { - testName: "delete prior, updated t2 and new t3", - updTbl: []string{"prior", "t2", "t3"}, + testName: "delete prior, updated T1 and new t3", + updTbl: []string{"prior", "T1", "t3"}, expTbl: map[string][]vindexes.Column{ - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: true}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}, {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: false, Default: &sqlparser.Literal{Val: "a@b.com"}}}, + "T1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}}, + "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME, CollationName: "binary", Size: 0, Nullable: true}}, }, }, { testName: "new t4", updTbl: []string{"t4"}, expTbl: map[string][]vindexes.Column{ - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, - "t4": {{Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: true}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}, {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: false, Default: &sqlparser.Literal{Val: "a@b.com"}}}, + "T1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}}, + "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME, CollationName: "binary", Size: 0, Nullable: true}}, + "t4": {{Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, Nullable: true}}, }, }} @@ -225,9 +225,9 @@ func TestViewsTracking(t *testing.T) { "prior": "create view prior as select 1 from tbl", }, { "t1": "create view t1 as select 1 from tbl1", - "t2": "create view t2 as select 1 from tbl2", + "V1": "create view V1 as select 1 from tbl2", }, { - "t2": "create view t2 as select 1,2 from tbl2", + "V1": "create view V1 as select 1,2 from tbl2", "t3": "create view t3 as select 1 from tbl3", }, { "t4": "create view t4 as select 1 from tbl4", @@ -238,25 +238,25 @@ func TestViewsTracking(t *testing.T) { expView: map[string]string{ "prior": "select 1 from tbl"}, }, { - testName: "new view t1, t2", - updView: []string{"t1", "t2"}, + testName: "new view t1, V1", + updView: []string{"t1", "V1"}, expView: map[string]string{ "t1": "select 1 from tbl1", - "t2": "select 1 from tbl2", + "V1": "select 1 from tbl2", "prior": "select 1 from tbl"}, }, { - testName: "delete prior, updated t2 and new t3", - updView: []string{"prior", "t2", "t3"}, + testName: "delete prior, updated V1 and new t3", + updView: []string{"prior", "V1", "t3"}, expView: map[string]string{ "t1": "select 1 from tbl1", - "t2": "select 1, 2 from tbl2", + "V1": "select 1, 2 from tbl2", "t3": "select 1 from tbl3"}, }, { testName: "new t4", updView: []string{"t4"}, expView: map[string]string{ "t1": "select 1 from tbl1", - "t2": "select 1, 2 from tbl2", + "V1": "select 1, 2 from tbl2", "t3": "select 1 from tbl3", "t4": "select 1 from tbl4"}, }} @@ -264,8 +264,8 @@ func TestViewsTracking(t *testing.T) { testTracker(t, schemaDefResult, testcases) } -// TestTableInfoRetrieval tests that the tracker is able to retrieve required information from ddl statement. -func TestTableInfoRetrieval(t *testing.T) { +// TestFKInfoRetrieval tests that the tracker is able to retrieve required foreign key information from ddl statement. +func TestFKInfoRetrieval(t *testing.T) { schemaDefResult := []map[string]string{{ "my_tbl": "CREATE TABLE `my_tbl` (" + "`id` bigint NOT NULL AUTO_INCREMENT," + @@ -292,9 +292,9 @@ func TestTableInfoRetrieval(t *testing.T) { testName: "initial table load", expTbl: map[string][]vindexes.Column{ "my_tbl": { - {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, - {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, - {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: false}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, CollationName: "latin1_swedish_ci", Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, Size: 100, CollationName: "binary", Nullable: true, Default: &sqlparser.NullVal{}}, }, }, }, { @@ -302,15 +302,15 @@ func TestTableInfoRetrieval(t *testing.T) { updTbl: []string{"my_child_tbl"}, expTbl: map[string][]vindexes.Column{ "my_tbl": { - {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, - {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, - {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: false}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, CollationName: "latin1_swedish_ci", Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, Size: 100, CollationName: "binary", Nullable: true, Default: &sqlparser.NullVal{}}, }, "my_child_tbl": { - {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, - {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, - {Name: sqlparser.NewIdentifierCI("code"), Type: querypb.Type_VARCHAR, CollationName: "utf8mb4_0900_ai_ci"}, - {Name: sqlparser.NewIdentifierCI("my_id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: false}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, Size: 50, CollationName: "latin1_swedish_ci", Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("code"), Type: querypb.Type_VARCHAR, Size: 6, CollationName: "utf8mb4_0900_ai_ci", Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("my_id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: true, Default: &sqlparser.NullVal{}}, }, }, expFk: map[string]string{ @@ -322,12 +322,73 @@ func TestTableInfoRetrieval(t *testing.T) { testTracker(t, schemaDefResult, testcases) } +// TestIndexInfoRetrieval tests that the tracker is able to retrieve required index information from ddl statement. +func TestIndexInfoRetrieval(t *testing.T) { + schemaDefResult := []map[string]string{{ + "my_tbl": "CREATE TABLE `my_tbl` (" + + "`id` bigint NOT NULL AUTO_INCREMENT," + + "`name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL," + + "`email` varbinary(100) DEFAULT NULL," + + "PRIMARY KEY (`id`)," + + "KEY `id` (`id`,`name`)) " + + "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + }, { + // initial load of view - kept empty + }, { + "my_tbl": "CREATE TABLE `my_tbl` (" + + "`id` bigint NOT NULL AUTO_INCREMENT," + + "`name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL," + + "`email` varbinary(100) DEFAULT NULL," + + "PRIMARY KEY (`id`)," + + "KEY `id` (`id`,`name`), " + + "UNIQUE KEY `email` (`email`)) " + + "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + }} + + testcases := []testCases{{ + testName: "initial table load", + expTbl: map[string][]vindexes.Column{ + "my_tbl": { + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: false}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci", Size: 50, Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "binary", Size: 100, Nullable: true, Default: &sqlparser.NullVal{}}, + }, + }, + expIdx: map[string][]string{ + "my_tbl": { + "primary key (id)", + "key id (id, `name`)", + }, + }, + }, { + testName: "next load", + updTbl: []string{"my_tbl"}, + expTbl: map[string][]vindexes.Column{ + "my_tbl": { + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "binary", Nullable: false}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci", Size: 50, Nullable: true, Default: &sqlparser.NullVal{}}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "binary", Size: 100, Nullable: true, Default: &sqlparser.NullVal{}}, + }, + }, + expIdx: map[string][]string{ + "my_tbl": { + "primary key (id)", + "key id (id, `name`)", + "unique key email (email)", + }, + }, + }} + + testTracker(t, schemaDefResult, testcases) +} + type testCases struct { testName string updTbl []string expTbl map[string][]vindexes.Column expFk map[string]string + expIdx map[string][]string updView []string expView map[string]string @@ -335,7 +396,7 @@ type testCases struct { func testTracker(t *testing.T, schemaDefResult []map[string]string, tcases []testCases) { ch := make(chan *discovery.TabletHealth) - tracker := NewTracker(ch, true) + tracker := NewTracker(ch, true, sqlparser.NewTestParser()) tracker.consumeDelay = 1 * time.Millisecond tracker.Start() defer tracker.Stop() @@ -376,6 +437,16 @@ func testTracker(t *testing.T, schemaDefResult []map[string]string, tcases []tes utils.MustMatch(t, tcase.expFk[k], sqlparser.String(fk), "mismatch foreign keys for table: ", k) } } + expIndexes := tcase.expIdx[k] + if len(expIndexes) > 0 { + idxs := tracker.GetIndexes(keyspace, k) + if len(expIndexes) != len(idxs) { + t.Fatalf("mismatch index for table: %s", k) + } + for i, idx := range idxs { + utils.MustMatch(t, expIndexes[i], sqlparser.String(idx), "mismatch index for table: ", k) + } + } } for k, v := range tcase.expView { diff --git a/go/vt/vtgate/schema/update_controller_flaky_test.go b/go/vt/vtgate/schema/update_controller_flaky_test.go index 971389af822..597705963b8 100644 --- a/go/vt/vtgate/schema/update_controller_flaky_test.go +++ b/go/vt/vtgate/schema/update_controller_flaky_test.go @@ -60,6 +60,16 @@ func TestMultipleUpdatesFromDifferentShards(t *testing.T) { }}, updateTables: []string{"a", "b"}, signalExpected: 1, + }, { + inputs: []input{{ + shard: "0", + tablesUpdates: []string{"a"}, + }, { + shard: "0", + tablesUpdates: []string{"A"}, + }}, + updateTables: []string{"a", "A"}, + signalExpected: 1, }, { inputs: []input{{ shard: "0", @@ -205,6 +215,11 @@ func TestViewsUpdates(t *testing.T) { inputs: []input{{shard: "0", viewUpdates: []string{"a"}}, {shard: "0", viewUpdates: []string{"b"}}}, updateViews: []string{"a", "b"}, signalExpected: 1, + }, { + desc: "received different view updates from shards - case sensitive names", + inputs: []input{{shard: "0", viewUpdates: []string{"a"}}, {shard: "0", viewUpdates: []string{"A"}}}, + updateViews: []string{"a", "A"}, + signalExpected: 1, }, { desc: "delay between inputs - different signals from each input", inputs: []input{{shard: "0", viewUpdates: []string{"a"}}, {shard: "0", viewUpdates: []string{"b"}}}, diff --git a/go/vt/vtgate/semantics/FakeSI.go b/go/vt/vtgate/semantics/FakeSI.go index b7043b42980..933f4cd40f8 100644 --- a/go/vt/vtgate/semantics/FakeSI.go +++ b/go/vt/vtgate/semantics/FakeSI.go @@ -24,6 +24,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -47,7 +48,11 @@ func (s *FakeSI) FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Tab } func (*FakeSI) ConnCollation() collations.ID { - return 45 + return collations.CollationUtf8mb4ID +} + +func (s *FakeSI) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() } func (s *FakeSI) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { @@ -61,6 +66,10 @@ func (s *FakeSI) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyM return vschemapb.Keyspace_unmanaged, nil } +func (s *FakeSI) GetForeignKeyChecksState() *bool { + return nil +} + func (s *FakeSI) KeyspaceError(keyspace string) error { if s.KsError != nil { fkErr, isPresent := s.KsError[keyspace] diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index e524b1a33cf..cf2b3300208 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -28,47 +28,61 @@ import ( // analyzer controls the flow of the analysis. // It starts the tree walking and controls which part of the analysis sees which parts of the tree type analyzer struct { - scoper *scoper - tables *tableCollector - binder *binder - typer *typer - rewriter *earlyRewriter - sig QuerySignature + scoper *scoper + earlyTables *earlyTableCollector + tables *tableCollector + binder *binder + typer *typer + rewriter *earlyRewriter + sig QuerySignature + si SchemaInformation + currentDb string err error inProjection int - projErr error - unshardedErr error - warning string + projErr error + unshardedErr error + warning string + singleUnshardedKeyspace bool + fullAnalysis bool } // newAnalyzer create the semantic analyzer -func newAnalyzer(dbName string, si SchemaInformation) *analyzer { +func newAnalyzer(dbName string, si SchemaInformation, fullAnalysis bool) *analyzer { // TODO dependencies between these components are a little tangled. We should try to clean up s := newScoper() a := &analyzer{ - scoper: s, - tables: newTableCollector(s, si, dbName), - typer: newTyper(), + scoper: s, + earlyTables: newEarlyTableCollector(si, dbName), + typer: newTyper(si.Environment().CollationEnv()), + si: si, + currentDb: dbName, + fullAnalysis: fullAnalysis, } s.org = a - a.tables.org = a + return a +} - b := newBinder(s, a, a.tables, a.typer) - a.binder = b +func (a *analyzer) lateInit() { + a.tables = a.earlyTables.newTableCollector(a.scoper, a) + a.binder = newBinder(a.scoper, a, a.tables, a.typer) + a.scoper.binder = a.binder a.rewriter = &earlyRewriter{ - scoper: s, - binder: b, + env: a.si.Environment(), + scoper: a.scoper, + binder: a.binder, expandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{}, } - s.binder = b - return a } // Analyze analyzes the parsed query. func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) { - analyzer := newAnalyzer(currentDb, newSchemaInfo(si)) + return analyseAndGetSemTable(statement, currentDb, si, false) +} + +func analyseAndGetSemTable(statement sqlparser.Statement, currentDb string, si SchemaInformation, fullAnalysis bool) (*SemTable, error) { + analyzer := newAnalyzer(currentDb, newSchemaInfo(si), fullAnalysis) // Analysis for initial scope err := analyzer.analyze(statement) @@ -77,12 +91,12 @@ func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformati } // Creation of the semantic table - return analyzer.newSemTable(statement, si.ConnCollation()) + return analyzer.newSemTable(statement, si.ConnCollation(), si.GetForeignKeyChecksState(), si.Environment().CollationEnv()) } // AnalyzeStrict analyzes the parsed query, and fails the analysis for any possible errors func AnalyzeStrict(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) { - st, err := Analyze(statement, currentDb, si) + st, err := analyseAndGetSemTable(statement, currentDb, si, true) if err != nil { return nil, err } @@ -97,18 +111,49 @@ func AnalyzeStrict(statement sqlparser.Statement, currentDb string, si SchemaInf return st, nil } -func (a *analyzer) newSemTable(statement sqlparser.Statement, coll collations.ID) (*SemTable, error) { +func (a *analyzer) newSemTable( + statement sqlparser.Statement, + coll collations.ID, + fkChecksState *bool, + env *collations.Environment, +) (*SemTable, error) { var comments *sqlparser.ParsedComments commentedStmt, isCommented := statement.(sqlparser.Commented) if isCommented { comments = commentedStmt.GetParsedComments() } + + if a.singleUnshardedKeyspace { + return &SemTable{ + Tables: a.earlyTables.Tables, + Comments: comments, + Warning: a.warning, + Collation: coll, + ExprTypes: map[sqlparser.Expr]evalengine.Type{}, + NotSingleRouteErr: a.projErr, + NotUnshardedErr: a.unshardedErr, + Recursive: ExprDependencies{}, + Direct: ExprDependencies{}, + Targets: map[sqlparser.IdentifierCS]TableSet{}, + ColumnEqualities: map[columnName][]sqlparser.Expr{}, + ExpandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{}, + columns: map[*sqlparser.Union]sqlparser.SelectExprs{}, + comparator: nil, + StatementIDs: a.scoper.statementIDs, + QuerySignature: QuerySignature{}, + childForeignKeysInvolved: map[TableSet][]vindexes.ChildFKInfo{}, + parentForeignKeysInvolved: map[TableSet][]vindexes.ParentFKInfo{}, + childFkToUpdExprs: map[string]sqlparser.UpdateExprs{}, + collEnv: env, + }, nil + } + columns := map[*sqlparser.Union]sqlparser.SelectExprs{} for union, info := range a.tables.unionInfo { columns[union] = info.exprs } - childFks, parentFks, err := a.getInvolvedForeignKeys(statement) + childFks, parentFks, childFkToUpdExprs, err := a.getInvolvedForeignKeys(statement, fkChecksState) if err != nil { return nil, err } @@ -118,6 +163,7 @@ func (a *analyzer) newSemTable(statement sqlparser.Statement, coll collations.ID Direct: a.binder.direct, ExprTypes: a.typer.m, Tables: a.tables.Tables, + Targets: a.binder.targets, NotSingleRouteErr: a.projErr, NotUnshardedErr: a.unshardedErr, Warning: a.warning, @@ -130,6 +176,8 @@ func (a *analyzer) newSemTable(statement sqlparser.Statement, coll collations.ID QuerySignature: a.sig, childForeignKeysInvolved: childFks, parentForeignKeysInvolved: parentFks, + childFkToUpdExprs: childFkToUpdExprs, + collEnv: env, }, nil } @@ -265,6 +313,12 @@ func isParentSelect(cursor *sqlparser.Cursor) bool { return isSelect } +func isParentDeleteOrUpdate(cursor *sqlparser.Cursor) bool { + _, isDelete := cursor.Parent().(*sqlparser.Delete) + _, isUpdate := cursor.Parent().(*sqlparser.Update) + return isDelete || isUpdate +} + func isParentSelectStatement(cursor *sqlparser.Cursor) bool { _, isSelect := cursor.Parent().(sqlparser.SelectStatement) return isSelect @@ -283,10 +337,66 @@ func (a *analyzer) depsForExpr(expr sqlparser.Expr) (direct, recursive TableSet, } func (a *analyzer) analyze(statement sqlparser.Statement) error { + _ = sqlparser.Rewrite(statement, nil, a.earlyUp) + if a.err != nil { + return a.err + } + + if a.canShortCut(statement) { + return nil + } + + a.lateInit() + _ = sqlparser.Rewrite(statement, a.analyzeDown, a.analyzeUp) return a.err } +// canShortCut checks if we are dealing with a single unsharded keyspace and no tables that have managed foreign keys +// if so, we can stop the analyzer early +func (a *analyzer) canShortCut(statement sqlparser.Statement) (canShortCut bool) { + if a.fullAnalysis { + return false + } + ks, _ := singleUnshardedKeyspace(a.earlyTables.Tables) + if ks == nil { + return false + } + + defer func() { + a.singleUnshardedKeyspace = canShortCut + }() + + if !sqlparser.IsDMLStatement(statement) { + return true + } + + fkMode, err := a.si.ForeignKeyMode(ks.Name) + if err != nil { + a.err = err + return false + } + if fkMode != vschemapb.Keyspace_managed { + return true + } + + for _, table := range a.earlyTables.Tables { + vtbl := table.GetVindexTable() + if len(vtbl.ChildForeignKeys) > 0 || len(vtbl.ParentForeignKeys) > 0 { + return false + } + } + + return true +} + +// earlyUp collects tables in the query, so we can check +// if this a single unsharded query we are dealing with +func (a *analyzer) earlyUp(cursor *sqlparser.Cursor) bool { + a.earlyTables.up(cursor) + return true +} + func (a *analyzer) shouldContinue() bool { return a.err == nil } @@ -313,18 +423,23 @@ func (a *analyzer) noteQuerySignature(node sqlparser.SQLNode) { } case sqlparser.AggrFunc: a.sig.Aggregation = true + case *sqlparser.Delete, *sqlparser.Update, *sqlparser.Insert: + a.sig.DML = true } } // getInvolvedForeignKeys gets the foreign keys that might require taking care off when executing the given statement. -func (a *analyzer) getInvolvedForeignKeys(statement sqlparser.Statement) (map[TableSet][]vindexes.ChildFKInfo, map[TableSet][]vindexes.ParentFKInfo, error) { +func (a *analyzer) getInvolvedForeignKeys(statement sqlparser.Statement, fkChecksState *bool) (map[TableSet][]vindexes.ChildFKInfo, map[TableSet][]vindexes.ParentFKInfo, map[string]sqlparser.UpdateExprs, error) { + if fkChecksState != nil && !*fkChecksState { + return nil, nil, nil, nil + } // There are only the DML statements that require any foreign keys handling. switch stmt := statement.(type) { case *sqlparser.Delete: // For DELETE statements, none of the parent foreign keys require handling. // So we collect all the child foreign keys. allChildFks, _, err := a.getAllManagedForeignKeys() - return allChildFks, nil, err + return allChildFks, nil, nil, err case *sqlparser.Insert: // For INSERT statements, we have 3 different cases: // 1. REPLACE statement: REPLACE statements are essentially DELETEs and INSERTs rolled into one. @@ -334,35 +449,34 @@ func (a *analyzer) getInvolvedForeignKeys(statement sqlparser.Statement) (map[Ta // 3. INSERT with ON DUPLICATE KEY UPDATE: This might trigger an update on the columns specified in the ON DUPLICATE KEY UPDATE clause. allChildFks, allParentFKs, err := a.getAllManagedForeignKeys() if err != nil { - return nil, nil, err + return nil, nil, nil, err } if stmt.Action == sqlparser.ReplaceAct { - return allChildFks, allParentFKs, nil + return allChildFks, allParentFKs, nil, nil } if len(stmt.OnDup) == 0 { - return nil, allParentFKs, nil + return nil, allParentFKs, nil, nil } // If only a certain set of columns are being updated, then there might be some child foreign keys that don't need any consideration since their columns aren't being updated. // So, we filter these child foreign keys out. We can't filter any parent foreign keys because the statement will INSERT a row too, which requires validating all the parent foreign keys. - updatedChildFks, _ := a.filterForeignKeysUsingUpdateExpressions(allChildFks, nil, sqlparser.UpdateExprs(stmt.OnDup)) - return updatedChildFks, allParentFKs, nil + updatedChildFks, _, childFkToUpdExprs, err := a.filterForeignKeysUsingUpdateExpressions(allChildFks, nil, sqlparser.UpdateExprs(stmt.OnDup)) + return updatedChildFks, allParentFKs, childFkToUpdExprs, err case *sqlparser.Update: // For UPDATE queries we get all the parent and child foreign keys, but we can filter some of them out if the columns that they consist off aren't being updated or are set to NULLs. allChildFks, allParentFks, err := a.getAllManagedForeignKeys() if err != nil { - return nil, nil, err + return nil, nil, nil, err } - childFks, parentFks := a.filterForeignKeysUsingUpdateExpressions(allChildFks, allParentFks, stmt.Exprs) - return childFks, parentFks, nil + return a.filterForeignKeysUsingUpdateExpressions(allChildFks, allParentFks, stmt.Exprs) default: - return nil, nil, nil + return nil, nil, nil, nil } } // filterForeignKeysUsingUpdateExpressions filters the child and parent foreign key constraints that don't require any validations/cascades given the updated expressions. -func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[TableSet][]vindexes.ChildFKInfo, allParentFks map[TableSet][]vindexes.ParentFKInfo, updExprs sqlparser.UpdateExprs) (map[TableSet][]vindexes.ChildFKInfo, map[TableSet][]vindexes.ParentFKInfo) { +func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[TableSet][]vindexes.ChildFKInfo, allParentFks map[TableSet][]vindexes.ParentFKInfo, updExprs sqlparser.UpdateExprs) (map[TableSet][]vindexes.ChildFKInfo, map[TableSet][]vindexes.ParentFKInfo, map[string]sqlparser.UpdateExprs, error) { if len(allChildFks) == 0 && len(allParentFks) == 0 { - return nil, nil + return nil, nil, nil, nil } pFksRequired := make(map[TableSet][]bool, len(allParentFks)) @@ -377,11 +491,17 @@ func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[Table // updExprToTableSet stores the tables that the updated expressions are from. updExprToTableSet := make(map[*sqlparser.ColName]TableSet) + // childFKToUpdExprs stores child foreign key to update expressions mapping. + childFKToUpdExprs := map[string]sqlparser.UpdateExprs{} + // Go over all the update expressions for _, updateExpr := range updExprs { deps := a.binder.direct.dependencies(updateExpr.Name) if deps.NumberOfTables() != 1 { - panic("expected to have single table dependency") + // If we don't get exactly one table for the given update expression, we would have definitely run into an error + // during the binder phase that we would have stored. We should return that error, since we can't safely proceed with + // foreign key related changes without having all the information. + return nil, nil, nil, a.getError() } updExprToTableSet[updateExpr.Name] = deps // Get all the child and parent foreign keys for the given table that the update expression belongs to. @@ -393,9 +513,13 @@ func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[Table for idx, childFk := range childFks { if childFk.ParentColumns.FindColumn(updateExpr.Name.Name) >= 0 { cFksRequired[deps][idx] = true + tbl, _ := a.tables.tableInfoFor(deps) + ue := childFKToUpdExprs[childFk.String(tbl.GetVindexTable())] + ue = append(ue, updateExpr) + childFKToUpdExprs[childFk.String(tbl.GetVindexTable())] = ue } } - // If we are setting a column to NULL, then we don't need to verify the existance of an + // If we are setting a column to NULL, then we don't need to verify the existence of an // equivalent row in the parent table, even if this column was part of a foreign key to a parent table. if sqlparser.IsNull(updateExpr.Expr) { continue @@ -434,7 +558,6 @@ func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[Table } } pFksNeedsHandling[ts] = pFKNeeded - } for ts, childFks := range allChildFks { var cFKNeeded []vindexes.ChildFKInfo @@ -444,12 +567,22 @@ func (a *analyzer) filterForeignKeysUsingUpdateExpressions(allChildFks map[Table } } cFksNeedsHandling[ts] = cFKNeeded + } + return cFksNeedsHandling, pFksNeedsHandling, childFKToUpdExprs, nil +} +// getError gets the error stored in the analyzer during previous phases. +func (a *analyzer) getError() error { + if a.projErr != nil { + return a.projErr + } + if a.unshardedErr != nil { + return a.unshardedErr } - return cFksNeedsHandling, pFksNeedsHandling + return a.err } -// getAllManagedForeignKeys gets all the foreign keys for the query we are analyzing that Vitess is reposible for managing. +// getAllManagedForeignKeys gets all the foreign keys for the query we are analyzing that Vitess is responsible for managing. func (a *analyzer) getAllManagedForeignKeys() (map[TableSet][]vindexes.ChildFKInfo, map[TableSet][]vindexes.ParentFKInfo, error) { allChildFKs := make(map[TableSet][]vindexes.ChildFKInfo) allParentFKs := make(map[TableSet][]vindexes.ParentFKInfo) @@ -462,7 +595,7 @@ func (a *analyzer) getAllManagedForeignKeys() (map[TableSet][]vindexes.ChildFKIn continue } // Check whether Vitess needs to manage the foreign keys in this keyspace or not. - fkMode, err := a.tables.si.ForeignKeyMode(vi.Keyspace.Name) + fkMode, err := a.si.ForeignKeyMode(vi.Keyspace.Name) if err != nil { return nil, nil, err } @@ -470,7 +603,7 @@ func (a *analyzer) getAllManagedForeignKeys() (map[TableSet][]vindexes.ChildFKIn continue } // Cyclic foreign key constraints error is stored in the keyspace. - ksErr := a.tables.si.KeyspaceError(vi.Keyspace.Name) + ksErr := a.si.KeyspaceError(vi.Keyspace.Name) if ksErr != nil { return nil, nil, ksErr } diff --git a/go/vt/vtgate/semantics/analyzer_fk_test.go b/go/vt/vtgate/semantics/analyzer_fk_test.go new file mode 100644 index 00000000000..05a5991b49f --- /dev/null +++ b/go/vt/vtgate/semantics/analyzer_fk_test.go @@ -0,0 +1,602 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package semantics + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +var parentTbl = &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("parentt"), + Keyspace: &vindexes.Keyspace{ + Name: "ks", + }, +} + +var tbl = map[string]TableInfo{ + "t0": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t0"), + Keyspace: &vindexes.Keyspace{Name: "ks"}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(parentTbl, []string{"col"}, []string{"col"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), + }, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + }, + "t1": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: "ks_unmanaged", Sharded: true}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(parentTbl, []string{"cola"}, []string{"cola"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola1", "cola2"}, []string{"ccola1", "ccola2"}, sqlparser.SetNull), + }, + }, + }, + "t2": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t2"), + Keyspace: &vindexes.Keyspace{Name: "ks"}, + }, + }, + "t3": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t3"), + Keyspace: &vindexes.Keyspace{Name: "undefined_ks", Sharded: true}, + }, + }, + "t4": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t4"), + Keyspace: &vindexes.Keyspace{Name: "ks"}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(parentTbl, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), + ckInfo(parentTbl, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), + ckInfo(parentTbl, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), + }, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(parentTbl, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), + pkInfo(parentTbl, []string{"pcolc"}, []string{"colc"}), + pkInfo(parentTbl, []string{"pcolb", "pcola"}, []string{"colb", "cola"}), + pkInfo(parentTbl, []string{"pcolb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"pcola"}, []string{"cola"}), + pkInfo(parentTbl, []string{"pcolb", "pcolx"}, []string{"colb", "colx"}), + }, + }, + }, + "t5": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t5"), + Keyspace: &vindexes.Keyspace{Name: "ks"}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(parentTbl, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), + ckInfo(parentTbl, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), + }, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(parentTbl, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), + pkInfo(parentTbl, []string{"pcola"}, []string{"cola"}), + pkInfo(parentTbl, []string{"pcold", "pcolc"}, []string{"cold", "colc"}), + pkInfo(parentTbl, []string{"pcold"}, []string{"cold"}), + pkInfo(parentTbl, []string{"pcold", "pcolx"}, []string{"cold", "colx"}), + }, + }, + }, + "t6": &RealTable{ + Table: &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t6"), + Keyspace: &vindexes.Keyspace{Name: "ks"}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(parentTbl, []string{"col"}, []string{"col"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), + ckInfo(parentTbl, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), + ckInfo(parentTbl, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), + ckInfo(parentTbl, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), + }, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + }, +} + +// TestGetAllManagedForeignKeys tests the functionality of getAllManagedForeignKeys. +func TestGetAllManagedForeignKeys(t *testing.T) { + tests := []struct { + name string + analyzer *analyzer + childFkWanted map[TableSet][]vindexes.ChildFKInfo + parentFkWanted map[TableSet][]vindexes.ParentFKInfo + expectedErr string + }{ + { + name: "Collect all foreign key constraints", + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t0"], + tbl["t1"], + &DerivedTable{}, + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + }, + }, + childFkWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"col"}, []string{"col"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), + }, + }, + parentFkWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + }, + { + name: "keyspace not found in schema information", + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t2"], + tbl["t3"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + }, + }, + }, + expectedErr: "undefined_ks keyspace not found", + }, + { + name: "Cyclic fk constraints error", + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t0"], tbl["t1"], + &DerivedTable{}, + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + KsError: map[string]error{ + "ks": fmt.Errorf("VT09019: keyspace 'ks' has cyclic foreign keys"), + }, + }, + }, + expectedErr: "VT09019: keyspace 'ks' has cyclic foreign keys", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + childFk, parentFk, err := tt.analyzer.getAllManagedForeignKeys() + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + return + } + require.EqualValues(t, tt.childFkWanted, childFk) + require.EqualValues(t, tt.parentFkWanted, parentFk) + }) + } +} + +// TestFilterForeignKeysUsingUpdateExpressions tests the functionality of filterForeignKeysUsingUpdateExpressions. +func TestFilterForeignKeysUsingUpdateExpressions(t *testing.T) { + cola := sqlparser.NewColName("cola") + colb := sqlparser.NewColName("colb") + colc := sqlparser.NewColName("colc") + cold := sqlparser.NewColName("cold") + a := &analyzer{ + binder: &binder{ + direct: map[sqlparser.Expr]TableSet{ + cola: SingleTableSet(0), + colb: SingleTableSet(0), + colc: SingleTableSet(1), + cold: SingleTableSet(1), + }, + }, + unshardedErr: fmt.Errorf("ambiguous test error"), + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t4"], + tbl["t5"], + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + }, + }, + }, + } + updateExprs := sqlparser.UpdateExprs{ + &sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}, + &sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}, + &sqlparser.UpdateExpr{Name: colc, Expr: sqlparser.NewIntLiteral("1")}, + &sqlparser.UpdateExpr{Name: cold, Expr: &sqlparser.NullVal{}}, + } + tests := []struct { + name string + analyzer *analyzer + allChildFks map[TableSet][]vindexes.ChildFKInfo + allParentFks map[TableSet][]vindexes.ParentFKInfo + updExprs sqlparser.UpdateExprs + childFksWanted map[TableSet][]vindexes.ChildFKInfo + parentFksWanted map[TableSet][]vindexes.ParentFKInfo + errWanted string + }{ + { + name: "Child Foreign Keys Filtering", + analyzer: a, + allParentFks: nil, + allChildFks: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): tbl["t4"].(*RealTable).Table.ChildForeignKeys, + SingleTableSet(1): tbl["t5"].(*RealTable).Table.ChildForeignKeys, + }, + updExprs: updateExprs, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), + }, + SingleTableSet(1): { + ckInfo(parentTbl, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), + }, + }, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{}, + }, { + name: "Parent Foreign Keys Filtering", + analyzer: a, + allParentFks: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): tbl["t4"].(*RealTable).Table.ParentForeignKeys, + SingleTableSet(1): tbl["t5"].(*RealTable).Table.ParentForeignKeys, + }, + allChildFks: nil, + updExprs: updateExprs, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{}, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), + pkInfo(parentTbl, []string{"pcola"}, []string{"cola"}), + }, + SingleTableSet(1): { + pkInfo(parentTbl, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), + }, + }, + }, { + name: "Unknown column", + analyzer: a, + allParentFks: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): tbl["t4"].(*RealTable).Table.ParentForeignKeys, + SingleTableSet(1): tbl["t5"].(*RealTable).Table.ParentForeignKeys, + }, + allChildFks: nil, + updExprs: sqlparser.UpdateExprs{ + &sqlparser.UpdateExpr{Name: sqlparser.NewColName("unknownCol"), Expr: sqlparser.NewIntLiteral("1")}, + }, + errWanted: "ambiguous test error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + childFks, parentFks, _, err := tt.analyzer.filterForeignKeysUsingUpdateExpressions(tt.allChildFks, tt.allParentFks, tt.updExprs) + require.EqualValues(t, tt.childFksWanted, childFks) + require.EqualValues(t, tt.parentFksWanted, parentFks) + if tt.errWanted != "" { + require.EqualError(t, err, tt.errWanted) + } else { + require.NoError(t, err) + } + }) + } +} + +// TestGetInvolvedForeignKeys tests the functionality of getInvolvedForeignKeys. +func TestGetInvolvedForeignKeys(t *testing.T) { + cola := sqlparser.NewColName("cola") + colb := sqlparser.NewColName("colb") + colc := sqlparser.NewColName("colc") + cold := sqlparser.NewColName("cold") + tests := []struct { + name string + stmt sqlparser.Statement + analyzer *analyzer + childFksWanted map[TableSet][]vindexes.ChildFKInfo + parentFksWanted map[TableSet][]vindexes.ParentFKInfo + childFkUpdateExprsWanted map[string]sqlparser.UpdateExprs + expectedErr string + }{ + { + name: "Delete Query", + stmt: &sqlparser.Delete{}, + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t0"], + tbl["t1"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + }, + }, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"col"}, []string{"col"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), + }, + }, + }, + { + name: "Update statement", + stmt: &sqlparser.Update{ + Exprs: sqlparser.UpdateExprs{ + &sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}, + &sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}, + &sqlparser.UpdateExpr{Name: colc, Expr: sqlparser.NewIntLiteral("1")}, + &sqlparser.UpdateExpr{Name: cold, Expr: &sqlparser.NullVal{}}, + }, + }, + analyzer: &analyzer{ + binder: &binder{ + direct: map[sqlparser.Expr]TableSet{ + cola: SingleTableSet(0), + colb: SingleTableSet(0), + colc: SingleTableSet(1), + cold: SingleTableSet(1), + }, + }, + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t4"], + tbl["t5"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + }, + }, + }, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), + }, + SingleTableSet(1): { + ckInfo(parentTbl, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), + }, + }, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), + pkInfo(parentTbl, []string{"pcola"}, []string{"cola"}), + }, + SingleTableSet(1): { + pkInfo(parentTbl, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), + }, + }, + childFkUpdateExprsWanted: map[string]sqlparser.UpdateExprs{ + "ks.parentt|child_cola|child_colx||ks.t4|cola|colx": {&sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}}, + "ks.parentt|child_colb||ks.t4|colb": {&sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}}, + "ks.parentt|child_colc|child_colx||ks.t5|colc|colx": {&sqlparser.UpdateExpr{Name: colc, Expr: sqlparser.NewIntLiteral("1")}}, + "ks.parentt|child_cold||ks.t5|cold": {&sqlparser.UpdateExpr{Name: cold, Expr: &sqlparser.NullVal{}}}, + }, + }, + { + name: "Replace Query", + stmt: &sqlparser.Insert{ + Action: sqlparser.ReplaceAct, + }, + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t0"], + tbl["t1"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + }, + }, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"col"}, []string{"col"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), + }, + }, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + }, + { + name: "Insert Query", + stmt: &sqlparser.Insert{ + Action: sqlparser.InsertAct, + }, + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t0"], + tbl["t1"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + }, + }, + childFksWanted: nil, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + }, + { + name: "Insert Query with On Duplicate", + stmt: &sqlparser.Insert{ + Action: sqlparser.InsertAct, + OnDup: sqlparser.OnDup{ + &sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}, + &sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}, + }, + }, + analyzer: &analyzer{ + binder: &binder{ + direct: map[sqlparser.Expr]TableSet{ + cola: SingleTableSet(0), + colb: SingleTableSet(0), + }, + }, + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t6"], + tbl["t1"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + "ks_unmanaged": vschemapb.Keyspace_unmanaged, + }, + }, + }, + childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ + SingleTableSet(0): { + ckInfo(parentTbl, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), + ckInfo(parentTbl, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), + }, + }, + parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ + SingleTableSet(0): { + pkInfo(parentTbl, []string{"colb"}, []string{"colb"}), + pkInfo(parentTbl, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), + }, + }, + childFkUpdateExprsWanted: map[string]sqlparser.UpdateExprs{ + "ks.parentt|child_cola|child_colx||ks.t6|cola|colx": {&sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}}, + "ks.parentt|child_colb||ks.t6|colb": {&sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}}, + }, + }, + { + name: "Insert error", + stmt: &sqlparser.Insert{}, + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t2"], + tbl["t3"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + }, + }, + }, + expectedErr: "undefined_ks keyspace not found", + }, + { + name: "Update error", + stmt: &sqlparser.Update{}, + analyzer: &analyzer{ + tables: &tableCollector{ + Tables: []TableInfo{ + tbl["t2"], + tbl["t3"], + }, + }, + si: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + "ks": vschemapb.Keyspace_managed, + }, + }, + }, + expectedErr: "undefined_ks keyspace not found", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fkState := true + childFks, parentFks, childFkUpdateExprs, err := tt.analyzer.getInvolvedForeignKeys(tt.stmt, &fkState) + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + return + } + require.EqualValues(t, tt.childFksWanted, childFks) + require.EqualValues(t, tt.childFkUpdateExprsWanted, childFkUpdateExprs) + require.EqualValues(t, tt.parentFksWanted, parentFks) + }) + } +} + +func ckInfo(cTable *vindexes.Table, pCols []string, cCols []string, refAction sqlparser.ReferenceAction) vindexes.ChildFKInfo { + return vindexes.ChildFKInfo{ + Table: cTable, + ParentColumns: sqlparser.MakeColumns(pCols...), + ChildColumns: sqlparser.MakeColumns(cCols...), + OnDelete: refAction, + } +} + +func pkInfo(parentTable *vindexes.Table, pCols []string, cCols []string) vindexes.ParentFKInfo { + return vindexes.ParentFKInfo{ + Table: parentTable, + ParentColumns: sqlparser.MakeColumns(pCols...), + ChildColumns: sqlparser.MakeColumns(cCols...), + } +} diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index c8251dd36c3..a7c173ccc96 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -17,7 +17,6 @@ limitations under the License. package semantics import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -25,12 +24,11 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" ) -var T0 TableSet +var NoTables TableSet var ( // Just here to make outputs more readable @@ -122,7 +120,7 @@ func TestBindingSingleTableNegative(t *testing.T) { } for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) st, err := Analyze(parse, "d", &FakeSI{}) require.NoError(t, err) @@ -142,7 +140,7 @@ func TestBindingSingleAliasedTableNegative(t *testing.T) { } for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) st, err := Analyze(parse, "", &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -240,7 +238,7 @@ func TestBindingMultiTableNegative(t *testing.T) { } for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) _, err = Analyze(parse, "d", &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -264,7 +262,7 @@ func TestBindingMultiAliasedTableNegative(t *testing.T) { } for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) _, err = Analyze(parse, "d", &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -277,6 +275,26 @@ func TestBindingMultiAliasedTableNegative(t *testing.T) { } } +func TestBindingDelete(t *testing.T) { + queries := []string{ + "delete tbl from tbl", + "delete from tbl", + "delete t1 from t1, t2", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + stmt, semTable := parseAndAnalyze(t, query, "d") + del := stmt.(*sqlparser.Delete) + t1 := del.TableExprs[0].(*sqlparser.AliasedTableExpr) + ts := semTable.TableSetFor(t1) + assert.Equal(t, SingleTableSet(0), ts) + + actualTs := semTable.Targets[del.Targets[0].Name] + assert.Equal(t, ts, actualTs) + }) + } +} + func TestNotUniqueTableName(t *testing.T) { queries := []string{ "select * from t, t", @@ -287,7 +305,7 @@ func TestNotUniqueTableName(t *testing.T) { for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, _ := sqlparser.Parse(query) + parse, _ := sqlparser.NewTestParser().Parse(query) _, err := Analyze(parse, "test", &FakeSI{}) require.Error(t, err) require.Contains(t, err.Error(), "VT03013: not unique table/alias") @@ -302,7 +320,7 @@ func TestMissingTable(t *testing.T) { for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, _ := sqlparser.Parse(query) + parse, _ := sqlparser.NewTestParser().Parse(query) st, err := Analyze(parse, "", &FakeSI{}) require.NoError(t, err) require.ErrorContains(t, st.NotUnshardedErr, "column 't.col' not found") @@ -390,7 +408,7 @@ func TestUnknownColumnMap2(t *testing.T) { queries := []string{"select col from a, b", "select col from a as user, b as extra"} for _, query := range queries { t.Run(query, func(t *testing.T) { - parse, _ := sqlparser.Parse(query) + parse, _ := sqlparser.NewTestParser().Parse(query) expr := extract(parse.(*sqlparser.Select), 0) for _, test := range tests { @@ -404,7 +422,7 @@ func TestUnknownColumnMap2(t *testing.T) { require.NoError(t, tbl.NotSingleRouteErr) typ, found := tbl.TypeForExpr(expr) assert.True(t, found) - assert.Equal(t, test.typ, typ.Type) + assert.Equal(t, test.typ, typ.Type()) } }) } @@ -421,7 +439,7 @@ func TestUnknownPredicate(t *testing.T) { Name: sqlparser.NewIdentifierCS("b"), } - parse, _ := sqlparser.Parse(query) + parse, _ := sqlparser.NewTestParser().Parse(query) tests := []struct { name string @@ -459,7 +477,7 @@ func TestScoping(t *testing.T) { } for _, query := range queries { t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) + parse, err := sqlparser.NewTestParser().Parse(query.query) require.NoError(t, err) st, err := Analyze(parse, "user", &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -539,7 +557,7 @@ func TestSubqueryOrderByBinding(t *testing.T) { for _, tc := range queries { t.Run(tc.query, func(t *testing.T) { - ast, err := sqlparser.Parse(tc.query) + ast, err := sqlparser.NewTestParser().Parse(tc.query) require.NoError(t, err) sel := ast.(*sqlparser.Select) @@ -580,7 +598,7 @@ func TestOrderByBindingTable(t *testing.T) { TS0, }, { "select 1 as c from tabl order by c", - T0, + NoTables, }, { "select name, name from t1, t2 order by name", TS1, @@ -628,6 +646,37 @@ func TestOrderByBindingTable(t *testing.T) { } } +func TestVindexHints(t *testing.T) { + // tests that vindex hints point to existing vindexes, or an error should be returned + tcases := []struct { + sql string + expectedErr string + }{{ + sql: "select col from t1 use vindex (does_not_exist)", + expectedErr: "Vindex 'does_not_exist' does not exist in table 'ks2.t1'", + }, { + sql: "select col from t1 ignore vindex (does_not_exist)", + expectedErr: "Vindex 'does_not_exist' does not exist in table 'ks2.t1'", + }, { + sql: "select id from t1 use vindex (id_vindex)", + }, { + sql: "select id from t1 ignore vindex (id_vindex)", + }} + for _, tc := range tcases { + t.Run(tc.sql, func(t *testing.T) { + parse, err := sqlparser.NewTestParser().Parse(tc.sql) + require.NoError(t, err) + + _, err = AnalyzeStrict(parse, "d", fakeSchemaInfo()) + if tc.expectedErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.expectedErr) + } + }) + } +} + func TestGroupByBinding(t *testing.T) { tcases := []struct { sql string @@ -661,7 +710,7 @@ func TestGroupByBinding(t *testing.T) { TS0, }, { "select 1 as c from tabl group by c", - T0, + NoTables, }, { "select t1.id from t1, t2 group by id", TS0, @@ -710,13 +759,13 @@ func TestHavingBinding(t *testing.T) { TS0, }, { "select col from tabl having 1 = 1", - T0, + NoTables, }, { "select col as c from tabl having c = 1", TS0, }, { "select 1 as c from tabl having c = 1", - T0, + NoTables, }, { "select t1.id from t1, t2 having id = 1", TS0, @@ -844,7 +893,7 @@ func TestInvalidQueries(t *testing.T) { for _, tc := range tcases { t.Run(tc.sql, func(t *testing.T) { - parse, err := sqlparser.Parse(tc.sql) + parse, err := sqlparser.NewTestParser().Parse(tc.sql) require.NoError(t, err) st, err := Analyze(parse, "dbName", fakeSchemaInfo()) @@ -883,109 +932,6 @@ func TestUnionWithOrderBy(t *testing.T) { assert.Equal(t, TS1, d2) } -func TestScopingWDerivedTables(t *testing.T) { - queries := []struct { - query string - errorMessage string - recursiveExpectation TableSet - expectation TableSet - }{ - { - query: "select id from (select x as id from user) as t", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select id from (select foo as id from user) as t", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select id from (select foo as id from (select x as foo from user) as c) as t", - recursiveExpectation: TS0, - expectation: TS2, - }, { - query: "select t.id from (select foo as id from user) as t", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select t.id2 from (select foo as id from user) as t", - errorMessage: "column 't.id2' not found", - }, { - query: "select id from (select 42 as id) as t", - recursiveExpectation: T0, - expectation: TS1, - }, { - query: "select t.id from (select 42 as id) as t", - recursiveExpectation: T0, - expectation: TS1, - }, { - query: "select ks.t.id from (select 42 as id) as t", - errorMessage: "column 'ks.t.id' not found", - }, { - query: "select * from (select id, id from user) as t", - errorMessage: "Duplicate column name 'id'", - }, { - query: "select t.baz = 1 from (select id as baz from user) as t", - expectation: TS1, - recursiveExpectation: TS0, - }, { - query: "select t.id from (select * from user, music) as t", - expectation: TS2, - recursiveExpectation: MergeTableSets(TS0, TS1), - }, { - query: "select t.id from (select * from user, music) as t order by t.id", - expectation: TS2, - recursiveExpectation: MergeTableSets(TS0, TS1), - }, { - query: "select t.id from (select * from user) as t join user as u on t.id = u.id", - expectation: TS1, - recursiveExpectation: TS0, - }, { - query: "select t.col1 from t3 ua join (select t1.id, t1.col1 from t1 join t2) as t", - expectation: TS3, - recursiveExpectation: TS1, - }, { - query: "select uu.test from (select id from t1) uu", - errorMessage: "column 'uu.test' not found", - }, { - query: "select uu.id from (select id as col from t1) uu", - errorMessage: "column 'uu.id' not found", - }, { - query: "select uu.id from (select id as col from t1) uu", - errorMessage: "column 'uu.id' not found", - }, { - query: "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", - expectation: TS1, - recursiveExpectation: TS0, - }, { - query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", - expectation: T0, - recursiveExpectation: T0, - }} - for _, query := range queries { - t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) - require.NoError(t, err) - st, err := Analyze(parse, "user", &FakeSI{ - Tables: map[string]*vindexes.Table{ - "t": {Name: sqlparser.NewIdentifierCS("t")}, - }, - }) - - switch { - case query.errorMessage != "" && err != nil: - require.EqualError(t, err, query.errorMessage) - case query.errorMessage != "": - require.EqualError(t, st.NotUnshardedErr, query.errorMessage) - default: - require.NoError(t, err) - sel := parse.(*sqlparser.Select) - assert.Equal(t, query.recursiveExpectation, st.RecursiveDeps(extract(sel, 0)), "RecursiveDeps") - assert.Equal(t, query.expectation, st.DirectDeps(extract(sel, 0)), "DirectDeps") - } - }) - } -} - func TestScopingWithWITH(t *testing.T) { queries := []struct { query string @@ -1003,7 +949,7 @@ func TestScopingWithWITH(t *testing.T) { }, { query: "with c as (select x as foo from user), t as (select foo as id from c) select id from t", recursive: TS0, - direct: TS2, + direct: TS3, }, { query: "with t as (select foo as id from user) select t.id from t", recursive: TS0, @@ -1013,11 +959,11 @@ func TestScopingWithWITH(t *testing.T) { errorMessage: "column 't.id2' not found", }, { query: "with t as (select 42 as id) select id from t", - recursive: T0, + recursive: NoTables, direct: TS1, }, { query: "with t as (select 42 as id) select t.id from t", - recursive: T0, + recursive: NoTables, direct: TS1, }, { query: "with t as (select 42 as id) select ks.t.id from t", @@ -1039,12 +985,12 @@ func TestScopingWithWITH(t *testing.T) { recursive: MergeTableSets(TS0, TS1), }, { query: "with t as (select * from user) select t.id from t join user as u on t.id = u.id", - direct: TS1, + direct: TS2, recursive: TS0, }, { query: "with t as (select t1.id, t1.col1 from t1 join t2) select t.col1 from t3 ua join t", direct: TS3, - recursive: TS1, + recursive: TS0, }, { query: "with uu as (select id from t1) select uu.test from uu", errorMessage: "column 'uu.test' not found", @@ -1056,16 +1002,16 @@ func TestScopingWithWITH(t *testing.T) { errorMessage: "column 'uu.id' not found", }, { query: "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", - direct: TS1, + direct: TS2, recursive: TS0, }, { query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", - direct: T0, - recursive: T0, + direct: NoTables, + recursive: NoTables, }} for _, query := range queries { t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) + parse, err := sqlparser.NewTestParser().Parse(query.query) require.NoError(t, err) st, err := Analyze(parse, "user", &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -1103,20 +1049,20 @@ func TestJoinPredicateDependencies(t *testing.T) { directExpect: MergeTableSets(TS0, TS1), }, { query: "select 1 from (select * from t1) x join t2 on x.id = t2.uid", - recursiveExpect: MergeTableSets(TS0, TS2), + recursiveExpect: MergeTableSets(TS0, TS1), directExpect: MergeTableSets(TS1, TS2), }, { query: "select 1 from (select id from t1) x join t2 on x.id = t2.uid", - recursiveExpect: MergeTableSets(TS0, TS2), + recursiveExpect: MergeTableSets(TS0, TS1), directExpect: MergeTableSets(TS1, TS2), }, { query: "select 1 from (select id from t1 union select id from t) x join t2 on x.id = t2.uid", - recursiveExpect: MergeTableSets(TS0, TS1, TS3), + recursiveExpect: MergeTableSets(TS0, TS1, TS2), directExpect: MergeTableSets(TS2, TS3), }} for _, query := range queries { t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) + parse, err := sqlparser.NewTestParser().Parse(query.query) require.NoError(t, err) st, err := Analyze(parse, "user", fakeSchemaInfo()) @@ -1130,107 +1076,6 @@ func TestJoinPredicateDependencies(t *testing.T) { } } -func TestDerivedTablesOrderClause(t *testing.T) { - queries := []struct { - query string - recursiveExpectation TableSet - expectation TableSet - }{{ - query: "select 1 from (select id from user) as t order by id", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select id from (select id from user) as t order by id", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select id from (select id from user) as t order by t.id", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select id as foo from (select id from user) as t order by foo", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select bar from (select id as bar from user) as t order by bar", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select bar as foo from (select id as bar from user) as t order by bar", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select bar as foo from (select id as bar from user) as t order by foo", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select bar as foo from (select id as bar, oo from user) as t order by oo", - recursiveExpectation: TS0, - expectation: TS1, - }, { - query: "select bar as foo from (select id, oo from user) as t(bar,oo) order by bar", - recursiveExpectation: TS0, - expectation: TS1, - }} - si := &FakeSI{Tables: map[string]*vindexes.Table{"t": {Name: sqlparser.NewIdentifierCS("t")}}} - for _, query := range queries { - t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) - require.NoError(t, err) - - st, err := Analyze(parse, "user", si) - require.NoError(t, err) - - sel := parse.(*sqlparser.Select) - assert.Equal(t, query.recursiveExpectation, st.RecursiveDeps(sel.OrderBy[0].Expr), "RecursiveDeps") - assert.Equal(t, query.expectation, st.DirectDeps(sel.OrderBy[0].Expr), "DirectDeps") - - }) - } -} - -func TestScopingWComplexDerivedTables(t *testing.T) { - queries := []struct { - query string - errorMessage string - rightExpectation TableSet - leftExpectation TableSet - }{ - { - query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", - rightExpectation: TS0, - leftExpectation: TS0, - }, - { - query: "select 1 from user.user uu where exists (select 1 from user.user as uu where exists (select 1 from (select 1 from user.t1) uu where uu.user_id = uu.id))", - rightExpectation: TS1, - leftExpectation: TS1, - }, - } - for _, query := range queries { - t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) - require.NoError(t, err) - st, err := Analyze(parse, "user", &FakeSI{ - Tables: map[string]*vindexes.Table{ - "t": {Name: sqlparser.NewIdentifierCS("t")}, - }, - }) - if query.errorMessage != "" { - require.EqualError(t, err, query.errorMessage) - } else { - require.NoError(t, err) - sel := parse.(*sqlparser.Select) - comparisonExpr := sel.Where.Expr.(*sqlparser.ExistsExpr).Subquery.Select.(*sqlparser.Select).Where.Expr.(*sqlparser.ExistsExpr).Subquery.Select.(*sqlparser.Select).Where.Expr.(*sqlparser.ComparisonExpr) - left := comparisonExpr.Left - right := comparisonExpr.Right - assert.Equal(t, query.leftExpectation, st.RecursiveDeps(left), "Left RecursiveDeps") - assert.Equal(t, query.rightExpectation, st.RecursiveDeps(right), "Right RecursiveDeps") - } - }) - } -} - func TestScopingWVindexTables(t *testing.T) { queries := []struct { query string @@ -1250,7 +1095,7 @@ func TestScopingWVindexTables(t *testing.T) { } for _, query := range queries { t.Run(query.query, func(t *testing.T) { - parse, err := sqlparser.Parse(query.query) + parse, err := sqlparser.NewTestParser().Parse(query.query) require.NoError(t, err) hash, _ := vindexes.CreateVindex("hash", "user_index", nil) st, err := Analyze(parse, "user", &FakeSI{ @@ -1292,7 +1137,7 @@ func BenchmarkAnalyzeMultipleDifferentQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1316,7 +1161,7 @@ func BenchmarkAnalyzeUnionQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1342,37 +1187,7 @@ func BenchmarkAnalyzeSubQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) - require.NoError(b, err) - - _, _ = Analyze(parse, "d", fakeSchemaInfo()) - } - } -} - -func BenchmarkAnalyzeDerivedTableQueries(b *testing.B) { - queries := []string{ - "select id from (select x as id from user) as t", - "select id from (select foo as id from user) as t", - "select id from (select foo as id from (select x as foo from user) as c) as t", - "select t.id from (select foo as id from user) as t", - "select t.id2 from (select foo as id from user) as t", - "select id from (select 42 as id) as t", - "select t.id from (select 42 as id) as t", - "select ks.t.id from (select 42 as id) as t", - "select * from (select id, id from user) as t", - "select t.baz = 1 from (select id as baz from user) as t", - "select t.id from (select * from user, music) as t", - "select t.id from (select * from user, music) as t order by t.id", - "select t.id from (select * from user) as t join user as u on t.id = u.id", - "select t.col1 from t3 ua join (select t1.id, t1.col1 from t1 join t2) as t", - "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", - "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", - } - - for i := 0; i < b.N; i++ { - for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1398,7 +1213,7 @@ func BenchmarkAnalyzeHavingQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1427,7 +1242,7 @@ func BenchmarkAnalyzeGroupByQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1450,7 +1265,7 @@ func BenchmarkAnalyzeOrderByQueries(b *testing.B) { for i := 0; i < b.N; i++ { for _, query := range queries { - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(b, err) _, _ = Analyze(parse, "d", fakeSchemaInfo()) @@ -1460,7 +1275,7 @@ func BenchmarkAnalyzeOrderByQueries(b *testing.B) { func parseAndAnalyze(t *testing.T, query, dbName string) (sqlparser.Statement, *SemTable) { t.Helper() - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) semTable, err := Analyze(parse, dbName, fakeSchemaInfo()) @@ -1472,43 +1287,30 @@ func TestSingleUnshardedKeyspace(t *testing.T) { tests := []struct { query string unsharded *vindexes.Keyspace - tables []*vindexes.Table }{ { query: "select 1 from t, t1", unsharded: nil, // both tables are unsharded, but from different keyspaces - tables: nil, }, { query: "select 1 from t2", unsharded: nil, - tables: nil, }, { query: "select 1 from t, t2", unsharded: nil, - tables: nil, }, { query: "select 1 from t as A, t as B", - unsharded: ks1, - tables: []*vindexes.Table{ - {Keyspace: ks1, Name: sqlparser.NewIdentifierCS("t")}, - {Keyspace: ks1, Name: sqlparser.NewIdentifierCS("t")}, - }, + unsharded: unsharded, }, { query: "insert into t select * from t", - unsharded: ks1, - tables: []*vindexes.Table{ - {Keyspace: ks1, Name: sqlparser.NewIdentifierCS("t")}, - {Keyspace: ks1, Name: sqlparser.NewIdentifierCS("t")}, - }, + unsharded: unsharded, }, } for _, test := range tests { t.Run(test.query, func(t *testing.T) { _, semTable := parseAndAnalyze(t, test.query, "d") - queryIsUnsharded, tables := semTable.SingleUnshardedKeyspace() + queryIsUnsharded, _ := semTable.SingleUnshardedKeyspace() assert.Equal(t, test.unsharded, queryIsUnsharded) - assert.Equal(t, test.tables, tables) }) } } @@ -1531,7 +1333,7 @@ func TestNextErrors(t *testing.T) { for _, test := range tests { t.Run(test.query, func(t *testing.T) { - parse, err := sqlparser.Parse(test.query) + parse, err := sqlparser.NewTestParser().Parse(test.query) require.NoError(t, err) _, err = Analyze(parse, "d", fakeSchemaInfo()) @@ -1540,40 +1342,13 @@ func TestNextErrors(t *testing.T) { } } -func TestUpdateErrors(t *testing.T) { - tests := []struct { - query, expectedError string - }{ - { - query: "update t1, t2 set id = 12", - expectedError: "VT12001: unsupported: multiple (2) tables in update", - }, { - query: "update (select 1 from dual) dt set id = 1", - expectedError: "The target table dt of the UPDATE is not updatable", - }, - } - - for _, test := range tests { - t.Run(test.query, func(t *testing.T) { - parse, err := sqlparser.Parse(test.query) - require.NoError(t, err) - - st, err := Analyze(parse, "d", fakeSchemaInfo()) - if err == nil { - err = st.NotUnshardedErr - } - assert.EqualError(t, err, test.expectedError) - }) - } -} - // TestScopingSubQueryJoinClause tests the scoping behavior of a subquery containing a join clause. // The test ensures that the scoping analysis correctly identifies and handles the relationships // between the tables involved in the join operation with the outer query. func TestScopingSubQueryJoinClause(t *testing.T) { query := "select (select 1 from u1 join u2 on u1.id = u2.id and u2.id = u3.id) x from u3" - parse, err := sqlparser.Parse(query) + parse, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) st, err := Analyze(parse, "user", &FakeSI{ @@ -1589,13 +1364,13 @@ func TestScopingSubQueryJoinClause(t *testing.T) { } -var ks1 = &vindexes.Keyspace{ - Name: "ks1", +var unsharded = &vindexes.Keyspace{ + Name: "unsharded", Sharded: false, } var ks2 = &vindexes.Keyspace{ Name: "ks2", - Sharded: false, + Sharded: true, } var ks3 = &vindexes.Keyspace{ Name: "ks3", @@ -1606,576 +1381,52 @@ var ks3 = &vindexes.Keyspace{ // create table t1(id bigint) // create table t2(uid bigint, name varchar(255)) func fakeSchemaInfo() *FakeSI { - cols1 := []vindexes.Column{{ - Name: sqlparser.NewIdentifierCI("id"), - Type: querypb.Type_INT64, - }} - cols2 := []vindexes.Column{{ - Name: sqlparser.NewIdentifierCI("uid"), - Type: querypb.Type_INT64, - }, { - Name: sqlparser.NewIdentifierCI("name"), - Type: querypb.Type_VARCHAR, - }} - si := &FakeSI{ Tables: map[string]*vindexes.Table{ - "t": {Name: sqlparser.NewIdentifierCS("t"), Keyspace: ks1}, - "t1": {Name: sqlparser.NewIdentifierCS("t1"), Columns: cols1, ColumnListAuthoritative: true, Keyspace: ks2}, - "t2": {Name: sqlparser.NewIdentifierCS("t2"), Columns: cols2, ColumnListAuthoritative: true, Keyspace: ks3}, + "t": tableT(), + "t1": tableT1(), + "t2": tableT2(), }, } return si } -var tbl = map[string]TableInfo{ - "t0": &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks"}, - ChildForeignKeys: []vindexes.ChildFKInfo{ - ckInfo(nil, []string{"col"}, []string{"col"}, sqlparser.Restrict), - ckInfo(nil, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), - }, - ParentForeignKeys: []vindexes.ParentFKInfo{ - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - "t1": &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks_unmanaged", Sharded: true}, - ChildForeignKeys: []vindexes.ChildFKInfo{ - ckInfo(nil, []string{"cola"}, []string{"cola"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola1", "cola2"}, []string{"ccola1", "ccola2"}, sqlparser.SetNull), - }, - }, - }, - "t2": &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks"}, - }, - }, - "t3": &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "undefined_ks", Sharded: true}, - }, - }, -} - -// TestGetAllManagedForeignKeys tests the functionality of getAllManagedForeignKeys. -func TestGetAllManagedForeignKeys(t *testing.T) { - tests := []struct { - name string - analyzer *analyzer - childFkWanted map[TableSet][]vindexes.ChildFKInfo - parentFkWanted map[TableSet][]vindexes.ParentFKInfo - expectedErr string - }{ - { - name: "Collect all foreign key constraints", - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{tbl["t0"], tbl["t1"], - &DerivedTable{}, - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - }, - }, - }, - childFkWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"col"}, []string{"col"}, sqlparser.Restrict), - ckInfo(nil, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), - }, - }, - parentFkWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - { - name: "keyspace not found in schema information", - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t2"], - tbl["t3"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - }, - }, - }, - }, - expectedErr: "undefined_ks keyspace not found", - }, - { - name: "Cyclic fk constraints error", - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t0"], tbl["t1"], - &DerivedTable{}, - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - KsError: map[string]error{ - "ks": fmt.Errorf("VT09019: ks has cyclic foreign keys"), - }, - }, - }, - }, - expectedErr: "VT09019: ks has cyclic foreign keys", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - childFk, parentFk, err := tt.analyzer.getAllManagedForeignKeys() - if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) - return - } - require.EqualValues(t, tt.childFkWanted, childFk) - require.EqualValues(t, tt.parentFkWanted, parentFk) - }) +func tableT() *vindexes.Table { + return &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t"), + Keyspace: unsharded, } } - -// TestFilterForeignKeysUsingUpdateExpressions tests the functionality of filterForeignKeysUsingUpdateExpressions. -func TestFilterForeignKeysUsingUpdateExpressions(t *testing.T) { - cola := sqlparser.NewColName("cola") - colb := sqlparser.NewColName("colb") - colc := sqlparser.NewColName("colc") - cold := sqlparser.NewColName("cold") - a := &analyzer{ - binder: &binder{ - direct: map[sqlparser.Expr]TableSet{ - cola: SingleTableSet(0), - colb: SingleTableSet(0), - colc: SingleTableSet(1), - cold: SingleTableSet(1), - }, - }, - } - updateExprs := sqlparser.UpdateExprs{ - &sqlparser.UpdateExpr{Name: cola, Expr: sqlparser.NewIntLiteral("1")}, - &sqlparser.UpdateExpr{Name: colb, Expr: &sqlparser.NullVal{}}, - &sqlparser.UpdateExpr{Name: colc, Expr: sqlparser.NewIntLiteral("1")}, - &sqlparser.UpdateExpr{Name: cold, Expr: &sqlparser.NullVal{}}, - } - tests := []struct { - name string - analyzer *analyzer - allChildFks map[TableSet][]vindexes.ChildFKInfo - allParentFks map[TableSet][]vindexes.ParentFKInfo - updExprs sqlparser.UpdateExprs - childFksWanted map[TableSet][]vindexes.ChildFKInfo - parentFksWanted map[TableSet][]vindexes.ParentFKInfo - }{ - { - name: "Child Foreign Keys Filtering", - analyzer: a, - allParentFks: nil, - allChildFks: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - ckInfo(nil, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - }, - SingleTableSet(1): { - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - ckInfo(nil, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), - ckInfo(nil, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), - }, - }, - updExprs: updateExprs, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - }, - SingleTableSet(1): { - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - ckInfo(nil, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), - }, - }, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{}, - }, { - name: "Parent Foreign Keys Filtering", - analyzer: a, - allParentFks: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), - pkInfo(nil, []string{"pcolc"}, []string{"colc"}), - pkInfo(nil, []string{"pcolb", "pcola"}, []string{"colb", "cola"}), - pkInfo(nil, []string{"pcolb"}, []string{"colb"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - pkInfo(nil, []string{"pcolb", "pcolx"}, []string{"colb", "colx"}), - }, - SingleTableSet(1): { - pkInfo(nil, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - pkInfo(nil, []string{"pcold", "pcolc"}, []string{"cold", "colc"}), - pkInfo(nil, []string{"pcold"}, []string{"cold"}), - pkInfo(nil, []string{"pcold", "pcolx"}, []string{"cold", "colx"}), - }, - }, - allChildFks: nil, - updExprs: updateExprs, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{}, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - }, - SingleTableSet(1): { - pkInfo(nil, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - childFks, parentFks := tt.analyzer.filterForeignKeysUsingUpdateExpressions(tt.allChildFks, tt.allParentFks, tt.updExprs) - require.EqualValues(t, tt.childFksWanted, childFks) - require.EqualValues(t, tt.parentFksWanted, parentFks) - }) - } -} - -// TestGetInvolvedForeignKeys tests the functionality of getInvolvedForeignKeys. -func TestGetInvolvedForeignKeys(t *testing.T) { - cola := sqlparser.NewColName("cola") - colb := sqlparser.NewColName("colb") - colc := sqlparser.NewColName("colc") - cold := sqlparser.NewColName("cold") - tests := []struct { - name string - stmt sqlparser.Statement - analyzer *analyzer - childFksWanted map[TableSet][]vindexes.ChildFKInfo - parentFksWanted map[TableSet][]vindexes.ParentFKInfo - expectedErr string - }{ - { - name: "Delete Query", - stmt: &sqlparser.Delete{}, - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t0"], - tbl["t1"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - }, - }, - }, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"col"}, []string{"col"}, sqlparser.Restrict), - ckInfo(nil, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), - }, - }, - }, - { - name: "Update statement", - stmt: &sqlparser.Update{ - Exprs: sqlparser.UpdateExprs{ - &sqlparser.UpdateExpr{ - Name: cola, - Expr: sqlparser.NewIntLiteral("1"), - }, - &sqlparser.UpdateExpr{ - Name: colb, - Expr: &sqlparser.NullVal{}, - }, - &sqlparser.UpdateExpr{ - Name: colc, - Expr: sqlparser.NewIntLiteral("1"), - }, - &sqlparser.UpdateExpr{ - Name: cold, - Expr: &sqlparser.NullVal{}, - }, - }, - }, - analyzer: &analyzer{ - binder: &binder{ - direct: map[sqlparser.Expr]TableSet{ - cola: SingleTableSet(0), - colb: SingleTableSet(0), - colc: SingleTableSet(1), - cold: SingleTableSet(1), - }, - }, - tables: &tableCollector{ - Tables: []TableInfo{ - &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks"}, - ChildForeignKeys: []vindexes.ChildFKInfo{ - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - ckInfo(nil, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - }, - ParentForeignKeys: []vindexes.ParentFKInfo{ - pkInfo(nil, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), - pkInfo(nil, []string{"pcolc"}, []string{"colc"}), - pkInfo(nil, []string{"pcolb", "pcola"}, []string{"colb", "cola"}), - pkInfo(nil, []string{"pcolb"}, []string{"colb"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - pkInfo(nil, []string{"pcolb", "pcolx"}, []string{"colb", "colx"}), - }, - }, - }, - &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks"}, - ChildForeignKeys: []vindexes.ChildFKInfo{ - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - ckInfo(nil, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), - ckInfo(nil, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), - }, - ParentForeignKeys: []vindexes.ParentFKInfo{ - pkInfo(nil, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - pkInfo(nil, []string{"pcold", "pcolc"}, []string{"cold", "colc"}), - pkInfo(nil, []string{"pcold"}, []string{"cold"}), - pkInfo(nil, []string{"pcold", "pcolx"}, []string{"cold", "colx"}), - }, - }, - }, - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - }, - }, - }, - }, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - }, - SingleTableSet(1): { - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - ckInfo(nil, []string{"colc", "colx"}, []string{"child_colc", "child_colx"}, sqlparser.SetNull), - }, - }, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"pcola", "pcolx"}, []string{"cola", "colx"}), - pkInfo(nil, []string{"pcola"}, []string{"cola"}), - }, - SingleTableSet(1): { - pkInfo(nil, []string{"pcolc", "pcolx"}, []string{"colc", "colx"}), - }, - }, - }, - { - name: "Replace Query", - stmt: &sqlparser.Insert{ - Action: sqlparser.ReplaceAct, - }, - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t0"], - tbl["t1"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - }, - }, - }, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"col"}, []string{"col"}, sqlparser.Restrict), - ckInfo(nil, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), - }, - }, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - { - name: "Insert Query", - stmt: &sqlparser.Insert{ - Action: sqlparser.InsertAct, - }, - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t0"], - tbl["t1"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - }, - }, - }, - childFksWanted: nil, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - { - name: "Insert Query with On Duplicate", - stmt: &sqlparser.Insert{ - Action: sqlparser.InsertAct, - OnDup: sqlparser.OnDup{ - &sqlparser.UpdateExpr{ - Name: cola, - Expr: sqlparser.NewIntLiteral("1"), - }, - &sqlparser.UpdateExpr{ - Name: colb, - Expr: &sqlparser.NullVal{}, - }, - }, - }, - analyzer: &analyzer{ - binder: &binder{ - direct: map[sqlparser.Expr]TableSet{ - cola: SingleTableSet(0), - colb: SingleTableSet(0), - }, - }, - tables: &tableCollector{ - Tables: []TableInfo{ - &RealTable{ - Table: &vindexes.Table{ - Keyspace: &vindexes.Keyspace{Name: "ks"}, - ChildForeignKeys: []vindexes.ChildFKInfo{ - ckInfo(nil, []string{"col"}, []string{"col"}, sqlparser.Restrict), - ckInfo(nil, []string{"col1", "col2"}, []string{"ccol1", "ccol2"}, sqlparser.SetNull), - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - ckInfo(nil, []string{"colx", "coly"}, []string{"child_colx", "child_coly"}, sqlparser.Cascade), - ckInfo(nil, []string{"cold"}, []string{"child_cold"}, sqlparser.Restrict), - }, - ParentForeignKeys: []vindexes.ParentFKInfo{ - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - tbl["t1"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - "ks_unmanaged": vschemapb.Keyspace_unmanaged, - }, - }, - }, - }, - childFksWanted: map[TableSet][]vindexes.ChildFKInfo{ - SingleTableSet(0): { - ckInfo(nil, []string{"colb"}, []string{"child_colb"}, sqlparser.Restrict), - ckInfo(nil, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}, sqlparser.SetNull), - }, - }, - parentFksWanted: map[TableSet][]vindexes.ParentFKInfo{ - SingleTableSet(0): { - pkInfo(nil, []string{"colb"}, []string{"colb"}), - pkInfo(nil, []string{"colb1", "colb2"}, []string{"ccolb1", "ccolb2"}), - }, - }, - }, - { - name: "Insert error", - stmt: &sqlparser.Insert{}, - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t2"], - tbl["t3"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - }, - }, - }, - }, - expectedErr: "undefined_ks keyspace not found", - }, - { - name: "Update error", - stmt: &sqlparser.Update{}, - analyzer: &analyzer{ - tables: &tableCollector{ - Tables: []TableInfo{ - tbl["t2"], - tbl["t3"], - }, - si: &FakeSI{ - KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ - "ks": vschemapb.Keyspace_managed, - }, - }, - }, - }, - expectedErr: "undefined_ks keyspace not found", +func tableT1() *vindexes.Table { + return &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t1"), + Columns: []vindexes.Column{{ + Name: sqlparser.NewIdentifierCI("id"), + Type: querypb.Type_INT64, + }}, + ColumnListAuthoritative: true, + ColumnVindexes: []*vindexes.ColumnVindex{ + {Name: "id_vindex"}, }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - childFks, parentFks, err := tt.analyzer.getInvolvedForeignKeys(tt.stmt) - if tt.expectedErr != "" { - require.EqualError(t, err, tt.expectedErr) - return - } - require.EqualValues(t, tt.childFksWanted, childFks) - require.EqualValues(t, tt.parentFksWanted, parentFks) - }) + Keyspace: ks2, } } - -func ckInfo(cTable *vindexes.Table, pCols []string, cCols []string, refAction sqlparser.ReferenceAction) vindexes.ChildFKInfo { - return vindexes.ChildFKInfo{ - Table: cTable, - ParentColumns: sqlparser.MakeColumns(pCols...), - ChildColumns: sqlparser.MakeColumns(cCols...), - OnDelete: refAction, - } -} - -func pkInfo(parentTable *vindexes.Table, pCols []string, cCols []string) vindexes.ParentFKInfo { - return vindexes.ParentFKInfo{ - Table: parentTable, - ParentColumns: sqlparser.MakeColumns(pCols...), - ChildColumns: sqlparser.MakeColumns(cCols...), +func tableT2() *vindexes.Table { + return &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t2"), + Columns: []vindexes.Column{{ + Name: sqlparser.NewIdentifierCI("uid"), + Type: querypb.Type_INT64, + }, { + Name: sqlparser.NewIdentifierCI("name"), + Type: querypb.Type_VARCHAR, + CollationName: "utf8_bin", + }, { + Name: sqlparser.NewIdentifierCI("textcol"), + Type: querypb.Type_VARCHAR, + CollationName: "big5_bin", + }}, + ColumnListAuthoritative: true, + Keyspace: ks3, } } diff --git a/go/vt/vtgate/semantics/binder.go b/go/vt/vtgate/semantics/binder.go index f7fc5d64c1a..f5e7d3c6297 100644 --- a/go/vt/vtgate/semantics/binder.go +++ b/go/vt/vtgate/semantics/binder.go @@ -19,8 +19,9 @@ package semantics import ( "strings" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) // binder is responsible for finding all the column references in @@ -30,6 +31,7 @@ import ( type binder struct { recursive ExprDependencies direct ExprDependencies + targets map[sqlparser.IdentifierCS]TableSet scoper *scoper tc *tableCollector org originable @@ -45,6 +47,7 @@ func newBinder(scoper *scoper, org originable, tc *tableCollector, typer *typer) return &binder{ recursive: map[sqlparser.Expr]TableSet{}, direct: map[sqlparser.Expr]TableSet{}, + targets: map[sqlparser.IdentifierCS]TableSet{}, scoper: scoper, org: org, tc: tc, @@ -88,7 +91,7 @@ func (b *binder) up(cursor *sqlparser.Cursor) error { } b.recursive[node] = deps.recursive b.direct[node] = deps.direct - if deps.typ.Type != sqltypes.Unknown { + if deps.typ.Valid() { b.typer.setTypeFor(node, deps.typ) } case *sqlparser.CountStar: @@ -103,14 +106,51 @@ func (b *binder) up(cursor *sqlparser.Cursor) error { for i, expr := range info.exprs { ae := expr.(*sqlparser.AliasedExpr) b.recursive[ae.Expr] = info.recursive[i] - if t := info.types[i]; t.Type != sqltypes.Unknown { + if t := info.types[i]; t.Valid() { b.typer.m[ae.Expr] = t } } + case sqlparser.TableNames: + _, isDelete := cursor.Parent().(*sqlparser.Delete) + if !isDelete { + return nil + } + current := b.scoper.currentScope() + for _, target := range node { + finalDep, err := b.findDependentTableSet(current, target) + if err != nil { + return err + } + b.targets[target.Name] = finalDep.direct + } } return nil } +func (b *binder) findDependentTableSet(current *scope, target sqlparser.TableName) (dependency, error) { + var deps dependencies = ¬hing{} + for _, table := range current.tables { + tblName, err := table.Name() + if err != nil { + continue + } + if tblName.Name.String() != target.Name.String() { + continue + } + ts := b.org.tableSetFor(table.GetAliasedTableExpr()) + c := createCertain(ts, ts, evalengine.Type{}) + deps = deps.merge(c, false) + } + finalDep, err := deps.get() + if err != nil { + return dependency{}, err + } + if finalDep.direct != finalDep.recursive { + return dependency{}, vterrors.VT03004(target.Name.String()) + } + return finalDep, nil +} + func (b *binder) bindCountStar(node *sqlparser.CountStar) { scope := b.scoper.currentScope() var ts TableSet @@ -229,7 +269,7 @@ func (b *binder) resolveColumn(colName *sqlparser.ColName, current *scope, allow func (b *binder) resolveColumnInScope(current *scope, expr *sqlparser.ColName, allowMulti bool) (dependencies, error) { var deps dependencies = ¬hing{} for _, table := range current.tables { - if !expr.Qualifier.IsEmpty() && !table.matches(expr.Qualifier) { + if !expr.Qualifier.IsEmpty() && !table.matches(expr.Qualifier) && !current.isUnion { continue } thisDeps, err := table.dependencies(expr.Name.String(), b.org) diff --git a/go/vt/vtgate/semantics/bitset/bitset_test.go b/go/vt/vtgate/semantics/bitset/bitset_test.go index 87bef299963..a283cbf1f35 100644 --- a/go/vt/vtgate/semantics/bitset/bitset_test.go +++ b/go/vt/vtgate/semantics/bitset/bitset_test.go @@ -19,6 +19,7 @@ package bitset import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -38,3 +39,335 @@ func TestSingletons(t *testing.T) { require.True(t, called) } } + +func TestSingleBitReturnsNegativeOne(t *testing.T) { + bs := Bitset("\x0F") + result := bs.SingleBit() + + assert.Equal(t, -1, result) +} + +func TestToBitsetPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + require.NotNil(t, r, "Expected panic, but none occurred") + } + }() + + byteEndsWithZero := []byte{8, 0} + + _ = toBitset(byteEndsWithZero) +} + +func TestBuild(t *testing.T) { + tt := []struct { + name string + bits []int + want Bitset + }{ + {"Empty Bits", []int{}, ""}, + {"Single Bit", []int{3}, "\x08"}, + {"Multiple Bits", []int{1, 3, 5, 7}, "\xAA"}, + {"Large Bits", []int{10, 11, 12}, "\x00\x1C"}, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := Build(tc.bits...) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestAnd(t *testing.T) { + tt := []struct { + name string + bs1, bs2 Bitset + expected Bitset + }{ + { + name: "Two NonEmpty", + bs1: Build(1, 2, 3, 4, 5), + bs2: Build(3, 4, 5, 6, 7), + expected: Build(3, 4, 5), + }, + { + name: "One Empty", + bs1: Build(1, 2, 3, 4, 5), + bs2: Build(), + expected: "", + }, + { + name: "Both Empty", + bs1: Build(), + bs2: Build(), + expected: "", + }, + { + name: "Different Word Sizes", + bs1: Build(1, 2, 3, 4, 5, 33), + bs2: Build(3, 4, 5, 6, 7), + expected: Build(3, 4, 5), + }, + { + name: "One Empty One NonEmpty", + bs1: Build(), + bs2: Build(3, 4, 5, 6, 7), + expected: "", + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result := tc.bs1.And(tc.bs2) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestAndNot(t *testing.T) { + tt := []struct { + name string + bs1 Bitset + bs2 Bitset + result Bitset + }{ + { + "Empty AndNot Empty", + "", + Build(1, 2, 3), + "", + }, + { + "NonEmpty And Empty", + Build(1, 2, 3), + "", + Build(1, 2, 3), + }, + { + "NonEmpty And NotEmpty", + Build(1, 2, 3), + Build(2, 3, 4), + Build(1), + }, + { + "Common BitsSet AndNot", + Build(1, 2, 3, 4, 5, 6, 7, 8), + Build(3, 4, 5, 6, 7, 8, 9, 10), + Build(1, 2), + }, + { + "bs1 Greater than bs2", + Build(1, 2, 3, 4, 5, 6, 7, 8), + Build(2, 3, 4), + Build(1, 5, 6, 7, 8), + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := tc.bs1.AndNot(tc.bs2) + assert.Equal(t, tc.result, got) + }) + } +} + +func TestOr(t *testing.T) { + tt := []struct { + name string + bs1 Bitset + bs2 Bitset + result Bitset + }{ + { + "Empty Or Empty", + "", + "", + "", + }, + { + "Empty Or NonEmpty", + "", + Build(1, 2, 3), + Build(1, 2, 3), + }, + { + "NonEmpty Or Empty", + Build(1, 2, 3), + "", + Build(1, 2, 3), + }, + { + "NonEmpty Or NonEmpty", + Build(1, 2, 3), + Build(4, 5, 6), + Build(1, 2, 3, 4, 5, 6), + }, + { + "Common BitsSet", + Build(1, 2, 3, 4), + Build(3, 4, 5, 6), + Build(1, 2, 3, 4, 5, 6), + }, + { + "Bs1 Larger Than Bs2", + Build(3, 4, 5, 6, 7, 8, 9, 10), + Build(1, 2), + Build(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := tc.bs1.Or(tc.bs2) + assert.Equal(t, tc.result, got) + }) + } +} + +func TestSet(t *testing.T) { + tt := []struct { + name string + bs Bitset + offset int + result Bitset + }{ + { + "Set On Empty Bitset", + "", + 3, + Build(3), + }, + { + "Set On NonEmpty Bitset", + Build(1, 2, 3), + 10, + Build(1, 2, 3, 10), + }, + { + "Set On Existing Bit", + Build(1, 2, 3, 4), + 3, + Build(1, 2, 3, 4), + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := tc.bs.Set(tc.offset) + assert.Equal(t, tc.result, got) + }) + } +} + +func TestIsContainedBy(t *testing.T) { + tt := []struct { + name string + bs1 Bitset + bs2 Bitset + expected bool + }{ + { + "Empty Is Contained By Empty", + "", + "", + true, + }, + { + "Empty Is Contained By NonEmpty", + "", + Build(1, 2, 3), + true, + }, + { + "NonEmpty Is Contained By Empty", + Build(1, 2, 3), + "", + false, + }, + { + "Subset Is Contained By Superset", + Build(1, 2, 3), + Build(1, 2, 3, 4, 5, 6), + true, + }, + { + "Not Contained", + Build(1, 2, 3), + Build(4, 5, 6), + false, + }, + { + "Equal Bitsets", + Build(1, 2, 3), + Build(1, 2, 3), + true, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := tc.bs1.IsContainedBy(tc.bs2) + assert.Equal(t, tc.expected, got) + }) + } +} + +func TestOverlaps(t *testing.T) { + tt := []struct { + name string + bs1 Bitset + bs2 Bitset + expected bool + }{ + { + "Empty Does Not Overlap Empty", + "", + "", + false, + }, + { + "Empty Does Not Overlap NonEmpty", + "", + Build(1, 2, 3), + false, + }, + { + "NonEmpty Does Not Overlap Empty", + Build(1, 2, 3), + "", + false, + }, + { + "Common Bits Overlap", + Build(1, 2, 3, 4), + Build(3, 4, 5, 6), + true, + }, + { + "No Common Bits Do Not Overlap", + Build(1, 2, 3, 4), + Build(5, 6, 7, 8), + false, + }, + { + "Partial Overlap", + Build(1, 2, 3, 4, 5), + Build(4, 5, 6), + true, + }, + { + "Equal Bitsets Overlap", + Build(1, 2, 3), + Build(1, 2, 3), + true, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + got := tc.bs1.Overlaps(tc.bs2) + assert.Equal(t, tc.expected, got) + }) + } +} diff --git a/go/vt/vtgate/semantics/check_invalid.go b/go/vt/vtgate/semantics/check_invalid.go index 54b2de5c36f..45c160e93a2 100644 --- a/go/vt/vtgate/semantics/check_invalid.go +++ b/go/vt/vtgate/semantics/check_invalid.go @@ -24,12 +24,12 @@ import ( func (a *analyzer) checkForInvalidConstructs(cursor *sqlparser.Cursor) error { switch node := cursor.Node().(type) { - case *sqlparser.Update: - return checkUpdate(node) case *sqlparser.Select: return a.checkSelect(cursor, node) case *sqlparser.Nextval: return a.checkNextVal() + case *sqlparser.AliasedTableExpr: + return checkAliasedTableExpr(node) case *sqlparser.JoinTableExpr: return a.checkJoin(node) case *sqlparser.LockingFunc: @@ -177,17 +177,25 @@ func (a *analyzer) checkSelect(cursor *sqlparser.Cursor, node *sqlparser.Select) return nil } -func checkUpdate(node *sqlparser.Update) error { - if len(node.TableExprs) != 1 { - return ShardedError{Inner: &UnsupportedMultiTablesInUpdateError{ExprCount: len(node.TableExprs)}} - } - alias, isAlias := node.TableExprs[0].(*sqlparser.AliasedTableExpr) - if !isAlias { - return ShardedError{Inner: &UnsupportedMultiTablesInUpdateError{NotAlias: true}} +// checkAliasedTableExpr checks the validity of AliasedTableExpr. +func checkAliasedTableExpr(node *sqlparser.AliasedTableExpr) error { + if len(node.Hints) == 0 { + return nil } - _, isDerived := alias.Expr.(*sqlparser.DerivedTable) - if isDerived { - return &TableNotUpdatableError{Table: alias.As.String()} + alreadySeenVindexHint := false + for _, hint := range node.Hints { + if hint.Type.IsVindexHint() { + if alreadySeenVindexHint { + // TableName is safe to call, because only TableExpr can have hints. + // And we already checked for hints being empty. + tableName, err := node.TableName() + if err != nil { + return err + } + return &CantUseMultipleVindexHints{Table: sqlparser.String(tableName)} + } + alreadySeenVindexHint = true + } } return nil } diff --git a/go/vt/vtgate/semantics/check_invalid_test.go b/go/vt/vtgate/semantics/check_invalid_test.go new file mode 100644 index 00000000000..004c37fd854 --- /dev/null +++ b/go/vt/vtgate/semantics/check_invalid_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package semantics + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/sqlparser" +) + +func TestCheckAliasedTableExpr(t *testing.T) { + tests := []struct { + name string + tableString string + wantErr string + }{ + { + name: "Valid AliasedTable - USE VINDEX", + tableString: "payment_pulls use vindex (lookup_vindex_name, x, t)", + }, { + name: "Valid AliasedTable - IGNORE VINDEX", + tableString: "payment_pulls ignore vindex (lookup_vindex_name, x, t)", + }, { + name: "Invalid AliasedTable - multiple USE VINDEX", + tableString: "payment_pulls use vindex (lookup_vindex_name, x, t) use vindex (x)", + wantErr: "VT09020: can not use multiple vindex hints for table payment_pulls", + }, { + name: "Invalid AliasedTable - mixed vindex hints", + tableString: "t.payment_pulls use vindex (lookup_vindex_name, x, t) ignore vindex (x)", + wantErr: "VT09020: can not use multiple vindex hints for table t.payment_pulls", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stmt, err := sqlparser.NewTestParser().Parse("select * from " + tt.tableString) + require.NoError(t, err) + node := stmt.(*sqlparser.Select).From[0].(*sqlparser.AliasedTableExpr) + err = checkAliasedTableExpr(node) + if tt.wantErr != "" { + require.EqualError(t, err, tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/go/vt/vtgate/semantics/dependencies.go b/go/vt/vtgate/semantics/dependencies.go index 89b6da7045d..e68c5100ef5 100644 --- a/go/vt/vtgate/semantics/dependencies.go +++ b/go/vt/vtgate/semantics/dependencies.go @@ -54,10 +54,9 @@ func createCertain(direct TableSet, recursive TableSet, qt evalengine.Type) *cer dependency: dependency{ direct: direct, recursive: recursive, - typ: evalengine.UnknownType(), }, } - if qt.Type != querypb.Type_NULL_TYPE { + if qt.Valid() && qt.Type() != querypb.Type_NULL_TYPE { c.typ = qt } return c @@ -68,7 +67,6 @@ func createUncertain(direct TableSet, recursive TableSet) *uncertain { dependency: dependency{ direct: direct, recursive: recursive, - typ: evalengine.UnknownType(), }, } } diff --git a/go/vt/vtgate/semantics/derived_table.go b/go/vt/vtgate/semantics/derived_table.go index 9001848f6b4..0425d78ed93 100644 --- a/go/vt/vtgate/semantics/derived_table.go +++ b/go/vt/vtgate/semantics/derived_table.go @@ -77,7 +77,7 @@ func handleAliasedExpr(vTbl *DerivedTable, expr *sqlparser.AliasedExpr, cols sql return } - if !expr.As.IsEmpty() { + if expr.As.NotEmpty() { vTbl.columnNames = append(vTbl.columnNames, expr.As.String()) return } @@ -107,6 +107,10 @@ func (dt *DerivedTable) dependencies(colName string, org originable) (dependenci if !strings.EqualFold(name, colName) { continue } + if len(dt.recursive) == 0 { + // we have unexpanded columns and can't figure this out + return nil, ShardedError{Inner: vterrors.VT09015()} + } recursiveDeps, qt := dt.recursive[i], dt.types[i] return createCertain(directDeps, recursiveDeps, qt), nil @@ -137,7 +141,7 @@ func (dt *DerivedTable) Name() (sqlparser.TableName, error) { return dt.ASTNode.TableName() } -func (dt *DerivedTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { +func (dt *DerivedTable) GetAliasedTableExpr() *sqlparser.AliasedTableExpr { return dt.ASTNode } @@ -161,7 +165,7 @@ func (dt *DerivedTable) getColumns() []ColumnInfo { } func (dt *DerivedTable) hasStar() bool { - return dt.tables.NonEmpty() + return dt.tables.NotEmpty() } // GetTables implements the TableInfo interface diff --git a/go/vt/vtgate/semantics/derived_test.go b/go/vt/vtgate/semantics/derived_test.go new file mode 100644 index 00000000000..8344fd1e261 --- /dev/null +++ b/go/vt/vtgate/semantics/derived_test.go @@ -0,0 +1,265 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package semantics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +func TestScopingWDerivedTables(t *testing.T) { + queries := []struct { + query string + errorMessage string + recursiveDeps TableSet + directDeps TableSet + }{ + { + query: "select id from (select x as id from user) as t", + recursiveDeps: TS0, + directDeps: TS1, + }, { + query: "select id from (select foo as id from user) as t", + recursiveDeps: TS0, + directDeps: TS1, + }, { + query: "select id from (select foo as id from (select x as foo from user) as c) as t", + recursiveDeps: TS0, + directDeps: TS2, + }, { + query: "select t.id from (select foo as id from user) as t", + recursiveDeps: TS0, + directDeps: TS1, + }, { + query: "select t.id2 from (select foo as id from user) as t", + errorMessage: "column 't.id2' not found", + }, { + query: "select id from (select 42 as id) as t", + recursiveDeps: NoTables, + directDeps: TS1, + }, { + query: "select t.id from (select 42 as id) as t", + recursiveDeps: NoTables, + directDeps: TS1, + }, { + query: "select ks.t.id from (select 42 as id) as t", + errorMessage: "column 'ks.t.id' not found", + }, { + query: "select * from (select id, id from user) as t", + errorMessage: "Duplicate column name 'id'", + }, { + query: "select t.baz = 1 from (select id as baz from user) as t", + directDeps: TS1, + recursiveDeps: TS0, + }, { + query: "select t.id from (select * from user, music) as t", + directDeps: TS2, + recursiveDeps: MergeTableSets(TS0, TS1), + }, { + query: "select t.id from (select * from user, music) as t order by t.id", + directDeps: TS2, + recursiveDeps: MergeTableSets(TS0, TS1), + }, { + query: "select t.id from (select * from user) as t join user as u on t.id = u.id", + directDeps: TS2, + recursiveDeps: TS0, + }, { + query: "select t.col1 from t3 ua join (select t1.id, t1.col1 from t1 join t2) as t", + directDeps: TS3, + recursiveDeps: TS1, + }, { + query: "select uu.test from (select id from t1) uu", + errorMessage: "column 'uu.test' not found", + }, { + query: "select uu.id from (select id as col from t1) uu", + errorMessage: "column 'uu.id' not found", + }, { + query: "select uu.id from (select id as col from t1) uu", + errorMessage: "column 'uu.id' not found", + }, { + query: "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", + directDeps: TS2, + recursiveDeps: TS0, + }, { + query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", + directDeps: NoTables, + recursiveDeps: NoTables, + }, { + query: "select uu.count from (select count(*) as `count` from t1) uu", + directDeps: TS1, + recursiveDeps: TS0, + }} + for _, query := range queries { + t.Run(query.query, func(t *testing.T) { + parse, err := sqlparser.NewTestParser().Parse(query.query) + require.NoError(t, err) + st, err := Analyze(parse, "user", &FakeSI{ + Tables: map[string]*vindexes.Table{ + "t": {Name: sqlparser.NewIdentifierCS("t"), Keyspace: ks2}, + }, + }) + + switch { + case query.errorMessage != "" && err != nil: + require.EqualError(t, err, query.errorMessage) + case query.errorMessage != "": + require.EqualError(t, st.NotUnshardedErr, query.errorMessage) + default: + require.NoError(t, err) + sel := parse.(*sqlparser.Select) + assert.Equal(t, query.recursiveDeps, st.RecursiveDeps(extract(sel, 0)), "RecursiveDeps") + assert.Equal(t, query.directDeps, st.DirectDeps(extract(sel, 0)), "DirectDeps") + } + }) + } +} + +func TestDerivedTablesOrderClause(t *testing.T) { + queries := []struct { + query string + recursiveExpectation TableSet + expectation TableSet + }{{ + query: "select 1 from (select id from user) as t order by id", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select id from (select id from user) as t order by id", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select id from (select id from user) as t order by t.id", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select id as foo from (select id from user) as t order by foo", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select bar from (select id as bar from user) as t order by bar", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select bar as foo from (select id as bar from user) as t order by bar", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select bar as foo from (select id as bar from user) as t order by foo", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select bar as foo from (select id as bar, oo from user) as t order by oo", + recursiveExpectation: TS0, + expectation: TS1, + }, { + query: "select bar as foo from (select id, oo from user) as t(bar,oo) order by bar", + recursiveExpectation: TS0, + expectation: TS1, + }} + si := &FakeSI{Tables: map[string]*vindexes.Table{"t": {Name: sqlparser.NewIdentifierCS("t")}}} + for _, query := range queries { + t.Run(query.query, func(t *testing.T) { + parse, err := sqlparser.NewTestParser().Parse(query.query) + require.NoError(t, err) + + st, err := Analyze(parse, "user", si) + require.NoError(t, err) + + sel := parse.(*sqlparser.Select) + assert.Equal(t, query.recursiveExpectation, st.RecursiveDeps(sel.OrderBy[0].Expr), "RecursiveDeps") + assert.Equal(t, query.expectation, st.DirectDeps(sel.OrderBy[0].Expr), "DirectDeps") + + }) + } +} + +func TestScopingWComplexDerivedTables(t *testing.T) { + queries := []struct { + query string + errorMessage string + rightExpectation TableSet + leftExpectation TableSet + }{ + { + query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", + rightExpectation: TS0, + leftExpectation: TS0, + }, + { + query: "select 1 from user.user uu where exists (select 1 from user.user as uu where exists (select 1 from (select 1 from user.t1) uu where uu.user_id = uu.id))", + rightExpectation: TS1, + leftExpectation: TS1, + }, + } + for _, query := range queries { + t.Run(query.query, func(t *testing.T) { + parse, err := sqlparser.NewTestParser().Parse(query.query) + require.NoError(t, err) + st, err := Analyze(parse, "user", &FakeSI{ + Tables: map[string]*vindexes.Table{ + "t": {Name: sqlparser.NewIdentifierCS("t")}, + }, + }) + if query.errorMessage != "" { + require.EqualError(t, err, query.errorMessage) + } else { + require.NoError(t, err) + sel := parse.(*sqlparser.Select) + comparisonExpr := sel.Where.Expr.(*sqlparser.ExistsExpr).Subquery.Select.(*sqlparser.Select).Where.Expr.(*sqlparser.ExistsExpr).Subquery.Select.(*sqlparser.Select).Where.Expr.(*sqlparser.ComparisonExpr) + left := comparisonExpr.Left + right := comparisonExpr.Right + assert.Equal(t, query.leftExpectation, st.RecursiveDeps(left), "Left RecursiveDeps") + assert.Equal(t, query.rightExpectation, st.RecursiveDeps(right), "Right RecursiveDeps") + } + }) + } +} + +func BenchmarkAnalyzeDerivedTableQueries(b *testing.B) { + queries := []string{ + "select id from (select x as id from user) as t", + "select id from (select foo as id from user) as t", + "select id from (select foo as id from (select x as foo from user) as c) as t", + "select t.id from (select foo as id from user) as t", + "select t.id2 from (select foo as id from user) as t", + "select id from (select 42 as id) as t", + "select t.id from (select 42 as id) as t", + "select ks.t.id from (select 42 as id) as t", + "select * from (select id, id from user) as t", + "select t.baz = 1 from (select id as baz from user) as t", + "select t.id from (select * from user, music) as t", + "select t.id from (select * from user, music) as t order by t.id", + "select t.id from (select * from user) as t join user as u on t.id = u.id", + "select t.col1 from t3 ua join (select t1.id, t1.col1 from t1 join t2) as t", + "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", + "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", + } + + for i := 0; i < b.N; i++ { + for _, query := range queries { + parse, err := sqlparser.NewTestParser().Parse(query) + require.NoError(b, err) + + _, _ = Analyze(parse, "d", fakeSchemaInfo()) + } + } +} diff --git a/go/vt/vtgate/semantics/early_rewriter.go b/go/vt/vtgate/semantics/early_rewriter.go index 36060ed8334..ed1f30c670e 100644 --- a/go/vt/vtgate/semantics/early_rewriter.go +++ b/go/vt/vtgate/semantics/early_rewriter.go @@ -20,9 +20,9 @@ import ( "fmt" "strconv" - "vitess.io/vitess/go/mysql/collations" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" ) @@ -33,41 +33,71 @@ type earlyRewriter struct { clause string warning string expandedColumns map[sqlparser.TableName][]*sqlparser.ColName + env *vtenv.Environment } func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error { switch node := cursor.Node().(type) { case *sqlparser.Where: - handleWhereClause(node, cursor.Parent()) + return r.handleWhereClause(node, cursor.Parent()) case sqlparser.SelectExprs: - return handleSelectExprs(r, cursor, node) + return r.handleSelectExprs(cursor, node) case *sqlparser.JoinTableExpr: - handleJoinTableExpr(r, node) + r.handleJoinTableExprDown(node) case sqlparser.OrderBy: - handleOrderBy(r, cursor, node) + r.clause = "order clause" + iter := &orderByIterator{ + node: node, + idx: -1, + } + + return r.handleOrderByAndGroupBy(cursor.Parent(), iter) case *sqlparser.OrExpr: - rewriteOrExpr(cursor, node) + rewriteOrExpr(r.env, cursor, node) + case *sqlparser.AndExpr: + rewriteAndExpr(r.env, cursor, node) case *sqlparser.NotExpr: rewriteNotExpr(cursor, node) case sqlparser.GroupBy: - r.clause = "group statement" - case *sqlparser.Literal: - return handleLiteral(r, cursor, node) - case *sqlparser.CollateExpr: - return handleCollateExpr(r, node) + r.clause = "group clause" + iter := &exprIterator{ + node: node, + idx: -1, + } + return r.handleOrderByAndGroupBy(cursor.Parent(), iter) case *sqlparser.ComparisonExpr: return handleComparisonExpr(cursor, node) case *sqlparser.With: return r.handleWith(node) case *sqlparser.AliasedTableExpr: return r.handleAliasedTable(node) + case *sqlparser.Delete: + return handleDelete(node) + } + return nil +} + +func handleDelete(del *sqlparser.Delete) error { + // When we do not have any target, it is a single table delete. + // In a single table delete, the table references is always a single aliased table expression. + if len(del.Targets) != 0 { + return nil + } + tblExpr, ok := del.TableExprs[0].(*sqlparser.AliasedTableExpr) + if !ok { + return nil + } + tblName, err := tblExpr.TableName() + if err != nil { + return err } + del.Targets = append(del.Targets, tblName) return nil } func (r *earlyRewriter) handleAliasedTable(node *sqlparser.AliasedTableExpr) error { tbl, ok := node.Expr.(sqlparser.TableName) - if !ok || !tbl.Qualifier.IsEmpty() { + if !ok || tbl.Qualifier.NotEmpty() { return nil } scope := r.scoper.currentScope() @@ -105,26 +135,42 @@ func rewriteNotExpr(cursor *sqlparser.Cursor, node *sqlparser.NotExpr) { return } + // There is no inverse operator for NullSafeEqualOp. + // There doesn't exist a null safe non-equality. + if cmp.Operator == sqlparser.NullSafeEqualOp { + return + } cmp.Operator = sqlparser.Inverse(cmp.Operator) cursor.Replace(cmp) } func (r *earlyRewriter) up(cursor *sqlparser.Cursor) error { + switch node := cursor.Node().(type) { + case *sqlparser.JoinTableExpr: + return r.handleJoinTableExprUp(node) + case *sqlparser.AliasedTableExpr: + // this rewriting is done in the `up` phase, because we need the vindex hints to have been + // processed while collecting the tables. + return removeVindexHints(node) + } + return nil +} + +func (r *earlyRewriter) handleJoinTableExprUp(join *sqlparser.JoinTableExpr) error { // this rewriting is done in the `up` phase, because we need the scope to have been // filled in with the available tables - node, ok := cursor.Node().(*sqlparser.JoinTableExpr) - if !ok || len(node.Condition.Using) == 0 { + if len(join.Condition.Using) == 0 { return nil } - err := rewriteJoinUsing(r.binder, node) + err := rewriteJoinUsing(r.binder, join) if err != nil { return err } // since the binder has already been over the join, we need to invoke it again, so it // can bind columns to the right tables - sqlparser.Rewrite(node.Condition.On, nil, func(cursor *sqlparser.Cursor) bool { + sqlparser.Rewrite(join.Condition.On, nil, func(cursor *sqlparser.Cursor) bool { innerErr := r.binder.up(cursor) if innerErr == nil { return true @@ -136,16 +182,42 @@ func (r *earlyRewriter) up(cursor *sqlparser.Cursor) error { return err } +// removeVindexHints removes the vindex hints from the aliased table expression provided. +func removeVindexHints(node *sqlparser.AliasedTableExpr) error { + if len(node.Hints) == 0 { + return nil + } + var newHints sqlparser.IndexHints + for _, hint := range node.Hints { + if hint.Type.IsVindexHint() { + continue + } + newHints = append(newHints, hint) + } + node.Hints = newHints + return nil +} + // handleWhereClause processes WHERE clauses, specifically the HAVING clause. -func handleWhereClause(node *sqlparser.Where, parent sqlparser.SQLNode) { +func (r *earlyRewriter) handleWhereClause(node *sqlparser.Where, parent sqlparser.SQLNode) error { + sel, ok := parent.(*sqlparser.Select) + if !ok { + return nil + } if node.Type != sqlparser.HavingClause { - return + return nil + } + expr, err := r.rewriteAliasesInOrderByHavingAndGroupBy(node.Expr, sel) + if err != nil { + return err } - rewriteHavingAndOrderBy(node, parent) + + node.Expr = expr + return nil } // handleSelectExprs expands * in SELECT expressions. -func handleSelectExprs(r *earlyRewriter, cursor *sqlparser.Cursor, node sqlparser.SelectExprs) error { +func (r *earlyRewriter) handleSelectExprs(cursor *sqlparser.Cursor, node sqlparser.SelectExprs) error { _, isSel := cursor.Parent().(*sqlparser.Select) if !isSel { return nil @@ -153,8 +225,8 @@ func handleSelectExprs(r *earlyRewriter, cursor *sqlparser.Cursor, node sqlparse return r.expandStar(cursor, node) } -// handleJoinTableExpr processes JOIN table expressions and handles the Straight Join type. -func handleJoinTableExpr(r *earlyRewriter, node *sqlparser.JoinTableExpr) { +// handleJoinTableExprDown processes JOIN table expressions and handles the Straight Join type. +func (r *earlyRewriter) handleJoinTableExprDown(node *sqlparser.JoinTableExpr) { if node.Join != sqlparser.StraightJoinType { return } @@ -162,93 +234,138 @@ func handleJoinTableExpr(r *earlyRewriter, node *sqlparser.JoinTableExpr) { r.warning = "straight join is converted to normal join" } -// handleOrderBy processes the ORDER BY clause. -func handleOrderBy(r *earlyRewriter, cursor *sqlparser.Cursor, node sqlparser.OrderBy) { - r.clause = "order clause" - rewriteHavingAndOrderBy(node, cursor.Parent()) +type orderByIterator struct { + node sqlparser.OrderBy + idx int } -// rewriteOrExpr rewrites OR expressions when the right side is FALSE. -func rewriteOrExpr(cursor *sqlparser.Cursor, node *sqlparser.OrExpr) { - newNode := rewriteOrFalse(*node) - if newNode != nil { - cursor.ReplaceAndRevisit(newNode) +func (it *orderByIterator) next() sqlparser.Expr { + it.idx++ + + if it.idx >= len(it.node) { + return nil } + + return it.node[it.idx].Expr } -// handleLiteral processes literals within the context of ORDER BY expressions. -func handleLiteral(r *earlyRewriter, cursor *sqlparser.Cursor, node *sqlparser.Literal) error { - newNode, err := r.rewriteOrderByExpr(node) - if err != nil { - return err - } - if newNode != nil { - cursor.Replace(newNode) +func (it *orderByIterator) replace(e sqlparser.Expr) error { + if it.idx >= len(it.node) { + return vterrors.VT13001("went past the last item") } + it.node[it.idx].Expr = e return nil } -// handleCollateExpr processes COLLATE expressions. -func handleCollateExpr(r *earlyRewriter, node *sqlparser.CollateExpr) error { - lit, ok := node.Expr.(*sqlparser.Literal) - if !ok { +type exprIterator struct { + node []sqlparser.Expr + idx int +} + +func (it *exprIterator) next() sqlparser.Expr { + it.idx++ + + if it.idx >= len(it.node) { return nil } - newNode, err := r.rewriteOrderByExpr(lit) - if err != nil { - return err - } - if newNode != nil { - node.Expr = newNode + + return it.node[it.idx] +} + +func (it *exprIterator) replace(e sqlparser.Expr) error { + if it.idx >= len(it.node) { + return vterrors.VT13001("went past the last item") } + it.node[it.idx] = e return nil } -// handleComparisonExpr processes Comparison expressions, specifically for tuples with equal length and EqualOp operator. -func handleComparisonExpr(cursor *sqlparser.Cursor, node *sqlparser.ComparisonExpr) error { - lft, lftOK := node.Left.(sqlparser.ValTuple) - rgt, rgtOK := node.Right.(sqlparser.ValTuple) - if !lftOK || !rgtOK || len(lft) != len(rgt) || node.Operator != sqlparser.EqualOp { +type iterator interface { + next() sqlparser.Expr + replace(e sqlparser.Expr) error +} + +func (r *earlyRewriter) replaceLiteralsInOrderByGroupBy(e sqlparser.Expr, iter iterator) (bool, error) { + lit := getIntLiteral(e) + if lit == nil { + return false, nil + } + + newExpr, err := r.rewriteOrderByExpr(lit) + if err != nil { + return false, err + } + + if getIntLiteral(newExpr) == nil { + coll, ok := e.(*sqlparser.CollateExpr) + if ok { + coll.Expr = newExpr + newExpr = coll + } + } else { + // the expression is still a literal int. that means that we don't really need to sort by it. + // we'll just replace the number with a string instead, just like mysql would do in this situation + // mysql> explain select 1 as foo from user group by 1; + // + // mysql> show warnings; + // +-------+------+-----------------------------------------------------------------+ + // | Level | Code | Message | + // +-------+------+-----------------------------------------------------------------+ + // | Note | 1003 | /* select#1 */ select 1 AS `foo` from `test`.`user` group by '' | + // +-------+------+-----------------------------------------------------------------+ + newExpr = sqlparser.NewStrLiteral("") + } + + err = iter.replace(newExpr) + return true, err +} + +func getIntLiteral(e sqlparser.Expr) *sqlparser.Literal { + var lit *sqlparser.Literal + switch node := e.(type) { + case *sqlparser.Literal: + lit = node + case *sqlparser.CollateExpr: + expr, ok := node.Expr.(*sqlparser.Literal) + if !ok { + return nil + } + lit = expr + default: return nil } - var predicates []sqlparser.Expr - for i, l := range lft { - r := rgt[i] - predicates = append(predicates, &sqlparser.ComparisonExpr{ - Operator: sqlparser.EqualOp, - Left: l, - Right: r, - Escape: node.Escape, - }) + if lit.Type != sqlparser.IntVal { + return nil } - cursor.Replace(sqlparser.AndExpressions(predicates...)) - return nil + return lit } -func (r *earlyRewriter) expandStar(cursor *sqlparser.Cursor, node sqlparser.SelectExprs) error { - currentScope := r.scoper.currentScope() - var selExprs sqlparser.SelectExprs - changed := false - for _, selectExpr := range node { - starExpr, isStarExpr := selectExpr.(*sqlparser.StarExpr) - if !isStarExpr { - selExprs = append(selExprs, selectExpr) +// handleOrderBy processes the ORDER BY clause. +func (r *earlyRewriter) handleOrderByAndGroupBy(parent sqlparser.SQLNode, iter iterator) error { + stmt, ok := parent.(sqlparser.SelectStatement) + if !ok { + return nil + } + + sel := sqlparser.GetFirstSelect(stmt) + for e := iter.next(); e != nil; e = iter.next() { + lit, err := r.replaceLiteralsInOrderByGroupBy(e, iter) + if err != nil { + return err + } + if lit { continue } - starExpanded, colNames, err := r.expandTableColumns(starExpr, currentScope.tables, r.binder.usingJoinInfo, r.scoper.org) + expr, err := r.rewriteAliasesInOrderByHavingAndGroupBy(e, sel) if err != nil { return err } - if !starExpanded || colNames == nil { - selExprs = append(selExprs, selectExpr) - continue + err = iter.replace(expr) + if err != nil { + return err } - selExprs = append(selExprs, colNames...) - changed = true - } - if changed { - cursor.ReplaceAndRevisit(selExprs) } + return nil } @@ -258,88 +375,121 @@ func (r *earlyRewriter) expandStar(cursor *sqlparser.Cursor, node sqlparser.Sele // in SELECT points to that expression, not any table column. // - However, if the aliased expression is an aggregation and the column identifier in // the HAVING/ORDER BY clause is inside an aggregation function, the rule does not apply. -func rewriteHavingAndOrderBy(node, parent sqlparser.SQLNode) { - sel, isSel := parent.(*sqlparser.Select) - if !isSel { - return +func (r *earlyRewriter) rewriteAliasesInOrderByHavingAndGroupBy(node sqlparser.Expr, sel *sqlparser.Select) (expr sqlparser.Expr, err error) { + type ExprContainer struct { + expr sqlparser.Expr + ambiguous bool } - sqlparser.SafeRewrite(node, avoidSubqueries, - func(cursor *sqlparser.Cursor) bool { - col, ok := cursor.Node().(*sqlparser.ColName) - if !ok || !col.Qualifier.IsEmpty() { - // we are only interested in columns not qualified by table names - return true - } - - _, parentIsAggr := cursor.Parent().(sqlparser.AggrFunc) + aliases := map[string]ExprContainer{} + for _, e := range sel.SelectExprs { + ae, ok := e.(*sqlparser.AliasedExpr) + if !ok { + continue + } - // Iterate through SELECT expressions. - for _, e := range sel.SelectExprs { - ae, ok := e.(*sqlparser.AliasedExpr) - if !ok || !ae.As.Equal(col.Name) { - // we are searching for aliased expressions that match the column we have found - continue - } + var alias string - expr := ae.Expr - if parentIsAggr { - if _, aliasPointsToAggr := expr.(sqlparser.AggrFunc); aliasPointsToAggr { - return false - } - } + item := ExprContainer{expr: ae.Expr} + if ae.As.NotEmpty() { + alias = ae.As.Lowered() + } else if col, ok := ae.Expr.(*sqlparser.ColName); ok { + alias = col.Name.Lowered() + } - if isSafeToRewrite(expr) { - cursor.Replace(expr) - } - } - return true - }) -} + if old, alreadyExists := aliases[alias]; alreadyExists && !sqlparser.Equals.Expr(old.expr, item.expr) { + item.ambiguous = true + } -func avoidSubqueries(node, _ sqlparser.SQLNode) bool { - _, isSubQ := node.(*sqlparser.Subquery) - return !isSubQ -} + aliases[alias] = item + } -func isSafeToRewrite(e sqlparser.Expr) bool { - safeToRewrite := true - _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + insideAggr := false + downF := func(node, _ sqlparser.SQLNode) bool { switch node.(type) { - case *sqlparser.ColName: - safeToRewrite = false - return false, nil + case *sqlparser.Subquery: + return false case sqlparser.AggrFunc: - return false, nil + insideAggr = true + } + + return true + } + + output := sqlparser.CopyOnRewrite(node, downF, func(cursor *sqlparser.CopyOnWriteCursor) { + switch col := cursor.Node().(type) { + case sqlparser.AggrFunc: + insideAggr = false + case *sqlparser.ColName: + if !col.Qualifier.IsEmpty() { + // we are only interested in columns not qualified by table names + break + } + + item, found := aliases[col.Name.Lowered()] + if !found { + break + } + + if item.ambiguous { + err = &AmbiguousColumnError{Column: sqlparser.String(col)} + cursor.StopTreeWalk() + return + } + + if insideAggr && sqlparser.ContainsAggregation(item.expr) { + // I'm not sure about this, but my experiments point to this being the behaviour mysql has + // mysql> select min(name) as name from user order by min(name); + // 1 row in set (0.00 sec) + // + // mysql> select id % 2, min(name) as name from user group by id % 2 order by min(name); + // 2 rows in set (0.00 sec) + // + // mysql> select id % 2, 'foobar' as name from user group by id % 2 order by min(name); + // 2 rows in set (0.00 sec) + // + // mysql> select id % 2 from user group by id % 2 order by min(min(name)); + // ERROR 1111 (HY000): Invalid use of group function + // + // mysql> select id % 2, min(name) as k from user group by id % 2 order by min(k); + // ERROR 1111 (HY000): Invalid use of group function + // + // mysql> select id % 2, -id as name from user group by id % 2, -id order by min(name); + // 6 rows in set (0.01 sec) + break + } + + cursor.Replace(sqlparser.CloneExpr(item.expr)) } - return true, nil - }, e) - return safeToRewrite + }, nil) + + expr = output.(sqlparser.Expr) + return } func (r *earlyRewriter) rewriteOrderByExpr(node *sqlparser.Literal) (sqlparser.Expr, error) { - currScope, found := r.scoper.specialExprScopes[node] + scope, found := r.scoper.specialExprScopes[node] if !found { - return nil, nil + return node, nil } num, err := strconv.Atoi(node.Val) if err != nil { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "error parsing column number: %s", node.Val) } - stmt, isSel := currScope.stmt.(*sqlparser.Select) + + stmt, isSel := scope.stmt.(*sqlparser.Select) if !isSel { - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "error invalid statement type, expect Select, got: %T", currScope.stmt) + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "error invalid statement type, expect Select, got: %T", scope.stmt) } if num < 1 || num > len(stmt.SelectExprs) { return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.BadFieldError, "Unknown column '%d' in '%s'", num, r.clause) } + // We loop like this instead of directly accessing the offset, to make sure there are no unexpanded `*` before for i := 0; i < num; i++ { - expr := stmt.SelectExprs[i] - _, ok := expr.(*sqlparser.AliasedExpr) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot use column offsets in %s when using `%s`", r.clause, sqlparser.String(expr)) + if _, ok := stmt.SelectExprs[i].(*sqlparser.AliasedExpr); !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot use column offsets in %s when using `%s`", r.clause, sqlparser.String(stmt.SelectExprs[i])) } } @@ -348,12 +498,116 @@ func (r *earlyRewriter) rewriteOrderByExpr(node *sqlparser.Literal) (sqlparser.E return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "don't know how to handle %s", sqlparser.String(node)) } - if !aliasedExpr.As.IsEmpty() { - return sqlparser.NewColName(aliasedExpr.As.String()), nil + if scope.isUnion { + col, isCol := aliasedExpr.Expr.(*sqlparser.ColName) + + if aliasedExpr.As.IsEmpty() && isCol { + return sqlparser.NewColName(col.Name.String()), nil + } + + return sqlparser.NewColName(aliasedExpr.ColumnName()), nil + } + + return realCloneOfColNames(aliasedExpr.Expr, false), nil +} + +// rewriteOrExpr rewrites OR expressions when the right side is FALSE. +func rewriteOrExpr(env *vtenv.Environment, cursor *sqlparser.Cursor, node *sqlparser.OrExpr) { + newNode := rewriteOrFalse(env, *node) + if newNode != nil { + cursor.ReplaceAndRevisit(newNode) + } +} + +// rewriteAndExpr rewrites AND expressions when either side is TRUE. +func rewriteAndExpr(env *vtenv.Environment, cursor *sqlparser.Cursor, node *sqlparser.AndExpr) { + newNode := rewriteAndTrue(env, *node) + if newNode != nil { + cursor.ReplaceAndRevisit(newNode) + } +} + +func rewriteAndTrue(env *vtenv.Environment, andExpr sqlparser.AndExpr) sqlparser.Expr { + // we are looking for the pattern `WHERE c = 1 AND 1 = 1` + isTrue := func(subExpr sqlparser.Expr) bool { + coll := env.CollationEnv().DefaultConnectionCharset() + evalEnginePred, err := evalengine.Translate(subExpr, &evalengine.Config{ + Environment: env, + Collation: coll, + }) + if err != nil { + return false + } + + env := evalengine.EmptyExpressionEnv(env) + res, err := env.Evaluate(evalEnginePred) + if err != nil { + return false + } + + boolValue, err := res.Value(coll).ToBool() + if err != nil { + return false + } + + return boolValue + } + + if isTrue(andExpr.Left) { + return andExpr.Right + } else if isTrue(andExpr.Right) { + return andExpr.Left + } + + return nil +} + +// handleComparisonExpr processes Comparison expressions, specifically for tuples with equal length and EqualOp operator. +func handleComparisonExpr(cursor *sqlparser.Cursor, node *sqlparser.ComparisonExpr) error { + lft, lftOK := node.Left.(sqlparser.ValTuple) + rgt, rgtOK := node.Right.(sqlparser.ValTuple) + if !lftOK || !rgtOK || len(lft) != len(rgt) || node.Operator != sqlparser.EqualOp { + return nil + } + var predicates []sqlparser.Expr + for i, l := range lft { + r := rgt[i] + predicates = append(predicates, &sqlparser.ComparisonExpr{ + Operator: sqlparser.EqualOp, + Left: l, + Right: r, + Escape: node.Escape, + }) } + cursor.Replace(sqlparser.AndExpressions(predicates...)) + return nil +} - expr := realCloneOfColNames(aliasedExpr.Expr, currScope.isUnion) - return expr, nil +func (r *earlyRewriter) expandStar(cursor *sqlparser.Cursor, node sqlparser.SelectExprs) error { + currentScope := r.scoper.currentScope() + var selExprs sqlparser.SelectExprs + changed := false + for _, selectExpr := range node { + starExpr, isStarExpr := selectExpr.(*sqlparser.StarExpr) + if !isStarExpr { + selExprs = append(selExprs, selectExpr) + continue + } + starExpanded, colNames, err := r.expandTableColumns(starExpr, currentScope.tables, r.binder.usingJoinInfo, r.scoper.org) + if err != nil { + return err + } + if !starExpanded || colNames == nil { + selExprs = append(selExprs, selectExpr) + continue + } + selExprs = append(selExprs, colNames...) + changed = true + } + if changed { + cursor.ReplaceAndRevisit(selExprs) + } + return nil } // realCloneOfColNames clones all the expressions including ColName. @@ -373,21 +627,25 @@ func realCloneOfColNames(expr sqlparser.Expr, union bool) sqlparser.Expr { }, nil).(sqlparser.Expr) } -func rewriteOrFalse(orExpr sqlparser.OrExpr) sqlparser.Expr { +func rewriteOrFalse(env *vtenv.Environment, orExpr sqlparser.OrExpr) sqlparser.Expr { // we are looking for the pattern `WHERE c = 1 OR 1 = 0` isFalse := func(subExpr sqlparser.Expr) bool { - evalEnginePred, err := evalengine.Translate(subExpr, nil) + coll := env.CollationEnv().DefaultConnectionCharset() + evalEnginePred, err := evalengine.Translate(subExpr, &evalengine.Config{ + Environment: env, + Collation: coll, + }) if err != nil { return false } - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(env) res, err := env.Evaluate(evalEnginePred) if err != nil { return false } - boolValue, err := res.Value(collations.Default()).ToBool() + boolValue, err := res.Value(coll).ToBool() if err != nil { return false } diff --git a/go/vt/vtgate/semantics/early_rewriter_test.go b/go/vt/vtgate/semantics/early_rewriter_test.go index bf09d2d5cc3..e681f722b1d 100644 --- a/go/vt/vtgate/semantics/early_rewriter_test.go +++ b/go/vt/vtgate/semantics/early_rewriter_test.go @@ -32,7 +32,7 @@ import ( func TestExpandStar(t *testing.T) { ks := &vindexes.Keyspace{ Name: "main", - Sharded: false, + Sharded: true, } schemaInfo := &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -170,7 +170,7 @@ func TestExpandStar(t *testing.T) { expanded: "main.t1.a, main.t1.b, main.t1.c, main.t5.a", }, { sql: "select * from t1 join t5 using (b) having b = 12", - expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 on t1.b = t5.b having b = 12", + expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 on t1.b = t5.b having t1.b = 12", }, { sql: "select 1 from t1 join t5 using (b) having b = 12", expSQL: "select 1 from t1 join t5 on t1.b = t5.b having t1.b = 12", @@ -187,7 +187,7 @@ func TestExpandStar(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) selectStatement, isSelectStatement := ast.(*sqlparser.Select) require.True(t, isSelectStatement, "analyzer expects a select statement") @@ -288,7 +288,7 @@ func TestRewriteJoinUsingColumns(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) selectStatement, isSelectStatement := ast.(*sqlparser.Select) require.True(t, isSelectStatement, "analyzer expects a select statement") @@ -315,22 +315,31 @@ func TestOrderByGroupByLiteral(t *testing.T) { expErr string }{{ sql: "select 1 as id from t1 order by 1", - expSQL: "select 1 as id from t1 order by id asc", + expSQL: "select 1 as id from t1 order by '' asc", }, { sql: "select t1.col from t1 order by 1", expSQL: "select t1.col from t1 order by t1.col asc", + }, { + sql: "select t1.col from t1 order by 1.0", + expSQL: "select t1.col from t1 order by 1.0 asc", + }, { + sql: "select t1.col from t1 order by 'fubick'", + expSQL: "select t1.col from t1 order by 'fubick' asc", + }, { + sql: "select t1.col as foo from t1 order by 1", + expSQL: "select t1.col as foo from t1 order by t1.col asc", }, { sql: "select t1.col from t1 group by 1", expSQL: "select t1.col from t1 group by t1.col", }, { sql: "select t1.col as xyz from t1 group by 1", - expSQL: "select t1.col as xyz from t1 group by xyz", + expSQL: "select t1.col as xyz from t1 group by t1.col", }, { sql: "select t1.col as xyz, count(*) from t1 group by 1 order by 2", - expSQL: "select t1.col as xyz, count(*) from t1 group by xyz order by count(*) asc", + expSQL: "select t1.col as xyz, count(*) from t1 group by t1.col order by count(*) asc", }, { sql: "select id from t1 group by 2", - expErr: "Unknown column '2' in 'group statement'", + expErr: "Unknown column '2' in 'group clause'", }, { sql: "select id from t1 order by 2", expErr: "Unknown column '2' in 'order clause'", @@ -339,16 +348,22 @@ func TestOrderByGroupByLiteral(t *testing.T) { expErr: "cannot use column offsets in order clause when using `*`", }, { sql: "select *, id from t1 group by 2", - expErr: "cannot use column offsets in group statement when using `*`", + expErr: "cannot use column offsets in group clause when using `*`", }, { sql: "select id from t1 order by 1 collate utf8_general_ci", expSQL: "select id from t1 order by id collate utf8_general_ci asc", + }, { + sql: "select a.id from `user` union select 1 from dual order by 1", + expSQL: "select a.id from `user` union select 1 from dual order by id asc", + }, { + sql: "select a.id, b.id from user as a, user_extra as b union select 1, 2 order by 1", + expErr: "Column 'id' in field list is ambiguous", }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) - selectStatement := ast.(*sqlparser.Select) + selectStatement := ast.(sqlparser.SelectStatement) _, err = Analyze(selectStatement, cDB, schemaInfo) if tcase.expErr == "" { require.NoError(t, err) @@ -378,12 +393,28 @@ func TestHavingAndOrderByColumnName(t *testing.T) { }, { sql: "select id, sum(foo) as foo from t1 having sum(foo) > 1", expSQL: "select id, sum(foo) as foo from t1 having sum(foo) > 1", + }, { + sql: "select id, lower(min(foo)) as foo from t1 order by min(foo)", + expSQL: "select id, lower(min(foo)) as foo from t1 order by min(foo) asc", + }, { + // invalid according to group by rules, but still accepted by mysql + sql: "select id, t1.bar as foo from t1 group by id order by min(foo)", + expSQL: "select id, t1.bar as foo from t1 group by id order by min(t1.bar) asc", + }, { + sql: "select foo + 2 as foo from t1 having foo = 42", + expSQL: "select foo + 2 as foo from t1 having foo + 2 = 42", + }, { + sql: "select id, b as id, count(*) from t1 order by id", + expErr: "Column 'id' in field list is ambiguous", + }, { + sql: "select id, id, count(*) from t1 order by id", + expSQL: "select id, id, count(*) from t1 order by id asc", }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) - selectStatement := ast.(*sqlparser.Select) + selectStatement := ast.(sqlparser.SelectStatement) _, err = Analyze(selectStatement, cDB, schemaInfo) if tcase.expErr == "" { require.NoError(t, err) @@ -426,7 +457,7 @@ func TestSemTableDependenciesAfterExpandStar(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) selectStatement, isSelectStatement := ast.(*sqlparser.Select) require.True(t, isSelectStatement, "analyzer expects a select statement") @@ -452,7 +483,7 @@ func TestSemTableDependenciesAfterExpandStar(t *testing.T) { func TestRewriteNot(t *testing.T) { ks := &vindexes.Keyspace{ Name: "main", - Sharded: false, + Sharded: true, } schemaInfo := &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -486,7 +517,7 @@ func TestRewriteNot(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) selectStatement, isSelectStatement := ast.(*sqlparser.Select) require.True(t, isSelectStatement, "analyzer expects a select statement") @@ -504,7 +535,7 @@ func TestRewriteNot(t *testing.T) { func TestConstantFolding(t *testing.T) { ks := &vindexes.Keyspace{ Name: "main", - Sharded: false, + Sharded: true, } schemaInfo := &FakeSI{ Tables: map[string]*vindexes.Table{ @@ -538,7 +569,7 @@ func TestConstantFolding(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) _, err = Analyze(ast, cDB, schemaInfo) require.NoError(t, err) @@ -565,7 +596,7 @@ func TestCTEToDerivedTableRewrite(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { - ast, err := sqlparser.Parse(tcase.sql) + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) require.NoError(t, err) _, err = Analyze(ast, cDB, fakeSchemaInfo()) require.NoError(t, err) @@ -573,3 +604,33 @@ func TestCTEToDerivedTableRewrite(t *testing.T) { }) } } + +// TestDeleteTargetTableRewrite checks that delete target rewrite is done correctly. +func TestDeleteTargetTableRewrite(t *testing.T) { + cDB := "db" + tcases := []struct { + sql string + target string + }{{ + sql: "delete from t1", + target: "t1", + }, { + sql: "delete from t1 XYZ", + target: "XYZ", + }, { + sql: "delete t2 from t1 t1, t t2", + target: "t2", + }, { + sql: "delete t2,t1 from t t1, t t2", + target: "t2, t1", + }} + for _, tcase := range tcases { + t.Run(tcase.sql, func(t *testing.T) { + ast, err := sqlparser.NewTestParser().Parse(tcase.sql) + require.NoError(t, err) + _, err = Analyze(ast, cDB, fakeSchemaInfo()) + require.NoError(t, err) + require.Equal(t, tcase.target, sqlparser.String(ast.(*sqlparser.Delete).Targets)) + }) + } +} diff --git a/go/vt/vtgate/semantics/errors.go b/go/vt/vtgate/semantics/errors.go index 8d0b23d7f82..0be85633632 100644 --- a/go/vt/vtgate/semantics/errors.go +++ b/go/vt/vtgate/semantics/errors.go @@ -51,6 +51,12 @@ type ( AmbiguousColumnError struct{ Column string } SubqueryColumnCountError struct{ Expected int } ColumnsMissingInSchemaError struct{} + CantUseMultipleVindexHints struct{ Table string } + + NoSuchVindexFound struct { + Table string + VindexName string + } UnsupportedMultiTablesInUpdateError struct { ExprCount int @@ -261,3 +267,21 @@ func (e *ColumnsMissingInSchemaError) Error() string { func (e *ColumnsMissingInSchemaError) ErrorCode() vtrpcpb.Code { return vtrpcpb.Code_INVALID_ARGUMENT } + +// CantUseMultipleVindexHints +func (c *CantUseMultipleVindexHints) Error() string { + return vterrors.VT09020(c.Table).Error() +} + +func (c *CantUseMultipleVindexHints) ErrorCode() vtrpcpb.Code { + return vtrpcpb.Code_FAILED_PRECONDITION +} + +// CantUseMultipleVindexHints +func (c *NoSuchVindexFound) Error() string { + return vterrors.VT09021(c.VindexName, c.Table).Error() +} + +func (c *NoSuchVindexFound) ErrorCode() vtrpcpb.Code { + return vtrpcpb.Code_FAILED_PRECONDITION +} diff --git a/go/vt/vtgate/semantics/info_schema.go b/go/vt/vtgate/semantics/info_schema.go index 838f6276472..d7470e2fd0a 100644 --- a/go/vt/vtgate/semantics/info_schema.go +++ b/go/vt/vtgate/semantics/info_schema.go @@ -17,6 +17,7 @@ limitations under the License. package semantics import ( + "fmt" "strings" "vitess.io/vitess/go/mysql/collations" @@ -24,1646 +25,1576 @@ import ( "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" ) -func createCol(name string, typ int) vindexes.Column { - return vindexes.Column{Name: sqlparser.NewIdentifierCI(name), Type: query.Type(typ)} +func createCol(parser *sqlparser.Parser, name string, typ int, collation string, def string, size, scale int32, notNullable bool, values string) vindexes.Column { + var expr sqlparser.Expr + if def != "" { + var err error + expr, err = parser.ParseExpr(def) + if err != nil { + panic(fmt.Sprintf("Failed to parse %q: %v", def, err)) + } + } + var vals []string + if values != "" { + quotedVals := strings.Split(values, ",") + vals = make([]string, 0, len(quotedVals)) + for _, v := range quotedVals { + u := strings.TrimFunc(v, func(r rune) bool { + return r == '\'' + }) + vals = append(vals, u) + } + } + + return vindexes.Column{ + Name: sqlparser.NewIdentifierCI(name), + Type: query.Type(typ), + CollationName: collation, + Default: expr, + Size: size, + Scale: scale, + Nullable: !notNullable, + Values: vals, + } } // getInfoSchema57 returns a map of all information_schema tables and their columns with types // To recreate this information from MySQL, you can run the test in info_schema_gen_test.go func getInfoSchema57() map[string][]vindexes.Column { + parser, err := sqlparser.New(sqlparser.Options{MySQLServerVersion: "5.7.9"}) + if err != nil { + panic(err) + } infSchema := map[string][]vindexes.Column{} var cols []vindexes.Column - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_COLLATE_NAME", 6165)) - cols = append(cols, createCol("DESCRIPTION", 6165)) - cols = append(cols, createCol("MAXLEN", 265)) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_COLLATE_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DESCRIPTION", 6165, "utf8mb3_general_ci", "", 60, 0, true, "")) + cols = append(cols, createCol(parser, "MAXLEN", 265, "utf8mb3_general_ci", "0", 3, 0, true, "")) infSchema["CHARACTER_SETS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) infSchema["COLLATION_CHARACTER_SET_APPLICABILITY"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("ID", 265)) - cols = append(cols, createCol("IS_DEFAULT", 6165)) - cols = append(cols, createCol("IS_COMPILED", 6165)) - cols = append(cols, createCol("SORTLEN", 265)) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "ID", 265, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DEFAULT", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "IS_COMPILED", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "SORTLEN", 265, "utf8mb3_general_ci", "0", 3, 0, true, "")) infSchema["COLLATIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 81, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["COLUMN_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 265)) - cols = append(cols, createCol("COLUMN_DEFAULT", 6163)) - cols = append(cols, createCol("IS_NULLABLE", 6165)) - cols = append(cols, createCol("DATA_TYPE", 6165)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 265)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 265)) - cols = append(cols, createCol("NUMERIC_PRECISION", 265)) - cols = append(cols, createCol("NUMERIC_SCALE", 265)) - cols = append(cols, createCol("DATETIME_PRECISION", 265)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("COLUMN_TYPE", 6163)) - cols = append(cols, createCol("COLUMN_KEY", 6165)) - cols = append(cols, createCol("EXTRA", 6165)) - cols = append(cols, createCol("PRIVILEGES", 6165)) - cols = append(cols, createCol("COLUMN_COMMENT", 6165)) - cols = append(cols, createCol("GENERATION_EXPRESSION", 6163)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_DEFAULT", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "IS_NULLABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_TYPE", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_KEY", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "EXTRA", 6165, "utf8mb3_general_ci", "", 30, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGES", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_COMMENT", 6165, "utf8mb3_general_ci", "", 1024, 0, true, "")) + cols = append(cols, createCol(parser, "GENERATION_EXPRESSION", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["COLUMNS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("SUPPORT", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) - cols = append(cols, createCol("TRANSACTIONS", 6165)) - cols = append(cols, createCol("XA", 6165)) - cols = append(cols, createCol("SAVEPOINTS", 6165)) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SUPPORT", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "TRANSACTIONS", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "XA", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "SAVEPOINTS", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) infSchema["ENGINES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("EVENT_CATALOG", 6165)) - cols = append(cols, createCol("EVENT_SCHEMA", 6165)) - cols = append(cols, createCol("EVENT_NAME", 6165)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("TIME_ZONE", 6165)) - cols = append(cols, createCol("EVENT_BODY", 6165)) - cols = append(cols, createCol("EVENT_DEFINITION", 6163)) - cols = append(cols, createCol("EVENT_TYPE", 6165)) - cols = append(cols, createCol("EXECUTE_AT", 2064)) - cols = append(cols, createCol("INTERVAL_VALUE", 6165)) - cols = append(cols, createCol("INTERVAL_FIELD", 6165)) - cols = append(cols, createCol("SQL_MODE", 6165)) - cols = append(cols, createCol("STARTS", 2064)) - cols = append(cols, createCol("ENDS", 2064)) - cols = append(cols, createCol("STATUS", 6165)) - cols = append(cols, createCol("ON_COMPLETION", 6165)) - cols = append(cols, createCol("CREATED", 2064)) - cols = append(cols, createCol("LAST_ALTERED", 2064)) - cols = append(cols, createCol("LAST_EXECUTED", 2064)) - cols = append(cols, createCol("EVENT_COMMENT", 6165)) - cols = append(cols, createCol("ORIGINATOR", 265)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "EVENT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 93, 0, true, "")) + cols = append(cols, createCol(parser, "TIME_ZONE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_BODY", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_TYPE", 6165, "utf8mb3_general_ci", "", 9, 0, true, "")) + cols = append(cols, createCol(parser, "EXECUTE_AT", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INTERVAL_VALUE", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "INTERVAL_FIELD", 6165, "utf8mb3_general_ci", "", 18, 0, false, "")) + cols = append(cols, createCol(parser, "SQL_MODE", 6165, "utf8mb3_general_ci", "", 8192, 0, true, "")) + cols = append(cols, createCol(parser, "STARTS", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ENDS", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 6165, "utf8mb3_general_ci", "", 18, 0, true, "")) + cols = append(cols, createCol(parser, "ON_COMPLETION", 6165, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "CREATED", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_ALTERED", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_EXECUTED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_COMMENT", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ORIGINATOR", 265, "utf8mb3_general_ci", "0", 10, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) infSchema["EVENTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("FILE_ID", 265)) - cols = append(cols, createCol("FILE_NAME", 6165)) - cols = append(cols, createCol("FILE_TYPE", 6165)) - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("LOGFILE_GROUP_NAME", 6165)) - cols = append(cols, createCol("LOGFILE_GROUP_NUMBER", 265)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("FULLTEXT_KEYS", 6165)) - cols = append(cols, createCol("DELETED_ROWS", 265)) - cols = append(cols, createCol("UPDATE_COUNT", 265)) - cols = append(cols, createCol("FREE_EXTENTS", 265)) - cols = append(cols, createCol("TOTAL_EXTENTS", 265)) - cols = append(cols, createCol("EXTENT_SIZE", 265)) - cols = append(cols, createCol("INITIAL_SIZE", 265)) - cols = append(cols, createCol("MAXIMUM_SIZE", 265)) - cols = append(cols, createCol("AUTOEXTEND_SIZE", 265)) - cols = append(cols, createCol("CREATION_TIME", 2064)) - cols = append(cols, createCol("LAST_UPDATE_TIME", 2064)) - cols = append(cols, createCol("LAST_ACCESS_TIME", 2064)) - cols = append(cols, createCol("RECOVER_TIME", 265)) - cols = append(cols, createCol("TRANSACTION_COUNTER", 265)) - cols = append(cols, createCol("VERSION", 265)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("TABLE_ROWS", 265)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 265)) - cols = append(cols, createCol("DATA_LENGTH", 265)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 265)) - cols = append(cols, createCol("INDEX_LENGTH", 265)) - cols = append(cols, createCol("DATA_FREE", 265)) - cols = append(cols, createCol("CREATE_TIME", 2064)) - cols = append(cols, createCol("UPDATE_TIME", 2064)) - cols = append(cols, createCol("CHECK_TIME", 2064)) - cols = append(cols, createCol("CHECKSUM", 265)) - cols = append(cols, createCol("STATUS", 6165)) - cols = append(cols, createCol("EXTRA", 6165)) + cols = append(cols, createCol(parser, "FILE_ID", 265, "utf8mb3_general_ci", "0", 4, 0, true, "")) + cols = append(cols, createCol(parser, "FILE_NAME", 6165, "utf8mb3_general_ci", "", 4000, 0, false, "")) + cols = append(cols, createCol(parser, "FILE_TYPE", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NUMBER", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "FULLTEXT_KEYS", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DELETED_ROWS", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_COUNT", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_EXTENTS", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "TOTAL_EXTENTS", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "EXTENT_SIZE", 265, "utf8mb3_general_ci", "0", 4, 0, true, "")) + cols = append(cols, createCol(parser, "INITIAL_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MAXIMUM_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AUTOEXTEND_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CREATION_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "LAST_UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "LAST_ACCESS_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "RECOVER_TIME", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "TRANSACTION_COUNTER", 265, "utf8mb3_general_ci", "", 4, 0, false, "")) + cols = append(cols, createCol(parser, "VERSION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "EXTRA", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) infSchema["FILES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("VARIABLE_NAME", 6165)) - cols = append(cols, createCol("VARIABLE_VALUE", 6165)) + cols = append(cols, createCol(parser, "VARIABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "VARIABLE_VALUE", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) infSchema["GLOBAL_STATUS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("VARIABLE_NAME", 6165)) - cols = append(cols, createCol("VARIABLE_VALUE", 6165)) + cols = append(cols, createCol(parser, "VARIABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "VARIABLE_VALUE", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) infSchema["GLOBAL_VARIABLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 265)) - cols = append(cols, createCol("BLOCK_ID", 265)) - cols = append(cols, createCol("SPACE", 265)) - cols = append(cols, createCol("PAGE_NUMBER", 265)) - cols = append(cols, createCol("PAGE_TYPE", 6165)) - cols = append(cols, createCol("FLUSH_TYPE", 265)) - cols = append(cols, createCol("FIX_COUNT", 265)) - cols = append(cols, createCol("IS_HASHED", 6165)) - cols = append(cols, createCol("NEWEST_MODIFICATION", 265)) - cols = append(cols, createCol("OLDEST_MODIFICATION", 265)) - cols = append(cols, createCol("ACCESS_TIME", 265)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("NUMBER_RECORDS", 265)) - cols = append(cols, createCol("DATA_SIZE", 265)) - cols = append(cols, createCol("COMPRESSED_SIZE", 265)) - cols = append(cols, createCol("PAGE_STATE", 6165)) - cols = append(cols, createCol("IO_FIX", 6165)) - cols = append(cols, createCol("IS_OLD", 6165)) - cols = append(cols, createCol("FREE_PAGE_CLOCK", 265)) + cols = append(cols, createCol(parser, "POOL_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "BLOCK_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_NUMBER", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "FLUSH_TYPE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "FIX_COUNT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "IS_HASHED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "NEWEST_MODIFICATION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "OLDEST_MODIFICATION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "ACCESS_TIME", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "NUMBER_RECORDS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_SIZE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED_SIZE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_STATE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IO_FIX", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IS_OLD", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_PAGE_CLOCK", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_BUFFER_PAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 265)) - cols = append(cols, createCol("LRU_POSITION", 265)) - cols = append(cols, createCol("SPACE", 265)) - cols = append(cols, createCol("PAGE_NUMBER", 265)) - cols = append(cols, createCol("PAGE_TYPE", 6165)) - cols = append(cols, createCol("FLUSH_TYPE", 265)) - cols = append(cols, createCol("FIX_COUNT", 265)) - cols = append(cols, createCol("IS_HASHED", 6165)) - cols = append(cols, createCol("NEWEST_MODIFICATION", 265)) - cols = append(cols, createCol("OLDEST_MODIFICATION", 265)) - cols = append(cols, createCol("ACCESS_TIME", 265)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("NUMBER_RECORDS", 265)) - cols = append(cols, createCol("DATA_SIZE", 265)) - cols = append(cols, createCol("COMPRESSED_SIZE", 265)) - cols = append(cols, createCol("COMPRESSED", 6165)) - cols = append(cols, createCol("IO_FIX", 6165)) - cols = append(cols, createCol("IS_OLD", 6165)) - cols = append(cols, createCol("FREE_PAGE_CLOCK", 265)) + cols = append(cols, createCol(parser, "POOL_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_POSITION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_NUMBER", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "FLUSH_TYPE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "FIX_COUNT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "IS_HASHED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "NEWEST_MODIFICATION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "OLDEST_MODIFICATION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "ACCESS_TIME", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "NUMBER_RECORDS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_SIZE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED_SIZE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "IO_FIX", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IS_OLD", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_PAGE_CLOCK", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_BUFFER_PAGE_LRU"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 265)) - cols = append(cols, createCol("POOL_SIZE", 265)) - cols = append(cols, createCol("FREE_BUFFERS", 265)) - cols = append(cols, createCol("DATABASE_PAGES", 265)) - cols = append(cols, createCol("OLD_DATABASE_PAGES", 265)) - cols = append(cols, createCol("MODIFIED_DATABASE_PAGES", 265)) - cols = append(cols, createCol("PENDING_DECOMPRESS", 265)) - cols = append(cols, createCol("PENDING_READS", 265)) - cols = append(cols, createCol("PENDING_FLUSH_LRU", 265)) - cols = append(cols, createCol("PENDING_FLUSH_LIST", 265)) - cols = append(cols, createCol("PAGES_MADE_YOUNG", 265)) - cols = append(cols, createCol("PAGES_NOT_MADE_YOUNG", 265)) - cols = append(cols, createCol("PAGES_MADE_YOUNG_RATE", 1036)) - cols = append(cols, createCol("PAGES_MADE_NOT_YOUNG_RATE", 1036)) - cols = append(cols, createCol("NUMBER_PAGES_READ", 265)) - cols = append(cols, createCol("NUMBER_PAGES_CREATED", 265)) - cols = append(cols, createCol("NUMBER_PAGES_WRITTEN", 265)) - cols = append(cols, createCol("PAGES_READ_RATE", 1036)) - cols = append(cols, createCol("PAGES_CREATE_RATE", 1036)) - cols = append(cols, createCol("PAGES_WRITTEN_RATE", 1036)) - cols = append(cols, createCol("NUMBER_PAGES_GET", 265)) - cols = append(cols, createCol("HIT_RATE", 265)) - cols = append(cols, createCol("YOUNG_MAKE_PER_THOUSAND_GETS", 265)) - cols = append(cols, createCol("NOT_YOUNG_MAKE_PER_THOUSAND_GETS", 265)) - cols = append(cols, createCol("NUMBER_PAGES_READ_AHEAD", 265)) - cols = append(cols, createCol("NUMBER_READ_AHEAD_EVICTED", 265)) - cols = append(cols, createCol("READ_AHEAD_RATE", 1036)) - cols = append(cols, createCol("READ_AHEAD_EVICTED_RATE", 1036)) - cols = append(cols, createCol("LRU_IO_TOTAL", 265)) - cols = append(cols, createCol("LRU_IO_CURRENT", 265)) - cols = append(cols, createCol("UNCOMPRESS_TOTAL", 265)) - cols = append(cols, createCol("UNCOMPRESS_CURRENT", 265)) + cols = append(cols, createCol(parser, "POOL_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "POOL_SIZE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "FREE_BUFFERS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_PAGES", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "OLD_DATABASE_PAGES", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "MODIFIED_DATABASE_PAGES", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_DECOMPRESS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_READS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_FLUSH_LRU", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_FLUSH_LIST", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_YOUNG", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_NOT_MADE_YOUNG", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_YOUNG_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_NOT_YOUNG_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_READ", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_CREATED", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_WRITTEN", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_READ_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_CREATE_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_WRITTEN_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_GET", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "HIT_RATE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "YOUNG_MAKE_PER_THOUSAND_GETS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NOT_YOUNG_MAKE_PER_THOUSAND_GETS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_READ_AHEAD", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_READ_AHEAD_EVICTED", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "READ_AHEAD_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "READ_AHEAD_EVICTED_RATE", 1036, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_IO_TOTAL", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_IO_CURRENT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "UNCOMPRESS_TOTAL", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "UNCOMPRESS_CURRENT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_BUFFER_POOL_STATS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "0", 5, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMP"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("database_name", 6165)) - cols = append(cols, createCol("table_name", 6165)) - cols = append(cols, createCol("index_name", 6165)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "database_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "table_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "index_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMP_PER_INDEX"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("database_name", 6165)) - cols = append(cols, createCol("table_name", 6165)) - cols = append(cols, createCol("index_name", 6165)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "database_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "table_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "index_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMP_PER_INDEX_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "0", 5, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMP_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("buffer_pool_instance", 263)) - cols = append(cols, createCol("pages_used", 263)) - cols = append(cols, createCol("pages_free", 263)) - cols = append(cols, createCol("relocation_ops", 265)) - cols = append(cols, createCol("relocation_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "0", 5, 0, true, "")) + cols = append(cols, createCol(parser, "buffer_pool_instance", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "pages_used", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "pages_free", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_ops", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMPMEM"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("buffer_pool_instance", 263)) - cols = append(cols, createCol("pages_used", 263)) - cols = append(cols, createCol("pages_free", 263)) - cols = append(cols, createCol("relocation_ops", 265)) - cols = append(cols, createCol("relocation_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "0", 5, 0, true, "")) + cols = append(cols, createCol(parser, "buffer_pool_instance", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "pages_used", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "pages_free", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_ops", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_time", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) infSchema["INNODB_CMPMEM_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("DOC_ID", 265)) + cols = append(cols, createCol(parser, "DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_FT_BEING_DELETED"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("KEY", 6165)) - cols = append(cols, createCol("VALUE", 6165)) + cols = append(cols, createCol(parser, "KEY", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "VALUE", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) infSchema["INNODB_FT_CONFIG"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("value", 6165)) + cols = append(cols, createCol(parser, "value", 6165, "utf8mb3_general_ci", "", 18, 0, true, "")) infSchema["INNODB_FT_DEFAULT_STOPWORD"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("DOC_ID", 265)) + cols = append(cols, createCol(parser, "DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_FT_DELETED"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("WORD", 6165)) - cols = append(cols, createCol("FIRST_DOC_ID", 265)) - cols = append(cols, createCol("LAST_DOC_ID", 265)) - cols = append(cols, createCol("DOC_COUNT", 265)) - cols = append(cols, createCol("DOC_ID", 265)) - cols = append(cols, createCol("POSITION", 265)) + cols = append(cols, createCol(parser, "WORD", 6165, "utf8mb3_general_ci", "", 337, 0, true, "")) + cols = append(cols, createCol(parser, "FIRST_DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_COUNT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_FT_INDEX_CACHE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("WORD", 6165)) - cols = append(cols, createCol("FIRST_DOC_ID", 265)) - cols = append(cols, createCol("LAST_DOC_ID", 265)) - cols = append(cols, createCol("DOC_COUNT", 265)) - cols = append(cols, createCol("DOC_ID", 265)) - cols = append(cols, createCol("POSITION", 265)) + cols = append(cols, createCol(parser, "WORD", 6165, "utf8mb3_general_ci", "", 337, 0, true, "")) + cols = append(cols, createCol(parser, "FIRST_DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_COUNT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) infSchema["INNODB_FT_INDEX_TABLE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("requesting_trx_id", 6165)) - cols = append(cols, createCol("requested_lock_id", 6165)) - cols = append(cols, createCol("blocking_trx_id", 6165)) - cols = append(cols, createCol("blocking_lock_id", 6165)) - infSchema["INNODB_LOCK_WAITS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("lock_id", 6165)) - cols = append(cols, createCol("lock_trx_id", 6165)) - cols = append(cols, createCol("lock_mode", 6165)) - cols = append(cols, createCol("lock_type", 6165)) - cols = append(cols, createCol("lock_table", 6165)) - cols = append(cols, createCol("lock_index", 6165)) - cols = append(cols, createCol("lock_space", 265)) - cols = append(cols, createCol("lock_page", 265)) - cols = append(cols, createCol("lock_rec", 265)) - cols = append(cols, createCol("lock_data", 6165)) - infSchema["INNODB_LOCKS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("SUBSYSTEM", 6165)) - cols = append(cols, createCol("COUNT", 265)) - cols = append(cols, createCol("MAX_COUNT", 265)) - cols = append(cols, createCol("MIN_COUNT", 265)) - cols = append(cols, createCol("AVG_COUNT", 1036)) - cols = append(cols, createCol("COUNT_RESET", 265)) - cols = append(cols, createCol("MAX_COUNT_RESET", 265)) - cols = append(cols, createCol("MIN_COUNT_RESET", 265)) - cols = append(cols, createCol("AVG_COUNT_RESET", 1036)) - cols = append(cols, createCol("TIME_ENABLED", 2064)) - cols = append(cols, createCol("TIME_DISABLED", 2064)) - cols = append(cols, createCol("TIME_ELAPSED", 265)) - cols = append(cols, createCol("TIME_RESET", 2064)) - cols = append(cols, createCol("STATUS", 6165)) - cols = append(cols, createCol("TYPE", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "SUBSYSTEM", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "COUNT", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "MAX_COUNT", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MIN_COUNT", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_COUNT", 1036, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "COUNT_RESET", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "MAX_COUNT_RESET", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MIN_COUNT_RESET", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_COUNT_RESET", 1036, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_ENABLED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_DISABLED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_ELAPSED", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_RESET", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "TYPE", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) infSchema["INNODB_METRICS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("POS", 265)) - cols = append(cols, createCol("MTYPE", 263)) - cols = append(cols, createCol("PRTYPE", 263)) - cols = append(cols, createCol("LEN", 263)) - infSchema["INNODB_SYS_COLUMNS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("PATH", 6165)) - infSchema["INNODB_SYS_DATAFILES"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("INDEX_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("POS", 263)) - infSchema["INNODB_SYS_FIELDS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 6165)) - cols = append(cols, createCol("FOR_NAME", 6165)) - cols = append(cols, createCol("REF_NAME", 6165)) - cols = append(cols, createCol("N_COLS", 263)) - cols = append(cols, createCol("TYPE", 263)) - infSchema["INNODB_SYS_FOREIGN"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 6165)) - cols = append(cols, createCol("FOR_COL_NAME", 6165)) - cols = append(cols, createCol("REF_COL_NAME", 6165)) - cols = append(cols, createCol("POS", 263)) - infSchema["INNODB_SYS_FOREIGN_COLS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("INDEX_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("TYPE", 263)) - cols = append(cols, createCol("N_FIELDS", 263)) - cols = append(cols, createCol("PAGE_NO", 263)) - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("MERGE_THRESHOLD", 263)) - infSchema["INNODB_SYS_INDEXES"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("FLAG", 263)) - cols = append(cols, createCol("N_COLS", 263)) - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("FILE_FORMAT", 6165)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("ZIP_PAGE_SIZE", 263)) - cols = append(cols, createCol("SPACE_TYPE", 6165)) - infSchema["INNODB_SYS_TABLES"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("FLAG", 263)) - cols = append(cols, createCol("FILE_FORMAT", 6165)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("PAGE_SIZE", 263)) - cols = append(cols, createCol("ZIP_PAGE_SIZE", 263)) - cols = append(cols, createCol("SPACE_TYPE", 6165)) - cols = append(cols, createCol("FS_BLOCK_SIZE", 263)) - cols = append(cols, createCol("FILE_SIZE", 265)) - cols = append(cols, createCol("ALLOCATED_SIZE", 265)) - infSchema["INNODB_SYS_TABLESPACES"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("STATS_INITIALIZED", 6165)) - cols = append(cols, createCol("NUM_ROWS", 265)) - cols = append(cols, createCol("CLUST_INDEX_SIZE", 265)) - cols = append(cols, createCol("OTHER_INDEX_SIZE", 265)) - cols = append(cols, createCol("MODIFIED_COUNTER", 265)) - cols = append(cols, createCol("AUTOINC", 265)) - cols = append(cols, createCol("REF_COUNT", 263)) - infSchema["INNODB_SYS_TABLESTATS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("POS", 263)) - cols = append(cols, createCol("BASE_POS", 263)) - infSchema["INNODB_SYS_VIRTUAL"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 265)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("N_COLS", 263)) - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("PER_TABLE_TABLESPACE", 6165)) - cols = append(cols, createCol("IS_COMPRESSED", 6165)) + cols = append(cols, createCol(parser, "TABLE_ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 202, 0, false, "")) + cols = append(cols, createCol(parser, "N_COLS", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 263, "utf8mb3_general_ci", "0", 11, 0, true, "")) + cols = append(cols, createCol(parser, "PER_TABLE_TABLESPACE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IS_COMPRESSED", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) infSchema["INNODB_TEMP_TABLE_INFO"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("trx_id", 6165)) - cols = append(cols, createCol("trx_state", 6165)) - cols = append(cols, createCol("trx_started", 2064)) - cols = append(cols, createCol("trx_requested_lock_id", 6165)) - cols = append(cols, createCol("trx_wait_started", 2064)) - cols = append(cols, createCol("trx_weight", 265)) - cols = append(cols, createCol("trx_mysql_thread_id", 265)) - cols = append(cols, createCol("trx_query", 6165)) - cols = append(cols, createCol("trx_operation_state", 6165)) - cols = append(cols, createCol("trx_tables_in_use", 265)) - cols = append(cols, createCol("trx_tables_locked", 265)) - cols = append(cols, createCol("trx_lock_structs", 265)) - cols = append(cols, createCol("trx_lock_memory_bytes", 265)) - cols = append(cols, createCol("trx_rows_locked", 265)) - cols = append(cols, createCol("trx_rows_modified", 265)) - cols = append(cols, createCol("trx_concurrency_tickets", 265)) - cols = append(cols, createCol("trx_isolation_level", 6165)) - cols = append(cols, createCol("trx_unique_checks", 263)) - cols = append(cols, createCol("trx_foreign_key_checks", 263)) - cols = append(cols, createCol("trx_last_foreign_key_error", 6165)) - cols = append(cols, createCol("trx_adaptive_hash_latched", 263)) - cols = append(cols, createCol("trx_adaptive_hash_timeout", 265)) - cols = append(cols, createCol("trx_is_read_only", 263)) - cols = append(cols, createCol("trx_autocommit_non_locking", 263)) + cols = append(cols, createCol(parser, "trx_id", 6165, "utf8mb3_general_ci", "", 18, 0, true, "")) + cols = append(cols, createCol(parser, "trx_state", 6165, "utf8mb3_general_ci", "", 13, 0, true, "")) + cols = append(cols, createCol(parser, "trx_started", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_requested_lock_id", 6165, "utf8mb3_general_ci", "", 81, 0, false, "")) + cols = append(cols, createCol(parser, "trx_wait_started", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "trx_weight", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_mysql_thread_id", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_query", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "trx_operation_state", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "trx_tables_in_use", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_tables_locked", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_lock_structs", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_lock_memory_bytes", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_rows_locked", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_rows_modified", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_concurrency_tickets", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_isolation_level", 6165, "utf8mb3_general_ci", "", 16, 0, true, "")) + cols = append(cols, createCol(parser, "trx_unique_checks", 263, "utf8mb3_general_ci", "0", 1, 0, true, "")) + cols = append(cols, createCol(parser, "trx_foreign_key_checks", 263, "utf8mb3_general_ci", "0", 1, 0, true, "")) + cols = append(cols, createCol(parser, "trx_last_foreign_key_error", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "trx_adaptive_hash_latched", 263, "utf8mb3_general_ci", "0", 1, 0, true, "")) + cols = append(cols, createCol(parser, "trx_adaptive_hash_timeout", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "trx_is_read_only", 263, "utf8mb3_general_ci", "0", 1, 0, true, "")) + cols = append(cols, createCol(parser, "trx_autocommit_non_locking", 263, "utf8mb3_general_ci", "0", 1, 0, true, "")) infSchema["INNODB_TRX"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 265)) - cols = append(cols, createCol("POSITION_IN_UNIQUE_CONSTRAINT", 265)) - cols = append(cols, createCol("REFERENCED_TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("REFERENCED_TABLE_NAME", 6165)) - cols = append(cols, createCol("REFERENCED_COLUMN_NAME", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 265, "utf8mb3_general_ci", "0", 10, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION_IN_UNIQUE_CONSTRAINT", 265, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) infSchema["KEY_COLUMN_USAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("QUERY", 6163)) - cols = append(cols, createCol("TRACE", 6163)) - cols = append(cols, createCol("MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 263)) - cols = append(cols, createCol("INSUFFICIENT_PRIVILEGES", 257)) + cols = append(cols, createCol(parser, "QUERY", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TRACE", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 263, "utf8mb3_general_ci", "0", 20, 0, true, "")) + cols = append(cols, createCol(parser, "INSUFFICIENT_PRIVILEGES", 257, "utf8mb3_general_ci", "0", 1, 0, true, "")) infSchema["OPTIMIZER_TRACE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPECIFIC_CATALOG", 6165)) - cols = append(cols, createCol("SPECIFIC_SCHEMA", 6165)) - cols = append(cols, createCol("SPECIFIC_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 263)) - cols = append(cols, createCol("PARAMETER_MODE", 6165)) - cols = append(cols, createCol("PARAMETER_NAME", 6165)) - cols = append(cols, createCol("DATA_TYPE", 6165)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 263)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 263)) - cols = append(cols, createCol("NUMERIC_PRECISION", 265)) - cols = append(cols, createCol("NUMERIC_SCALE", 263)) - cols = append(cols, createCol("DATETIME_PRECISION", 265)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("DTD_IDENTIFIER", 6163)) - cols = append(cols, createCol("ROUTINE_TYPE", 6165)) + cols = append(cols, createCol(parser, "SPECIFIC_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "SPECIFIC_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 263, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "PARAMETER_MODE", 6165, "utf8mb3_general_ci", "", 5, 0, false, "")) + cols = append(cols, createCol(parser, "PARAMETER_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DTD_IDENTIFIER", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_TYPE", 6165, "utf8mb3_general_ci", "", 9, 0, true, "")) infSchema["PARAMETERS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("PARTITION_NAME", 6165)) - cols = append(cols, createCol("SUBPARTITION_NAME", 6165)) - cols = append(cols, createCol("PARTITION_ORDINAL_POSITION", 265)) - cols = append(cols, createCol("SUBPARTITION_ORDINAL_POSITION", 265)) - cols = append(cols, createCol("PARTITION_METHOD", 6165)) - cols = append(cols, createCol("SUBPARTITION_METHOD", 6165)) - cols = append(cols, createCol("PARTITION_EXPRESSION", 6163)) - cols = append(cols, createCol("SUBPARTITION_EXPRESSION", 6163)) - cols = append(cols, createCol("PARTITION_DESCRIPTION", 6163)) - cols = append(cols, createCol("TABLE_ROWS", 265)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 265)) - cols = append(cols, createCol("DATA_LENGTH", 265)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 265)) - cols = append(cols, createCol("INDEX_LENGTH", 265)) - cols = append(cols, createCol("DATA_FREE", 265)) - cols = append(cols, createCol("CREATE_TIME", 2064)) - cols = append(cols, createCol("UPDATE_TIME", 2064)) - cols = append(cols, createCol("CHECK_TIME", 2064)) - cols = append(cols, createCol("CHECKSUM", 265)) - cols = append(cols, createCol("PARTITION_COMMENT", 6165)) - cols = append(cols, createCol("NODEGROUP", 6165)) - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PARTITION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_ORDINAL_POSITION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_ORDINAL_POSITION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_METHOD", 6165, "utf8mb3_general_ci", "", 18, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_METHOD", 6165, "utf8mb3_general_ci", "", 12, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_EXPRESSION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_EXPRESSION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_DESCRIPTION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_COMMENT", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "NODEGROUP", 6165, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) infSchema["PARTITIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("PLUGIN_NAME", 6165)) - cols = append(cols, createCol("PLUGIN_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_STATUS", 6165)) - cols = append(cols, createCol("PLUGIN_TYPE", 6165)) - cols = append(cols, createCol("PLUGIN_TYPE_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_LIBRARY", 6165)) - cols = append(cols, createCol("PLUGIN_LIBRARY_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_AUTHOR", 6165)) - cols = append(cols, createCol("PLUGIN_DESCRIPTION", 6163)) - cols = append(cols, createCol("PLUGIN_LICENSE", 6165)) - cols = append(cols, createCol("LOAD_OPTION", 6165)) + cols = append(cols, createCol(parser, "PLUGIN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_STATUS", 6165, "utf8mb3_general_ci", "", 10, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_TYPE", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_TYPE_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_LIBRARY", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_LIBRARY_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_AUTHOR", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_DESCRIPTION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_LICENSE", 6165, "utf8mb3_general_ci", "", 80, 0, false, "")) + cols = append(cols, createCol(parser, "LOAD_OPTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["PLUGINS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 265)) - cols = append(cols, createCol("USER", 6165)) - cols = append(cols, createCol("HOST", 6165)) - cols = append(cols, createCol("DB", 6165)) - cols = append(cols, createCol("COMMAND", 6165)) - cols = append(cols, createCol("TIME", 263)) - cols = append(cols, createCol("STATE", 6165)) - cols = append(cols, createCol("INFO", 6163)) + cols = append(cols, createCol(parser, "ID", 265, "utf8mb3_general_ci", "0", 21, 0, true, "")) + cols = append(cols, createCol(parser, "USER", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "HOST", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DB", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COMMAND", 6165, "utf8mb3_general_ci", "", 16, 0, true, "")) + cols = append(cols, createCol(parser, "TIME", 263, "utf8mb3_general_ci", "0", 7, 0, true, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "INFO", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["PROCESSLIST"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("QUERY_ID", 263)) - cols = append(cols, createCol("SEQ", 263)) - cols = append(cols, createCol("STATE", 6165)) - cols = append(cols, createCol("DURATION", 18)) - cols = append(cols, createCol("CPU_USER", 18)) - cols = append(cols, createCol("CPU_SYSTEM", 18)) - cols = append(cols, createCol("CONTEXT_VOLUNTARY", 263)) - cols = append(cols, createCol("CONTEXT_INVOLUNTARY", 263)) - cols = append(cols, createCol("BLOCK_OPS_IN", 263)) - cols = append(cols, createCol("BLOCK_OPS_OUT", 263)) - cols = append(cols, createCol("MESSAGES_SENT", 263)) - cols = append(cols, createCol("MESSAGES_RECEIVED", 263)) - cols = append(cols, createCol("PAGE_FAULTS_MAJOR", 263)) - cols = append(cols, createCol("PAGE_FAULTS_MINOR", 263)) - cols = append(cols, createCol("SWAPS", 263)) - cols = append(cols, createCol("SOURCE_FUNCTION", 6165)) - cols = append(cols, createCol("SOURCE_FILE", 6165)) - cols = append(cols, createCol("SOURCE_LINE", 263)) + cols = append(cols, createCol(parser, "QUERY_ID", 263, "utf8mb3_general_ci", "0", 20, 0, true, "")) + cols = append(cols, createCol(parser, "SEQ", 263, "utf8mb3_general_ci", "0", 20, 0, true, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 30, 0, true, "")) + cols = append(cols, createCol(parser, "DURATION", 18, "utf8mb3_general_ci", "0.000000", 9, 6, true, "")) + cols = append(cols, createCol(parser, "CPU_USER", 18, "utf8mb3_general_ci", "", 9, 6, false, "")) + cols = append(cols, createCol(parser, "CPU_SYSTEM", 18, "utf8mb3_general_ci", "", 9, 6, false, "")) + cols = append(cols, createCol(parser, "CONTEXT_VOLUNTARY", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "CONTEXT_INVOLUNTARY", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "BLOCK_OPS_IN", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "BLOCK_OPS_OUT", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "MESSAGES_SENT", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "MESSAGES_RECEIVED", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "PAGE_FAULTS_MAJOR", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "PAGE_FAULTS_MINOR", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "SWAPS", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_FUNCTION", 6165, "utf8mb3_general_ci", "", 30, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_FILE", 6165, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_LINE", 263, "utf8mb3_general_ci", "", 20, 0, false, "")) infSchema["PROFILING"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("MATCH_OPTION", 6165)) - cols = append(cols, createCol("UPDATE_RULE", 6165)) - cols = append(cols, createCol("DELETE_RULE", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("REFERENCED_TABLE_NAME", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "MATCH_OPTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "UPDATE_RULE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DELETE_RULE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["REFERENTIAL_CONSTRAINTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPECIFIC_NAME", 6165)) - cols = append(cols, createCol("ROUTINE_CATALOG", 6165)) - cols = append(cols, createCol("ROUTINE_SCHEMA", 6165)) - cols = append(cols, createCol("ROUTINE_NAME", 6165)) - cols = append(cols, createCol("ROUTINE_TYPE", 6165)) - cols = append(cols, createCol("DATA_TYPE", 6165)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 263)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 263)) - cols = append(cols, createCol("NUMERIC_PRECISION", 265)) - cols = append(cols, createCol("NUMERIC_SCALE", 263)) - cols = append(cols, createCol("DATETIME_PRECISION", 265)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("DTD_IDENTIFIER", 6163)) - cols = append(cols, createCol("ROUTINE_BODY", 6165)) - cols = append(cols, createCol("ROUTINE_DEFINITION", 6163)) - cols = append(cols, createCol("EXTERNAL_NAME", 6165)) - cols = append(cols, createCol("EXTERNAL_LANGUAGE", 6165)) - cols = append(cols, createCol("PARAMETER_STYLE", 6165)) - cols = append(cols, createCol("IS_DETERMINISTIC", 6165)) - cols = append(cols, createCol("SQL_DATA_ACCESS", 6165)) - cols = append(cols, createCol("SQL_PATH", 6165)) - cols = append(cols, createCol("SECURITY_TYPE", 6165)) - cols = append(cols, createCol("CREATED", 2064)) - cols = append(cols, createCol("LAST_ALTERED", 2064)) - cols = append(cols, createCol("SQL_MODE", 6165)) - cols = append(cols, createCol("ROUTINE_COMMENT", 6163)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_TYPE", 6165, "utf8mb3_general_ci", "", 9, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 263, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DTD_IDENTIFIER", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ROUTINE_BODY", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EXTERNAL_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EXTERNAL_LANGUAGE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PARAMETER_STYLE", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DETERMINISTIC", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_DATA_ACCESS", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_PATH", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SECURITY_TYPE", 6165, "utf8mb3_general_ci", "", 7, 0, true, "")) + cols = append(cols, createCol(parser, "CREATED", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_ALTERED", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_MODE", 6165, "utf8mb3_general_ci", "", 8192, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_COMMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 93, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) infSchema["ROUTINES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 81, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["SCHEMA_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CATALOG_NAME", 6165)) - cols = append(cols, createCol("SCHEMA_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_COLLATION_NAME", 6165)) - cols = append(cols, createCol("SQL_PATH", 6165)) + cols = append(cols, createCol(parser, "CATALOG_NAME", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "SCHEMA_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_PATH", 6165, "utf8mb3_general_ci", "", 512, 0, false, "")) infSchema["SCHEMATA"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("VARIABLE_NAME", 6165)) - cols = append(cols, createCol("VARIABLE_VALUE", 6165)) - infSchema["SESSION_STATUS"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("VARIABLE_NAME", 6165)) - cols = append(cols, createCol("VARIABLE_VALUE", 6165)) - infSchema["SESSION_VARIABLES"] = cols - cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("NON_UNIQUE", 265)) - cols = append(cols, createCol("INDEX_SCHEMA", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("SEQ_IN_INDEX", 265)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("COLLATION", 6165)) - cols = append(cols, createCol("CARDINALITY", 265)) - cols = append(cols, createCol("SUB_PART", 265)) - cols = append(cols, createCol("PACKED", 6165)) - cols = append(cols, createCol("NULLABLE", 6165)) - cols = append(cols, createCol("INDEX_TYPE", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) - cols = append(cols, createCol("INDEX_COMMENT", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "NON_UNIQUE", 265, "utf8mb3_general_ci", "0", 1, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SEQ_IN_INDEX", 265, "utf8mb3_general_ci", "0", 2, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION", 6165, "utf8mb3_general_ci", "", 1, 0, false, "")) + cols = append(cols, createCol(parser, "CARDINALITY", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "SUB_PART", 265, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "PACKED", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "NULLABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_TYPE", 6165, "utf8mb3_general_ci", "", 16, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 16, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_COMMENT", 6165, "utf8mb3_general_ci", "", 1024, 0, true, "")) infSchema["STATISTICS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("CONSTRAINT_TYPE", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["TABLE_CONSTRAINTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 81, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["TABLE_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("TABLE_TYPE", 6165)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("VERSION", 265)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("TABLE_ROWS", 265)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 265)) - cols = append(cols, createCol("DATA_LENGTH", 265)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 265)) - cols = append(cols, createCol("INDEX_LENGTH", 265)) - cols = append(cols, createCol("DATA_FREE", 265)) - cols = append(cols, createCol("AUTO_INCREMENT", 265)) - cols = append(cols, createCol("CREATE_TIME", 2064)) - cols = append(cols, createCol("UPDATE_TIME", 2064)) - cols = append(cols, createCol("CHECK_TIME", 2064)) - cols = append(cols, createCol("TABLE_COLLATION", 6165)) - cols = append(cols, createCol("CHECKSUM", 265)) - cols = append(cols, createCol("CREATE_OPTIONS", 6165)) - cols = append(cols, createCol("TABLE_COMMENT", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "VERSION", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AUTO_INCREMENT", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_COLLATION", 6165, "utf8mb3_general_ci", "", 32, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_OPTIONS", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_COMMENT", 6165, "utf8mb3_general_ci", "", 2048, 0, true, "")) infSchema["TABLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("TABLESPACE_TYPE", 6165)) - cols = append(cols, createCol("LOGFILE_GROUP_NAME", 6165)) - cols = append(cols, createCol("EXTENT_SIZE", 265)) - cols = append(cols, createCol("AUTOEXTEND_SIZE", 265)) - cols = append(cols, createCol("MAXIMUM_SIZE", 265)) - cols = append(cols, createCol("NODEGROUP_ID", 265)) - cols = append(cols, createCol("TABLESPACE_COMMENT", 6165)) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLESPACE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EXTENT_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "AUTOEXTEND_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "MAXIMUM_SIZE", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "NODEGROUP_ID", 265, "utf8mb3_general_ci", "", 21, 0, false, "")) + cols = append(cols, createCol(parser, "TABLESPACE_COMMENT", 6165, "utf8mb3_general_ci", "", 2048, 0, false, "")) infSchema["TABLESPACES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TRIGGER_CATALOG", 6165)) - cols = append(cols, createCol("TRIGGER_SCHEMA", 6165)) - cols = append(cols, createCol("TRIGGER_NAME", 6165)) - cols = append(cols, createCol("EVENT_MANIPULATION", 6165)) - cols = append(cols, createCol("EVENT_OBJECT_CATALOG", 6165)) - cols = append(cols, createCol("EVENT_OBJECT_SCHEMA", 6165)) - cols = append(cols, createCol("EVENT_OBJECT_TABLE", 6165)) - cols = append(cols, createCol("ACTION_ORDER", 265)) - cols = append(cols, createCol("ACTION_CONDITION", 6163)) - cols = append(cols, createCol("ACTION_STATEMENT", 6163)) - cols = append(cols, createCol("ACTION_ORIENTATION", 6165)) - cols = append(cols, createCol("ACTION_TIMING", 6165)) - cols = append(cols, createCol("ACTION_REFERENCE_OLD_TABLE", 6165)) - cols = append(cols, createCol("ACTION_REFERENCE_NEW_TABLE", 6165)) - cols = append(cols, createCol("ACTION_REFERENCE_OLD_ROW", 6165)) - cols = append(cols, createCol("ACTION_REFERENCE_NEW_ROW", 6165)) - cols = append(cols, createCol("CREATED", 2064)) - cols = append(cols, createCol("SQL_MODE", 6165)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "TRIGGER_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TRIGGER_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TRIGGER_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_MANIPULATION", 6165, "utf8mb3_general_ci", "", 6, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_TABLE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_ORDER", 265, "utf8mb3_general_ci", "0", 4, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_CONDITION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_STATEMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_ORIENTATION", 6165, "utf8mb3_general_ci", "", 9, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_TIMING", 6165, "utf8mb3_general_ci", "", 6, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_OLD_TABLE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_NEW_TABLE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_OLD_ROW", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_NEW_ROW", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "CREATED", 2064, "utf8mb3_general_ci", "", 2, 0, false, "")) + cols = append(cols, createCol(parser, "SQL_MODE", 6165, "utf8mb3_general_ci", "", 8192, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 93, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) infSchema["TRIGGERS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 81, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["USER_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("VIEW_DEFINITION", 6163)) - cols = append(cols, createCol("CHECK_OPTION", 6165)) - cols = append(cols, createCol("IS_UPDATABLE", 6165)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("SECURITY_TYPE", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "VIEW_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "CHECK_OPTION", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "IS_UPDATABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 93, 0, true, "")) + cols = append(cols, createCol(parser, "SECURITY_TYPE", 6165, "utf8mb3_general_ci", "", 7, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) infSchema["VIEWS"] = cols - return infSchema } // getInfoSchema80 returns a map of all information_schema tables and their columns with types // To recreate this information from MySQL, you can run the test in info_schema_gen_test.go func getInfoSchema80() map[string][]vindexes.Column { + parser, err := sqlparser.New(sqlparser.Options{MySQLServerVersion: "8.0.30"}) + if err != nil { + panic(err) + } infSchema := map[string][]vindexes.Column{} var cols []vindexes.Column - cols = append(cols, createCol("USER", 6165)) - cols = append(cols, createCol("HOST", 6165)) - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("GRANTEE_HOST", 6165)) - cols = append(cols, createCol("ROLE_NAME", 6165)) - cols = append(cols, createCol("ROLE_HOST", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) - cols = append(cols, createCol("IS_DEFAULT", 6165)) - cols = append(cols, createCol("IS_MANDATORY", 6165)) + cols = append(cols, createCol(parser, "USER", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "ROLE_NAME", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "ROLE_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DEFAULT", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "IS_MANDATORY", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["ADMINISTRABLE_ROLE_AUTHORIZATIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("USER", 6165)) - cols = append(cols, createCol("HOST", 6165)) - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("GRANTEE_HOST", 6165)) - cols = append(cols, createCol("ROLE_NAME", 6165)) - cols = append(cols, createCol("ROLE_HOST", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) - cols = append(cols, createCol("IS_DEFAULT", 6165)) - cols = append(cols, createCol("IS_MANDATORY", 6165)) + cols = append(cols, createCol(parser, "USER", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "ROLE_NAME", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "ROLE_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DEFAULT", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "IS_MANDATORY", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["APPLICABLE_ROLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_COLLATE_NAME", 6165)) - cols = append(cols, createCol("DESCRIPTION", 6165)) - cols = append(cols, createCol("MAXLEN", 776)) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_COLLATE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DESCRIPTION", 6165, "utf8mb3_general_ci", "", 2048, 0, true, "")) + cols = append(cols, createCol(parser, "MAXLEN", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["CHARACTER_SETS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("CHECK_CLAUSE", 6163)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHECK_CLAUSE", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["CHECK_CONSTRAINTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["COLLATION_CHARACTER_SET_APPLICABILITY"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("ID", 778)) - cols = append(cols, createCol("IS_DEFAULT", 6165)) - cols = append(cols, createCol("IS_COMPILED", 6165)) - cols = append(cols, createCol("SORTLEN", 776)) - cols = append(cols, createCol("PAD_ATTRIBUTE", 2074)) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ID", 778, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DEFAULT", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "IS_COMPILED", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "SORTLEN", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAD_ATTRIBUTE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'PAD SPACE','NO PAD'")) infSchema["COLLATIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 292, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["COLUMN_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SCHEMA_NAME", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("HISTOGRAM", 2078)) + cols = append(cols, createCol(parser, "SCHEMA_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "HISTOGRAM", 2078, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["COLUMN_STATISTICS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 776)) - cols = append(cols, createCol("COLUMN_DEFAULT", 6163)) - cols = append(cols, createCol("IS_NULLABLE", 6165)) - cols = append(cols, createCol("DATA_TYPE", 6163)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 265)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 265)) - cols = append(cols, createCol("NUMERIC_PRECISION", 778)) - cols = append(cols, createCol("NUMERIC_SCALE", 778)) - cols = append(cols, createCol("DATETIME_PRECISION", 776)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("COLUMN_TYPE", 6163)) - cols = append(cols, createCol("COLUMN_KEY", 2074)) - cols = append(cols, createCol("EXTRA", 6165)) - cols = append(cols, createCol("PRIVILEGES", 6165)) - cols = append(cols, createCol("COLUMN_COMMENT", 6163)) - cols = append(cols, createCol("GENERATION_EXPRESSION", 6163)) - cols = append(cols, createCol("SRS_ID", 776)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_DEFAULT", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "IS_NULLABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_TYPE", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_KEY", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'','PRI','UNI','MUL'")) + cols = append(cols, createCol(parser, "EXTRA", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "PRIVILEGES", 6165, "utf8mb3_general_ci", "", 154, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_COMMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "GENERATION_EXPRESSION", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SRS_ID", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["COLUMNS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("ENGINE_ATTRIBUTE", 2078)) - cols = append(cols, createCol("SECONDARY_ENGINE_ATTRIBUTE", 2078)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SECONDARY_ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["COLUMNS_EXTENSIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ROLE_NAME", 6165)) - cols = append(cols, createCol("ROLE_HOST", 6165)) - cols = append(cols, createCol("IS_DEFAULT", 6165)) - cols = append(cols, createCol("IS_MANDATORY", 6165)) + cols = append(cols, createCol(parser, "ROLE_NAME", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "ROLE_HOST", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "IS_DEFAULT", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "IS_MANDATORY", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["ENABLED_ROLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("SUPPORT", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) - cols = append(cols, createCol("TRANSACTIONS", 6165)) - cols = append(cols, createCol("XA", 6165)) - cols = append(cols, createCol("SAVEPOINTS", 6165)) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SUPPORT", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "TRANSACTIONS", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "XA", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "SAVEPOINTS", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) infSchema["ENGINES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("EVENT_CATALOG", 6165)) - cols = append(cols, createCol("EVENT_SCHEMA", 6165)) - cols = append(cols, createCol("EVENT_NAME", 6165)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("TIME_ZONE", 6165)) - cols = append(cols, createCol("EVENT_BODY", 6165)) - cols = append(cols, createCol("EVENT_DEFINITION", 6163)) - cols = append(cols, createCol("EVENT_TYPE", 6165)) - cols = append(cols, createCol("EXECUTE_AT", 2064)) - cols = append(cols, createCol("INTERVAL_VALUE", 6165)) - cols = append(cols, createCol("INTERVAL_FIELD", 2074)) - cols = append(cols, createCol("SQL_MODE", 2075)) - cols = append(cols, createCol("STARTS", 2064)) - cols = append(cols, createCol("ENDS", 2064)) - cols = append(cols, createCol("STATUS", 2074)) - cols = append(cols, createCol("ON_COMPLETION", 6165)) - cols = append(cols, createCol("CREATED", 2061)) - cols = append(cols, createCol("LAST_ALTERED", 2061)) - cols = append(cols, createCol("LAST_EXECUTED", 2064)) - cols = append(cols, createCol("EVENT_COMMENT", 6165)) - cols = append(cols, createCol("ORIGINATOR", 776)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "EVENT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 288, 0, true, "")) + cols = append(cols, createCol(parser, "TIME_ZONE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_BODY", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_TYPE", 6165, "utf8mb3_general_ci", "", 9, 0, true, "")) + cols = append(cols, createCol(parser, "EXECUTE_AT", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INTERVAL_VALUE", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "INTERVAL_FIELD", 2074, "utf8mb3_general_ci", "", 0, 0, false, "'YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND'")) + cols = append(cols, createCol(parser, "SQL_MODE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL'")) + cols = append(cols, createCol(parser, "STARTS", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ENDS", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'ENABLED','DISABLED','SLAVESIDE_DISABLED'")) + cols = append(cols, createCol(parser, "ON_COMPLETION", 6165, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "CREATED", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_ALTERED", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_EXECUTED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_COMMENT", 6165, "utf8mb3_general_ci", "", 2048, 0, true, "")) + cols = append(cols, createCol(parser, "ORIGINATOR", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["EVENTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("FILE_ID", 265)) - cols = append(cols, createCol("FILE_NAME", 6163)) - cols = append(cols, createCol("FILE_TYPE", 6165)) - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6167)) - cols = append(cols, createCol("TABLE_SCHEMA", 10264)) - cols = append(cols, createCol("TABLE_NAME", 10264)) - cols = append(cols, createCol("LOGFILE_GROUP_NAME", 6165)) - cols = append(cols, createCol("LOGFILE_GROUP_NUMBER", 265)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("FULLTEXT_KEYS", 10264)) - cols = append(cols, createCol("DELETED_ROWS", 10264)) - cols = append(cols, createCol("UPDATE_COUNT", 10264)) - cols = append(cols, createCol("FREE_EXTENTS", 265)) - cols = append(cols, createCol("TOTAL_EXTENTS", 265)) - cols = append(cols, createCol("EXTENT_SIZE", 265)) - cols = append(cols, createCol("INITIAL_SIZE", 265)) - cols = append(cols, createCol("MAXIMUM_SIZE", 265)) - cols = append(cols, createCol("AUTOEXTEND_SIZE", 265)) - cols = append(cols, createCol("CREATION_TIME", 10264)) - cols = append(cols, createCol("LAST_UPDATE_TIME", 10264)) - cols = append(cols, createCol("LAST_ACCESS_TIME", 10264)) - cols = append(cols, createCol("RECOVER_TIME", 10264)) - cols = append(cols, createCol("TRANSACTION_COUNTER", 10264)) - cols = append(cols, createCol("VERSION", 265)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("TABLE_ROWS", 10264)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 10264)) - cols = append(cols, createCol("DATA_LENGTH", 10264)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 10264)) - cols = append(cols, createCol("INDEX_LENGTH", 10264)) - cols = append(cols, createCol("DATA_FREE", 265)) - cols = append(cols, createCol("CREATE_TIME", 10264)) - cols = append(cols, createCol("UPDATE_TIME", 10264)) - cols = append(cols, createCol("CHECK_TIME", 10264)) - cols = append(cols, createCol("CHECKSUM", 10264)) - cols = append(cols, createCol("STATUS", 6165)) - cols = append(cols, createCol("EXTRA", 6165)) + cols = append(cols, createCol(parser, "FILE_ID", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "FILE_NAME", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "FILE_TYPE", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 268, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6167, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NAME", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NUMBER", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "FULLTEXT_KEYS", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DELETED_ROWS", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_COUNT", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_EXTENTS", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TOTAL_EXTENTS", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EXTENT_SIZE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INITIAL_SIZE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MAXIMUM_SIZE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AUTOEXTEND_SIZE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CREATION_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "LAST_UPDATE_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "LAST_ACCESS_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "RECOVER_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TRANSACTION_COUNTER", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "VERSION", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "EXTRA", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) infSchema["FILES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 778)) - cols = append(cols, createCol("BLOCK_ID", 778)) - cols = append(cols, createCol("SPACE", 778)) - cols = append(cols, createCol("PAGE_NUMBER", 778)) - cols = append(cols, createCol("PAGE_TYPE", 6165)) - cols = append(cols, createCol("FLUSH_TYPE", 778)) - cols = append(cols, createCol("FIX_COUNT", 778)) - cols = append(cols, createCol("IS_HASHED", 6165)) - cols = append(cols, createCol("NEWEST_MODIFICATION", 778)) - cols = append(cols, createCol("OLDEST_MODIFICATION", 778)) - cols = append(cols, createCol("ACCESS_TIME", 778)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("NUMBER_RECORDS", 778)) - cols = append(cols, createCol("DATA_SIZE", 778)) - cols = append(cols, createCol("COMPRESSED_SIZE", 778)) - cols = append(cols, createCol("PAGE_STATE", 6165)) - cols = append(cols, createCol("IO_FIX", 6165)) - cols = append(cols, createCol("IS_OLD", 6165)) - cols = append(cols, createCol("FREE_PAGE_CLOCK", 778)) - cols = append(cols, createCol("IS_STALE", 6165)) + cols = append(cols, createCol(parser, "POOL_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "BLOCK_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_NUMBER", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "FLUSH_TYPE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "FIX_COUNT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "IS_HASHED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "NEWEST_MODIFICATION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "OLDEST_MODIFICATION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ACCESS_TIME", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "NUMBER_RECORDS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_STATE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IO_FIX", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IS_OLD", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_PAGE_CLOCK", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "IS_STALE", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) infSchema["INNODB_BUFFER_PAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 778)) - cols = append(cols, createCol("LRU_POSITION", 778)) - cols = append(cols, createCol("SPACE", 778)) - cols = append(cols, createCol("PAGE_NUMBER", 778)) - cols = append(cols, createCol("PAGE_TYPE", 6165)) - cols = append(cols, createCol("FLUSH_TYPE", 778)) - cols = append(cols, createCol("FIX_COUNT", 778)) - cols = append(cols, createCol("IS_HASHED", 6165)) - cols = append(cols, createCol("NEWEST_MODIFICATION", 778)) - cols = append(cols, createCol("OLDEST_MODIFICATION", 778)) - cols = append(cols, createCol("ACCESS_TIME", 778)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("NUMBER_RECORDS", 778)) - cols = append(cols, createCol("DATA_SIZE", 778)) - cols = append(cols, createCol("COMPRESSED_SIZE", 778)) - cols = append(cols, createCol("COMPRESSED", 6165)) - cols = append(cols, createCol("IO_FIX", 6165)) - cols = append(cols, createCol("IS_OLD", 6165)) - cols = append(cols, createCol("FREE_PAGE_CLOCK", 778)) + cols = append(cols, createCol(parser, "POOL_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_POSITION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_NUMBER", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "FLUSH_TYPE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "FIX_COUNT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "IS_HASHED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "NEWEST_MODIFICATION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "OLDEST_MODIFICATION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ACCESS_TIME", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "NUMBER_RECORDS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DATA_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COMPRESSED", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "IO_FIX", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "IS_OLD", 6165, "utf8mb3_general_ci", "", 3, 0, false, "")) + cols = append(cols, createCol(parser, "FREE_PAGE_CLOCK", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_BUFFER_PAGE_LRU"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("POOL_ID", 778)) - cols = append(cols, createCol("POOL_SIZE", 778)) - cols = append(cols, createCol("FREE_BUFFERS", 778)) - cols = append(cols, createCol("DATABASE_PAGES", 778)) - cols = append(cols, createCol("OLD_DATABASE_PAGES", 778)) - cols = append(cols, createCol("MODIFIED_DATABASE_PAGES", 778)) - cols = append(cols, createCol("PENDING_DECOMPRESS", 778)) - cols = append(cols, createCol("PENDING_READS", 778)) - cols = append(cols, createCol("PENDING_FLUSH_LRU", 778)) - cols = append(cols, createCol("PENDING_FLUSH_LIST", 778)) - cols = append(cols, createCol("PAGES_MADE_YOUNG", 778)) - cols = append(cols, createCol("PAGES_NOT_MADE_YOUNG", 778)) - cols = append(cols, createCol("PAGES_MADE_YOUNG_RATE", 1035)) - cols = append(cols, createCol("PAGES_MADE_NOT_YOUNG_RATE", 1035)) - cols = append(cols, createCol("NUMBER_PAGES_READ", 778)) - cols = append(cols, createCol("NUMBER_PAGES_CREATED", 778)) - cols = append(cols, createCol("NUMBER_PAGES_WRITTEN", 778)) - cols = append(cols, createCol("PAGES_READ_RATE", 1035)) - cols = append(cols, createCol("PAGES_CREATE_RATE", 1035)) - cols = append(cols, createCol("PAGES_WRITTEN_RATE", 1035)) - cols = append(cols, createCol("NUMBER_PAGES_GET", 778)) - cols = append(cols, createCol("HIT_RATE", 778)) - cols = append(cols, createCol("YOUNG_MAKE_PER_THOUSAND_GETS", 778)) - cols = append(cols, createCol("NOT_YOUNG_MAKE_PER_THOUSAND_GETS", 778)) - cols = append(cols, createCol("NUMBER_PAGES_READ_AHEAD", 778)) - cols = append(cols, createCol("NUMBER_READ_AHEAD_EVICTED", 778)) - cols = append(cols, createCol("READ_AHEAD_RATE", 1035)) - cols = append(cols, createCol("READ_AHEAD_EVICTED_RATE", 1035)) - cols = append(cols, createCol("LRU_IO_TOTAL", 778)) - cols = append(cols, createCol("LRU_IO_CURRENT", 778)) - cols = append(cols, createCol("UNCOMPRESS_TOTAL", 778)) - cols = append(cols, createCol("UNCOMPRESS_CURRENT", 778)) + cols = append(cols, createCol(parser, "POOL_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "POOL_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "FREE_BUFFERS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_PAGES", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "OLD_DATABASE_PAGES", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MODIFIED_DATABASE_PAGES", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_DECOMPRESS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_READS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_FLUSH_LRU", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PENDING_FLUSH_LIST", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_YOUNG", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_NOT_MADE_YOUNG", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_YOUNG_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_MADE_NOT_YOUNG_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_READ", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_CREATED", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_WRITTEN", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_READ_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_CREATE_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "PAGES_WRITTEN_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_GET", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "HIT_RATE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "YOUNG_MAKE_PER_THOUSAND_GETS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NOT_YOUNG_MAKE_PER_THOUSAND_GETS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_PAGES_READ_AHEAD", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NUMBER_READ_AHEAD_EVICTED", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "READ_AHEAD_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "READ_AHEAD_EVICTED_RATE", 1035, "utf8mb3_general_ci", "", 12, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_IO_TOTAL", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LRU_IO_CURRENT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "UNCOMPRESS_TOTAL", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "UNCOMPRESS_CURRENT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_BUFFER_POOL_STATS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE_ID", 776)) - cols = append(cols, createCol("INDEX_ID", 778)) - cols = append(cols, createCol("N_CACHED_PAGES", 778)) + cols = append(cols, createCol(parser, "SPACE_ID", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "N_CACHED_PAGES", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CACHED_INDEXES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMP"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("database_name", 6165)) - cols = append(cols, createCol("table_name", 6165)) - cols = append(cols, createCol("index_name", 6165)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "database_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "table_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "index_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMP_PER_INDEX"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("database_name", 6165)) - cols = append(cols, createCol("table_name", 6165)) - cols = append(cols, createCol("index_name", 6165)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "database_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "table_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "index_name", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMP_PER_INDEX_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("compress_ops", 263)) - cols = append(cols, createCol("compress_ops_ok", 263)) - cols = append(cols, createCol("compress_time", 263)) - cols = append(cols, createCol("uncompress_ops", 263)) - cols = append(cols, createCol("uncompress_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_ops_ok", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "compress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_ops", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "uncompress_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMP_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("buffer_pool_instance", 263)) - cols = append(cols, createCol("pages_used", 263)) - cols = append(cols, createCol("pages_free", 263)) - cols = append(cols, createCol("relocation_ops", 265)) - cols = append(cols, createCol("relocation_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "buffer_pool_instance", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "pages_used", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "pages_free", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_ops", 265, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMPMEM"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("page_size", 263)) - cols = append(cols, createCol("buffer_pool_instance", 263)) - cols = append(cols, createCol("pages_used", 263)) - cols = append(cols, createCol("pages_free", 263)) - cols = append(cols, createCol("relocation_ops", 265)) - cols = append(cols, createCol("relocation_time", 263)) + cols = append(cols, createCol(parser, "page_size", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "buffer_pool_instance", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "pages_used", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "pages_free", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_ops", 265, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "relocation_time", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_CMPMEM_RESET"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("POS", 778)) - cols = append(cols, createCol("MTYPE", 263)) - cols = append(cols, createCol("PRTYPE", 263)) - cols = append(cols, createCol("LEN", 263)) - cols = append(cols, createCol("HAS_DEFAULT", 263)) - cols = append(cols, createCol("DEFAULT_VALUE", 6163)) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "POS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MTYPE", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PRTYPE", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LEN", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "HAS_DEFAULT", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_VALUE", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["INNODB_COLUMNS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE", 10262)) - cols = append(cols, createCol("PATH", 6165)) + cols = append(cols, createCol(parser, "SPACE", 10262, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "PATH", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) infSchema["INNODB_DATAFILES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("INDEX_ID", 10262)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("POS", 778)) + cols = append(cols, createCol(parser, "INDEX_ID", 10262, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "POS", 778, "utf8mb3_general_ci", "0", 0, 0, true, "")) infSchema["INNODB_FIELDS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 6165)) - cols = append(cols, createCol("FOR_NAME", 6165)) - cols = append(cols, createCol("REF_NAME", 6165)) - cols = append(cols, createCol("N_COLS", 265)) - cols = append(cols, createCol("TYPE", 778)) + cols = append(cols, createCol(parser, "ID", 6165, "utf8mb3_general_ci", "", 129, 0, false, "")) + cols = append(cols, createCol(parser, "FOR_NAME", 6165, "utf8mb3_general_ci", "", 129, 0, false, "")) + cols = append(cols, createCol(parser, "REF_NAME", 6165, "utf8mb3_general_ci", "", 129, 0, false, "")) + cols = append(cols, createCol(parser, "N_COLS", 265, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TYPE", 778, "utf8mb3_general_ci", "0", 0, 0, true, "")) infSchema["INNODB_FOREIGN"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 6165)) - cols = append(cols, createCol("FOR_COL_NAME", 6165)) - cols = append(cols, createCol("REF_COL_NAME", 6165)) - cols = append(cols, createCol("POS", 776)) + cols = append(cols, createCol(parser, "ID", 6165, "utf8mb3_general_ci", "", 129, 0, false, "")) + cols = append(cols, createCol(parser, "FOR_COL_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "REF_COL_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "POS", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_FOREIGN_COLS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("DOC_ID", 778)) + cols = append(cols, createCol(parser, "DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_FT_BEING_DELETED"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("KEY", 6165)) - cols = append(cols, createCol("VALUE", 6165)) + cols = append(cols, createCol(parser, "KEY", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "VALUE", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) infSchema["INNODB_FT_CONFIG"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("value", 6165)) + cols = append(cols, createCol(parser, "value", 6165, "utf8mb3_general_ci", "", 18, 0, true, "")) infSchema["INNODB_FT_DEFAULT_STOPWORD"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("DOC_ID", 778)) + cols = append(cols, createCol(parser, "DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_FT_DELETED"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("WORD", 6165)) - cols = append(cols, createCol("FIRST_DOC_ID", 778)) - cols = append(cols, createCol("LAST_DOC_ID", 778)) - cols = append(cols, createCol("DOC_COUNT", 778)) - cols = append(cols, createCol("DOC_ID", 778)) - cols = append(cols, createCol("POSITION", 778)) + cols = append(cols, createCol(parser, "WORD", 6165, "utf8mb3_general_ci", "", 337, 0, true, "")) + cols = append(cols, createCol(parser, "FIRST_DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_COUNT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_FT_INDEX_CACHE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("WORD", 6165)) - cols = append(cols, createCol("FIRST_DOC_ID", 778)) - cols = append(cols, createCol("LAST_DOC_ID", 778)) - cols = append(cols, createCol("DOC_COUNT", 778)) - cols = append(cols, createCol("DOC_ID", 778)) - cols = append(cols, createCol("POSITION", 778)) + cols = append(cols, createCol(parser, "WORD", 6165, "utf8mb3_general_ci", "", 337, 0, true, "")) + cols = append(cols, createCol(parser, "FIRST_DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_COUNT", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DOC_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_FT_INDEX_TABLE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("INDEX_ID", 778)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("TYPE", 263)) - cols = append(cols, createCol("N_FIELDS", 263)) - cols = append(cols, createCol("PAGE_NO", 263)) - cols = append(cols, createCol("SPACE", 263)) - cols = append(cols, createCol("MERGE_THRESHOLD", 263)) + cols = append(cols, createCol(parser, "INDEX_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TYPE", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "N_FIELDS", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PAGE_NO", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MERGE_THRESHOLD", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_INDEXES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("SUBSYSTEM", 6165)) - cols = append(cols, createCol("COUNT", 265)) - cols = append(cols, createCol("MAX_COUNT", 265)) - cols = append(cols, createCol("MIN_COUNT", 265)) - cols = append(cols, createCol("AVG_COUNT", 1035)) - cols = append(cols, createCol("COUNT_RESET", 265)) - cols = append(cols, createCol("MAX_COUNT_RESET", 265)) - cols = append(cols, createCol("MIN_COUNT_RESET", 265)) - cols = append(cols, createCol("AVG_COUNT_RESET", 1035)) - cols = append(cols, createCol("TIME_ENABLED", 2064)) - cols = append(cols, createCol("TIME_DISABLED", 2064)) - cols = append(cols, createCol("TIME_ELAPSED", 265)) - cols = append(cols, createCol("TIME_RESET", 2064)) - cols = append(cols, createCol("STATUS", 6165)) - cols = append(cols, createCol("TYPE", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "SUBSYSTEM", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "COUNT", 265, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MAX_COUNT", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MIN_COUNT", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_COUNT", 1035, "utf8mb3_general_ci", "", 12, 0, false, "")) + cols = append(cols, createCol(parser, "COUNT_RESET", 265, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MAX_COUNT_RESET", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MIN_COUNT_RESET", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_COUNT_RESET", 1035, "utf8mb3_general_ci", "", 12, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_ENABLED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_DISABLED", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_ELAPSED", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TIME_RESET", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "STATUS", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "TYPE", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) infSchema["INNODB_METRICS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 776)) - cols = append(cols, createCol("SPACE", 776)) - cols = append(cols, createCol("PATH", 6165)) - cols = append(cols, createCol("SIZE", 778)) - cols = append(cols, createCol("STATE", 6165)) - cols = append(cols, createCol("PURPOSE", 6165)) + cols = append(cols, createCol(parser, "ID", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PATH", 6165, "utf8mb3_general_ci", "", 4001, 0, true, "")) + cols = append(cols, createCol(parser, "SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) + cols = append(cols, createCol(parser, "PURPOSE", 6165, "utf8mb3_general_ci", "", 192, 0, true, "")) infSchema["INNODB_SESSION_TEMP_TABLESPACES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("FLAG", 263)) - cols = append(cols, createCol("N_COLS", 263)) - cols = append(cols, createCol("SPACE", 265)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("ZIP_PAGE_SIZE", 776)) - cols = append(cols, createCol("SPACE_TYPE", 6165)) - cols = append(cols, createCol("INSTANT_COLS", 263)) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 655, 0, true, "")) + cols = append(cols, createCol(parser, "FLAG", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "N_COLS", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 265, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 6165, "utf8mb3_general_ci", "", 12, 0, false, "")) + cols = append(cols, createCol(parser, "ZIP_PAGE_SIZE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE_TYPE", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "INSTANT_COLS", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "TOTAL_ROW_VERSIONS", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_TABLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE", 776)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("FLAG", 776)) - cols = append(cols, createCol("ROW_FORMAT", 6165)) - cols = append(cols, createCol("PAGE_SIZE", 776)) - cols = append(cols, createCol("ZIP_PAGE_SIZE", 776)) - cols = append(cols, createCol("SPACE_TYPE", 6165)) - cols = append(cols, createCol("FS_BLOCK_SIZE", 776)) - cols = append(cols, createCol("FILE_SIZE", 778)) - cols = append(cols, createCol("ALLOCATED_SIZE", 778)) - cols = append(cols, createCol("AUTOEXTEND_SIZE", 778)) - cols = append(cols, createCol("SERVER_VERSION", 6165)) - cols = append(cols, createCol("SPACE_VERSION", 776)) - cols = append(cols, createCol("ENCRYPTION", 6165)) - cols = append(cols, createCol("STATE", 6165)) + cols = append(cols, createCol(parser, "SPACE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 655, 0, true, "")) + cols = append(cols, createCol(parser, "FLAG", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 6165, "utf8mb3_general_ci", "", 22, 0, false, "")) + cols = append(cols, createCol(parser, "PAGE_SIZE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ZIP_PAGE_SIZE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE_TYPE", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "FS_BLOCK_SIZE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "FILE_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ALLOCATED_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "AUTOEXTEND_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SERVER_VERSION", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) + cols = append(cols, createCol(parser, "SPACE_VERSION", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ENCRYPTION", 6165, "utf8mb3_general_ci", "", 1, 0, false, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 10, 0, false, "")) infSchema["INNODB_TABLESPACES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPACE", 10262)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("PATH", 6165)) - cols = append(cols, createCol("FLAG", 10262)) - cols = append(cols, createCol("SPACE_TYPE", 6165)) + cols = append(cols, createCol(parser, "SPACE", 10262, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 268, 0, true, "")) + cols = append(cols, createCol(parser, "PATH", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "FLAG", 10262, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "SPACE_TYPE", 6165, "utf8mb3_general_ci", "", 7, 0, true, "")) infSchema["INNODB_TABLESPACES_BRIEF"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("STATS_INITIALIZED", 6165)) - cols = append(cols, createCol("NUM_ROWS", 778)) - cols = append(cols, createCol("CLUST_INDEX_SIZE", 778)) - cols = append(cols, createCol("OTHER_INDEX_SIZE", 778)) - cols = append(cols, createCol("MODIFIED_COUNTER", 778)) - cols = append(cols, createCol("AUTOINC", 778)) - cols = append(cols, createCol("REF_COUNT", 263)) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "STATS_INITIALIZED", 6165, "utf8mb3_general_ci", "", 193, 0, true, "")) + cols = append(cols, createCol(parser, "NUM_ROWS", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "CLUST_INDEX_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "OTHER_INDEX_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "MODIFIED_COUNTER", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "AUTOINC", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "REF_COUNT", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_TABLESTATS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("NAME", 6165)) - cols = append(cols, createCol("N_COLS", 776)) - cols = append(cols, createCol("SPACE", 776)) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "N_COLS", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SPACE", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_TEMP_TABLE_INFO"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("trx_id", 778)) - cols = append(cols, createCol("trx_state", 6165)) - cols = append(cols, createCol("trx_started", 2064)) - cols = append(cols, createCol("trx_requested_lock_id", 6165)) - cols = append(cols, createCol("trx_wait_started", 2064)) - cols = append(cols, createCol("trx_weight", 778)) - cols = append(cols, createCol("trx_mysql_thread_id", 778)) - cols = append(cols, createCol("trx_query", 6165)) - cols = append(cols, createCol("trx_operation_state", 6165)) - cols = append(cols, createCol("trx_tables_in_use", 778)) - cols = append(cols, createCol("trx_tables_locked", 778)) - cols = append(cols, createCol("trx_lock_structs", 778)) - cols = append(cols, createCol("trx_lock_memory_bytes", 778)) - cols = append(cols, createCol("trx_rows_locked", 778)) - cols = append(cols, createCol("trx_rows_modified", 778)) - cols = append(cols, createCol("trx_concurrency_tickets", 778)) - cols = append(cols, createCol("trx_isolation_level", 6165)) - cols = append(cols, createCol("trx_unique_checks", 263)) - cols = append(cols, createCol("trx_foreign_key_checks", 263)) - cols = append(cols, createCol("trx_last_foreign_key_error", 6165)) - cols = append(cols, createCol("trx_adaptive_hash_latched", 263)) - cols = append(cols, createCol("trx_adaptive_hash_timeout", 778)) - cols = append(cols, createCol("trx_is_read_only", 263)) - cols = append(cols, createCol("trx_autocommit_non_locking", 263)) - cols = append(cols, createCol("trx_schedule_weight", 778)) + cols = append(cols, createCol(parser, "trx_id", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_state", 6165, "utf8mb3_general_ci", "", 13, 0, true, "")) + cols = append(cols, createCol(parser, "trx_started", 2064, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_requested_lock_id", 6165, "utf8mb3_general_ci", "", 105, 0, false, "")) + cols = append(cols, createCol(parser, "trx_wait_started", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "trx_weight", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_mysql_thread_id", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_query", 6165, "utf8mb3_general_ci", "", 1024, 0, false, "")) + cols = append(cols, createCol(parser, "trx_operation_state", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "trx_tables_in_use", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_tables_locked", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_lock_structs", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_lock_memory_bytes", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_rows_locked", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_rows_modified", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_concurrency_tickets", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_isolation_level", 6165, "utf8mb3_general_ci", "", 16, 0, true, "")) + cols = append(cols, createCol(parser, "trx_unique_checks", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_foreign_key_checks", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_last_foreign_key_error", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "trx_adaptive_hash_latched", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_adaptive_hash_timeout", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_is_read_only", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_autocommit_non_locking", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "trx_schedule_weight", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["INNODB_TRX"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_ID", 778)) - cols = append(cols, createCol("POS", 776)) - cols = append(cols, createCol("BASE_POS", 776)) + cols = append(cols, createCol(parser, "TABLE_ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "POS", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "BASE_POS", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["INNODB_VIRTUAL"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 776)) - cols = append(cols, createCol("POSITION_IN_UNIQUE_CONSTRAINT", 776)) - cols = append(cols, createCol("REFERENCED_TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("REFERENCED_TABLE_NAME", 6165)) - cols = append(cols, createCol("REFERENCED_COLUMN_NAME", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 776, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "POSITION_IN_UNIQUE_CONSTRAINT", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "REFERENCED_COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) infSchema["KEY_COLUMN_USAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("WORD", 6165)) - cols = append(cols, createCol("RESERVED", 263)) + cols = append(cols, createCol(parser, "WORD", 6165, "utf8mb3_general_ci", "", 128, 0, false, "")) + cols = append(cols, createCol(parser, "RESERVED", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["KEYWORDS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("QUERY", 6165)) - cols = append(cols, createCol("TRACE", 6165)) - cols = append(cols, createCol("MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 263)) - cols = append(cols, createCol("INSUFFICIENT_PRIVILEGES", 257)) + cols = append(cols, createCol(parser, "QUERY", 6165, "utf8mb3_general_ci", "", 65535, 0, true, "")) + cols = append(cols, createCol(parser, "TRACE", 6165, "utf8mb3_general_ci", "", 65535, 0, true, "")) + cols = append(cols, createCol(parser, "MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "INSUFFICIENT_PRIVILEGES", 257, "utf8mb3_general_ci", "", 1, 0, true, "")) infSchema["OPTIMIZER_TRACE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPECIFIC_CATALOG", 6165)) - cols = append(cols, createCol("SPECIFIC_SCHEMA", 6165)) - cols = append(cols, createCol("SPECIFIC_NAME", 6165)) - cols = append(cols, createCol("ORDINAL_POSITION", 778)) - cols = append(cols, createCol("PARAMETER_MODE", 6165)) - cols = append(cols, createCol("PARAMETER_NAME", 6165)) - cols = append(cols, createCol("DATA_TYPE", 6163)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 265)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 265)) - cols = append(cols, createCol("NUMERIC_PRECISION", 776)) - cols = append(cols, createCol("NUMERIC_SCALE", 265)) - cols = append(cols, createCol("DATETIME_PRECISION", 776)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("DTD_IDENTIFIER", 6163)) - cols = append(cols, createCol("ROUTINE_TYPE", 2074)) + cols = append(cols, createCol(parser, "SPECIFIC_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SPECIFIC_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ORDINAL_POSITION", 778, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "PARAMETER_MODE", 6165, "utf8mb3_general_ci", "", 5, 0, false, "")) + cols = append(cols, createCol(parser, "PARAMETER_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DTD_IDENTIFIER", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_TYPE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'FUNCTION','PROCEDURE'")) infSchema["PARAMETERS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("PARTITION_NAME", 6165)) - cols = append(cols, createCol("SUBPARTITION_NAME", 6165)) - cols = append(cols, createCol("PARTITION_ORDINAL_POSITION", 776)) - cols = append(cols, createCol("SUBPARTITION_ORDINAL_POSITION", 776)) - cols = append(cols, createCol("PARTITION_METHOD", 6165)) - cols = append(cols, createCol("SUBPARTITION_METHOD", 6165)) - cols = append(cols, createCol("PARTITION_EXPRESSION", 6165)) - cols = append(cols, createCol("SUBPARTITION_EXPRESSION", 6165)) - cols = append(cols, createCol("PARTITION_DESCRIPTION", 6163)) - cols = append(cols, createCol("TABLE_ROWS", 778)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 778)) - cols = append(cols, createCol("DATA_LENGTH", 778)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 778)) - cols = append(cols, createCol("INDEX_LENGTH", 778)) - cols = append(cols, createCol("DATA_FREE", 778)) - cols = append(cols, createCol("CREATE_TIME", 2061)) - cols = append(cols, createCol("UPDATE_TIME", 2064)) - cols = append(cols, createCol("CHECK_TIME", 2064)) - cols = append(cols, createCol("CHECKSUM", 265)) - cols = append(cols, createCol("PARTITION_COMMENT", 6163)) - cols = append(cols, createCol("NODEGROUP", 6165)) - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PARTITION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_ORDINAL_POSITION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_ORDINAL_POSITION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_METHOD", 6165, "utf8mb3_general_ci", "", 13, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_METHOD", 6165, "utf8mb3_general_ci", "", 13, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_EXPRESSION", 6165, "utf8mb3_general_ci", "", 2048, 0, false, "")) + cols = append(cols, createCol(parser, "SUBPARTITION_EXPRESSION", 6165, "utf8mb3_general_ci", "", 2048, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_DESCRIPTION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PARTITION_COMMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "NODEGROUP", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 268, 0, false, "")) infSchema["PARTITIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("PLUGIN_NAME", 6165)) - cols = append(cols, createCol("PLUGIN_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_STATUS", 6165)) - cols = append(cols, createCol("PLUGIN_TYPE", 6165)) - cols = append(cols, createCol("PLUGIN_TYPE_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_LIBRARY", 6165)) - cols = append(cols, createCol("PLUGIN_LIBRARY_VERSION", 6165)) - cols = append(cols, createCol("PLUGIN_AUTHOR", 6165)) - cols = append(cols, createCol("PLUGIN_DESCRIPTION", 6165)) - cols = append(cols, createCol("PLUGIN_LICENSE", 6165)) - cols = append(cols, createCol("LOAD_OPTION", 6165)) + cols = append(cols, createCol(parser, "PLUGIN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_STATUS", 6165, "utf8mb3_general_ci", "", 10, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_TYPE", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_TYPE_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, true, "")) + cols = append(cols, createCol(parser, "PLUGIN_LIBRARY", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_LIBRARY_VERSION", 6165, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_AUTHOR", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_DESCRIPTION", 6165, "utf8mb3_general_ci", "", 65535, 0, false, "")) + cols = append(cols, createCol(parser, "PLUGIN_LICENSE", 6165, "utf8mb3_general_ci", "", 80, 0, false, "")) + cols = append(cols, createCol(parser, "LOAD_OPTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["PLUGINS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("ID", 778)) - cols = append(cols, createCol("USER", 6165)) - cols = append(cols, createCol("HOST", 6165)) - cols = append(cols, createCol("DB", 6165)) - cols = append(cols, createCol("COMMAND", 6165)) - cols = append(cols, createCol("TIME", 263)) - cols = append(cols, createCol("STATE", 6165)) - cols = append(cols, createCol("INFO", 6165)) + cols = append(cols, createCol(parser, "ID", 778, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "USER", 6165, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "HOST", 6165, "utf8mb3_general_ci", "", 261, 0, true, "")) + cols = append(cols, createCol(parser, "DB", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COMMAND", 6165, "utf8mb3_general_ci", "", 16, 0, true, "")) + cols = append(cols, createCol(parser, "TIME", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "INFO", 6165, "utf8mb3_general_ci", "", 65535, 0, false, "")) infSchema["PROCESSLIST"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("QUERY_ID", 263)) - cols = append(cols, createCol("SEQ", 263)) - cols = append(cols, createCol("STATE", 6165)) - cols = append(cols, createCol("DURATION", 18)) - cols = append(cols, createCol("CPU_USER", 18)) - cols = append(cols, createCol("CPU_SYSTEM", 18)) - cols = append(cols, createCol("CONTEXT_VOLUNTARY", 263)) - cols = append(cols, createCol("CONTEXT_INVOLUNTARY", 263)) - cols = append(cols, createCol("BLOCK_OPS_IN", 263)) - cols = append(cols, createCol("BLOCK_OPS_OUT", 263)) - cols = append(cols, createCol("MESSAGES_SENT", 263)) - cols = append(cols, createCol("MESSAGES_RECEIVED", 263)) - cols = append(cols, createCol("PAGE_FAULTS_MAJOR", 263)) - cols = append(cols, createCol("PAGE_FAULTS_MINOR", 263)) - cols = append(cols, createCol("SWAPS", 263)) - cols = append(cols, createCol("SOURCE_FUNCTION", 6165)) - cols = append(cols, createCol("SOURCE_FILE", 6165)) - cols = append(cols, createCol("SOURCE_LINE", 263)) + cols = append(cols, createCol(parser, "QUERY_ID", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SEQ", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "STATE", 6165, "utf8mb3_general_ci", "", 30, 0, true, "")) + cols = append(cols, createCol(parser, "DURATION", 18, "utf8mb3_general_ci", "", 905, 0, true, "")) + cols = append(cols, createCol(parser, "CPU_USER", 18, "utf8mb3_general_ci", "", 905, 0, false, "")) + cols = append(cols, createCol(parser, "CPU_SYSTEM", 18, "utf8mb3_general_ci", "", 905, 0, false, "")) + cols = append(cols, createCol(parser, "CONTEXT_VOLUNTARY", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CONTEXT_INVOLUNTARY", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "BLOCK_OPS_IN", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "BLOCK_OPS_OUT", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MESSAGES_SENT", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MESSAGES_RECEIVED", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PAGE_FAULTS_MAJOR", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PAGE_FAULTS_MINOR", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SWAPS", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_FUNCTION", 6165, "utf8mb3_general_ci", "", 30, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_FILE", 6165, "utf8mb3_general_ci", "", 20, 0, false, "")) + cols = append(cols, createCol(parser, "SOURCE_LINE", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["PROFILING"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("UNIQUE_CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("MATCH_OPTION", 2074)) - cols = append(cols, createCol("UPDATE_RULE", 2074)) - cols = append(cols, createCol("DELETE_RULE", 2074)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("REFERENCED_TABLE_NAME", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "UNIQUE_CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "MATCH_OPTION", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'NONE','PARTIAL','FULL'")) + cols = append(cols, createCol(parser, "UPDATE_RULE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'NO ACTION','RESTRICT','CASCADE','SET NULL','SET DEFAULT'")) + cols = append(cols, createCol(parser, "DELETE_RULE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'NO ACTION','RESTRICT','CASCADE','SET NULL','SET DEFAULT'")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "REFERENCED_TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["REFERENTIAL_CONSTRAINTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("RESOURCE_GROUP_NAME", 6165)) - cols = append(cols, createCol("RESOURCE_GROUP_TYPE", 2074)) - cols = append(cols, createCol("RESOURCE_GROUP_ENABLED", 257)) - cols = append(cols, createCol("VCPU_IDS", 10260)) - cols = append(cols, createCol("THREAD_PRIORITY", 263)) + cols = append(cols, createCol(parser, "RESOURCE_GROUP_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "RESOURCE_GROUP_TYPE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'SYSTEM','USER'")) + cols = append(cols, createCol(parser, "RESOURCE_GROUP_ENABLED", 257, "utf8mb3_general_ci", "", 1, 0, true, "")) + cols = append(cols, createCol(parser, "VCPU_IDS", 10260, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "THREAD_PRIORITY", 263, "utf8mb3_general_ci", "", 0, 0, true, "")) infSchema["RESOURCE_GROUPS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTOR", 6165)) - cols = append(cols, createCol("GRANTOR_HOST", 6165)) - cols = append(cols, createCol("GRANTEE", 6167)) - cols = append(cols, createCol("GRANTEE_HOST", 6167)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6167)) - cols = append(cols, createCol("TABLE_NAME", 6167)) - cols = append(cols, createCol("COLUMN_NAME", 6167)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 2075)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTOR", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTOR_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE", 6167, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "GRANTEE_HOST", 6167, "utf8mb3_general_ci", "", 255, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'Select','Insert','Update','References'")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["ROLE_COLUMN_GRANTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTOR", 6165)) - cols = append(cols, createCol("GRANTOR_HOST", 6165)) - cols = append(cols, createCol("GRANTEE", 6167)) - cols = append(cols, createCol("GRANTEE_HOST", 6167)) - cols = append(cols, createCol("SPECIFIC_CATALOG", 6165)) - cols = append(cols, createCol("SPECIFIC_SCHEMA", 6167)) - cols = append(cols, createCol("SPECIFIC_NAME", 6167)) - cols = append(cols, createCol("ROUTINE_CATALOG", 6165)) - cols = append(cols, createCol("ROUTINE_SCHEMA", 6167)) - cols = append(cols, createCol("ROUTINE_NAME", 6167)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 2075)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTOR", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTOR_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE", 6167, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "GRANTEE_HOST", 6167, "utf8mb3_general_ci", "", 255, 0, true, "")) + cols = append(cols, createCol(parser, "SPECIFIC_CATALOG", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "SPECIFIC_SCHEMA", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_CATALOG", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_SCHEMA", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_NAME", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'Execute','Alter Routine','Grant'")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["ROLE_ROUTINE_GRANTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTOR", 6165)) - cols = append(cols, createCol("GRANTOR_HOST", 6165)) - cols = append(cols, createCol("GRANTEE", 6167)) - cols = append(cols, createCol("GRANTEE_HOST", 6167)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6167)) - cols = append(cols, createCol("TABLE_NAME", 6167)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 2075)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTOR", 6165, "utf8mb3_general_ci", "", 97, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTOR_HOST", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "GRANTEE", 6167, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "GRANTEE_HOST", 6167, "utf8mb3_general_ci", "", 255, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6167, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger'")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["ROLE_TABLE_GRANTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SPECIFIC_NAME", 6165)) - cols = append(cols, createCol("ROUTINE_CATALOG", 6165)) - cols = append(cols, createCol("ROUTINE_SCHEMA", 6165)) - cols = append(cols, createCol("ROUTINE_NAME", 6165)) - cols = append(cols, createCol("ROUTINE_TYPE", 2074)) - cols = append(cols, createCol("DATA_TYPE", 6163)) - cols = append(cols, createCol("CHARACTER_MAXIMUM_LENGTH", 265)) - cols = append(cols, createCol("CHARACTER_OCTET_LENGTH", 265)) - cols = append(cols, createCol("NUMERIC_PRECISION", 776)) - cols = append(cols, createCol("NUMERIC_SCALE", 776)) - cols = append(cols, createCol("DATETIME_PRECISION", 776)) - cols = append(cols, createCol("CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("COLLATION_NAME", 6165)) - cols = append(cols, createCol("DTD_IDENTIFIER", 6163)) - cols = append(cols, createCol("ROUTINE_BODY", 6165)) - cols = append(cols, createCol("ROUTINE_DEFINITION", 6163)) - cols = append(cols, createCol("EXTERNAL_NAME", 10264)) - cols = append(cols, createCol("EXTERNAL_LANGUAGE", 6165)) - cols = append(cols, createCol("PARAMETER_STYLE", 6165)) - cols = append(cols, createCol("IS_DETERMINISTIC", 6165)) - cols = append(cols, createCol("SQL_DATA_ACCESS", 2074)) - cols = append(cols, createCol("SQL_PATH", 10264)) - cols = append(cols, createCol("SECURITY_TYPE", 2074)) - cols = append(cols, createCol("CREATED", 2061)) - cols = append(cols, createCol("LAST_ALTERED", 2061)) - cols = append(cols, createCol("SQL_MODE", 2075)) - cols = append(cols, createCol("ROUTINE_COMMENT", 6163)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ROUTINE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ROUTINE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_TYPE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'FUNCTION','PROCEDURE'")) + cols = append(cols, createCol(parser, "DATA_TYPE", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_MAXIMUM_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_OCTET_LENGTH", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_PRECISION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NUMERIC_SCALE", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATETIME_PRECISION", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DTD_IDENTIFIER", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ROUTINE_BODY", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "ROUTINE_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EXTERNAL_NAME", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "EXTERNAL_LANGUAGE", 6165, "utf8mb3_general_ci", "SQL", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PARAMETER_STYLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "IS_DETERMINISTIC", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_DATA_ACCESS", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'CONTAINS SQL','NO SQL','READS SQL DATA','MODIFIES SQL DATA'")) + cols = append(cols, createCol(parser, "SQL_PATH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SECURITY_TYPE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'DEFAULT','INVOKER','DEFINER'")) + cols = append(cols, createCol(parser, "CREATED", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "LAST_ALTERED", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_MODE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL'")) + cols = append(cols, createCol(parser, "ROUTINE_COMMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 288, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["ROUTINES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 292, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["SCHEMA_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CATALOG_NAME", 6165)) - cols = append(cols, createCol("SCHEMA_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_CHARACTER_SET_NAME", 6165)) - cols = append(cols, createCol("DEFAULT_COLLATION_NAME", 6165)) - cols = append(cols, createCol("SQL_PATH", 10264)) - cols = append(cols, createCol("DEFAULT_ENCRYPTION", 2074)) + cols = append(cols, createCol(parser, "CATALOG_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SCHEMA_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "DEFAULT_CHARACTER_SET_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DEFAULT_COLLATION_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_PATH", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DEFAULT_ENCRYPTION", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'NO','YES'")) infSchema["SCHEMATA"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CATALOG_NAME", 6165)) - cols = append(cols, createCol("SCHEMA_NAME", 6165)) - cols = append(cols, createCol("OPTIONS", 6165)) + cols = append(cols, createCol(parser, "CATALOG_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SCHEMA_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "OPTIONS", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) infSchema["SCHEMATA_EXTENSIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("SRS_NAME", 6165)) - cols = append(cols, createCol("SRS_ID", 776)) - cols = append(cols, createCol("GEOMETRY_TYPE_NAME", 6163)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SRS_NAME", 6165, "utf8mb3_general_ci", "", 80, 0, false, "")) + cols = append(cols, createCol(parser, "SRS_ID", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "GEOMETRY_TYPE_NAME", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["ST_GEOMETRY_COLUMNS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("SRS_NAME", 6165)) - cols = append(cols, createCol("SRS_ID", 776)) - cols = append(cols, createCol("ORGANIZATION", 6165)) - cols = append(cols, createCol("ORGANIZATION_COORDSYS_ID", 776)) - cols = append(cols, createCol("DEFINITION", 6165)) - cols = append(cols, createCol("DESCRIPTION", 6165)) + cols = append(cols, createCol(parser, "SRS_NAME", 6165, "utf8mb3_general_ci", "", 80, 0, true, "")) + cols = append(cols, createCol(parser, "SRS_ID", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ORGANIZATION", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "ORGANIZATION_COORDSYS_ID", 776, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DEFINITION", 6165, "utf8mb3_general_ci", "", 4096, 0, true, "")) + cols = append(cols, createCol(parser, "DESCRIPTION", 6165, "utf8mb3_general_ci", "", 2048, 0, false, "")) infSchema["ST_SPATIAL_REFERENCE_SYSTEMS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("UNIT_NAME", 6165)) - cols = append(cols, createCol("UNIT_TYPE", 6165)) - cols = append(cols, createCol("CONVERSION_FACTOR", 1036)) - cols = append(cols, createCol("DESCRIPTION", 6165)) + cols = append(cols, createCol(parser, "UNIT_NAME", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) + cols = append(cols, createCol(parser, "UNIT_TYPE", 6165, "utf8mb3_general_ci", "", 7, 0, false, "")) + cols = append(cols, createCol(parser, "CONVERSION_FACTOR", 1036, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DESCRIPTION", 6165, "utf8mb3_general_ci", "", 255, 0, false, "")) infSchema["ST_UNITS_OF_MEASURE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("NON_UNIQUE", 263)) - cols = append(cols, createCol("INDEX_SCHEMA", 6165)) - cols = append(cols, createCol("INDEX_NAME", 6165)) - cols = append(cols, createCol("SEQ_IN_INDEX", 776)) - cols = append(cols, createCol("COLUMN_NAME", 6165)) - cols = append(cols, createCol("COLLATION", 6165)) - cols = append(cols, createCol("CARDINALITY", 265)) - cols = append(cols, createCol("SUB_PART", 265)) - cols = append(cols, createCol("PACKED", 10264)) - cols = append(cols, createCol("NULLABLE", 6165)) - cols = append(cols, createCol("INDEX_TYPE", 6165)) - cols = append(cols, createCol("COMMENT", 6165)) - cols = append(cols, createCol("INDEX_COMMENT", 6165)) - cols = append(cols, createCol("IS_VISIBLE", 6165)) - cols = append(cols, createCol("EXPRESSION", 6163)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "NON_UNIQUE", 263, "utf8mb3_general_ci", "0", 0, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SEQ_IN_INDEX", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "COLUMN_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "COLLATION", 6165, "utf8mb3_general_ci", "", 1, 0, false, "")) + cols = append(cols, createCol(parser, "CARDINALITY", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SUB_PART", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "PACKED", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NULLABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_TYPE", 6165, "utf8mb3_general_ci", "", 11, 0, true, "")) + cols = append(cols, createCol(parser, "COMMENT", 6165, "utf8mb3_general_ci", "", 8, 0, true, "")) + cols = append(cols, createCol(parser, "INDEX_COMMENT", 6165, "utf8mb3_general_ci", "", 2048, 0, true, "")) + cols = append(cols, createCol(parser, "IS_VISIBLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "EXPRESSION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["STATISTICS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("CONSTRAINT_TYPE", 6165)) - cols = append(cols, createCol("ENFORCED", 6165)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_TYPE", 6165, "utf8mb3_general_ci", "", 11, 0, true, "")) + cols = append(cols, createCol(parser, "ENFORCED", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["TABLE_CONSTRAINTS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("CONSTRAINT_CATALOG", 6165)) - cols = append(cols, createCol("CONSTRAINT_SCHEMA", 6165)) - cols = append(cols, createCol("CONSTRAINT_NAME", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("ENGINE_ATTRIBUTE", 2078)) - cols = append(cols, createCol("SECONDARY_ENGINE_ATTRIBUTE", 2078)) + cols = append(cols, createCol(parser, "CONSTRAINT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "CONSTRAINT_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SECONDARY_ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["TABLE_CONSTRAINTS_EXTENSIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 292, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["TABLE_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("TABLE_TYPE", 2074)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("VERSION", 263)) - cols = append(cols, createCol("ROW_FORMAT", 2074)) - cols = append(cols, createCol("TABLE_ROWS", 778)) - cols = append(cols, createCol("AVG_ROW_LENGTH", 778)) - cols = append(cols, createCol("DATA_LENGTH", 778)) - cols = append(cols, createCol("MAX_DATA_LENGTH", 778)) - cols = append(cols, createCol("INDEX_LENGTH", 778)) - cols = append(cols, createCol("DATA_FREE", 778)) - cols = append(cols, createCol("AUTO_INCREMENT", 778)) - cols = append(cols, createCol("CREATE_TIME", 2061)) - cols = append(cols, createCol("UPDATE_TIME", 2064)) - cols = append(cols, createCol("CHECK_TIME", 2064)) - cols = append(cols, createCol("TABLE_COLLATION", 6165)) - cols = append(cols, createCol("CHECKSUM", 265)) - cols = append(cols, createCol("CREATE_OPTIONS", 6165)) - cols = append(cols, createCol("TABLE_COMMENT", 6163)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_TYPE", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'BASE TABLE','VIEW','SYSTEM VIEW'")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "VERSION", 263, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ROW_FORMAT", 2074, "utf8mb3_general_ci", "", 0, 0, false, "'Fixed','Dynamic','Compressed','Redundant','Compact','Paged'")) + cols = append(cols, createCol(parser, "TABLE_ROWS", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AVG_ROW_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MAX_DATA_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "INDEX_LENGTH", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "DATA_FREE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AUTO_INCREMENT", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_TIME", 2061, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "UPDATE_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_TIME", 2064, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_COLLATION", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "CHECKSUM", 265, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CREATE_OPTIONS", 6165, "utf8mb3_general_ci", "", 256, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_COMMENT", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["TABLES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("ENGINE_ATTRIBUTE", 2078)) - cols = append(cols, createCol("SECONDARY_ENGINE_ATTRIBUTE", 2078)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "SECONDARY_ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["TABLES_EXTENSIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) - cols = append(cols, createCol("ENGINE", 6165)) - cols = append(cols, createCol("TABLESPACE_TYPE", 6165)) - cols = append(cols, createCol("LOGFILE_GROUP_NAME", 6165)) - cols = append(cols, createCol("EXTENT_SIZE", 778)) - cols = append(cols, createCol("AUTOEXTEND_SIZE", 778)) - cols = append(cols, createCol("MAXIMUM_SIZE", 778)) - cols = append(cols, createCol("NODEGROUP_ID", 778)) - cols = append(cols, createCol("TABLESPACE_COMMENT", 6165)) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "TABLESPACE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "LOGFILE_GROUP_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EXTENT_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "AUTOEXTEND_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "MAXIMUM_SIZE", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "NODEGROUP_ID", 778, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "TABLESPACE_COMMENT", 6165, "utf8mb3_general_ci", "", 2048, 0, false, "")) infSchema["TABLESPACES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLESPACE_NAME", 6165)) - cols = append(cols, createCol("ENGINE_ATTRIBUTE", 2078)) + cols = append(cols, createCol(parser, "TABLESPACE_NAME", 6165, "utf8mb3_general_ci", "", 268, 0, true, "")) + cols = append(cols, createCol(parser, "ENGINE_ATTRIBUTE", 2078, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["TABLESPACES_EXTENSIONS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TRIGGER_CATALOG", 6165)) - cols = append(cols, createCol("TRIGGER_SCHEMA", 6165)) - cols = append(cols, createCol("TRIGGER_NAME", 6165)) - cols = append(cols, createCol("EVENT_MANIPULATION", 2074)) - cols = append(cols, createCol("EVENT_OBJECT_CATALOG", 6165)) - cols = append(cols, createCol("EVENT_OBJECT_SCHEMA", 6165)) - cols = append(cols, createCol("EVENT_OBJECT_TABLE", 6165)) - cols = append(cols, createCol("ACTION_ORDER", 776)) - cols = append(cols, createCol("ACTION_CONDITION", 10264)) - cols = append(cols, createCol("ACTION_STATEMENT", 6163)) - cols = append(cols, createCol("ACTION_ORIENTATION", 6165)) - cols = append(cols, createCol("ACTION_TIMING", 2074)) - cols = append(cols, createCol("ACTION_REFERENCE_OLD_TABLE", 10264)) - cols = append(cols, createCol("ACTION_REFERENCE_NEW_TABLE", 10264)) - cols = append(cols, createCol("ACTION_REFERENCE_OLD_ROW", 6165)) - cols = append(cols, createCol("ACTION_REFERENCE_NEW_ROW", 6165)) - cols = append(cols, createCol("CREATED", 2061)) - cols = append(cols, createCol("SQL_MODE", 2075)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) - cols = append(cols, createCol("DATABASE_COLLATION", 6165)) + cols = append(cols, createCol(parser, "TRIGGER_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TRIGGER_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TRIGGER_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "EVENT_MANIPULATION", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'INSERT','UPDATE','DELETE'")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "EVENT_OBJECT_TABLE", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_ORDER", 776, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_CONDITION", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_STATEMENT", 6163, "utf8mb3_general_ci", "", 0, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_ORIENTATION", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_TIMING", 2074, "utf8mb3_general_ci", "", 0, 0, true, "'BEFORE','AFTER'")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_OLD_TABLE", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_NEW_TABLE", 10264, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_OLD_ROW", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "ACTION_REFERENCE_NEW_ROW", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) + cols = append(cols, createCol(parser, "CREATED", 2061, "utf8mb3_general_ci", "", 2, 0, true, "")) + cols = append(cols, createCol(parser, "SQL_MODE", 2075, "utf8mb3_general_ci", "", 0, 0, true, "'REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','NOT_USED_9','NOT_USED_10','NOT_USED_11','NOT_USED_12','NOT_USED_13','NOT_USED_14','NOT_USED_15','NOT_USED_16','NOT_USED_17','NOT_USED_18','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','ALLOW_INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NOT_USED_29','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','TIME_TRUNCATE_FRACTIONAL'")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 288, 0, true, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "DATABASE_COLLATION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["TRIGGERS"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("USER", 6167)) - cols = append(cols, createCol("HOST", 6167)) - cols = append(cols, createCol("ATTRIBUTE", 6163)) + cols = append(cols, createCol(parser, "USER", 6167, "utf8mb3_general_ci", "", 32, 0, true, "")) + cols = append(cols, createCol(parser, "HOST", 6167, "utf8mb3_general_ci", "", 255, 0, true, "")) + cols = append(cols, createCol(parser, "ATTRIBUTE", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) infSchema["USER_ATTRIBUTES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("GRANTEE", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("PRIVILEGE_TYPE", 6165)) - cols = append(cols, createCol("IS_GRANTABLE", 6165)) + cols = append(cols, createCol(parser, "GRANTEE", 6165, "utf8mb3_general_ci", "", 292, 0, true, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 512, 0, true, "")) + cols = append(cols, createCol(parser, "PRIVILEGE_TYPE", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "IS_GRANTABLE", 6165, "utf8mb3_general_ci", "", 3, 0, true, "")) infSchema["USER_PRIVILEGES"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("SPECIFIC_CATALOG", 6165)) - cols = append(cols, createCol("SPECIFIC_SCHEMA", 6165)) - cols = append(cols, createCol("SPECIFIC_NAME", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SPECIFIC_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SPECIFIC_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "SPECIFIC_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["VIEW_ROUTINE_USAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("VIEW_CATALOG", 6165)) - cols = append(cols, createCol("VIEW_SCHEMA", 6165)) - cols = append(cols, createCol("VIEW_NAME", 6165)) - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) + cols = append(cols, createCol(parser, "VIEW_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "VIEW_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "VIEW_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) infSchema["VIEW_TABLE_USAGE"] = cols cols = []vindexes.Column{} - cols = append(cols, createCol("TABLE_CATALOG", 6165)) - cols = append(cols, createCol("TABLE_SCHEMA", 6165)) - cols = append(cols, createCol("TABLE_NAME", 6165)) - cols = append(cols, createCol("VIEW_DEFINITION", 6163)) - cols = append(cols, createCol("CHECK_OPTION", 2074)) - cols = append(cols, createCol("IS_UPDATABLE", 2074)) - cols = append(cols, createCol("DEFINER", 6165)) - cols = append(cols, createCol("SECURITY_TYPE", 6165)) - cols = append(cols, createCol("CHARACTER_SET_CLIENT", 6165)) - cols = append(cols, createCol("COLLATION_CONNECTION", 6165)) + cols = append(cols, createCol(parser, "TABLE_CATALOG", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_SCHEMA", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "TABLE_NAME", 6165, "utf8mb3_general_ci", "", 64, 0, false, "")) + cols = append(cols, createCol(parser, "VIEW_DEFINITION", 6163, "utf8mb3_general_ci", "", 0, 0, false, "")) + cols = append(cols, createCol(parser, "CHECK_OPTION", 2074, "utf8mb3_general_ci", "", 0, 0, false, "'NONE','LOCAL','CASCADED'")) + cols = append(cols, createCol(parser, "IS_UPDATABLE", 2074, "utf8mb3_general_ci", "", 0, 0, false, "'NO','YES'")) + cols = append(cols, createCol(parser, "DEFINER", 6165, "utf8mb3_general_ci", "", 288, 0, false, "")) + cols = append(cols, createCol(parser, "SECURITY_TYPE", 6165, "utf8mb3_general_ci", "", 7, 0, false, "")) + cols = append(cols, createCol(parser, "CHARACTER_SET_CLIENT", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) + cols = append(cols, createCol(parser, "COLLATION_CONNECTION", 6165, "utf8mb3_general_ci", "", 64, 0, true, "")) infSchema["VIEWS"] = cols - return infSchema } @@ -1672,17 +1603,18 @@ type infoSchemaWithColumns struct { infoSchemaData map[string][]vindexes.Column } +// MySQLVersion implements SchemaInformation. + // We cache this information, since these are maps that are not changed var infoSchema57 = getInfoSchema57() var infoSchema80 = getInfoSchema80() // newSchemaInfo returns a SchemaInformation that has the column information for all info_schema tables func newSchemaInfo(inner SchemaInformation) SchemaInformation { - return &infoSchemaWithColumns{inner: inner, infoSchemaData: loadSchemaInfo()} + return &infoSchemaWithColumns{inner: inner, infoSchemaData: loadSchemaInfo(inner.Environment().MySQLVersion())} } -func loadSchemaInfo() map[string][]vindexes.Column { - version := servenv.MySQLServerVersion() +func loadSchemaInfo(version string) map[string][]vindexes.Column { if strings.HasPrefix(version, "5.7") { return infoSchema57 } @@ -1714,10 +1646,18 @@ func (i *infoSchemaWithColumns) ConnCollation() collations.ID { return i.inner.ConnCollation() } +func (i *infoSchemaWithColumns) Environment() *vtenv.Environment { + return i.inner.Environment() +} + func (i *infoSchemaWithColumns) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { return i.inner.ForeignKeyMode(keyspace) } +func (i *infoSchemaWithColumns) GetForeignKeyChecksState() *bool { + return nil +} + func (i *infoSchemaWithColumns) KeyspaceError(keyspace string) error { return i.inner.KeyspaceError(keyspace) } diff --git a/go/vt/vtgate/semantics/info_schema_gen_test.go b/go/vt/vtgate/semantics/info_schema_gen_test.go index 61241d96653..efa7433dd05 100644 --- a/go/vt/vtgate/semantics/info_schema_gen_test.go +++ b/go/vt/vtgate/semantics/info_schema_gen_test.go @@ -20,12 +20,15 @@ import ( "database/sql" "fmt" "regexp" + "strconv" "strings" "testing" _ "github.com/go-sql-driver/mysql" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" ) @@ -37,11 +40,16 @@ func TestGenerateInfoSchemaMap(t *testing.T) { require.NoError(t, err) defer db.Close() + collationName := collations.MySQL8().LookupName(collations.SystemCollation.Collation) + for _, tbl := range informationSchemaTables80 { - b.WriteString("cols = []vindexes.Column{}\n") result, err := db.Query(fmt.Sprintf("show columns from information_schema.`%s`", tbl)) - require.NoError(t, err) + if err != nil { + t.Logf("error querying table %s: %v", tbl, err) + continue + } defer result.Close() + b.WriteString("cols = []vindexes.Column{}\n") for result.Next() { var r row result.Scan(&r.Field, &r.Type, &r.Null, &r.Key, &r.Default, &r.Extra) @@ -61,7 +69,24 @@ func TestGenerateInfoSchemaMap(t *testing.T) { if int(i2) == 0 { t.Fatalf("%s %s", tbl, r.Field) } - b.WriteString(fmt.Sprintf("cols = append(cols, createCol(\"%s\", %d))\n", r.Field, int(i2))) + var size, scale int64 + var values string + switch i2 { + case sqltypes.Enum, sqltypes.Set: + values = allString[2] + default: + if len(allString) > 1 && allString[2] != "" { + parts := strings.Split(allString[2], ",") + size, err = strconv.ParseInt(parts[0], 10, 32) + require.NoError(t, err) + if len(parts) > 1 { + scale, err = strconv.ParseInt(parts[1], 10, 32) + require.NoError(t, err) + } + } + } + // createCol(name string, typ int, collation string, def string, invisible bool, size, scale int32, notNullable bool) + b.WriteString(fmt.Sprintf("cols = append(cols, createCol(\"%s\", %d, \"%s\", \"%s\", %d, %d, %t, \"%s\"))\n", r.Field, int(i2), collationName, r.Default, size, scale, r.Null == "NO", values)) } b.WriteString(fmt.Sprintf("infSchema[\"%s\"] = cols\n", tbl)) } @@ -85,6 +110,8 @@ var ( "ENGINES", "EVENTS", "FILES", + "GLOBAL_STATUS", + "GLOBAL_VARIABLES", "INNODB_BUFFER_PAGE", "INNODB_BUFFER_PAGE_LRU", "INNODB_BUFFER_POOL_STATS", @@ -158,7 +185,7 @@ type row struct { Type string Null string Key any - Default any + Default string Extra any } diff --git a/go/vt/vtgate/semantics/real_table.go b/go/vt/vtgate/semantics/real_table.go index cf7811f4404..a8c3d699b59 100644 --- a/go/vt/vtgate/semantics/real_table.go +++ b/go/vt/vtgate/semantics/real_table.go @@ -20,11 +20,9 @@ import ( "strings" "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -33,7 +31,9 @@ type RealTable struct { dbName, tableName string ASTNode *sqlparser.AliasedTableExpr Table *vindexes.Table + VindexHint *sqlparser.IndexHint isInfSchema bool + collationEnv *collations.Environment } var _ TableInfo = (*RealTable)(nil) @@ -70,11 +70,11 @@ func (r *RealTable) IsInfSchema() bool { // GetColumns implements the TableInfo interface func (r *RealTable) getColumns() []ColumnInfo { - return vindexTableToColumnInfo(r.Table) + return vindexTableToColumnInfo(r.Table, r.collationEnv) } // GetExpr implements the TableInfo interface -func (r *RealTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { +func (r *RealTable) GetAliasedTableExpr() *sqlparser.AliasedTableExpr { return r.ASTNode } @@ -103,6 +103,11 @@ func (r *RealTable) GetVindexTable() *vindexes.Table { return r.Table } +// GetVindexHint implements the TableInfo interface +func (r *RealTable) GetVindexHint() *sqlparser.IndexHint { + return r.VindexHint +} + // Name implements the TableInfo interface func (r *RealTable) Name() (sqlparser.TableName, error) { return r.ASTNode.TableName() @@ -118,27 +123,16 @@ func (r *RealTable) matches(name sqlparser.TableName) bool { return (name.Qualifier.IsEmpty() || name.Qualifier.String() == r.dbName) && r.tableName == name.Name.String() } -func vindexTableToColumnInfo(tbl *vindexes.Table) []ColumnInfo { +func vindexTableToColumnInfo(tbl *vindexes.Table, collationEnv *collations.Environment) []ColumnInfo { if tbl == nil { return nil } nameMap := map[string]any{} cols := make([]ColumnInfo, 0, len(tbl.Columns)) for _, col := range tbl.Columns { - collation := collations.DefaultCollationForType(col.Type) - if sqltypes.IsText(col.Type) { - coll, found := collations.Local().LookupID(col.CollationName) - if found { - collation = coll - } - } - cols = append(cols, ColumnInfo{ - Name: col.Name.String(), - Type: evalengine.Type{ - Type: col.Type, - Coll: collation, - }, + Name: col.Name.String(), + Type: col.ToEvalengineType(collationEnv), Invisible: col.Invisible, }) nameMap[col.Name.String()] = nil diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index 458e08b1f15..faef930b488 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -197,7 +197,7 @@ func (s *scoper) up(cursor *sqlparser.Cursor) error { if isParentSelectStatement(cursor) { s.popScope() } - case *sqlparser.Select, sqlparser.GroupBy, *sqlparser.Update, *sqlparser.Delete, *sqlparser.Insert, *sqlparser.Union: + case *sqlparser.Select, sqlparser.GroupBy, *sqlparser.Update, *sqlparser.Insert, *sqlparser.Union, *sqlparser.Delete: id := EmptyTableSet() for _, tableInfo := range s.currentScope().tables { set := tableInfo.getTableSet(s.org) @@ -223,6 +223,12 @@ func (s *scoper) up(cursor *sqlparser.Cursor) error { } } } + if isParentDeleteOrUpdate(cursor) { + usingMap := s.currentScope().prepareUsingMap() + for ts, m := range usingMap { + s.binder.usingJoinInfo[ts] = m + } + } } return nil } @@ -320,7 +326,7 @@ func checkForInvalidAliasUse(cte *sqlparser.CommonTableExpr, name string) (err e // TODO I'm sure there is a better. way, but we need to do this to stop infinite loops from occurring down := func(node sqlparser.SQLNode, parent sqlparser.SQLNode) bool { tbl, ok := node.(sqlparser.TableName) - if !ok || !tbl.Qualifier.IsEmpty() { + if !ok || tbl.Qualifier.NotEmpty() { return err == nil } if tbl.Name.String() == name { diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index 94b1302b357..91c535ffaff 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -26,6 +26,7 @@ import ( vschemapb "vitess.io/vitess/go/vt/proto/vschema" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -50,7 +51,7 @@ type ( authoritative() bool // getAliasedTableExpr returns the AST struct behind this table - getAliasedTableExpr() *sqlparser.AliasedTableExpr + GetAliasedTableExpr() *sqlparser.AliasedTableExpr // canShortCut will return nil when the keyspace needs to be checked, // and a true/false if the decision has been made already @@ -76,10 +77,12 @@ type ( // QuerySignature is used to identify shortcuts in the planning process QuerySignature struct { - Union bool Aggregation bool + DML bool Distinct bool + HashJoin bool SubQueries bool + Union bool } // SemTable contains semantic analysis information about the query. @@ -116,6 +119,8 @@ type ( // It doesn't recurse inside derived tables to find the original dependencies. Direct ExprDependencies + Targets map[sqlparser.IdentifierCS]TableSet + // ColumnEqualities is used for transitive closures (e.g., if a == b and b == c, then a == c). ColumnEqualities map[columnName][]sqlparser.Expr @@ -137,6 +142,8 @@ type ( // The map is keyed by the tableset of the table that each of the foreign key belongs to. childForeignKeysInvolved map[TableSet][]vindexes.ChildFKInfo parentForeignKeysInvolved map[TableSet][]vindexes.ParentFKInfo + childFkToUpdExprs map[string]sqlparser.UpdateExprs + collEnv *collations.Environment } columnName struct { @@ -144,12 +151,14 @@ type ( ColumnName string } - // SchemaInformation is used tp provide table information from Vschema. + // SchemaInformation is used to provide table information from Vschema. SchemaInformation interface { FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) ConnCollation() collations.ID + Environment() *vtenv.Environment // ForeignKeyMode returns the foreign_key flag value ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) + GetForeignKeyChecksState() *bool KeyspaceError(keyspace string) error } @@ -173,11 +182,18 @@ func (st *SemTable) CopyDependencies(from, to sqlparser.Expr) { st.Recursive[to] = st.RecursiveDeps(from) st.Direct[to] = st.DirectDeps(from) if ValidAsMapKey(from) { - st.ExprTypes[to] = st.ExprTypes[from] + if typ, found := st.ExprTypes[from]; found { + st.ExprTypes[to] = typ + } } } } +// GetChildForeignKeysForTable gets the child foreign keys as a list for the specified table. +func (st *SemTable) GetChildForeignKeysForTable(tableName sqlparser.TableName) []vindexes.ChildFKInfo { + return st.childForeignKeysInvolved[st.Targets[tableName.Name]] +} + // GetChildForeignKeysList gets the child foreign keys as a list. func (st *SemTable) GetChildForeignKeysList() []vindexes.ChildFKInfo { var childFkInfos []vindexes.ChildFKInfo @@ -196,6 +212,11 @@ func (st *SemTable) GetParentForeignKeysList() []vindexes.ParentFKInfo { return parentFkInfos } +// GetUpdateExpressionsForFk gets the update expressions for the given serialized foreign key constraint. +func (st *SemTable) GetUpdateExpressionsForFk(foreignKey string) sqlparser.UpdateExprs { + return st.childFkToUpdExprs[foreignKey] +} + // RemoveParentForeignKey removes the given foreign key from the parent foreign keys that sem table stores. func (st *SemTable) RemoveParentForeignKey(fkToIgnore string) error { for ts, fkInfos := range st.parentForeignKeysInvolved { @@ -279,6 +300,89 @@ func (st *SemTable) RemoveNonRequiredForeignKeys(verifyAllFks bool, getAction fu return nil } +// ErrIfFkDependentColumnUpdated checks if a foreign key column that is being updated is dependent on another column which also being updated. +func (st *SemTable) ErrIfFkDependentColumnUpdated(updateExprs sqlparser.UpdateExprs) error { + // Go over all the update expressions + for _, updateExpr := range updateExprs { + deps := st.RecursiveDeps(updateExpr.Name) + if deps.NumberOfTables() != 1 { + return vterrors.VT13001("expected to have single table dependency") + } + // Get all the child and parent foreign keys for the given table that the update expression belongs to. + childFks := st.childForeignKeysInvolved[deps] + parentFKs := st.parentForeignKeysInvolved[deps] + + involvedInFk := false + // Check if this updated column is part of any child or parent foreign key. + for _, childFk := range childFks { + if childFk.ParentColumns.FindColumn(updateExpr.Name.Name) >= 0 { + involvedInFk = true + break + } + } + for _, parentFk := range parentFKs { + if parentFk.ChildColumns.FindColumn(updateExpr.Name.Name) >= 0 { + involvedInFk = true + break + } + } + + if !involvedInFk { + continue + } + + // We cannot support updating a foreign key column that is using a column which is also being updated for 2 reasons— + // 1. For the child foreign keys, we aren't sure what the final value of the updated foreign key column will be. So we don't know + // what to cascade to the child. The selection that we do isn't enough to know if the updated value, since one of the columns used in the update is also being updated. + // 2. For the parent foreign keys, we don't know if we need to reject this update. Because we don't know the final updated value, the update might need to be failed, + // but we can't say for certain. + var dependencyUpdatedErr error + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + col, ok := node.(*sqlparser.ColName) + if !ok { + return true, nil + } + // self reference column dependency is not considered a dependent column being updated. + if st.EqualsExpr(updateExpr.Name, col) { + return true, nil + } + for _, updExpr := range updateExprs { + if st.EqualsExpr(updExpr.Name, col) { + dependencyUpdatedErr = vterrors.VT12001(fmt.Sprintf("%v column referenced in foreign key column %v is itself updated", sqlparser.String(col), sqlparser.String(updateExpr.Name))) + return false, nil + } + } + return false, nil + }, updateExpr.Expr) + if dependencyUpdatedErr != nil { + return dependencyUpdatedErr + } + } + return nil +} + +// HasNonLiteralForeignKeyUpdate checks for non-literal updates in expressions linked to a foreign key. +func (st *SemTable) HasNonLiteralForeignKeyUpdate(updExprs sqlparser.UpdateExprs) bool { + for _, updateExpr := range updExprs { + if sqlparser.IsLiteral(updateExpr.Expr) { + continue + } + parentFks := st.parentForeignKeysInvolved[st.RecursiveDeps(updateExpr.Name)] + for _, parentFk := range parentFks { + if parentFk.ChildColumns.FindColumn(updateExpr.Name.Name) >= 0 { + return true + } + } + childFks := st.childForeignKeysInvolved[st.RecursiveDeps(updateExpr.Name)] + for _, childFk := range childFks { + if childFk.ParentColumns.FindColumn(updateExpr.Name.Name) >= 0 { + return true + } + } + } + return false +} + // isShardScoped checks if the foreign key constraint is shard-scoped or not. It uses the vindex information to make this call. func isShardScoped(pTable *vindexes.Table, cTable *vindexes.Table, pCols sqlparser.Columns, cCols sqlparser.Columns) bool { if !pTable.Keyspace.Sharded { @@ -416,13 +520,14 @@ func EmptySemTable() *SemTable { Direct: map[sqlparser.Expr]TableSet{}, ColumnEqualities: map[columnName][]sqlparser.Expr{}, columns: map[*sqlparser.Union]sqlparser.SelectExprs{}, + ExprTypes: make(map[sqlparser.Expr]evalengine.Type), } } // TableSetFor returns the bitmask for this particular table func (st *SemTable) TableSetFor(t *sqlparser.AliasedTableExpr) TableSet { for idx, t2 := range st.Tables { - if t == t2.getAliasedTableExpr() { + if t == t2.GetAliasedTableExpr() { return SingleTableSet(idx) } } @@ -517,16 +622,13 @@ func (st *SemTable) TypeForExpr(e sqlparser.Expr) (evalengine.Type, bool) { // We add a lot of WeightString() expressions to queries at late stages of the planning, // which means that they don't have any type information. We can safely assume that they // are VarBinary, since that's the only type that WeightString() can return. - _, isWS := e.(*sqlparser.WeightStringFuncExpr) + ws, isWS := e.(*sqlparser.WeightStringFuncExpr) if isWS { - return evalengine.Type{ - Type: sqltypes.VarBinary, - Coll: collations.CollationBinaryID, - Nullable: false, // TODO: we should check if the argument is nullable - }, true + wt, _ := st.TypeForExpr(ws.Expr) + return evalengine.NewTypeEx(sqltypes.VarBinary, collations.CollationBinaryID, wt.Nullable(), 0, 0), true } - return evalengine.UnknownType(), false + return evalengine.Type{}, false } // NeedsWeightString returns true if the given expression needs weight_string to do safe comparisons @@ -539,7 +641,12 @@ func (st *SemTable) NeedsWeightString(e sqlparser.Expr) bool { if !found { return true } - return typ.Coll == collations.Unknown && !sqltypes.IsNumber(typ.Type) + + if !sqltypes.IsText(typ.Type()) { + return false + } + + return !st.collEnv.IsSupported(typ.Collation()) } } @@ -609,8 +716,7 @@ func RewriteDerivedTableExpression(expr sqlparser.Expr, vt TableInfo) sqlparser. // CopyExprInfo lookups src in the ExprTypes map and, if a key is found, assign // the corresponding Type value of src to dest. func (st *SemTable) CopyExprInfo(src, dest sqlparser.Expr) { - srcType, found := st.ExprTypes[src] - if found { + if srcType, found := st.ExprTypes[src]; found { st.ExprTypes[dest] = srcType } } @@ -624,6 +730,10 @@ func (st *SemTable) ColumnLookup(col *sqlparser.ColName) (int, error) { // SingleUnshardedKeyspace returns the single keyspace if all tables in the query are in the same, unsharded keyspace func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []*vindexes.Table) { + return singleUnshardedKeyspace(st.Tables) +} + +func singleUnshardedKeyspace(tableInfos []TableInfo) (ks *vindexes.Keyspace, tables []*vindexes.Table) { validKS := func(this *vindexes.Keyspace) bool { if this == nil || this.Sharded { return false @@ -638,7 +748,7 @@ func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []* return true } - for _, table := range st.Tables { + for _, table := range tableInfos { if _, isDT := table.(*DerivedTable); isDT { continue } @@ -669,6 +779,34 @@ func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []* return ks, tables } +// SingleUnshardedKeyspace returns the single keyspace if all tables in the query are in the same keyspace +func (st *SemTable) SingleKeyspace() (ks *vindexes.Keyspace) { + validKS := func(this *vindexes.Keyspace) bool { + if this == nil { + return true + } + if ks == nil { + // first keyspace we see + ks = this + } else if ks != this { + return false + } + return true + } + + for _, table := range st.Tables { + if _, isDT := table.(*DerivedTable); isDT { + continue + } + + vtbl := table.GetVindexTable() + if !validKS(vtbl.Keyspace) { + return nil + } + } + return +} + // EqualsExpr compares two expressions using the semantic analysis information. // This means that we use the binding info to recognize that two ColName's can point to the same // table column even though they are written differently. Example would be the `foobar` column in the following query: @@ -678,7 +816,7 @@ func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []* func (st *SemTable) EqualsExpr(a, b sqlparser.Expr) bool { // If there is no SemTable, then we cannot compare the expressions. if st == nil { - return false + return sqlparser.Equals.Expr(a, b) } return st.ASTEquals().Expr(a, b) } @@ -779,3 +917,23 @@ func (st *SemTable) ASTEquals() *sqlparser.Comparator { } return st.comparator } + +func (st *SemTable) Clone(n sqlparser.SQLNode) sqlparser.SQLNode { + return sqlparser.CopyOnRewrite(n, nil, func(cursor *sqlparser.CopyOnWriteCursor) { + expr, isExpr := cursor.Node().(sqlparser.Expr) + if !isExpr { + return + } + cursor.Replace(sqlparser.CloneExpr(expr)) + }, st.CopySemanticInfo) +} + +func (st *SemTable) UpdateChildFKExpr(origUpdExpr *sqlparser.UpdateExpr, newExpr sqlparser.Expr) { + for _, exprs := range st.childFkToUpdExprs { + for idx, updateExpr := range exprs { + if updateExpr == origUpdExpr { + exprs[idx].Expr = newExpr + } + } + } +} diff --git a/go/vt/vtgate/semantics/semantic_state_test.go b/go/vt/vtgate/semantics/semantic_state_test.go index ab855322d76..84f8cec6cf9 100644 --- a/go/vt/vtgate/semantics/semantic_state_test.go +++ b/go/vt/vtgate/semantics/semantic_state_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" querypb "vitess.io/vitess/go/vt/proto/query" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -45,7 +46,7 @@ func TestBindingAndExprEquality(t *testing.T) { for _, test := range tests { t.Run(test.expressions, func(t *testing.T) { - parse, err := sqlparser.Parse(fmt.Sprintf("select %s from t1, t2", test.expressions)) + parse, err := sqlparser.NewTestParser().Parse(fmt.Sprintf("select %s from t1, t2", test.expressions)) require.NoError(t, err) st, err := Analyze(parse, "db", fakeSchemaInfoTest()) require.NoError(t, err) @@ -418,7 +419,7 @@ func TestRemoveParentForeignKey(t *testing.T) { }, }, }, - fkToIgnore: "ks.t2child_coldks.t3cold", + fkToIgnore: "ks.t2|child_cold||ks.t3|cold", parentFksWanted: []vindexes.ParentFKInfo{ pkInfo(t3Table, []string{"colb"}, []string{"child_colb"}), pkInfo(t3Table, []string{"cola", "colx"}, []string{"child_cola", "child_colx"}), @@ -748,3 +749,233 @@ func TestRemoveNonRequiredForeignKeys(t *testing.T) { }) } } + +func TestIsFkDependentColumnUpdated(t *testing.T) { + keyspaceName := "ks" + t3Table := &vindexes.Table{ + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + Name: sqlparser.NewIdentifierCS("t3"), + } + tests := []struct { + name string + query string + fakeSi *FakeSI + updatedErr string + }{ + { + name: "updated child foreign key column is dependent on another updated column", + query: "update t1 set col = id + 1, id = 6 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(t3Table, []string{"col"}, []string{"col"}, sqlparser.Cascade), + }, + }, + }, + }, + updatedErr: "VT12001: unsupported: id column referenced in foreign key column col is itself updated", + }, { + name: "updated parent foreign key column is dependent on another updated column", + query: "update t1 set col = id + 1, id = 6 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + updatedErr: "VT12001: unsupported: id column referenced in foreign key column col is itself updated", + }, { + name: "no foreign key column is dependent on a updated value", + query: "update t1 set col = id + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + updatedErr: "", + }, { + name: "self-referenced foreign key", + query: "update t1 set col = col + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + updatedErr: "", + }, { + name: "no foreign keys", + query: "update t1 set col = id + 1, id = 6 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName, Sharded: true}, + }, + }, + }, + updatedErr: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stmt, err := sqlparser.NewTestParser().Parse(tt.query) + require.NoError(t, err) + semTable, err := Analyze(stmt, keyspaceName, tt.fakeSi) + require.NoError(t, err) + got := semTable.ErrIfFkDependentColumnUpdated(stmt.(*sqlparser.Update).Exprs) + if tt.updatedErr == "" { + require.NoError(t, got) + } else { + require.EqualError(t, got, tt.updatedErr) + } + }) + } +} + +func TestHasNonLiteralForeignKeyUpdate(t *testing.T) { + keyspaceName := "ks" + t3Table := &vindexes.Table{ + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + Name: sqlparser.NewIdentifierCS("t3"), + } + tests := []struct { + name string + query string + fakeSi *FakeSI + hasNonLiteral bool + }{ + { + name: "non literal child foreign key update", + query: "update t1 set col = id + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ChildForeignKeys: []vindexes.ChildFKInfo{ + ckInfo(t3Table, []string{"col"}, []string{"col"}, sqlparser.Cascade), + }, + }, + }, + }, + hasNonLiteral: true, + }, { + name: "non literal parent foreign key update", + query: "update t1 set col = id + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + hasNonLiteral: true, + }, { + name: "literal updates only", + query: "update t1 set col = 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + hasNonLiteral: false, + }, { + name: "self-referenced foreign key", + query: "update t1 set col = col + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + ParentForeignKeys: []vindexes.ParentFKInfo{ + pkInfo(t3Table, []string{"col"}, []string{"col"}), + }, + }, + }, + }, + hasNonLiteral: true, + }, { + name: "no foreign keys", + query: "update t1 set col = id + 1 where foo = 3", + fakeSi: &FakeSI{ + KsForeignKeyMode: map[string]vschemapb.Keyspace_ForeignKeyMode{ + keyspaceName: vschemapb.Keyspace_managed, + }, + Tables: map[string]*vindexes.Table{ + "t1": { + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: &vindexes.Keyspace{Name: keyspaceName}, + }, + }, + }, + hasNonLiteral: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stmt, err := sqlparser.NewTestParser().Parse(tt.query) + require.NoError(t, err) + semTable, err := Analyze(stmt, keyspaceName, tt.fakeSi) + require.NoError(t, err) + got := semTable.HasNonLiteralForeignKeyUpdate(stmt.(*sqlparser.Update).Exprs) + require.EqualValues(t, tt.hasNonLiteral, got) + }) + } +} diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index 12fb691874f..5bc160f52a6 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -17,6 +17,8 @@ limitations under the License. package semantics import ( + "fmt" + querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" @@ -33,17 +35,75 @@ type tableCollector struct { currentDb string org originable unionInfo map[*sqlparser.Union]unionInfo + done map[*sqlparser.AliasedTableExpr]TableInfo +} + +type earlyTableCollector struct { + si SchemaInformation + currentDb string + Tables []TableInfo + done map[*sqlparser.AliasedTableExpr]TableInfo + withTables map[sqlparser.IdentifierCS]any +} + +func newEarlyTableCollector(si SchemaInformation, currentDb string) *earlyTableCollector { + return &earlyTableCollector{ + si: si, + currentDb: currentDb, + done: map[*sqlparser.AliasedTableExpr]TableInfo{}, + withTables: map[sqlparser.IdentifierCS]any{}, + } +} + +func (etc *earlyTableCollector) up(cursor *sqlparser.Cursor) { + switch node := cursor.Node().(type) { + case *sqlparser.AliasedTableExpr: + etc.visitAliasedTableExpr(node) + case *sqlparser.With: + for _, cte := range node.CTEs { + etc.withTables[cte.ID] = nil + } + } +} + +func (etc *earlyTableCollector) visitAliasedTableExpr(aet *sqlparser.AliasedTableExpr) { + tbl, ok := aet.Expr.(sqlparser.TableName) + if !ok { + return + } + etc.handleTableName(tbl, aet) } -func newTableCollector(scoper *scoper, si SchemaInformation, currentDb string) *tableCollector { +func (etc *earlyTableCollector) newTableCollector(scoper *scoper, org originable) *tableCollector { return &tableCollector{ + Tables: etc.Tables, scoper: scoper, - si: si, - currentDb: currentDb, + si: etc.si, + currentDb: etc.currentDb, unionInfo: map[*sqlparser.Union]unionInfo{}, + done: etc.done, + org: org, } } +func (etc *earlyTableCollector) handleTableName(tbl sqlparser.TableName, aet *sqlparser.AliasedTableExpr) { + if tbl.Qualifier.IsEmpty() { + _, isCTE := etc.withTables[tbl.Name] + if isCTE { + // no need to handle these tables here, we wait for the late phase instead + return + } + } + tableInfo, err := getTableInfo(aet, tbl, etc.si, etc.currentDb) + if err != nil { + // this could just be a CTE that we haven't processed, so we'll give it the benefit of the doubt for now + return + } + + etc.done[aet] = tableInfo + etc.Tables = append(etc.Tables, tableInfo) +} + func (tc *tableCollector) up(cursor *sqlparser.Cursor) error { switch node := cursor.Node().(type) { case *sqlparser.AliasedTableExpr: @@ -101,25 +161,42 @@ func (tc *tableCollector) visitAliasedTableExpr(node *sqlparser.AliasedTableExpr return nil } -func (tc *tableCollector) handleTableName(node *sqlparser.AliasedTableExpr, t sqlparser.TableName) error { +func (tc *tableCollector) handleTableName(node *sqlparser.AliasedTableExpr, t sqlparser.TableName) (err error) { + var tableInfo TableInfo + var found bool + + tableInfo, found = tc.done[node] + if !found { + tableInfo, err = getTableInfo(node, t, tc.si, tc.currentDb) + if err != nil { + return err + } + tc.Tables = append(tc.Tables, tableInfo) + } + + scope := tc.scoper.currentScope() + return scope.addTable(tableInfo) +} + +func getTableInfo(node *sqlparser.AliasedTableExpr, t sqlparser.TableName, si SchemaInformation, currentDb string) (TableInfo, error) { var tbl *vindexes.Table var vindex vindexes.Vindex isInfSchema := sqlparser.SystemSchema(t.Qualifier.String()) var err error - tbl, vindex, _, _, _, err = tc.si.FindTableOrVindex(t) + tbl, vindex, _, _, _, err = si.FindTableOrVindex(t) if err != nil && !isInfSchema { // if we are dealing with a system table, it might not be available in the vschema, but that is OK - return err + return nil, err } if tbl == nil && vindex != nil { tbl = newVindexTable(t.Name) } - scope := tc.scoper.currentScope() - tableInfo := tc.createTable(t, node, tbl, isInfSchema, vindex) - - tc.Tables = append(tc.Tables, tableInfo) - return scope.addTable(tableInfo) + tableInfo, err := createTable(t, node, tbl, isInfSchema, vindex, si, currentDb) + if err != nil { + return nil, err + } + return tableInfo, nil } func (tc *tableCollector) handleDerivedTable(node *sqlparser.AliasedTableExpr, t *sqlparser.DerivedTable) error { @@ -207,7 +284,7 @@ func newVindexTable(t sqlparser.IdentifierCS) *vindexes.Table { // The code lives in this file since it is only touching tableCollector data func (tc *tableCollector) tableSetFor(t *sqlparser.AliasedTableExpr) TableSet { for i, t2 := range tc.Tables { - if t == t2.getAliasedTableExpr() { + if t == t2.GetAliasedTableExpr() { return SingleTableSet(i) } } @@ -223,24 +300,34 @@ func (tc *tableCollector) tableInfoFor(id TableSet) (TableInfo, error) { return tc.Tables[offset], nil } -func (tc *tableCollector) createTable( +func createTable( t sqlparser.TableName, alias *sqlparser.AliasedTableExpr, tbl *vindexes.Table, isInfSchema bool, vindex vindexes.Vindex, -) TableInfo { + si SchemaInformation, + currentDb string, +) (TableInfo, error) { + hint := getVindexHint(alias.Hints) + + if err := checkValidVindexHints(hint, tbl); err != nil { + return nil, err + } + table := &RealTable{ - tableName: alias.As.String(), - ASTNode: alias, - Table: tbl, - isInfSchema: isInfSchema, + tableName: alias.As.String(), + ASTNode: alias, + Table: tbl, + VindexHint: hint, + isInfSchema: isInfSchema, + collationEnv: si.Environment().CollationEnv(), } if alias.As.IsEmpty() { dbName := t.Qualifier.String() if dbName == "" { - dbName = tc.currentDb + dbName = currentDb } table.dbName = dbName @@ -251,7 +338,37 @@ func (tc *tableCollector) createTable( return &VindexTable{ Table: table, Vindex: vindex, + }, nil + } + return table, nil +} + +func checkValidVindexHints(hint *sqlparser.IndexHint, tbl *vindexes.Table) error { + if hint == nil { + return nil + } +outer: + for _, index := range hint.Indexes { + for _, columnVindex := range tbl.ColumnVindexes { + if index.EqualString(columnVindex.Name) { + continue outer + } + } + // we found a hint on a non-existing vindex + return &NoSuchVindexFound{ + Table: fmt.Sprintf("%s.%s", tbl.Keyspace.Name, tbl.Name.String()), + VindexName: index.String(), } } - return table + return nil +} + +// getVindexHint gets the vindex hint from the list of IndexHints. +func getVindexHint(hints sqlparser.IndexHints) *sqlparser.IndexHint { + for _, hint := range hints { + if hint.Type.IsVindexHint() { + return hint + } + } + return nil } diff --git a/go/vt/vtgate/semantics/table_set.go b/go/vt/vtgate/semantics/table_set.go index 0ddbc87a224..acc83306869 100644 --- a/go/vt/vtgate/semantics/table_set.go +++ b/go/vt/vtgate/semantics/table_set.go @@ -57,7 +57,7 @@ func (ts TableSet) NumberOfTables() int { } // NonEmpty returns true if there are tables in the tableset -func (ts TableSet) NonEmpty() bool { +func (ts TableSet) NotEmpty() bool { return !ts.IsEmpty() } diff --git a/go/vt/vtgate/semantics/typer.go b/go/vt/vtgate/semantics/typer.go index 625077f4da1..54261339114 100644 --- a/go/vt/vtgate/semantics/typer.go +++ b/go/vt/vtgate/semantics/typer.go @@ -18,7 +18,6 @@ package semantics import ( "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine/opcode" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -27,45 +26,41 @@ import ( // typer is responsible for setting the type for expressions // it does it's work after visiting the children (up), since the children types is often needed to type a node. type typer struct { - m map[sqlparser.Expr]evalengine.Type + m map[sqlparser.Expr]evalengine.Type + collationEnv *collations.Environment } -func newTyper() *typer { +func newTyper(collationEnv *collations.Environment) *typer { return &typer{ - m: map[sqlparser.Expr]evalengine.Type{}, + m: map[sqlparser.Expr]evalengine.Type{}, + collationEnv: collationEnv, } } func (t *typer) exprType(expr sqlparser.Expr) evalengine.Type { - res, ok := t.m[expr] - if ok { - return res - } - - return evalengine.UnknownType() + return t.m[expr] } func (t *typer) up(cursor *sqlparser.Cursor) error { switch node := cursor.Node().(type) { case *sqlparser.Literal: - t.m[node] = evalengine.Type{Type: node.SQLType(), Coll: collations.DefaultCollationForType(node.SQLType())} + t.m[node] = evalengine.NewType(node.SQLType(), collations.CollationForType(node.SQLType(), t.collationEnv.DefaultConnectionCharset())) case *sqlparser.Argument: if node.Type >= 0 { - t.m[node] = evalengine.Type{Type: node.Type, Coll: collations.DefaultCollationForType(node.Type)} + t.m[node] = evalengine.NewType(node.Type, collations.CollationForType(node.Type, t.collationEnv.DefaultConnectionCharset())) } case sqlparser.AggrFunc: code, ok := opcode.SupportedAggregates[node.AggrName()] if !ok { return nil } - inputType := sqltypes.Unknown + var inputType evalengine.Type if arg := node.GetArg(); arg != nil { if tt, ok := t.m[arg]; ok { - inputType = tt.Type + inputType = tt } } - type_ := code.Type(inputType) - t.m[node] = evalengine.Type{Type: type_, Coll: collations.DefaultCollationForType(type_)} + t.m[node] = code.ResolveType(inputType, t.collationEnv) } return nil } diff --git a/go/vt/vtgate/semantics/typer_test.go b/go/vt/vtgate/semantics/typer_test.go index 4c77e6f5657..7de5ecf1340 100644 --- a/go/vt/vtgate/semantics/typer_test.go +++ b/go/vt/vtgate/semantics/typer_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations/colldata" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" ) @@ -40,7 +41,7 @@ func TestNormalizerAndSemanticAnalysisIntegration(t *testing.T) { for _, test := range tests { t.Run(test.query, func(t *testing.T) { - parse, err := sqlparser.Parse(test.query) + parse, err := sqlparser.NewTestParser().Parse(test.query) require.NoError(t, err) err = sqlparser.Normalize(parse, sqlparser.NewReservedVars("bv", sqlparser.BindVars{}), map[string]*querypb.BindVariable{}) @@ -51,8 +52,43 @@ func TestNormalizerAndSemanticAnalysisIntegration(t *testing.T) { bv := parse.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr.(*sqlparser.Argument) typ, found := st.ExprTypes[bv] require.True(t, found, "bindvar was not typed") - require.Equal(t, test.typ, typ.Type.String()) + require.Equal(t, test.typ, typ.Type().String()) }) } +} + +// Tests that the types correctly picks up and sets the collation on columns +func TestColumnCollations(t *testing.T) { + tests := []struct { + query, collation string + }{ + {query: "select textcol from t2"}, + {query: "select name from t2", collation: "utf8mb3_bin"}, + } + + for _, test := range tests { + t.Run(test.query, func(t *testing.T) { + parse, err := sqlparser.NewTestParser().Parse(test.query) + require.NoError(t, err) + err = sqlparser.Normalize(parse, sqlparser.NewReservedVars("bv", sqlparser.BindVars{}), map[string]*querypb.BindVariable{}) + require.NoError(t, err) + + st, err := Analyze(parse, "d", fakeSchemaInfo()) + require.NoError(t, err) + col := extract(parse.(*sqlparser.Select), 0) + typ, found := st.TypeForExpr(col) + require.True(t, found, "column was not typed") + + require.Equal(t, "VARCHAR", typ.Type().String()) + collation := colldata.Lookup(typ.Collation()) + if test.collation != "" { + collation := colldata.Lookup(typ.Collation()) + require.NotNil(t, collation) + require.Equal(t, test.collation, collation.Name()) + } else { + require.Nil(t, collation) + } + }) + } } diff --git a/go/vt/vtgate/semantics/vindex_table.go b/go/vt/vtgate/semantics/vindex_table.go index f78e68cbd5b..fba8f8ab9a0 100644 --- a/go/vt/vtgate/semantics/vindex_table.go +++ b/go/vt/vtgate/semantics/vindex_table.go @@ -67,8 +67,8 @@ func (v *VindexTable) Name() (sqlparser.TableName, error) { } // GetExpr implements the TableInfo interface -func (v *VindexTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { - return v.Table.getAliasedTableExpr() +func (v *VindexTable) GetAliasedTableExpr() *sqlparser.AliasedTableExpr { + return v.Table.GetAliasedTableExpr() } func (v *VindexTable) canShortCut() shortCut { diff --git a/go/vt/vtgate/semantics/vtable.go b/go/vt/vtgate/semantics/vtable.go index 48439694b47..133e38ff505 100644 --- a/go/vt/vtgate/semantics/vtable.go +++ b/go/vt/vtgate/semantics/vtable.go @@ -70,7 +70,7 @@ func (v *vTableInfo) Name() (sqlparser.TableName, error) { return sqlparser.TableName{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "oh noes") } -func (v *vTableInfo) getAliasedTableExpr() *sqlparser.AliasedTableExpr { +func (v *vTableInfo) GetAliasedTableExpr() *sqlparser.AliasedTableExpr { return nil } @@ -94,7 +94,7 @@ func (v *vTableInfo) getColumns() []ColumnInfo { } func (v *vTableInfo) hasStar() bool { - return v.tables.NonEmpty() + return v.tables.NotEmpty() } // GetTables implements the TableInfo interface diff --git a/go/vt/vtgate/simplifier/expression_simplifier.go b/go/vt/vtgate/simplifier/expression_simplifier.go index 4537a137e76..5582ac3993d 100644 --- a/go/vt/vtgate/simplifier/expression_simplifier.go +++ b/go/vt/vtgate/simplifier/expression_simplifier.go @@ -21,7 +21,6 @@ import ( "strconv" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" ) @@ -44,10 +43,10 @@ func SimplifyExpr(in sqlparser.Expr, test CheckF) sqlparser.Expr { cursor.Replace(expr) valid := test(smallestKnown[0]) - log.Errorf("test: %t: simplified %s to %s, full expr: %s", valid, sqlparser.String(node), sqlparser.String(expr), sqlparser.String(smallestKnown)) if valid { break // we will still continue trying to simplify other expressions at this level } else { + log.Errorf("failed attempt: tried changing {%s} to {%s} in {%s}", sqlparser.String(node), sqlparser.String(expr), sqlparser.String(in)) // undo the change cursor.Replace(node) } @@ -105,6 +104,8 @@ func (s *shrinker) fillQueue() bool { s.queue = append(s.queue, e.Left, e.Right) case *sqlparser.BinaryExpr: s.queue = append(s.queue, e.Left, e.Right) + case *sqlparser.BetweenExpr: + s.queue = append(s.queue, e.Left, e.From, e.To) case *sqlparser.Literal: switch e.Type { case sqlparser.StrVal: diff --git a/go/vt/vtgate/simplifier/simplifier.go b/go/vt/vtgate/simplifier/simplifier.go index 0e19935caba..522da172557 100644 --- a/go/vt/vtgate/simplifier/simplifier.go +++ b/go/vt/vtgate/simplifier/simplifier.go @@ -201,7 +201,7 @@ func tryRemoveTable(tables []semantics.TableInfo, in sqlparser.SelectStatement, simplified := removeTable(clone, searchedTS, currentDB, si) name, _ := tbl.Name() if simplified && test(clone) { - log.Errorf("removed table %s: %s -> %s", sqlparser.String(name), sqlparser.String(in), sqlparser.String(clone)) + log.Errorf("removed table `%s`: \n%s\n%s", sqlparser.String(name), sqlparser.String(in), sqlparser.String(clone)) return clone } } diff --git a/go/vt/vtgate/simplifier/simplifier_test.go b/go/vt/vtgate/simplifier/simplifier_test.go index c9edbbab8d8..340497da8ef 100644 --- a/go/vt/vtgate/simplifier/simplifier_test.go +++ b/go/vt/vtgate/simplifier/simplifier_test.go @@ -20,14 +20,13 @@ import ( "fmt" "testing" - "vitess.io/vitess/go/vt/log" - - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/vt/vtgate/evalengine" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) func TestFindAllExpressions(t *testing.T) { @@ -51,7 +50,7 @@ order by unsharded.orderByExpr2 asc limit 123 offset 456 ` - ast, err := sqlparser.Parse(query) + ast, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) visitAllExpressionsInAST(ast.(sqlparser.SelectStatement), func(cursor expressionCursor) bool { fmt.Printf(">> found expression: %s\n", sqlparser.String(cursor.expr)) @@ -69,7 +68,7 @@ limit 123 offset 456 func TestAbortExpressionCursor(t *testing.T) { query := "select user.id, count(*), unsharded.name from user join unsharded on 13 = 14 where unsharded.id = 42 and name = 'foo' and user.id = unsharded.id" - ast, err := sqlparser.Parse(query) + ast, err := sqlparser.NewTestParser().Parse(query) require.NoError(t, err) visitAllExpressionsInAST(ast.(sqlparser.SelectStatement), func(cursor expressionCursor) bool { fmt.Println(sqlparser.String(cursor.expr)) @@ -121,16 +120,21 @@ func TestSimplifyEvalEngineExpr(t *testing.T) { // L0 p0 := plus(p11, p12) + venv := vtenv.NewTestEnv() expr := SimplifyExpr(p0, func(expr sqlparser.Expr) bool { - local, err := evalengine.Translate(expr, nil) + collationEnv := collations.MySQL8() + local, err := evalengine.Translate(expr, &evalengine.Config{ + Environment: venv, + Collation: collationEnv.DefaultConnectionCharset(), + }) if err != nil { return false } - res, err := evalengine.EmptyExpressionEnv().Evaluate(local) + res, err := evalengine.EmptyExpressionEnv(venv).Evaluate(local) if err != nil { return false } - toInt64, err := res.Value(collations.Default()).ToInt64() + toInt64, err := res.Value(collationEnv.DefaultConnectionCharset()).ToInt64() if err != nil { return false } diff --git a/go/vt/vtgate/tabletgateway.go b/go/vt/vtgate/tabletgateway.go index de63da87907..b289cf3b74e 100644 --- a/go/vt/vtgate/tabletgateway.go +++ b/go/vt/vtgate/tabletgateway.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" @@ -49,17 +50,16 @@ var ( // CellsToWatch is the list of cells the healthcheck operates over. If it is empty, only the local cell is watched CellsToWatch string - bufferImplementation = "keyspace_events" initialTabletTimeout = 30 * time.Second // retryCount is the number of times a query will be retried on error retryCount = 2 + + logCollations = logutil.NewThrottledLogger("CollationInconsistent", 1*time.Minute) ) func init() { servenv.OnParseFor("vtgate", func(fs *pflag.FlagSet) { fs.StringVar(&CellsToWatch, "cells_to_watch", "", "comma-separated list of cells for watching tablets") - fs.StringVar(&bufferImplementation, "buffer_implementation", "keyspace_events", "Allowed values: healthcheck (legacy implementation), keyspace_events (default)") - fs.MarkDeprecated("buffer_implementation", "The 'healthcheck' buffer implementation has been removed in v18 and this option will be removed in v19") fs.DurationVar(&initialTabletTimeout, "gateway_initial_tablet_timeout", 30*time.Second, "At startup, the tabletGateway will wait up to this duration to get at least one tablet per keyspace/shard/tablet type") fs.IntVar(&retryCount, "retry-count", 2, "retry count") }) @@ -74,7 +74,7 @@ type TabletGateway struct { srvTopoServer srvtopo.Server localCell string retryCount int - defaultConnCollation uint32 + defaultConnCollation atomic.Uint32 // mu protects the fields of this group. mu sync.Mutex @@ -92,7 +92,7 @@ func createHealthCheck(ctx context.Context, retryDelay, timeout time.Duration, t // NewTabletGateway creates and returns a new TabletGateway func NewTabletGateway(ctx context.Context, hc discovery.HealthCheck, serv srvtopo.Server, localCell string) *TabletGateway { - // hack to accomodate various users of gateway + tests + // hack to accommodate various users of gateway + tests if hc == nil { var topoServer *topo.Server if serv != nil { @@ -428,18 +428,23 @@ func (gw *TabletGateway) TabletsCacheStatus() discovery.TabletsCacheStatusList { return gw.hc.CacheStatus() } +// TabletsHealthyStatus returns a displayable version of the health check healthy list. +func (gw *TabletGateway) TabletsHealthyStatus() discovery.TabletsCacheStatusList { + return gw.hc.HealthyStatus() +} + func (gw *TabletGateway) updateDefaultConnCollation(tablet *topodatapb.Tablet) { - if atomic.CompareAndSwapUint32(&gw.defaultConnCollation, 0, tablet.DefaultConnCollation) { + if gw.defaultConnCollation.CompareAndSwap(0, tablet.DefaultConnCollation) { return } - if atomic.LoadUint32(&gw.defaultConnCollation) != tablet.DefaultConnCollation { - log.Warning("this Vitess cluster has tablets with different default connection collations") + if gw.defaultConnCollation.Load() != tablet.DefaultConnCollation { + logCollations.Warningf("this Vitess cluster has tablets with different default connection collations") } } // DefaultConnCollation returns the default connection collation of this TabletGateway func (gw *TabletGateway) DefaultConnCollation() collations.ID { - return collations.ID(atomic.LoadUint32(&gw.defaultConnCollation)) + return collations.ID(gw.defaultConnCollation.Load()) } // NewShardError returns a new error with the shard info amended. diff --git a/go/vt/vtgate/tabletgateway_flaky_test.go b/go/vt/vtgate/tabletgateway_flaky_test.go index f625b5599cd..21107c8d30e 100644 --- a/go/vt/vtgate/tabletgateway_flaky_test.go +++ b/go/vt/vtgate/tabletgateway_flaky_test.go @@ -22,15 +22,14 @@ import ( "github.com/stretchr/testify/require" - "vitess.io/vitess/go/test/utils" - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/discovery" + "vitess.io/vitess/go/vt/vtgate/buffer" + querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/vtgate/buffer" ) // TestGatewayBufferingWhenPrimarySwitchesServingState is used to test that the buffering mechanism buffers the queries when a primary goes to a non serving state and @@ -61,15 +60,29 @@ func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) - // add a primary tabelt which is serving + // add a primary tablet which is serving sbc := hc.AddTestTablet("cell", host, port, keyspace, shard, tabletType, true, 10, nil) + bufferingWaitTimeout := 60 * time.Second + waitForBuffering := func(enabled bool) { + timer := time.NewTimer(bufferingWaitTimeout) + defer timer.Stop() + for _, buffering := tg.kev.PrimaryIsNotServing(ctx, target); buffering != enabled; _, buffering = tg.kev.PrimaryIsNotServing(ctx, target) { + select { + case <-timer.C: + require.Fail(t, "timed out waiting for buffering of enabled: %t", enabled) + default: + } + time.Sleep(10 * time.Millisecond) + } + } + // add a result to the sandbox connection sqlResult1 := &sqltypes.Result{ Fields: []*querypb.Field{{ Name: "col1", Type: sqltypes.VarChar, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, RowsAffected: 1, Rows: [][]sqltypes.Value{{ @@ -94,6 +107,8 @@ func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { // add another result to the sandbox connection sbc.SetResults([]*sqltypes.Result{sqlResult1}) + waitForBuffering(true) + // execute the query in a go routine since it should be buffered, and check that it eventually succeed queryChan := make(chan struct{}) go func() { @@ -102,17 +117,17 @@ func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { }() // set the serving type for the primary tablet true and broadcast it so that the buffering code registers this change - // this should stop the buffering and the query executed in the go routine should work. This should be done with some delay so - // that we know that the query was buffered - time.Sleep(1 * time.Second) + // this should stop the buffering and the query executed in the go routine should work. hc.SetServing(primaryTablet, true) hc.Broadcast(primaryTablet) + waitForBuffering(false) + // wait for the query to execute before checking for results select { case <-queryChan: require.NoError(t, err) - require.Equal(t, res, sqlResult1) + require.Equal(t, sqlResult1, res) case <-time.After(15 * time.Second): t.Fatalf("timed out waiting for query to execute") } @@ -148,7 +163,7 @@ func TestGatewayBufferingWhileReparenting(t *testing.T) { tg := NewTabletGateway(ctx, hc, ts, "cell") defer tg.Close(ctx) - // add a primary tabelt which is serving + // add a primary tablet which is serving sbc := hc.AddTestTablet("cell", host, port, keyspace, shard, tabletType, true, 10, nil) // also add a replica which is serving sbcReplica := hc.AddTestTablet("cell", hostReplica, portReplica, keyspace, shard, topodatapb.TabletType_REPLICA, true, 0, nil) @@ -158,7 +173,7 @@ func TestGatewayBufferingWhileReparenting(t *testing.T) { Fields: []*querypb.Field{{ Name: "col1", Type: sqltypes.VarChar, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, RowsAffected: 1, Rows: [][]sqltypes.Value{{ @@ -279,7 +294,7 @@ func TestInconsistentStateDetectedBuffering(t *testing.T) { tg.retryCount = 0 - // add a primary tabelt which is serving + // add a primary tablet which is serving sbc := hc.AddTestTablet("cell", host, port, keyspace, shard, tabletType, true, 10, nil) // add a result to the sandbox connection @@ -287,7 +302,7 @@ func TestInconsistentStateDetectedBuffering(t *testing.T) { Fields: []*querypb.Field{{ Name: "col1", Type: sqltypes.VarChar, - Charset: uint32(collations.Default()), + Charset: uint32(collations.MySQL8().DefaultConnectionCharset()), }}, RowsAffected: 1, Rows: [][]sqltypes.Value{{ diff --git a/go/vt/vtgate/testdata/executorVSchema.json b/go/vt/vtgate/testdata/executorVSchema.json index da12a3b9946..ba917708df8 100644 --- a/go/vt/vtgate/testdata/executorVSchema.json +++ b/go/vt/vtgate/testdata/executorVSchema.json @@ -132,6 +132,13 @@ }, "cfc": { "type": "cfc" + }, + "multicol_vdx": { + "type": "multicol", + "params": { + "column_count": "2", + "column_vindex": "xxhash,binary" + } } }, "tables": { @@ -346,6 +353,14 @@ "zip_detail": { "type": "reference", "source": "TestUnsharded.zip_detail" - } - } + }, + "multicol_tbl": { + "column_vindexes": [ + { + "columns": ["cola", "colb"], + "name": "multicol_vdx" + } + ] + } + } } diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go index 9170093c23e..f21686d01d8 100644 --- a/go/vt/vtgate/tx_conn.go +++ b/go/vt/vtgate/tx_conn.go @@ -19,8 +19,10 @@ package vtgate import ( "context" "fmt" + "strings" "sync" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/dtids" "vitess.io/vitess/go/vt/log" @@ -34,6 +36,10 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +// nonAtomicCommitWarnMaxShards limits the number of shard names reported in +// non-atomic commit warnings. +const nonAtomicCommitWarnMaxShards = 16 + // TxConn is used for executing transactional requests. type TxConn struct { tabletGateway *TabletGateway @@ -132,8 +138,28 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error } // Retain backward compatibility on commit order for the normal session. - for _, shardSession := range session.ShardSessions { + for i, shardSession := range session.ShardSessions { if err := txc.commitShard(ctx, shardSession, session.logging); err != nil { + if i > 0 { + nShards := i + elipsis := false + if i > nonAtomicCommitWarnMaxShards { + nShards = nonAtomicCommitWarnMaxShards + elipsis = true + } + sNames := make([]string, nShards, nShards+1 /*...*/) + for j := 0; j < nShards; j++ { + sNames[j] = session.ShardSessions[j].Target.Shard + } + if elipsis { + sNames = append(sNames, "...") + } + session.RecordWarning(&querypb.QueryWarning{ + Code: uint32(sqlerror.ERNonAtomicCommit), + Message: fmt.Sprintf("multi-db commit failed after committing to %d shards: %s", i, strings.Join(sNames, ", ")), + }) + warnings.Add("NonAtomicCommit", 1) + } _ = txc.Release(ctx, session) return err } diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index 3fc141c64ac..4d77ea16c92 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -19,6 +19,7 @@ package vtgate import ( "context" "fmt" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -27,6 +28,7 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/srvtopo" @@ -41,6 +43,7 @@ import ( var queries = []*querypb.BoundQuery{{Sql: "query1"}} var twoQueries = []*querypb.BoundQuery{{Sql: "query1"}, {Sql: "query1"}} +var threeQueries = []*querypb.BoundQuery{{Sql: "query1"}, {Sql: "query1"}, {Sql: "query1"}} func TestTxConnBegin(t *testing.T) { ctx := utils.LeakCheckContext(t) @@ -67,12 +70,14 @@ func TestTxConnBegin(t *testing.T) { func TestTxConnCommitFailure(t *testing.T) { ctx := utils.LeakCheckContext(t) - sc, sbc0, sbc1, rss0, rss1, rss01 := newTestTxConnEnv(t, ctx, "TestTxConn") + sc, sbcs, rssm, rssa := newTestTxConnEnvNShards(t, ctx, "TestTxConn", 3) sc.txConn.mode = vtgatepb.TransactionMode_MULTI + nonAtomicCommitCount := warnings.Counts()["NonAtomicCommit"] // Sequence the executes to ensure commit order + session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.ExecuteMultiShard(ctx, nil, rss0, queries, session, false, false) + sc.ExecuteMultiShard(ctx, nil, rssm[0], queries, session, false, false) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -82,11 +87,12 @@ func TestTxConnCommitFailure(t *testing.T) { TabletType: topodatapb.TabletType_PRIMARY, }, TransactionId: 1, - TabletAlias: sbc0.Tablet().Alias, + TabletAlias: sbcs[0].Tablet().Alias, }}, } utils.MustMatch(t, &wantSession, session.Session, "Session") - sc.ExecuteMultiShard(ctx, nil, rss01, twoQueries, session, false, false) + + sc.ExecuteMultiShard(ctx, nil, rssm[1], queries, session, false, false) wantSession = vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -96,7 +102,7 @@ func TestTxConnCommitFailure(t *testing.T) { TabletType: topodatapb.TabletType_PRIMARY, }, TransactionId: 1, - TabletAlias: sbc0.Tablet().Alias, + TabletAlias: sbcs[0].Tablet().Alias, }, { Target: &querypb.Target{ Keyspace: "TestTxConn", @@ -104,23 +110,170 @@ func TestTxConnCommitFailure(t *testing.T) { TabletType: topodatapb.TabletType_PRIMARY, }, TransactionId: 1, - TabletAlias: sbc1.Tablet().Alias, + TabletAlias: sbcs[1].Tablet().Alias, + }}, + } + utils.MustMatch(t, &wantSession, session.Session, "Session") + + sc.ExecuteMultiShard(ctx, nil, rssa, threeQueries, session, false, false) + wantSession = vtgatepb.Session{ + InTransaction: true, + ShardSessions: []*vtgatepb.Session_ShardSession{{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "0", + TabletType: topodatapb.TabletType_PRIMARY, + }, + TransactionId: 1, + TabletAlias: sbcs[0].Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "1", + TabletType: topodatapb.TabletType_PRIMARY, + }, + TransactionId: 1, + TabletAlias: sbcs[1].Tablet().Alias, + }, { + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: "2", + TabletType: topodatapb.TabletType_PRIMARY, + }, + TransactionId: 1, + TabletAlias: sbcs[2].Tablet().Alias, }}, } utils.MustMatch(t, &wantSession, session.Session, "Session") - sbc1.MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 1 + sbcs[2].MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 1 expectErr := NewShardError(vterrors.New( vtrpcpb.Code_DEADLINE_EXCEEDED, fmt.Sprintf("%v error", vtrpcpb.Code_DEADLINE_EXCEEDED)), - rss1[0].Target) + rssm[2][0].Target) require.ErrorContains(t, sc.txConn.Commit(ctx, session), expectErr.Error()) - wantSession = vtgatepb.Session{} + wantSession = vtgatepb.Session{ + Warnings: []*querypb.QueryWarning{ + { + Code: uint32(sqlerror.ERNonAtomicCommit), + Message: "multi-db commit failed after committing to 2 shards: 0, 1", + }, + }, + } utils.MustMatch(t, &wantSession, session.Session, "Session") - assert.EqualValues(t, 1, sbc0.CommitCount.Load(), "sbc0.CommitCount") - assert.EqualValues(t, 1, sbc1.CommitCount.Load(), "sbc1.CommitCount") + assert.EqualValues(t, 1, sbcs[0].CommitCount.Load(), "sbc0.CommitCount") + + require.Equal(t, nonAtomicCommitCount+1, warnings.Counts()["NonAtomicCommit"]) +} + +func TestTxConnCommitFailureAfterNonAtomicCommitMaxShards(t *testing.T) { + ctx := utils.LeakCheckContext(t) + + sc, sbcs, rssm, _ := newTestTxConnEnvNShards(t, ctx, "TestTxConn", 18) + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + nonAtomicCommitCount := warnings.Counts()["NonAtomicCommit"] + + // Sequence the executes to ensure commit order + + session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + wantSession := vtgatepb.Session{ + InTransaction: true, + ShardSessions: []*vtgatepb.Session_ShardSession{}, + } + + for i := 0; i < 18; i++ { + sc.ExecuteMultiShard(ctx, nil, rssm[i], queries, session, false, false) + wantSession.ShardSessions = append(wantSession.ShardSessions, &vtgatepb.Session_ShardSession{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: rssm[i][0].Target.Shard, + TabletType: topodatapb.TabletType_PRIMARY, + }, + TransactionId: 1, + TabletAlias: sbcs[i].Tablet().Alias, + }) + utils.MustMatch(t, &wantSession, session.Session, "Session") + } + + sbcs[17].MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 1 + + expectErr := NewShardError(vterrors.New( + vtrpcpb.Code_DEADLINE_EXCEEDED, + fmt.Sprintf("%v error", vtrpcpb.Code_DEADLINE_EXCEEDED)), + rssm[17][0].Target) + + require.ErrorContains(t, sc.txConn.Commit(ctx, session), expectErr.Error()) + wantSession = vtgatepb.Session{ + Warnings: []*querypb.QueryWarning{ + { + Code: uint32(sqlerror.ERNonAtomicCommit), + Message: "multi-db commit failed after committing to 17 shards: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ...", + }, + }, + } + + utils.MustMatch(t, &wantSession, session.Session, "Session") + for i := 0; i < 17; i++ { + assert.EqualValues(t, 1, sbcs[i].CommitCount.Load(), fmt.Sprintf("sbc%d.CommitCount", i)) + } + + require.Equal(t, nonAtomicCommitCount+1, warnings.Counts()["NonAtomicCommit"]) +} + +func TestTxConnCommitFailureBeforeNonAtomicCommitMaxShards(t *testing.T) { + ctx := utils.LeakCheckContext(t) + + sc, sbcs, rssm, _ := newTestTxConnEnvNShards(t, ctx, "TestTxConn", 17) + sc.txConn.mode = vtgatepb.TransactionMode_MULTI + nonAtomicCommitCount := warnings.Counts()["NonAtomicCommit"] + + // Sequence the executes to ensure commit order + + session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + wantSession := vtgatepb.Session{ + InTransaction: true, + ShardSessions: []*vtgatepb.Session_ShardSession{}, + } + + for i := 0; i < 17; i++ { + sc.ExecuteMultiShard(ctx, nil, rssm[i], queries, session, false, false) + wantSession.ShardSessions = append(wantSession.ShardSessions, &vtgatepb.Session_ShardSession{ + Target: &querypb.Target{ + Keyspace: "TestTxConn", + Shard: rssm[i][0].Target.Shard, + TabletType: topodatapb.TabletType_PRIMARY, + }, + TransactionId: 1, + TabletAlias: sbcs[i].Tablet().Alias, + }) + utils.MustMatch(t, &wantSession, session.Session, "Session") + } + + sbcs[16].MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 1 + + expectErr := NewShardError(vterrors.New( + vtrpcpb.Code_DEADLINE_EXCEEDED, + fmt.Sprintf("%v error", vtrpcpb.Code_DEADLINE_EXCEEDED)), + rssm[16][0].Target) + + require.ErrorContains(t, sc.txConn.Commit(ctx, session), expectErr.Error()) + wantSession = vtgatepb.Session{ + Warnings: []*querypb.QueryWarning{ + { + Code: uint32(sqlerror.ERNonAtomicCommit), + Message: "multi-db commit failed after committing to 16 shards: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15", + }, + }, + } + + utils.MustMatch(t, &wantSession, session.Session, "Session") + for i := 0; i < 16; i++ { + assert.EqualValues(t, 1, sbcs[i].CommitCount.Load(), fmt.Sprintf("sbc%d.CommitCount", i)) + } + + require.Equal(t, nonAtomicCommitCount+1, warnings.Counts()["NonAtomicCommit"]) } func TestTxConnCommitSuccess(t *testing.T) { @@ -1359,17 +1512,40 @@ func TestTxConnAccessModeReset(t *testing.T) { func newTestTxConnEnv(t *testing.T, ctx context.Context, name string) (sc *ScatterConn, sbc0, sbc1 *sandboxconn.SandboxConn, rss0, rss1, rss01 []*srvtopo.ResolvedShard) { t.Helper() createSandbox(name) + sc, sbcs, rssl, rssa := newTestTxConnEnvNShards(t, ctx, name, 2) + return sc, sbcs[0], sbcs[1], rssl[0], rssl[1], rssa +} + +func newTestTxConnEnvNShards(t *testing.T, ctx context.Context, name string, n int) ( + sc *ScatterConn, sbcl []*sandboxconn.SandboxConn, rssl [][]*srvtopo.ResolvedShard, rssa []*srvtopo.ResolvedShard, +) { + t.Helper() + createSandbox(name) + hc := discovery.NewFakeHealthCheck(nil) sc = newTestScatterConn(ctx, hc, newSandboxForCells(ctx, []string{"aa"}), "aa") - sbc0 = hc.AddTestTablet("aa", "0", 1, name, "0", topodatapb.TabletType_PRIMARY, true, 1, nil) - sbc1 = hc.AddTestTablet("aa", "1", 1, name, "1", topodatapb.TabletType_PRIMARY, true, 1, nil) + + sNames := make([]string, n) + for i := 0; i < n; i++ { + sNames[i] = strconv.FormatInt(int64(i), 10) + } + + sbcl = make([]*sandboxconn.SandboxConn, len(sNames)) + for i, sName := range sNames { + sbcl[i] = hc.AddTestTablet("aa", sName, int32(i)+1, name, sName, topodatapb.TabletType_PRIMARY, true, 1, nil) + } + res := srvtopo.NewResolver(newSandboxForCells(ctx, []string{"aa"}), sc.gateway, "aa") - var err error - rss0, err = res.ResolveDestination(ctx, name, topodatapb.TabletType_PRIMARY, key.DestinationShard("0")) - require.NoError(t, err) - rss1, err = res.ResolveDestination(ctx, name, topodatapb.TabletType_PRIMARY, key.DestinationShard("1")) - require.NoError(t, err) - rss01, err = res.ResolveDestination(ctx, name, topodatapb.TabletType_PRIMARY, key.DestinationShards([]string{"0", "1"})) + + rssl = make([][]*srvtopo.ResolvedShard, len(sNames)) + for i, sName := range sNames { + rss, err := res.ResolveDestination(ctx, name, topodatapb.TabletType_PRIMARY, key.DestinationShard(sName)) + require.NoError(t, err) + rssl[i] = rss + } + + rssa, err := res.ResolveDestination(ctx, name, topodatapb.TabletType_PRIMARY, key.DestinationShards(sNames)) require.NoError(t, err) - return sc, sbc0, sbc1, rss0, rss1, rss01 + + return sc, sbcl, rssl, rssa } diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/vcursor_impl.go index 0e89d6fbc95..18508a2e94c 100644 --- a/go/vt/vtgate/vcursor_impl.go +++ b/go/vt/vtgate/vcursor_impl.go @@ -27,9 +27,9 @@ import ( "github.com/google/uuid" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/mysql/config" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/discovery" @@ -46,6 +46,7 @@ import ( "vitess.io/vitess/go/vt/topo" topoprotopb "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/buffer" "vitess.io/vitess/go/vt/vtgate/engine" @@ -83,6 +84,8 @@ type iExecute interface { ParseDestinationTarget(targetString string) (string, topodatapb.TabletType, key.Destination, error) VSchema() *vindexes.VSchema planPrepareStmt(ctx context.Context, vcursor *vcursorImpl, query string) (*engine.Plan, sqlparser.Statement, error) + + environment() *vtenv.Environment } // VSchemaOperator is an interface to Vschema Operations @@ -105,6 +108,11 @@ type vcursorImpl struct { logStats *logstats.LogStats collation collations.ID + // fkChecksState stores the state of foreign key checks variable. + // This state is meant to be the final fk checks state after consulting the + // session state, and the given query's comments for `SET_VAR` optimizer hints. + // A nil value represents that no foreign_key_checks value was provided. + fkChecksState *bool ignoreMaxMemoryRows bool vschema *vindexes.VSchema vm VSchemaOperator @@ -157,7 +165,7 @@ func newVCursorImpl( } } if connCollation == collations.Unknown { - connCollation = collations.Default() + connCollation = executor.env.CollationEnv().DefaultConnectionCharset() } warmingReadsPct := 0 @@ -201,10 +209,21 @@ func (vc *vcursorImpl) ConnCollation() collations.ID { return vc.collation } +// Environment returns the vtenv associated with this session +func (vc *vcursorImpl) Environment() *vtenv.Environment { + return vc.executor.environment() +} + func (vc *vcursorImpl) TimeZone() *time.Location { return vc.safeSession.TimeZone() } +func (vc *vcursorImpl) SQLMode() string { + // TODO: Implement return the current sql_mode. + // This is currently hardcoded to the default in MySQL 8.0. + return config.DefaultSQLMode +} + // MaxMemoryRows returns the maxMemoryRows flag value. func (vc *vcursorImpl) MaxMemoryRows() int { return maxMemoryRows @@ -1021,6 +1040,7 @@ func (vc *vcursorImpl) WarnUnshardedOnly(format string, params ...any) { Code: uint32(sqlerror.ERNotSupportedYet), Message: fmt.Sprintf(format, params...), }) + warnings.Add("WarnUnshardedOnly", 1) } } @@ -1071,7 +1091,7 @@ func (vc *vcursorImpl) keyForPlan(ctx context.Context, query string, buf io.Stri _, _ = buf.WriteString(vc.keyspace) _, _ = buf.WriteString(vindexes.TabletTypeSuffix[vc.tabletType]) _, _ = buf.WriteString("+Collate:") - _, _ = buf.WriteString(collations.Local().LookupName(vc.collation)) + _, _ = buf.WriteString(vc.Environment().CollationEnv().LookupName(vc.collation)) if vc.destination != nil { switch vc.destination.(type) { @@ -1229,7 +1249,7 @@ func (vc *vcursorImpl) ThrottleApp(ctx context.Context, throttledAppRule *topoda } func (vc *vcursorImpl) CanUseSetVar() bool { - return sqlparser.IsMySQL80AndAbove() && setVarEnabled + return vc.Environment().Parser().IsMySQL80AndAbove() && setVarEnabled } func (vc *vcursorImpl) ReleaseLock(ctx context.Context) error { @@ -1258,7 +1278,7 @@ func (vc *vcursorImpl) cloneWithAutocommitSession() *vcursorImpl { } func (vc *vcursorImpl) VExplainLogging() { - vc.safeSession.EnableLogging() + vc.safeSession.EnableLogging(vc.Environment().Parser()) } func (vc *vcursorImpl) GetVExplainLogs() []engine.ExecuteEntry { @@ -1331,3 +1351,22 @@ func (vc *vcursorImpl) CloneForReplicaWarming(ctx context.Context) engine.VCurso return v } + +// UpdateForeignKeyChecksState updates the foreign key checks state of the vcursor. +func (vc *vcursorImpl) UpdateForeignKeyChecksState(fkStateFromQuery *bool) { + // Initialize the state to unspecified. + vc.fkChecksState = nil + // If the query has a SET_VAR optimizer hint that explicitly sets the foreign key checks state, + // we should use that. + if fkStateFromQuery != nil { + vc.fkChecksState = fkStateFromQuery + return + } + // If the query doesn't have anything, then we consult the session state. + vc.fkChecksState = vc.safeSession.ForeignKeyChecks() +} + +// GetForeignKeyChecksState gets the stored foreign key checks state in the vcursor. +func (vc *vcursorImpl) GetForeignKeyChecksState() *bool { + return vc.fkChecksState +} diff --git a/go/vt/vtgate/vcursor_impl_test.go b/go/vt/vtgate/vcursor_impl_test.go index 3160b8a9b1a..b8e4a0d3a0a 100644 --- a/go/vt/vtgate/vcursor_impl_test.go +++ b/go/vt/vtgate/vcursor_impl_test.go @@ -184,9 +184,10 @@ func TestDestinationKeyspace(t *testing.T) { expectedError: errNoKeyspace.Error(), }} + r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(strconv.Itoa(i)+tc.targetString, func(t *testing.T) { - impl, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + impl, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) impl.vschema = tc.vschema dest, keyspace, tabletType, err := impl.TargetDestination(tc.qualifier) if tc.expectedError == "" { @@ -242,9 +243,10 @@ func TestSetTarget(t *testing.T) { expectedError: "can't execute the given command because you have an active transaction", }} + r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) { - vc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) + vc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4) vc.vschema = tc.vschema err := vc.SetTarget(tc.targetString) if tc.expectedError == "" { @@ -280,13 +282,22 @@ func TestKeyForPlan(t *testing.T) { vschema: vschemaWith1KS, targetString: "ks1[deadbeef]", expectedPlanPrefixKey: "ks1@primary+Collate:utf8mb4_0900_ai_ci+KsIDsResolved:80-+Query:SELECT 1", + }, { + vschema: vschemaWith1KS, + targetString: "", + expectedPlanPrefixKey: "ks1@primary+Collate:utf8mb4_0900_ai_ci+Query:SELECT 1", + }, { + vschema: vschemaWith1KS, + targetString: "ks1@replica", + expectedPlanPrefixKey: "ks1@replica+Collate:utf8mb4_0900_ai_ci+Query:SELECT 1", }} + r, _, _, _, _ := createExecutorEnv(t) for i, tc := range tests { t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) { ss := NewSafeSession(&vtgatepb.Session{InTransaction: false}) ss.SetTargetString(tc.targetString) - vc, err := newVCursorImpl(ss, sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) + vc, err := newVCursorImpl(ss, sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) require.NoError(t, err) vc.vschema = tc.vschema @@ -308,7 +319,8 @@ func TestFirstSortedKeyspace(t *testing.T) { ks3Schema.Keyspace.Name: ks3Schema, }} - vc, err := newVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) + r, _, _, _, _ := createExecutorEnv(t) + vc, err := newVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, r, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4) require.NoError(t, err) ks, err := vc.FirstSortedKeyspace() require.NoError(t, err) diff --git a/go/vt/vtgate/vindexes/cfc_test.go b/go/vt/vtgate/vindexes/cfc_test.go index 553d36de6c6..aaf639adec6 100644 --- a/go/vt/vtgate/vindexes/cfc_test.go +++ b/go/vt/vtgate/vindexes/cfc_test.go @@ -199,7 +199,7 @@ func TestCFCComputeKsid(t *testing.T) { testName: "misaligned prefix", id: [][]byte{{3, 4, 5}, {1}}, prefix: true, - // use the first component that's availabe + // use the first component that's available expected: expectedHash([][]byte{{3, 4, 5}}), err: nil, }, @@ -207,7 +207,7 @@ func TestCFCComputeKsid(t *testing.T) { testName: "misaligned prefix", id: [][]byte{{3, 4}}, prefix: true, - // use the first component that's availabe + // use the first component that's available expected: nil, err: nil, }, @@ -286,7 +286,7 @@ func TestCFCComputeKsidXxhash(t *testing.T) { testName: "misaligned prefix", id: [][]byte{{3, 4, 5}, {1}}, prefix: true, - // use the first component that's availabe + // use the first component that's available expected: expectedHashXX([][]byte{{3, 4, 5}}), err: nil, }, @@ -294,7 +294,7 @@ func TestCFCComputeKsidXxhash(t *testing.T) { testName: "misaligned prefix", id: [][]byte{{3, 4}}, prefix: true, - // use the first component that's availabe + // use the first component that's available expected: nil, err: nil, }, diff --git a/go/vt/vtgate/vindexes/consistent_lookup.go b/go/vt/vtgate/vindexes/consistent_lookup.go index d73631cc6ca..f32adc0f772 100644 --- a/go/vt/vtgate/vindexes/consistent_lookup.go +++ b/go/vt/vtgate/vindexes/consistent_lookup.go @@ -21,11 +21,13 @@ import ( "context" "encoding/json" "fmt" + "strings" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" querypb "vitess.io/vitess/go/vt/proto/query" @@ -170,7 +172,7 @@ func (lu *ConsistentLookup) UnknownParams() []string { return lu.unknownParams } -//==================================================================== +// ==================================================================== // ConsistentLookupUnique defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's @@ -270,7 +272,7 @@ func (lu *ConsistentLookupUnique) AutoCommitEnabled() bool { return lu.lkp.Autocommit } -//==================================================================== +// ==================================================================== // clCommon defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's @@ -308,7 +310,7 @@ func (lu *clCommon) SetOwnerInfo(keyspace, table string, cols []sqlparser.Identi lu.keyspace = keyspace lu.ownerTable = sqlparser.String(sqlparser.NewIdentifierCS(table)) if len(cols) != len(lu.lkp.FromColumns) { - return fmt.Errorf("owner table column count does not match vindex %s", lu.name) + return vterrors.VT03029(lu.name) } lu.ownerColumns = make([]string, len(cols)) for i, col := range cols { @@ -382,8 +384,7 @@ func (lu *clCommon) handleDup(ctx context.Context, vcursor VCursor, values []sql return err } // Lock the target row using normal transaction priority. - // TODO: context needs to be passed on. - qr, err = vcursor.ExecuteKeyspaceID(context.Background(), lu.keyspace, existingksid, lu.lockOwnerQuery, bindVars, false /* rollbackOnError */, false /* autocommit */) + qr, err = vcursor.ExecuteKeyspaceID(ctx, lu.keyspace, existingksid, lu.lockOwnerQuery, bindVars, false /* rollbackOnError */, false /* autocommit */) if err != nil { return err } @@ -411,7 +412,7 @@ func (lu *clCommon) Delete(ctx context.Context, vcursor VCursor, rowsColValues [ func (lu *clCommon) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { equal := true for i := range oldValues { - result, err := evalengine.NullsafeCompare(oldValues[i], newValues[i], vcursor.ConnCollation()) + result, err := evalengine.NullsafeCompare(oldValues[i], newValues[i], vcursor.Environment().CollationEnv(), vcursor.ConnCollation()) // errors from NullsafeCompare can be ignored. if they are real problems, we'll see them in the Create/Update if err != nil || result != 0 { equal = false @@ -433,7 +434,7 @@ func (lu *clCommon) MarshalJSON() ([]byte, error) { } func (lu *clCommon) generateLockLookup() string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "select %s from %s", lu.lkp.To, lu.lkp.Table) lu.addWhere(&buf, lu.lkp.FromColumns) fmt.Fprintf(&buf, " for update") @@ -441,7 +442,7 @@ func (lu *clCommon) generateLockLookup() string { } func (lu *clCommon) generateLockOwner() string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "select %s from %s", lu.ownerColumns[0], lu.ownerTable) lu.addWhere(&buf, lu.ownerColumns) // We can lock in share mode because we only want to check @@ -452,7 +453,7 @@ func (lu *clCommon) generateLockOwner() string { } func (lu *clCommon) generateInsertLookup() string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "insert into %s(", lu.lkp.Table) for _, col := range lu.lkp.FromColumns { fmt.Fprintf(&buf, "%s, ", col) @@ -466,13 +467,13 @@ func (lu *clCommon) generateInsertLookup() string { } func (lu *clCommon) generateUpdateLookup() string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "update %s set %s=:%s", lu.lkp.Table, lu.lkp.To, lu.lkp.To) lu.addWhere(&buf, lu.lkp.FromColumns) return buf.String() } -func (lu *clCommon) addWhere(buf *bytes.Buffer, cols []string) { +func (lu *clCommon) addWhere(buf *strings.Builder, cols []string) { buf.WriteString(" where ") for colIdx, column := range cols { if colIdx != 0 { @@ -488,7 +489,7 @@ func (lu *clCommon) GetCommitOrder() vtgatepb.CommitOrder { } // IsBackfilling implements the LookupBackfill interface -func (lu *ConsistentLookupUnique) IsBackfilling() bool { +func (lu *clCommon) IsBackfilling() bool { return lu.writeOnly } diff --git a/go/vt/vtgate/vindexes/consistent_lookup_test.go b/go/vt/vtgate/vindexes/consistent_lookup_test.go index deecc23ebdd..0279ecaba78 100644 --- a/go/vt/vtgate/vindexes/consistent_lookup_test.go +++ b/go/vt/vtgate/vindexes/consistent_lookup_test.go @@ -40,6 +40,7 @@ import ( vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" ) @@ -116,8 +117,9 @@ func TestConsistentLookupMap(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", false) vc := &loggingVCursor{} vc.AddResult(makeTestResultLookup([]int{2, 2}), nil) + ctx := newTestContext() - got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) + got, err := lookup.Map(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ @@ -135,10 +137,11 @@ func TestConsistentLookupMap(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false", }) + vc.verifyContext(t, ctx) // Test query fail. vc.AddResult(nil, fmt.Errorf("execute failed")) - _, err = lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}) + _, err = lookup.Map(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1)}) wantErr := "lookup.Map: execute failed" if err == nil || err.Error() != wantErr { t.Errorf("lookup(query fail) err: %v, want %s", err, wantErr) @@ -167,8 +170,9 @@ func TestConsistentLookupUniqueMap(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup_unique", false) vc := &loggingVCursor{} vc.AddResult(makeTestResultLookup([]int{0, 1}), nil) + ctx := newTestContext() - got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) + got, err := lookup.Map(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, @@ -180,10 +184,11 @@ func TestConsistentLookupUniqueMap(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false", }) + vc.verifyContext(t, ctx) // More than one result is invalid vc.AddResult(makeTestResultLookup([]int{2}), nil) - _, err = lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}) + _, err = lookup.Map(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1)}) wanterr := "Lookup.Map: unexpected multiple results from vindex t: INT64(1)" if err == nil || err.Error() != wanterr { t.Errorf("lookup(query fail) err: %v, want %s", err, wanterr) @@ -212,8 +217,9 @@ func TestConsistentLookupMapAbsent(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", false) vc := &loggingVCursor{} vc.AddResult(makeTestResultLookup([]int{0, 0}), nil) + ctx := newTestContext() - got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) + got, err := lookup.Map(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, @@ -225,6 +231,7 @@ func TestConsistentLookupMapAbsent(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupVerify(t *testing.T) { @@ -232,17 +239,19 @@ func TestConsistentLookupVerify(t *testing.T) { vc := &loggingVCursor{} vc.AddResult(makeTestResult(1), nil) vc.AddResult(makeTestResult(1), nil) + ctx := newTestContext() - _, err := lookup.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")}) + _, err := lookup.Verify(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")}) require.NoError(t, err) vc.verifyLog(t, []string{ "ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 1} {toc test1}] false", "ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 2} {toc test2}] false", }) + vc.verifyContext(t, ctx) // Test query fail. vc.AddResult(nil, fmt.Errorf("execute failed")) - _, err = lookup.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}) + _, err = lookup.Verify(ctx, vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}) want := "lookup.Verify: execute failed" if err == nil || err.Error() != want { t.Errorf("lookup(query fail) err: %v, want %s", err, want) @@ -250,7 +259,7 @@ func TestConsistentLookupVerify(t *testing.T) { // Test write_only. lookup = createConsistentLookup(t, "consistent_lookup", true) - got, err := lookup.Verify(context.Background(), nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) + got, err := lookup.Verify(ctx, nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) require.NoError(t, err) wantBools := []bool{true, true} if !reflect.DeepEqual(got, wantBools) { @@ -262,8 +271,9 @@ func TestConsistentLookupCreateSimple(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", false) vc := &loggingVCursor{} vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + if err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }, { @@ -275,6 +285,7 @@ func TestConsistentLookupCreateSimple(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0), (:fromc1_1, :fromc2_1, :toc_1) [{fromc1_0 1} {fromc1_1 3} {fromc2_0 2} {fromc2_1 4} {toc_0 test1} {toc_1 test2}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateThenRecreate(t *testing.T) { @@ -283,8 +294,9 @@ func TestConsistentLookupCreateThenRecreate(t *testing.T) { vc.AddResult(nil, sqlerror.NewSQLError(sqlerror.ERDupEntry, sqlerror.SSConstraintViolation, "Duplicate entry")) vc.AddResult(&sqltypes.Result{}, nil) vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + if err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("test1")}, false); err != nil { @@ -295,6 +307,7 @@ func TestConsistentLookupCreateThenRecreate(t *testing.T) { "ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false", "ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1, :fromc2, :toc) [{fromc1 1} {fromc2 2} {toc test1}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateThenUpdate(t *testing.T) { @@ -304,8 +317,9 @@ func TestConsistentLookupCreateThenUpdate(t *testing.T) { vc.AddResult(makeTestResult(1), nil) vc.AddResult(&sqltypes.Result{}, nil) vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + if err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("test1")}, false); err != nil { @@ -317,6 +331,7 @@ func TestConsistentLookupCreateThenUpdate(t *testing.T) { "ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc test1}] false", "ExecutePre update t set toc=:toc where fromc1 = :fromc1 and fromc2 = :fromc2 [{fromc1 1} {fromc2 2} {toc test1}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateThenSkipUpdate(t *testing.T) { @@ -326,8 +341,9 @@ func TestConsistentLookupCreateThenSkipUpdate(t *testing.T) { vc.AddResult(makeTestResult(1), nil) vc.AddResult(&sqltypes.Result{}, nil) vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + if err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("1")}, false); err != nil { @@ -338,6 +354,7 @@ func TestConsistentLookupCreateThenSkipUpdate(t *testing.T) { "ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc 1}] false", "ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc 1}] false", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateThenDupkey(t *testing.T) { @@ -347,8 +364,9 @@ func TestConsistentLookupCreateThenDupkey(t *testing.T) { vc.AddResult(makeTestResult(1), nil) vc.AddResult(makeTestResult(1), nil) vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("test1")}, false) @@ -359,14 +377,16 @@ func TestConsistentLookupCreateThenDupkey(t *testing.T) { "ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false", "ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc test1}] false", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateNonDupError(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", false) vc := &loggingVCursor{} vc.AddResult(nil, errors.New("general error")) + ctx := newTestContext() - err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("test1")}, false) @@ -377,6 +397,7 @@ func TestConsistentLookupCreateNonDupError(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupCreateThenBadRows(t *testing.T) { @@ -384,8 +405,9 @@ func TestConsistentLookupCreateThenBadRows(t *testing.T) { vc := &loggingVCursor{} vc.AddResult(nil, vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "(errno 1062) (sqlstate 23000) Duplicate entry")) vc.AddResult(makeTestResult(2), nil) + ctx := newTestContext() - err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{ + err := lookup.(Lookup).Create(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, [][]byte{[]byte("test1")}, false) @@ -397,14 +419,16 @@ func TestConsistentLookupCreateThenBadRows(t *testing.T) { "ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true", "ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupDelete(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", false) vc := &loggingVCursor{} vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{ + if err := lookup.(Lookup).Delete(ctx, vc, [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }}, []byte("test")); err != nil { @@ -413,6 +437,7 @@ func TestConsistentLookupDelete(t *testing.T) { vc.verifyLog(t, []string{ "ExecutePost delete from t where fromc1 = :fromc1 and fromc2 = :fromc2 and toc = :toc [{fromc1 1} {fromc2 2} {toc test}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupUpdate(t *testing.T) { @@ -420,8 +445,9 @@ func TestConsistentLookupUpdate(t *testing.T) { vc := &loggingVCursor{} vc.AddResult(&sqltypes.Result{}, nil) vc.AddResult(&sqltypes.Result{}, nil) + ctx := newTestContext() - if err := lookup.(Lookup).Update(context.Background(), vc, []sqltypes.Value{ + if err := lookup.(Lookup).Update(ctx, vc, []sqltypes.Value{ sqltypes.NewInt64(1), sqltypes.NewInt64(2), }, []byte("test"), []sqltypes.Value{ @@ -434,6 +460,7 @@ func TestConsistentLookupUpdate(t *testing.T) { "ExecutePost delete from t where fromc1 = :fromc1 and fromc2 = :fromc2 and toc = :toc [{fromc1 1} {fromc2 2} {toc test}] true", "ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 3} {fromc2_0 4} {toc_0 test}] true", }) + vc.verifyContext(t, ctx) } func TestConsistentLookupNoUpdate(t *testing.T) { @@ -510,13 +537,19 @@ func createConsistentLookup(t *testing.T, name string, writeOnly bool) SingleCol return l.(SingleColumn) } +func newTestContext() context.Context { + type testContextKey string // keep static checks from complaining about built-in types as context keys + return context.WithValue(context.Background(), (testContextKey)("test"), "foo") +} + var _ VCursor = (*loggingVCursor)(nil) type loggingVCursor struct { - results []*sqltypes.Result - errors []error - index int - log []string + results []*sqltypes.Result + errors []error + index int + log []string + contexts []context.Context } func (vc *loggingVCursor) LookupRowLockShardSession() vtgatepb.CommitOrder { @@ -528,7 +561,11 @@ func (vc *loggingVCursor) InTransactionAndIsDML() bool { } func (vc *loggingVCursor) ConnCollation() collations.ID { - return collations.Default() + return vc.Environment().CollationEnv().DefaultConnectionCharset() +} + +func (vc *loggingVCursor) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() } type bv struct { @@ -553,14 +590,14 @@ func (vc *loggingVCursor) Execute(ctx context.Context, method string, query stri case vtgatepb.CommitOrder_AUTOCOMMIT: name = "ExecuteAutocommit" } - return vc.execute(name, query, bindvars, rollbackOnError) + return vc.execute(ctx, name, query, bindvars, rollbackOnError) } func (vc *loggingVCursor) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) { - return vc.execute("ExecuteKeyspaceID", query, bindVars, rollbackOnError) + return vc.execute(ctx, "ExecuteKeyspaceID", query, bindVars, rollbackOnError) } -func (vc *loggingVCursor) execute(method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool) (*sqltypes.Result, error) { +func (vc *loggingVCursor) execute(ctx context.Context, method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool) (*sqltypes.Result, error) { if vc.index >= len(vc.results) { return nil, fmt.Errorf("ran out of results to return: %s", query) } @@ -570,6 +607,7 @@ func (vc *loggingVCursor) execute(method string, query string, bindvars map[stri } sort.Slice(bvl, func(i, j int) bool { return bvl[i].Name < bvl[j].Name }) vc.log = append(vc.log, fmt.Sprintf("%s %s %v %v", method, query, bvl, rollbackOnError)) + vc.contexts = append(vc.contexts, ctx) idx := vc.index vc.index++ if vc.errors[idx] != nil { @@ -593,6 +631,15 @@ func (vc *loggingVCursor) verifyLog(t *testing.T, want []string) { } } +func (vc *loggingVCursor) verifyContext(t *testing.T, want context.Context) { + t.Helper() + for i, got := range vc.contexts { + if got != want { + t.Errorf("context(%d):\ngot: %v\nwant: %v", i, got, want) + } + } +} + // create lookup result with one to one mapping func makeTestResult(numRows int) *sqltypes.Result { result := &sqltypes.Result{ diff --git a/go/vt/vtgate/vindexes/foreign_keys.go b/go/vt/vtgate/vindexes/foreign_keys.go index db984462b25..275a0674998 100644 --- a/go/vt/vtgate/vindexes/foreign_keys.go +++ b/go/vt/vtgate/vindexes/foreign_keys.go @@ -46,13 +46,13 @@ func (fk *ParentFKInfo) MarshalJSON() ([]byte, error) { func (fk *ParentFKInfo) String(childTable *Table) string { var str strings.Builder - str.WriteString(childTable.String()) + str.WriteString(sqlparser.String(childTable.GetTableName())) for _, column := range fk.ChildColumns { - str.WriteString(column.String()) + str.WriteString("|" + sqlparser.String(column)) } - str.WriteString(fk.Table.String()) + str.WriteString("||" + sqlparser.String(fk.Table.GetTableName())) for _, column := range fk.ParentColumns { - str.WriteString(column.String()) + str.WriteString("|" + sqlparser.String(column)) } return str.String() } @@ -91,13 +91,13 @@ func (fk *ChildFKInfo) MarshalJSON() ([]byte, error) { func (fk *ChildFKInfo) String(parentTable *Table) string { var str strings.Builder - str.WriteString(fk.Table.String()) + str.WriteString(sqlparser.String(fk.Table.GetTableName())) for _, column := range fk.ChildColumns { - str.WriteString(column.String()) + str.WriteString("|" + sqlparser.String(column)) } - str.WriteString(parentTable.String()) + str.WriteString("||" + sqlparser.String(parentTable.GetTableName())) for _, column := range fk.ParentColumns { - str.WriteString(column.String()) + str.WriteString("|" + sqlparser.String(column)) } return str.String() } @@ -144,3 +144,33 @@ func (vschema *VSchema) AddForeignKey(ksname, childTableName string, fkConstrain cTbl.ParentForeignKeys = append(cTbl.ParentForeignKeys, NewParentFkInfo(pTbl, fkConstraint)) return nil } + +// AddPrimaryKey is for testing only. +func (vschema *VSchema) AddPrimaryKey(ksname, tblName string, cols []string) error { + ks, ok := vschema.Keyspaces[ksname] + if !ok { + return fmt.Errorf("keyspace %s not found in vschema", ksname) + } + tbl, ok := ks.Tables[tblName] + if !ok { + return fmt.Errorf("table %s not found in keyspace %s", tblName, ksname) + } + for _, col := range cols { + tbl.PrimaryKey = append(tbl.PrimaryKey, sqlparser.NewIdentifierCI(col)) + } + return nil +} + +// AddUniqueKey is for testing only. +func (vschema *VSchema) AddUniqueKey(ksname, tblName string, exprs sqlparser.Exprs) error { + ks, ok := vschema.Keyspaces[ksname] + if !ok { + return fmt.Errorf("keyspace %s not found in vschema", ksname) + } + tbl, ok := ks.Tables[tblName] + if !ok { + return fmt.Errorf("table %s not found in keyspace %s", tblName, ksname) + } + tbl.UniqueKeys = append(tbl.UniqueKeys, exprs) + return nil +} diff --git a/go/vt/vtgate/vindexes/lookup.go b/go/vt/vtgate/vindexes/lookup.go index b3e14fa01f6..33462470010 100644 --- a/go/vt/vtgate/vindexes/lookup.go +++ b/go/vt/vtgate/vindexes/lookup.go @@ -181,6 +181,11 @@ func (ln *LookupNonUnique) MarshalJSON() ([]byte, error) { return json.Marshal(ln.lkp) } +// IsBackfilling implements the LookupBackfill interface +func (ln *LookupNonUnique) IsBackfilling() bool { + return ln.writeOnly +} + // Query implements the LookupPlanable interface func (ln *LookupNonUnique) Query() (selQuery string, arguments []string) { return ln.lkp.query() diff --git a/go/vt/vtgate/vindexes/lookup_hash.go b/go/vt/vtgate/vindexes/lookup_hash.go index de3d078f556..28f38942afa 100644 --- a/go/vt/vtgate/vindexes/lookup_hash.go +++ b/go/vt/vtgate/vindexes/lookup_hash.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + "vitess.io/vitess/go/vt/vterrors" ) const ( @@ -52,7 +53,7 @@ func init() { Register("lookup_hash_unique", newLookupHashUnique) } -//==================================================================== +// ==================================================================== // LookupHash defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's @@ -205,7 +206,7 @@ func (lh *LookupHash) Verify(ctx context.Context, vcursor VCursor, ids []sqltype values, err := unhashList(ksids) if err != nil { - return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) + return nil, vterrors.Wrap(err, "lookup.Verify.vunhash") } return lh.lkp.Verify(ctx, vcursor, ids, values) } @@ -214,7 +215,7 @@ func (lh *LookupHash) Verify(ctx context.Context, vcursor VCursor, ids []sqltype func (lh *LookupHash) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { values, err := unhashList(ksids) if err != nil { - return fmt.Errorf("lookup.Create.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Create.vunhash") } return lh.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) } @@ -223,7 +224,7 @@ func (lh *LookupHash) Create(ctx context.Context, vcursor VCursor, rowsColValues func (lh *LookupHash) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { v, err := vunhash(ksid) if err != nil { - return fmt.Errorf("lookup.Update.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Update.vunhash") } return lh.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) } @@ -232,7 +233,7 @@ func (lh *LookupHash) Update(ctx context.Context, vcursor VCursor, oldValues []s func (lh *LookupHash) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { v, err := vunhash(ksid) if err != nil { - return fmt.Errorf("lookup.Delete.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Delete.vunhash") } return lh.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) } @@ -242,7 +243,7 @@ func (lh *LookupHash) MarshalJSON() ([]byte, error) { return json.Marshal(lh.lkp) } -// UnknownParams satisifes the ParamValidating interface. +// UnknownParams satisfies the ParamValidating interface. func (lh *LookupHash) UnknownParams() []string { return lh.unknownParams } @@ -260,12 +261,12 @@ func unhashList(ksids [][]byte) ([]sqltypes.Value, error) { return values, nil } -//==================================================================== +// ==================================================================== // LookupHashUnique defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's // Unique and a Lookup. -// Warning: This Vindex is being depcreated in favor of LookupUnique +// Warning: This Vindex is being deprecated in favor of LookupUnique type LookupHashUnique struct { name string writeOnly bool @@ -383,7 +384,7 @@ func (lhu *LookupHashUnique) Verify(ctx context.Context, vcursor VCursor, ids [] values, err := unhashList(ksids) if err != nil { - return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) + return nil, vterrors.Wrap(err, "lookup.Verify.vunhash") } return lhu.lkp.Verify(ctx, vcursor, ids, values) } @@ -392,7 +393,7 @@ func (lhu *LookupHashUnique) Verify(ctx context.Context, vcursor VCursor, ids [] func (lhu *LookupHashUnique) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { values, err := unhashList(ksids) if err != nil { - return fmt.Errorf("lookup.Create.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Create.vunhash") } return lhu.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) } @@ -401,7 +402,7 @@ func (lhu *LookupHashUnique) Create(ctx context.Context, vcursor VCursor, rowsCo func (lhu *LookupHashUnique) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { v, err := vunhash(ksid) if err != nil { - return fmt.Errorf("lookup.Delete.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Delete.vunhash") } return lhu.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) } @@ -410,7 +411,7 @@ func (lhu *LookupHashUnique) Delete(ctx context.Context, vcursor VCursor, rowsCo func (lhu *LookupHashUnique) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { v, err := vunhash(ksid) if err != nil { - return fmt.Errorf("lookup.Update.vunhash: %v", err) + return vterrors.Wrap(err, "lookup.Update.vunhash") } return lhu.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) } diff --git a/go/vt/vtgate/vindexes/lookup_hash_test.go b/go/vt/vtgate/vindexes/lookup_hash_test.go index 69bff9f6f34..fd07f6ab7d8 100644 --- a/go/vt/vtgate/vindexes/lookup_hash_test.go +++ b/go/vt/vtgate/vindexes/lookup_hash_test.go @@ -236,10 +236,7 @@ func TestLookupHashCreate(t *testing.T) { } err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) - want := "lookup.Create: input has null values: row: 0, col: 0" - if err == nil || err.Error() != want { - t.Errorf("lookuphash.Create(NULL) err: %v, want %s", err, want) - } + require.ErrorContains(t, err, "VT03028: Column 'fromc' cannot be null on row 0, col 0") vc.queries = nil lookuphash.(*LookupHash).lkp.IgnoreNulls = true @@ -250,10 +247,7 @@ func TestLookupHashCreate(t *testing.T) { } err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("bogus")}, false /* ignoreMode */) - want = "lookup.Create.vunhash: invalid keyspace id: 626f677573" - if err == nil || err.Error() != want { - t.Errorf("lookuphash.Create(bogus) err: %v, want %s", err, want) - } + require.ErrorContains(t, err, "lookup.Create.vunhash: invalid keyspace id: 626f677573") } func TestLookupHashDelete(t *testing.T) { diff --git a/go/vt/vtgate/vindexes/lookup_internal.go b/go/vt/vtgate/vindexes/lookup_internal.go index 673b3fcb64b..5e224259d1d 100644 --- a/go/vt/vtgate/vindexes/lookup_internal.go +++ b/go/vt/vtgate/vindexes/lookup_internal.go @@ -24,13 +24,11 @@ import ( "strconv" "strings" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" ) const ( @@ -143,7 +141,7 @@ func (lkp *lookupInternal) Init(lookupQueryParams map[string]string, autocommit, // Lookup performs a lookup for the ids. func (lkp *lookupInternal) Lookup(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, co vtgatepb.CommitOrder) ([]*sqltypes.Result, error) { if vcursor == nil { - return nil, fmt.Errorf("cannot perform lookup: no vcursor provided") + return nil, vterrors.VT13001("cannot perform lookup: no vcursor provided") } results := make([]*sqltypes.Result, 0, len(ids)) if lkp.Autocommit { @@ -159,14 +157,14 @@ func (lkp *lookupInternal) Lookup(ctx context.Context, vcursor VCursor, ids []sq // for integral types, batch query all ids and then map them back to the input order vars, err := sqltypes.BuildBindVariable(ids) if err != nil { - return nil, fmt.Errorf("lookup.Map: %v", err) + return nil, err } bindVars := map[string]*querypb.BindVariable{ lkp.FromColumns[0]: vars, } result, err := vcursor.Execute(ctx, "VindexLookup", sel, bindVars, false /* rollbackOnError */, co) if err != nil { - return nil, fmt.Errorf("lookup.Map: %v", err) + return nil, vterrors.Wrap(err, "lookup.Map") } resultMap := make(map[string][][]sqltypes.Value) for _, row := range result.Rows { @@ -183,7 +181,7 @@ func (lkp *lookupInternal) Lookup(ctx context.Context, vcursor VCursor, ids []sq for _, id := range ids { vars, err := sqltypes.BuildBindVariable([]any{id}) if err != nil { - return nil, fmt.Errorf("lookup.Map: %v", err) + return nil, err } bindVars := map[string]*querypb.BindVariable{ lkp.FromColumns[0]: vars, @@ -191,7 +189,7 @@ func (lkp *lookupInternal) Lookup(ctx context.Context, vcursor VCursor, ids []sq var result *sqltypes.Result result, err = vcursor.Execute(ctx, "VindexLookup", sel, bindVars, false /* rollbackOnError */, co) if err != nil { - return nil, fmt.Errorf("lookup.Map: %v", err) + return nil, vterrors.Wrap(err, "lookup.Map") } rows := make([][]sqltypes.Value, 0, len(result.Rows)) for _, row := range result.Rows { @@ -223,7 +221,7 @@ func (lkp *lookupInternal) VerifyCustom(ctx context.Context, vcursor VCursor, id } result, err := vcursor.Execute(ctx, "VindexVerify", lkp.ver, bindVars, false /* rollbackOnError */, co) if err != nil { - return nil, fmt.Errorf("lookup.Verify: %v", err) + return nil, vterrors.Wrap(err, "lookup.Verify") } out[i] = (len(result.Rows) != 0) } @@ -290,7 +288,8 @@ nextRow: for j, col := range row { if col.IsNull() { if !lkp.IgnoreNulls { - return fmt.Errorf("lookup.Create: input has null values: row: %d, col: %d", i, j) + cols := strings.Join(lkp.FromColumns, ",") + return vterrors.VT03028(cols, i, j) } continue nextRow } @@ -304,7 +303,7 @@ nextRow: // We only need to check the first row. Number of cols per row // is guaranteed by the engine to be uniform. if len(trimmedRowsCols[0]) != len(lkp.FromColumns) { - return fmt.Errorf("lookup.Create: column vindex count does not match the columns in the lookup: %d vs %v", len(trimmedRowsCols[0]), lkp.FromColumns) + return vterrors.VT03030(lkp.FromColumns, len(trimmedRowsCols[0])) } sort.Sort(&sorter{rowsColValues: trimmedRowsCols, toValues: trimmedToValues}) @@ -312,16 +311,16 @@ nextRow: if lkp.MultiShardAutocommit { insStmt = "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */" } - buf := new(bytes.Buffer) + var buf strings.Builder if ignoreMode { - fmt.Fprintf(buf, "%s ignore into %s(", insStmt, lkp.Table) + fmt.Fprintf(&buf, "%s ignore into %s(", insStmt, lkp.Table) } else { - fmt.Fprintf(buf, "%s into %s(", insStmt, lkp.Table) + fmt.Fprintf(&buf, "%s into %s(", insStmt, lkp.Table) } for _, col := range lkp.FromColumns { - fmt.Fprintf(buf, "%s, ", col) + fmt.Fprintf(&buf, "%s, ", col) } - fmt.Fprintf(buf, "%s) values(", lkp.To) + fmt.Fprintf(&buf, "%s) values(", lkp.To) bindVars := make(map[string]*querypb.BindVariable, 2*len(trimmedRowsCols)) for rowIdx := range trimmedToValues { @@ -340,15 +339,15 @@ nextRow: } if lkp.Upsert { - fmt.Fprintf(buf, " on duplicate key update ") + fmt.Fprintf(&buf, " on duplicate key update ") for _, col := range lkp.FromColumns { - fmt.Fprintf(buf, "%s=values(%s), ", col, col) + fmt.Fprintf(&buf, "%s=values(%s), ", col, col) } - fmt.Fprintf(buf, "%s=values(%s)", lkp.To, lkp.To) + fmt.Fprintf(&buf, "%s=values(%s)", lkp.To, lkp.To) } if _, err := vcursor.Execute(ctx, "VindexCreate", buf.String(), bindVars, true /* rollbackOnError */, co); err != nil { - return fmt.Errorf("lookup.Create: %v", err) + return vterrors.Wrap(err, "lookup.Create") } return nil } @@ -356,7 +355,7 @@ nextRow: // Delete deletes the association between ids and value. // rowsColValues contains all the rows that are being deleted. // For each row, we store the value of each column defined in the vindex. -// value cointains the keyspace_id of the vindex entry being deleted. +// value contains the keyspace_id of the vindex entry being deleted. // // Given the following information in a vindex table with two columns: // @@ -380,7 +379,7 @@ func (lkp *lookupInternal) Delete(ctx context.Context, vcursor VCursor, rowsColV // We only need to check the first row. Number of cols per row // is guaranteed by the engine to be uniform. if len(rowsColValues[0]) != len(lkp.FromColumns) { - return fmt.Errorf("lookup.Delete: column vindex count does not match the columns in the lookup: %d vs %v", len(rowsColValues[0]), lkp.FromColumns) + return vterrors.VT03030(lkp.FromColumns, len(rowsColValues[0])) } for _, column := range rowsColValues { bindVars := make(map[string]*querypb.BindVariable, len(rowsColValues)) @@ -390,7 +389,7 @@ func (lkp *lookupInternal) Delete(ctx context.Context, vcursor VCursor, rowsColV bindVars[lkp.To] = sqltypes.ValueBindVariable(value) _, err := vcursor.Execute(ctx, "VindexDelete", lkp.del, bindVars, true /* rollbackOnError */, co) if err != nil { - return fmt.Errorf("lookup.Delete: %v", err) + return vterrors.Wrap(err, "lookup.Delete") } } return nil @@ -405,7 +404,7 @@ func (lkp *lookupInternal) Update(ctx context.Context, vcursor VCursor, oldValue } func (lkp *lookupInternal) initDelStmt() string { - var delBuffer bytes.Buffer + var delBuffer strings.Builder fmt.Fprintf(&delBuffer, "delete from %s where ", lkp.Table) for colIdx, column := range lkp.FromColumns { if colIdx != 0 { diff --git a/go/vt/vtgate/vindexes/lookup_test.go b/go/vt/vtgate/vindexes/lookup_test.go index a59fcbf1da9..8041a395a8e 100644 --- a/go/vt/vtgate/vindexes/lookup_test.go +++ b/go/vt/vtgate/vindexes/lookup_test.go @@ -35,6 +35,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" ) @@ -115,7 +116,11 @@ func (vc *vcursor) execute(query string, bindvars map[string]*querypb.BindVariab } func (vc *vcursor) ConnCollation() collations.ID { - return collations.Default() + return vc.Environment().CollationEnv().DefaultConnectionCharset() +} + +func (vc *vcursor) Environment() *vtenv.Environment { + return vtenv.NewTestEnv() } func lookupCreateVindexTestCase( @@ -369,7 +374,7 @@ func TestLookupNonUniqueNew(t *testing.T) { func TestLookupNilVCursor(t *testing.T) { lnu := createLookup(t, "lookup", false /* writeOnly */) _, err := lnu.Map(context.Background(), nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - require.EqualError(t, err, "cannot perform lookup: no vcursor provided") + require.EqualError(t, err, "VT13001: [BUG] cannot perform lookup: no vcursor provided") } func TestLookupNonUniqueMap(t *testing.T) { @@ -620,7 +625,7 @@ func TestLookupNonUniqueCreate(t *testing.T) { // With ignore_nulls off err = lnu.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(2)}, {sqltypes.NULL}}, [][]byte{[]byte("test2"), []byte("test1")}, true /* ignoreMode */) - assert.EqualError(t, err, "lookup.Create: input has null values: row: 1, col: 0") + assert.EqualError(t, err, "VT03028: Column 'fromc' cannot be null on row 1, col 0") // With ignore_nulls on vc.queries = nil @@ -644,7 +649,7 @@ func TestLookupNonUniqueCreate(t *testing.T) { // Test column mismatch. err = lnu.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) - assert.EqualError(t, err, "lookup.Create: column vindex count does not match the columns in the lookup: 2 vs [fromc]") + assert.EqualError(t, err, "VT03030: lookup column count does not match value count with the row (columns, count): ([fromc], 2)") } func TestLookupNonUniqueCreateAutocommit(t *testing.T) { @@ -710,7 +715,7 @@ func TestLookupNonUniqueDelete(t *testing.T) { // Test column count fail. err = lnu.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - assert.EqualError(t, err, "lookup.Delete: column vindex count does not match the columns in the lookup: 2 vs [fromc]") + assert.EqualError(t, err, "VT03030: lookup column count does not match value count with the row (columns, count): ([fromc], 2)") } func TestLookupNonUniqueDeleteAutocommit(t *testing.T) { diff --git a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash.go b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash.go index 070aee90305..f7af93187da 100644 --- a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash.go +++ b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash.go @@ -56,7 +56,7 @@ func init() { // LookupUnicodeLooseMD5Hash defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's // NonUnique and a Lookup and stores the from value in a hashed form. -// Warning: This Vindex is being depcreated in favor of Lookup +// Warning: This Vindex is being deprecated in favor of Lookup type LookupUnicodeLooseMD5Hash struct { name string writeOnly bool @@ -246,7 +246,7 @@ func (lh *LookupUnicodeLooseMD5Hash) UnknownParams() []string { // LookupUnicodeLooseMD5HashUnique defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's // Unique and a Lookup and will store the from value in a hashed format. -// Warning: This Vindex is being depcreated in favor of LookupUnique +// Warning: This Vindex is being deprecated in favor of LookupUnique type LookupUnicodeLooseMD5HashUnique struct { name string writeOnly bool diff --git a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go index 989458ccc13..c0e4611d684 100644 --- a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go +++ b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go @@ -320,10 +320,7 @@ func TestLookupUnicodeLooseMD5HashCreate(t *testing.T) { // Test column mismatch. err = lnu.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false) - want = "lookup.Create: column vindex count does not match the columns in the lookup: 2 vs [fromc]" - if err == nil || err.Error() != want { - t.Errorf("lookupNonUnique(query fail) err: %v, want %s", err, want) - } + require.ErrorContains(t, err, "VT03030: lookup column count does not match value count with the row (columns, count): ([fromc], 2)") } func TestLookupUnicodeLooseMD5HashCreateAutocommit(t *testing.T) { @@ -443,10 +440,7 @@ func TestLookupUnicodeLooseMD5HashDelete(t *testing.T) { // Test column count fail. err = lnu.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - want = "lookup.Delete: column vindex count does not match the columns in the lookup: 2 vs [fromc]" - if err == nil || err.Error() != want { - t.Errorf("lookupNonUnique(query fail) err: %v, want %s", err, want) - } + require.ErrorContains(t, err, "VT03030: lookup column count does not match value count with the row (columns, count): ([fromc], 2)") } func TestLookupUnicodeLooseMD5HashDeleteAutocommit(t *testing.T) { diff --git a/go/vt/vtgate/vindexes/vindex.go b/go/vt/vtgate/vindexes/vindex.go index a5295681248..e3d5a6d7e4d 100644 --- a/go/vt/vtgate/vindexes/vindex.go +++ b/go/vt/vtgate/vindexes/vindex.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" @@ -44,6 +45,7 @@ type ( InTransactionAndIsDML() bool LookupRowLockShardSession() vtgatepb.CommitOrder ConnCollation() collations.ID + Environment() *vtenv.Environment } // Vindex defines the interface required to register a vindex. diff --git a/go/vt/vtgate/vindexes/vschema.go b/go/vt/vtgate/vindexes/vschema.go index 4e9f527eb83..8559cabc447 100644 --- a/go/vt/vtgate/vindexes/vschema.go +++ b/go/vt/vtgate/vindexes/vschema.go @@ -25,17 +25,17 @@ import ( "strings" "time" - "vitess.io/vitess/go/sqlescape" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/evalengine" ) // TabletTypeSuffix maps the tablet type to its suffix string. @@ -53,6 +53,7 @@ var TabletTypeSuffix = map[topodatapb.TabletType]string{ // The following constants represent table types. const ( + TypeTable = "" TypeSequence = "sequence" TypeReference = "reference" ) @@ -118,6 +119,12 @@ type Table struct { ChildForeignKeys []ChildFKInfo `json:"child_foreign_keys,omitempty"` ParentForeignKeys []ParentFKInfo `json:"parent_foreign_keys,omitempty"` + + // index can be columns or expression. + // For Primary key, functional indexes are not allowed, therefore it will only be columns. + // MySQL error message: ERROR 3756 (HY000): The primary key cannot be a functional index + PrimaryKey sqlparser.Columns `json:"primary_key,omitempty"` + UniqueKeys []sqlparser.Exprs `json:"unique_keys,omitempty"` } // GetTableName gets the sqlparser.TableName for the vindex Table. @@ -148,6 +155,7 @@ type ColumnVindex struct { type TableInfo struct { Columns []Column ForeignKeys []*sqlparser.ForeignKeyDefinition + Indexes []*sqlparser.IndexDefinition } // IsUnique is used to tell whether the ColumnVindex @@ -178,20 +186,45 @@ type Column struct { Name sqlparser.IdentifierCI `json:"name"` Type querypb.Type `json:"type"` CollationName string `json:"collation_name"` + Default sqlparser.Expr `json:"default,omitempty"` // Invisible marks this as a column that will not be automatically included in `*` projections - Invisible bool `json:"invisible"` + Invisible bool `json:"invisible,omitempty"` + Size int32 `json:"size,omitempty"` + Scale int32 `json:"scale,omitempty"` + Nullable bool `json:"nullable,omitempty"` + // Values contains the list of values for enum and set types. + Values []string `json:"values,omitempty"` } // MarshalJSON returns a JSON representation of Column. func (col *Column) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Name string `json:"name"` - Type string `json:"type,omitempty"` + cj := struct { + Name string `json:"name"` + Type string `json:"type,omitempty"` + Invisible bool `json:"invisible,omitempty"` + Default string `json:"default,omitempty"` }{ Name: col.Name.String(), Type: querypb.Type_name[int32(col.Type)], - }) + } + if col.Invisible { + cj.Invisible = true + } + if col.Default != nil { + cj.Default = sqlparser.String(col.Default) + } + return json.Marshal(cj) +} + +func (col *Column) ToEvalengineType(collationEnv *collations.Environment) evalengine.Type { + var collation collations.ID + if sqltypes.IsText(col.Type) { + collation, _ = collationEnv.LookupID(col.CollationName) + } else { + collation = collations.CollationForType(col.Type, collationEnv.DefaultConnectionCharset()) + } + return evalengine.NewTypeEx(col.Type, collation, col.Nullable, col.Size, col.Scale) } // KeyspaceSchema contains the schema(table) for a keyspace. @@ -274,7 +307,7 @@ func (source *Source) String() string { } // BuildVSchema builds a VSchema from a SrvVSchema. -func BuildVSchema(source *vschemapb.SrvVSchema) (vschema *VSchema) { +func BuildVSchema(source *vschemapb.SrvVSchema, parser *sqlparser.Parser) (vschema *VSchema) { vschema = &VSchema{ RoutingRules: make(map[string]*RoutingRule), globalTables: make(map[string]*Table), @@ -282,22 +315,22 @@ func BuildVSchema(source *vschemapb.SrvVSchema) (vschema *VSchema) { Keyspaces: make(map[string]*KeyspaceSchema), created: time.Now(), } - buildKeyspaces(source, vschema) + buildKeyspaces(source, vschema, parser) // buildGlobalTables before buildReferences so that buildReferences can // resolve sources which reference global tables. buildGlobalTables(source, vschema) buildReferences(source, vschema) - buildRoutingRule(source, vschema) + buildRoutingRule(source, vschema, parser) buildShardRoutingRule(source, vschema) // Resolve auto-increments after routing rules are built since sequence tables also obey routing rules. - resolveAutoIncrement(source, vschema) + resolveAutoIncrement(source, vschema, parser) return vschema } // BuildKeyspaceSchema builds the vschema portion for one keyspace. // The build ignores sequence references because those dependencies can // go cross-keyspace. -func BuildKeyspaceSchema(input *vschemapb.Keyspace, keyspace string) (*KeyspaceSchema, error) { +func BuildKeyspaceSchema(input *vschemapb.Keyspace, keyspace string, parser *sqlparser.Parser) (*KeyspaceSchema, error) { if input == nil { input = &vschemapb.Keyspace{} } @@ -311,18 +344,18 @@ func BuildKeyspaceSchema(input *vschemapb.Keyspace, keyspace string) (*KeyspaceS uniqueVindexes: make(map[string]Vindex), Keyspaces: make(map[string]*KeyspaceSchema), } - buildKeyspaces(formal, vschema) + buildKeyspaces(formal, vschema, parser) err := vschema.Keyspaces[keyspace].Error return vschema.Keyspaces[keyspace], err } // BuildKeyspace ensures that the keyspace vschema is valid. // External references (like sequence) are not validated. -func BuildKeyspace(input *vschemapb.Keyspace) (*KeyspaceSchema, error) { - return BuildKeyspaceSchema(input, "") +func BuildKeyspace(input *vschemapb.Keyspace, parser *sqlparser.Parser) (*KeyspaceSchema, error) { + return BuildKeyspaceSchema(input, "", parser) } -func buildKeyspaces(source *vschemapb.SrvVSchema, vschema *VSchema) { +func buildKeyspaces(source *vschemapb.SrvVSchema, vschema *VSchema, parser *sqlparser.Parser) { for ksname, ks := range source.Keyspaces { ksvschema := &KeyspaceSchema{ Keyspace: &Keyspace{ @@ -334,7 +367,7 @@ func buildKeyspaces(source *vschemapb.SrvVSchema, vschema *VSchema) { Vindexes: make(map[string]Vindex), } vschema.Keyspaces[ksname] = ksvschema - ksvschema.Error = buildTables(ks, vschema, ksvschema) + ksvschema.Error = buildTables(ks, vschema, ksvschema, parser) } } @@ -346,12 +379,12 @@ func replaceUnspecifiedForeignKeyMode(fkMode vschemapb.Keyspace_ForeignKeyMode) return fkMode } -func (vschema *VSchema) AddView(ksname string, viewName, query string) error { +func (vschema *VSchema) AddView(ksname, viewName, query string, parser *sqlparser.Parser) error { ks, ok := vschema.Keyspaces[ksname] if !ok { return fmt.Errorf("keyspace %s not found in vschema", ksname) } - ast, err := sqlparser.Parse(query) + ast, err := parser.Parse(query) if err != nil { return err } @@ -520,7 +553,7 @@ func buildKeyspaceReferences(vschema *VSchema, ksvschema *KeyspaceSchema) error return nil } -func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSchema) error { +func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSchema, parser *sqlparser.Parser) error { keyspace := ksvschema.Keyspace for vname, vindexInfo := range ks.Vindexes { vindex, err := CreateVindex(vindexInfo.Type, vname, vindexInfo.Params) @@ -612,8 +645,31 @@ func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSc tname, ) } + var colDefault sqlparser.Expr + if col.Default != "" { + var err error + colDefault, err = parser.ParseExpr(col.Default) + if err != nil { + return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, + "could not parse the '%s' column's default expression '%s' for table '%s'", col.Name, col.Default, tname) + } + } + nullable := true + if col.Nullable != nil { + nullable = *col.Nullable + } colNames[name.Lowered()] = true - t.Columns = append(t.Columns, Column{Name: name, Type: col.Type, Invisible: col.Invisible}) + t.Columns = append(t.Columns, Column{ + Name: name, + Type: col.Type, + CollationName: col.CollationName, + Default: colDefault, + Invisible: col.Invisible, + Size: col.Size, + Scale: col.Scale, + Nullable: nullable, + Values: col.Values, + }) } // Initialize ColumnVindexes. @@ -751,7 +807,7 @@ func (vschema *VSchema) addTableName(t *Table) { } } -func resolveAutoIncrement(source *vschemapb.SrvVSchema, vschema *VSchema) { +func resolveAutoIncrement(source *vschemapb.SrvVSchema, vschema *VSchema, parser *sqlparser.Parser) { for ksname, ks := range source.Keyspaces { ksvschema := vschema.Keyspaces[ksname] for tname, table := range ks.Tables { @@ -759,7 +815,7 @@ func resolveAutoIncrement(source *vschemapb.SrvVSchema, vschema *VSchema) { if t == nil || table.AutoIncrement == nil { continue } - seqks, seqtab, err := sqlparser.ParseTable(table.AutoIncrement.Sequence) + seqks, seqtab, err := parser.ParseTable(table.AutoIncrement.Sequence) var seq *Table if err == nil { // Ensure that sequence tables also obey routing rules. @@ -795,10 +851,16 @@ func escapeQualifiedTable(qualifiedTableName string) (string, error) { if err != nil { return "", err } - return fmt.Sprintf("%s.%s", - // unescape() first in case an already escaped string was passed - sqlescape.EscapeID(sqlescape.UnescapeID(keyspace)), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName))), nil + // unescape() first in case an already escaped string was passed + keyspace, err = sqlescape.EnsureEscaped(keyspace) + if err != nil { + return "", err + } + tableName, err = sqlescape.EnsureEscaped(tableName) + if err != nil { + return "", err + } + return fmt.Sprintf("%s.%s", keyspace, tableName), nil } func extractTableParts(tableName string, allowUnqualified bool) (string, string, error) { @@ -835,7 +897,7 @@ func parseTable(tableName string) (sqlparser.TableName, error) { }, nil } -func buildRoutingRule(source *vschemapb.SrvVSchema, vschema *VSchema) { +func buildRoutingRule(source *vschemapb.SrvVSchema, vschema *VSchema, parser *sqlparser.Parser) { var err error if source.RoutingRules == nil { return @@ -878,7 +940,7 @@ outer: continue outer } - toKeyspace, toTableName, err := sqlparser.ParseTable(toTable) + toKeyspace, toTableName, err := parser.ParseTable(toTable) if err != nil { vschema.RoutingRules[rule.FromTable] = &RoutingRule{ diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go index a59ec78139d..ad892a66ccb 100644 --- a/go/vt/vtgate/vindexes/vschema_test.go +++ b/go/vt/vtgate/vindexes/vschema_test.go @@ -235,7 +235,7 @@ func init() { } func buildVSchema(source *vschemapb.SrvVSchema) (vschema *VSchema) { - vs := BuildVSchema(source) + vs := BuildVSchema(source, sqlparser.NewTestParser()) if vs != nil { vs.ResetCreated() } @@ -247,7 +247,7 @@ func TestUnshardedVSchemaValid(t *testing.T) { Sharded: false, Vindexes: make(map[string]*vschemapb.Vindex), Tables: make(map[string]*vschemapb.Table), - }) + }, sqlparser.NewTestParser()) require.NoError(t, err) } @@ -282,7 +282,7 @@ func TestForeignKeyMode(t *testing.T) { ForeignKeyMode: test.fkMode, Vindexes: make(map[string]*vschemapb.Vindex), Tables: make(map[string]*vschemapb.Table), - }) + }, sqlparser.NewTestParser()) require.NoError(t, err) require.Equal(t, test.wantedFkMode, ksSchema.ForeignKeyMode) }) @@ -297,7 +297,7 @@ func TestUnshardedVSchema(t *testing.T) { Tables: map[string]*vschemapb.Table{ "t1": {}}}}} - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) require.NoError(t, got.Keyspaces["unsharded"].Error) table, err := got.FindTable("unsharded", "t1") @@ -315,18 +315,22 @@ func TestVSchemaColumns(t *testing.T) { "unsharded": { Tables: map[string]*vschemapb.Table{ "t1": { - Columns: []*vschemapb.Column{{ - Name: "c1"}, { - Name: "c2", - Type: sqltypes.VarChar}}}}}}} - - got := BuildVSchema(&good) + Columns: []*vschemapb.Column{ + {Name: "c1"}, + {Name: "c2", Type: sqltypes.VarChar}, + {Name: "c3", Type: sqltypes.VarChar, Default: "''"}, + {Name: "c4", Type: sqltypes.TypeJSON, Default: "json_array()"}, + }}}}}} + + got := BuildVSchema(&good, sqlparser.NewTestParser()) require.NoError(t, got.Keyspaces["unsharded"].Error) t1, err := got.FindTable("unsharded", "t1") require.NoError(t, err) assertColumn(t, t1.Columns[0], "c1", sqltypes.Null) assertColumn(t, t1.Columns[1], "c2", sqltypes.VarChar) + assertColumnWithDefault(t, t1.Columns[2], "c3", sqltypes.VarChar, sqlparser.NewStrLiteral("")) + assertColumnWithDefault(t, t1.Columns[3], "c4", sqltypes.TypeJSON, &sqlparser.JSONArrayExpr{}) } func TestVSchemaViews(t *testing.T) { @@ -348,11 +352,11 @@ func TestVSchemaViews(t *testing.T) { }, { Name: "c2", Type: sqltypes.VarChar}}}}}}} - vschema := BuildVSchema(&good) + vschema := BuildVSchema(&good, sqlparser.NewTestParser()) require.NoError(t, vschema.Keyspaces["unsharded"].Error) // add view to unsharded keyspace. - vschema.AddView("unsharded", "v1", "SELECT c1+c2 AS added FROM t1") + vschema.AddView("unsharded", "v1", "SELECT c1+c2 AS added FROM t1", sqlparser.NewTestParser()) view := vschema.FindView("unsharded", "v1") assert.Equal(t, "select c1 + c2 as added from t1", sqlparser.String(view)) @@ -407,10 +411,10 @@ func TestVSchemaForeignKeys(t *testing.T) { }, { Name: "c2", Type: sqltypes.VarChar}}}}}}} - vschema := BuildVSchema(&good) + vschema := BuildVSchema(&good, sqlparser.NewTestParser()) require.NoError(t, vschema.Keyspaces["main"].Error) - // add fk containst a keyspace. + // add fk constraints to a keyspace. vschema.AddForeignKey("main", "t1", &sqlparser.ForeignKeyDefinition{ Source: sqlparser.Columns{sqlparser.NewIdentifierCI("c2")}, ReferenceDefinition: &sqlparser.ReferenceDefinition{ @@ -470,7 +474,7 @@ func TestVSchemaColumnListAuthoritative(t *testing.T) { Type: sqltypes.VarChar}}, ColumnListAuthoritative: true}}}}} - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) t1, err := got.FindTable("unsharded", "t1") require.NoError(t, err) @@ -489,7 +493,7 @@ func TestVSchemaColumnsFail(t *testing.T) { Name: "c1"}, { Name: "c1"}}}}}}} - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) require.EqualError(t, got.Keyspaces["unsharded"].Error, "duplicate column name 'c1' for table: t1") } @@ -502,7 +506,7 @@ func TestVSchemaPinned(t *testing.T) { "t1": { Pinned: "80"}}}}} - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error require.NoError(t, err) @@ -534,7 +538,7 @@ func TestShardedVSchemaOwned(t *testing.T) { Column: "c2", Name: "stln1"}}}}}}} - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error require.NoError(t, err) @@ -604,7 +608,7 @@ func TestShardedVSchemaOwnerInfo(t *testing.T) { }, }, } - got := BuildVSchema(&good) + got := BuildVSchema(&good, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error require.NoError(t, err) results := []struct { @@ -706,7 +710,7 @@ func TestVSchemaRoutingRules(t *testing.T) { }, }, } - got := BuildVSchema(&input) + got := BuildVSchema(&input, sqlparser.NewTestParser()) ks1 := &Keyspace{ Name: "ks1", Sharded: true, @@ -954,7 +958,7 @@ func TestFindBestColVindex(t *testing.T) { Tables: map[string]*vschemapb.Table{ "t2": {}}}}} - vs := BuildVSchema(testSrvVSchema) + vs := BuildVSchema(testSrvVSchema, sqlparser.NewTestParser()) testcases := []struct { tablename string @@ -1270,7 +1274,7 @@ func TestBuildVSchemaVindexNotFoundFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := `vindexType "noexist" not found` if err == nil || err.Error() != want { @@ -1294,7 +1298,7 @@ func TestBuildVSchemaNoColumnVindexFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "missing primary col vindex for table: t1" if err == nil || err.Error() != want { @@ -1579,7 +1583,7 @@ func TestBuildVSchemaNoindexFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "vindex notexist not found for table t1" if err == nil || err.Error() != want { @@ -1611,7 +1615,7 @@ func TestBuildVSchemaColumnAndColumnsFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := `can't use column and columns at the same time in vindex (stfu) and table (t1)` if err == nil || err.Error() != want { @@ -1641,7 +1645,7 @@ func TestBuildVSchemaNoColumnsFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := `must specify at least one column for vindex (stfu) and table (t1)` if err == nil || err.Error() != want { @@ -1672,7 +1676,7 @@ func TestBuildVSchemaNotUniqueFail(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "primary vindex stln is not Unique for table t1" if err == nil || err.Error() != want { @@ -1704,7 +1708,7 @@ func TestBuildVSchemaPrimaryCannotBeOwned(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "primary vindex stlu cannot be owned for table t1" if err == nil || err.Error() != want { @@ -1732,7 +1736,7 @@ func TestBuildVSchemaReferenceTableSourceMayBeUnqualified(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.NoError(t, vschema.Keyspaces["unsharded"].Error) require.NoError(t, vschema.Keyspaces["sharded"].Error) } @@ -1764,7 +1768,7 @@ func TestBuildVSchemaReferenceTableSourceMustBeInDifferentKeyspace(t *testing.T) }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["sharded"].Error) require.EqualError(t, vschema.Keyspaces["sharded"].Error, "source \"sharded.src\" may not reference a table in the same keyspace as table: ref") @@ -1784,7 +1788,7 @@ func TestBuildVSchemaReferenceTableSourceKeyspaceMustExist(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["sharded"].Error) require.EqualError(t, vschema.Keyspaces["sharded"].Error, "source \"unsharded.src\" references a non-existent keyspace \"unsharded\"") @@ -1810,7 +1814,7 @@ func TestBuildVSchemaReferenceTableSourceTableMustExist(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["sharded"].Error) require.EqualError(t, vschema.Keyspaces["sharded"].Error, "source \"unsharded.src\" references a table \"src\" that is not present in the VSchema of keyspace \"unsharded\"") @@ -1848,7 +1852,7 @@ func TestBuildVSchemaReferenceTableSourceMayUseShardedKeyspace(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.NoError(t, vschema.Keyspaces["sharded1"].Error) require.NoError(t, vschema.Keyspaces["sharded2"].Error) } @@ -1915,7 +1919,7 @@ func TestBuildVSchemaReferenceTableSourceTableMustBeBasicOrReferenceWithoutSourc }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["sharded1"].Error) require.EqualError(t, vschema.Keyspaces["sharded1"].Error, "source \"unsharded1.src1\" may not reference a table of type \"sequence\": ref1") @@ -1949,7 +1953,7 @@ func TestBuildVSchemaSourceMayBeReferencedAtMostOncePerKeyspace(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["sharded"].Error) require.EqualError(t, vschema.Keyspaces["sharded"].Error, "source \"unsharded.src\" may not be referenced more than once per keyspace: ref1, ref2") @@ -1987,7 +1991,7 @@ func TestBuildVSchemaMayNotChainReferences(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) require.Error(t, vschema.Keyspaces["unsharded1"].Error) require.EqualError(t, vschema.Keyspaces["unsharded1"].Error, "reference chaining is not allowed ref => unsharded2.ref => unsharded3.ref: ref") @@ -2189,7 +2193,7 @@ func TestBadSequence(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "cannot resolve sequence invalid_seq: table invalid_seq not found" if err == nil || err.Error() != want { @@ -2237,7 +2241,7 @@ func TestBadSequenceName(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "invalid table name: a.b.seq" if err == nil || !strings.Contains(err.Error(), want) { @@ -2261,7 +2265,7 @@ func TestBadShardedSequence(t *testing.T) { }, }, } - got := BuildVSchema(&bad) + got := BuildVSchema(&bad, sqlparser.NewTestParser()) err := got.Keyspaces["sharded"].Error want := "sequence table has to be in an unsharded keyspace or must be pinned: t1" if err == nil || err.Error() != want { @@ -2312,7 +2316,7 @@ func TestFindTable(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) _, err := vschema.FindTable("", "t1") require.EqualError(t, err, "ambiguous table reference: t1") @@ -2436,7 +2440,7 @@ func TestFindTableOrVindex(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) ta := vschema.Keyspaces["ksa"].Tables["ta"] t1 := vschema.Keyspaces["ksb"].Tables["t1"] @@ -2539,7 +2543,7 @@ func TestBuildKeyspaceSchema(t *testing.T) { "t2": {}, }, } - got, _ := BuildKeyspaceSchema(good, "ks") + got, _ := BuildKeyspaceSchema(good, "ks", sqlparser.NewTestParser()) err := got.Error require.NoError(t, err) ks := &Keyspace{ @@ -2581,7 +2585,7 @@ func TestValidate(t *testing.T) { "t2": {}, }, } - _, err := BuildKeyspace(good) + _, err := BuildKeyspace(good, sqlparser.NewTestParser()) require.NoError(t, err) bad := &vschemapb.Keyspace{ Sharded: true, @@ -2594,7 +2598,7 @@ func TestValidate(t *testing.T) { "t2": {}, }, } - _, err = BuildKeyspace(bad) + _, err = BuildKeyspace(bad, sqlparser.NewTestParser()) want := `vindexType "absent" not found` if err == nil || !strings.HasPrefix(err.Error(), want) { t.Errorf("Validate: %v, must start with %s", err, want) @@ -2687,8 +2691,9 @@ func TestVSchemaJSON(t *testing.T) { Columns: []Column{{ Name: sqlparser.NewIdentifierCI("c1"), }, { - Name: sqlparser.NewIdentifierCI("c2"), - Type: sqltypes.VarChar, + Name: sqlparser.NewIdentifierCI("c2"), + Type: sqltypes.VarChar, + Invisible: true, }}, }, "t2": { @@ -2761,7 +2766,8 @@ func TestVSchemaJSON(t *testing.T) { }, { "name": "c2", - "type": "VARCHAR" + "type": "VARCHAR", + "invisible": true } ] }, @@ -2788,7 +2794,7 @@ func TestFindSingleKeyspace(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) none := &Table{ Name: sqlparser.NewIdentifierCS("none"), Keyspace: &Keyspace{ @@ -2829,7 +2835,7 @@ func TestFindSingleKeyspace(t *testing.T) { }, }, } - vschema = BuildVSchema(&input) + vschema = BuildVSchema(&input, sqlparser.NewTestParser()) _, err := vschema.FindTable("", "none") wantErr := "table none not found" if err == nil || err.Error() != wantErr { @@ -2863,7 +2869,7 @@ func TestMultiColVindexPartialAllowed(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) table, err := vschema.FindTable("ksa", "user_region") require.NoError(t, err) require.Len(t, table.ColumnVindexes, 2) @@ -2896,7 +2902,7 @@ func TestMultiColVindexPartialNotAllowed(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) table, err := vschema.FindTable("ksa", "multiColTbl") require.NoError(t, err) require.Len(t, table.ColumnVindexes, 1) @@ -2933,7 +2939,7 @@ func TestSourceTableHasReferencedBy(t *testing.T) { }, }, } - vs := BuildVSchema(&input) + vs := BuildVSchema(&input, sqlparser.NewTestParser()) ref1, err := vs.FindTable("sharded1", "ref") require.NoError(t, err) ref2, err := vs.FindTable("sharded2", "ref") @@ -2967,7 +2973,7 @@ func TestReferenceTableAndSourceAreGloballyRoutable(t *testing.T) { }, } - vs := BuildVSchema(&input) + vs := BuildVSchema(&input, sqlparser.NewTestParser()) t1, err := vs.FindTable("unsharded", "t1") require.NoError(t, err) // If the source of a reference table does not require explicit routing, @@ -2977,11 +2983,11 @@ func TestReferenceTableAndSourceAreGloballyRoutable(t *testing.T) { require.Equal(t, t1, globalT1) input.Keyspaces["unsharded"].RequireExplicitRouting = true - vs = BuildVSchema(&input) + vs = BuildVSchema(&input, sqlparser.NewTestParser()) _, err = vs.FindTable("sharded", "t1") require.NoError(t, err) // If the source of a reference table requires explicit routing, then - // neither the reference table nor its souce can be globally routed. + // neither the reference table nor its source can be globally routed. _, err = vs.FindTable("", "t1") require.Error(t, err) require.EqualError(t, err, "table t1 not found") @@ -3013,7 +3019,7 @@ func TestOtherTablesMakeReferenceTableAndSourceAmbiguous(t *testing.T) { }, }, } - vs := BuildVSchema(&input) + vs := BuildVSchema(&input, sqlparser.NewTestParser()) _, err := vs.FindTable("", "t1") require.Error(t, err) } @@ -3114,7 +3120,7 @@ func TestFindTableWithSequences(t *testing.T) { }, }, } - vschema := BuildVSchema(&input) + vschema := BuildVSchema(&input, sqlparser.NewTestParser()) notFoundError := func(table string) string { return fmt.Sprintf("table %s not found", table) @@ -3165,5 +3171,11 @@ func assertVindexMatches(t *testing.T, cv *ColumnVindex, v Vindex, name string, func assertColumn(t *testing.T, col Column, expectedName string, expectedType querypb.Type) { assert.True(t, col.Name.EqualString(expectedName), "column name does not match") assert.Equal(t, expectedType, col.Type, "column type does not match") +} +func assertColumnWithDefault(t *testing.T, col Column, expectedName string, expectedType querypb.Type, expDefault sqlparser.Expr) { + assertColumn(t, col, expectedName, expectedType) + if expDefault != nil { + assert.Equal(t, expDefault, col.Default, "column default does not match") + } } diff --git a/go/vt/vtgate/vschema_manager.go b/go/vt/vtgate/vschema_manager.go index 7f2b7267dc0..f215fd9df11 100644 --- a/go/vt/vtgate/vschema_manager.go +++ b/go/vt/vtgate/vschema_manager.go @@ -23,6 +23,7 @@ import ( "vitess.io/vitess/go/vt/graph" "vitess.io/vitess/go/vt/log" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" @@ -44,6 +45,7 @@ type VSchemaManager struct { cell string subscriber func(vschema *vindexes.VSchema, stats *VSchemaStats) schema SchemaInfo + parser *sqlparser.Parser } // SchemaInfo is an interface to schema tracker. @@ -71,7 +73,7 @@ func (vm *VSchemaManager) UpdateVSchema(ctx context.Context, ksName string, vsch ks := vschema.Keyspaces[ksName] - _, err = vindexes.BuildKeyspace(ks) + _, err = vindexes.BuildKeyspace(ks, vm.parser) if err != nil { return err } @@ -132,7 +134,7 @@ func (vm *VSchemaManager) VSchemaUpdate(v *vschemapb.SrvVSchema, err error) bool if v == nil { // We encountered an error, build an empty vschema. if vm.currentVschema == nil { - vschema = vindexes.BuildVSchema(&vschemapb.SrvVSchema{}) + vschema = vindexes.BuildVSchema(&vschemapb.SrvVSchema{}, vm.parser) } } else { vschema = vm.buildAndEnhanceVSchema(v) @@ -187,7 +189,7 @@ func (vm *VSchemaManager) Rebuild() { // buildAndEnhanceVSchema builds a new VSchema and uses information from the schema tracker to update it func (vm *VSchemaManager) buildAndEnhanceVSchema(v *vschemapb.SrvVSchema) *vindexes.VSchema { - vschema := vindexes.BuildVSchema(v) + vschema := vindexes.BuildVSchema(v, vm.parser) if vm.schema != nil { vm.updateFromSchema(vschema) // We mark the keyspaces that have foreign key management in Vitess and have cyclic foreign keys @@ -210,19 +212,41 @@ func (vm *VSchemaManager) updateFromSchema(vschema *vindexes.VSchema) { // Now that we have ensured that all the tables are created, we can start populating the foreign keys // in the tables. for tblName, tblInfo := range m { + rTbl, err := vschema.FindRoutedTable(ksName, tblName, topodatapb.TabletType_PRIMARY) + if err != nil { + log.Errorf("error finding routed table %s: %v", tblName, err) + continue + } for _, fkDef := range tblInfo.ForeignKeys { + // Ignore internal tables as part of foreign key references. + if schema.IsInternalOperationTableName(fkDef.ReferenceDefinition.ReferencedTable.Name.String()) { + continue + } parentTbl, err := vschema.FindRoutedTable(ksName, fkDef.ReferenceDefinition.ReferencedTable.Name.String(), topodatapb.TabletType_PRIMARY) if err != nil { log.Errorf("error finding parent table %s: %v", fkDef.ReferenceDefinition.ReferencedTable.Name.String(), err) continue } - childTbl, err := vschema.FindRoutedTable(ksName, tblName, topodatapb.TabletType_PRIMARY) - if err != nil { - log.Errorf("error finding child table %s: %v", tblName, err) - continue + rTbl.ParentForeignKeys = append(rTbl.ParentForeignKeys, vindexes.NewParentFkInfo(parentTbl, fkDef)) + parentTbl.ChildForeignKeys = append(parentTbl.ChildForeignKeys, vindexes.NewChildFkInfo(rTbl, fkDef)) + } + for _, idxDef := range tblInfo.Indexes { + switch idxDef.Info.Type { + case sqlparser.IndexTypePrimary: + for _, idxCol := range idxDef.Columns { + rTbl.PrimaryKey = append(rTbl.PrimaryKey, idxCol.Column) + } + case sqlparser.IndexTypeUnique: + var uniqueKey sqlparser.Exprs + for _, idxCol := range idxDef.Columns { + if idxCol.Expression == nil { + uniqueKey = append(uniqueKey, sqlparser.NewColName(idxCol.Column.String())) + } else { + uniqueKey = append(uniqueKey, idxCol.Expression) + } + } + rTbl.UniqueKeys = append(rTbl.UniqueKeys, uniqueKey) } - childTbl.ParentForeignKeys = append(childTbl.ParentForeignKeys, vindexes.NewParentFkInfo(parentTbl, fkDef)) - parentTbl.ChildForeignKeys = append(parentTbl.ChildForeignKeys, vindexes.NewChildFkInfo(childTbl, fkDef)) } } diff --git a/go/vt/vtgate/vschema_manager_test.go b/go/vt/vtgate/vschema_manager_test.go index 9c51266c26a..f810d7c42af 100644 --- a/go/vt/vtgate/vschema_manager_test.go +++ b/go/vt/vtgate/vschema_manager_test.go @@ -18,11 +18,13 @@ func TestVSchemaUpdate(t *testing.T) { Type: querypb.Type_INT64, }} cols2 := []vindexes.Column{{ - Name: sqlparser.NewIdentifierCI("uid"), - Type: querypb.Type_INT64, + Name: sqlparser.NewIdentifierCI("uid"), + Type: querypb.Type_INT64, + Nullable: true, }, { - Name: sqlparser.NewIdentifierCI("name"), - Type: querypb.Type_VARCHAR, + Name: sqlparser.NewIdentifierCI("name"), + Type: querypb.Type_VARCHAR, + Nullable: true, }} ks := &vindexes.Keyspace{Name: "ks"} tblNoCol := &vindexes.Table{Name: sqlparser.NewIdentifierCS("tbl"), Keyspace: ks, ColumnListAuthoritative: true} @@ -82,6 +84,27 @@ func TestVSchemaUpdate(t *testing.T) { ParentColumns: sqlparserCols1, }) + idxTbl1 := &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("idxTbl1"), + Keyspace: ks, + ColumnListAuthoritative: true, + PrimaryKey: sqlparser.Columns{sqlparser.NewIdentifierCI("a")}, + UniqueKeys: []sqlparser.Exprs{ + {sqlparser.NewColName("b")}, + {sqlparser.NewColName("c"), sqlparser.NewColName("d")}, + }, + } + idxTbl2 := &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("idxTbl2"), + Keyspace: ks, + ColumnListAuthoritative: true, + PrimaryKey: sqlparser.Columns{sqlparser.NewIdentifierCI("a")}, + UniqueKeys: []sqlparser.Exprs{ + {&sqlparser.BinaryExpr{Operator: sqlparser.DivOp, Left: sqlparser.NewColName("b"), Right: sqlparser.NewIntLiteral("2")}}, + {sqlparser.NewColName("c"), &sqlparser.BinaryExpr{Operator: sqlparser.PlusOp, Left: sqlparser.NewColName("d"), Right: sqlparser.NewColName("e")}}, + }, + } + tcases := []struct { name string srvVschema *vschemapb.SrvVSchema @@ -192,41 +215,18 @@ func TestVSchemaUpdate(t *testing.T) { Sharded: false, ForeignKeyMode: vschemapb.Keyspace_managed, Tables: map[string]*vschemapb.Table{ - "t1": { - Columns: []*vschemapb.Column{ - { - Name: "id", - Type: querypb.Type_INT64, - }, - }, - }, - "t2": { - Columns: []*vschemapb.Column{ - { - Name: "id", - Type: querypb.Type_INT64, - }, - }, - }, + "t1": {Columns: []*vschemapb.Column{{Name: "id", Type: querypb.Type_INT64}}}, + "t2": {Columns: []*vschemapb.Column{{Name: "id", Type: querypb.Type_INT64}}}, "multicol_t1": { Columns: []*vschemapb.Column{ - { - Name: "uid", - Type: querypb.Type_INT64, - }, { - Name: "name", - Type: querypb.Type_VARCHAR, - }, + {Name: "uid", Type: querypb.Type_INT64}, + {Name: "name", Type: querypb.Type_VARCHAR}, }, - }, "multicol_t2": { + }, + "multicol_t2": { Columns: []*vschemapb.Column{ - { - Name: "uid", - Type: querypb.Type_INT64, - }, { - Name: "name", - Type: querypb.Type_VARCHAR, - }, + {Name: "uid", Type: querypb.Type_INT64}, + {Name: "name", Type: querypb.Type_VARCHAR}, }, }, }, @@ -249,6 +249,69 @@ func TestVSchemaUpdate(t *testing.T) { }, }, }, + }, { + name: "indexes in schema using columns", + currentVSchema: &vindexes.VSchema{}, + schema: map[string]*vindexes.TableInfo{ + "idxTbl1": { + Indexes: []*sqlparser.IndexDefinition{{ + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypePrimary}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("a")}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeUnique}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("b")}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeDefault}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("x")}, + {Column: sqlparser.NewIdentifierCI("y")}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeUnique}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("c")}, + {Column: sqlparser.NewIdentifierCI("d")}, + }, + }}, + }, + }, + srvVschema: makeTestSrvVSchema("ks", false, nil), + expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"idxTbl1": idxTbl1}), + }, { + name: "indexes in schema using expressions", + currentVSchema: &vindexes.VSchema{}, + schema: map[string]*vindexes.TableInfo{ + "idxTbl2": { + Indexes: []*sqlparser.IndexDefinition{{ + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypePrimary}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("a")}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeUnique}, + Columns: []*sqlparser.IndexColumn{ + {Expression: &sqlparser.BinaryExpr{Operator: sqlparser.DivOp, Left: sqlparser.NewColName("b"), Right: sqlparser.NewIntLiteral("2")}}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeDefault}, + Columns: []*sqlparser.IndexColumn{ + {Expression: &sqlparser.BinaryExpr{Operator: sqlparser.PlusOp, Left: sqlparser.NewColName("x"), Right: sqlparser.NewColName("y")}}, + }, + }, { + Info: &sqlparser.IndexInfo{Type: sqlparser.IndexTypeUnique}, + Columns: []*sqlparser.IndexColumn{ + {Column: sqlparser.NewIdentifierCI("c")}, + {Expression: &sqlparser.BinaryExpr{Operator: sqlparser.PlusOp, Left: sqlparser.NewColName("d"), Right: sqlparser.NewColName("e")}}, + }, + }}, + }, + }, + srvVschema: makeTestSrvVSchema("ks", false, nil), + expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"idxTbl2": idxTbl2}), }} vm := &VSchemaManager{} @@ -279,11 +342,13 @@ func TestRebuildVSchema(t *testing.T) { Type: querypb.Type_INT64, }} cols2 := []vindexes.Column{{ - Name: sqlparser.NewIdentifierCI("uid"), - Type: querypb.Type_INT64, + Name: sqlparser.NewIdentifierCI("uid"), + Type: querypb.Type_INT64, + Nullable: true, }, { - Name: sqlparser.NewIdentifierCI("name"), - Type: querypb.Type_VARCHAR, + Name: sqlparser.NewIdentifierCI("name"), + Type: querypb.Type_VARCHAR, + Nullable: true, }} ks := &vindexes.Keyspace{Name: "ks"} tblNoCol := &vindexes.Table{Name: sqlparser.NewIdentifierCS("tbl"), Keyspace: ks, ColumnListAuthoritative: true} @@ -373,56 +438,6 @@ func TestRebuildVSchema(t *testing.T) { } } -func makeTestVSchema(ks string, sharded bool, tbls map[string]*vindexes.Table) *vindexes.VSchema { - keyspaceSchema := &vindexes.KeyspaceSchema{ - Keyspace: &vindexes.Keyspace{ - Name: ks, - Sharded: sharded, - }, - // Default foreign key mode - ForeignKeyMode: vschemapb.Keyspace_unmanaged, - Tables: tbls, - Vindexes: map[string]vindexes.Vindex{}, - } - vs := makeTestEmptyVSchema() - vs.Keyspaces[ks] = keyspaceSchema - vs.ResetCreated() - return vs -} - -func makeTestEmptyVSchema() *vindexes.VSchema { - return &vindexes.VSchema{ - RoutingRules: map[string]*vindexes.RoutingRule{}, - Keyspaces: map[string]*vindexes.KeyspaceSchema{}, - } -} - -func makeTestSrvVSchema(ks string, sharded bool, tbls map[string]*vschemapb.Table) *vschemapb.SrvVSchema { - keyspaceSchema := &vschemapb.Keyspace{ - Sharded: sharded, - Tables: tbls, - // Default foreign key mode - ForeignKeyMode: vschemapb.Keyspace_unmanaged, - } - return &vschemapb.SrvVSchema{ - Keyspaces: map[string]*vschemapb.Keyspace{ks: keyspaceSchema}, - } -} - -type fakeSchema struct { - t map[string]*vindexes.TableInfo -} - -func (f *fakeSchema) Tables(string) map[string]*vindexes.TableInfo { - return f.t -} - -func (f *fakeSchema) Views(string) map[string]sqlparser.SelectStatement { - return nil -} - -var _ SchemaInfo = (*fakeSchema)(nil) - func TestMarkErrorIfCyclesInFk(t *testing.T) { ksName := "ks" keyspace := &vindexes.Keyspace{ @@ -462,7 +477,7 @@ func TestMarkErrorIfCyclesInFk(t *testing.T) { _ = vschema.AddForeignKey("ks", "t1", createFkDefinition([]string{"col"}, "t3", []string{"col"}, sqlparser.Cascade, sqlparser.Cascade)) return vschema }, - errWanted: "VT09019: ks has cyclic foreign keys", + errWanted: "VT09019: keyspace 'ks' has cyclic foreign keys", }, { name: "No cycle", @@ -508,9 +523,89 @@ func TestMarkErrorIfCyclesInFk(t *testing.T) { } } +// TestVSchemaUpdateWithFKReferenceToInternalTables tests that any internal table as part of fk reference is ignored. +func TestVSchemaUpdateWithFKReferenceToInternalTables(t *testing.T) { + ks := &vindexes.Keyspace{Name: "ks"} + cols1 := []vindexes.Column{{ + Name: sqlparser.NewIdentifierCI("id"), + Type: querypb.Type_INT64, + }} + sqlparserCols1 := sqlparser.MakeColumns("id") + + vindexTable_t1 := &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t1"), + Keyspace: ks, + Columns: cols1, + ColumnListAuthoritative: true, + } + vindexTable_t2 := &vindexes.Table{ + Name: sqlparser.NewIdentifierCS("t2"), + Keyspace: ks, + Columns: cols1, + ColumnListAuthoritative: true, + } + + vindexTable_t1.ChildForeignKeys = append(vindexTable_t1.ChildForeignKeys, vindexes.ChildFKInfo{ + Table: vindexTable_t2, + ChildColumns: sqlparserCols1, + ParentColumns: sqlparserCols1, + OnDelete: sqlparser.SetNull, + OnUpdate: sqlparser.Cascade, + }) + vindexTable_t2.ParentForeignKeys = append(vindexTable_t2.ParentForeignKeys, vindexes.ParentFKInfo{ + Table: vindexTable_t1, + ChildColumns: sqlparserCols1, + ParentColumns: sqlparserCols1, + }) + + vm := &VSchemaManager{} + var vs *vindexes.VSchema + vm.subscriber = func(vschema *vindexes.VSchema, _ *VSchemaStats) { + vs = vschema + vs.ResetCreated() + } + vm.schema = &fakeSchema{t: map[string]*vindexes.TableInfo{ + "t1": {Columns: cols1}, + "t2": { + Columns: cols1, + ForeignKeys: []*sqlparser.ForeignKeyDefinition{ + createFkDefinition([]string{"id"}, "t1", []string{"id"}, sqlparser.Cascade, sqlparser.SetNull), + createFkDefinition([]string{"id"}, "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", []string{"id"}, sqlparser.Cascade, sqlparser.SetNull), + }, + }, + }} + vm.VSchemaUpdate(&vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "ks": { + ForeignKeyMode: vschemapb.Keyspace_managed, + Tables: map[string]*vschemapb.Table{ + "t1": {Columns: []*vschemapb.Column{{Name: "id", Type: querypb.Type_INT64}}}, + "t2": {Columns: []*vschemapb.Column{{Name: "id", Type: querypb.Type_INT64}}}, + }, + }, + }, + }, nil) + + utils.MustMatchFn(".globalTables", ".uniqueVindexes")(t, &vindexes.VSchema{ + RoutingRules: map[string]*vindexes.RoutingRule{}, + Keyspaces: map[string]*vindexes.KeyspaceSchema{ + "ks": { + Keyspace: ks, + ForeignKeyMode: vschemapb.Keyspace_managed, + Vindexes: map[string]vindexes.Vindex{}, + Tables: map[string]*vindexes.Table{ + "t1": vindexTable_t1, + "t2": vindexTable_t2, + }, + }, + }, + }, vs) + utils.MustMatch(t, vs, vm.currentVschema, "currentVschema should have same reference as Vschema") +} + // createFkDefinition is a helper function to create a Foreign key definition struct from the columns used in it provided as list of strings. func createFkDefinition(childCols []string, parentTableName string, parentCols []string, onUpdate, onDelete sqlparser.ReferenceAction) *sqlparser.ForeignKeyDefinition { - pKs, pTbl, _ := sqlparser.ParseTable(parentTableName) + pKs, pTbl, _ := sqlparser.NewTestParser().ParseTable(parentTableName) return &sqlparser.ForeignKeyDefinition{ Source: sqlparser.MakeColumns(childCols...), ReferenceDefinition: &sqlparser.ReferenceDefinition{ @@ -521,3 +616,53 @@ func createFkDefinition(childCols []string, parentTableName string, parentCols [ }, } } + +func makeTestVSchema(ks string, sharded bool, tbls map[string]*vindexes.Table) *vindexes.VSchema { + keyspaceSchema := &vindexes.KeyspaceSchema{ + Keyspace: &vindexes.Keyspace{ + Name: ks, + Sharded: sharded, + }, + // Default foreign key mode + ForeignKeyMode: vschemapb.Keyspace_unmanaged, + Tables: tbls, + Vindexes: map[string]vindexes.Vindex{}, + } + vs := makeTestEmptyVSchema() + vs.Keyspaces[ks] = keyspaceSchema + vs.ResetCreated() + return vs +} + +func makeTestEmptyVSchema() *vindexes.VSchema { + return &vindexes.VSchema{ + RoutingRules: map[string]*vindexes.RoutingRule{}, + Keyspaces: map[string]*vindexes.KeyspaceSchema{}, + } +} + +func makeTestSrvVSchema(ks string, sharded bool, tbls map[string]*vschemapb.Table) *vschemapb.SrvVSchema { + keyspaceSchema := &vschemapb.Keyspace{ + Sharded: sharded, + Tables: tbls, + // Default foreign key mode + ForeignKeyMode: vschemapb.Keyspace_unmanaged, + } + return &vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ks: keyspaceSchema}, + } +} + +type fakeSchema struct { + t map[string]*vindexes.TableInfo +} + +func (f *fakeSchema) Tables(string) map[string]*vindexes.TableInfo { + return f.t +} + +func (f *fakeSchema) Views(string) map[string]sqlparser.SelectStatement { + return nil +} + +var _ SchemaInfo = (*fakeSchema)(nil) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index ffb8989ca5d..08553969a50 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -705,7 +705,7 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha // shouldRetry determines whether we should exit immediately or retry the vstream. // The first return value determines if the error can be retried, while the second -// indicates whether the tablet with which the error occurred should be ommitted +// indicates whether the tablet with which the error occurred should be omitted // from the candidate list of tablets to choose from on the retry. // // An error should be retried if it is expected to be transient. diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index b66ea93226e..477a1e40cae 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -30,6 +30,8 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" @@ -151,16 +153,8 @@ func registerFlags(fs *pflag.FlagSet) { fs.IntVar(&warmingReadsPercent, "warming-reads-percent", 0, "Percentage of reads on the primary to forward to replicas. Useful for keeping buffer pools warm") fs.IntVar(&warmingReadsConcurrency, "warming-reads-concurrency", 500, "Number of concurrent warming reads allowed") fs.DurationVar(&warmingReadsQueryTimeout, "warming-reads-query-timeout", 5*time.Second, "Timeout of warming read queries") - - _ = fs.String("schema_change_signal_user", "", "User to be used to send down query to vttablet to retrieve schema changes") - _ = fs.MarkDeprecated("schema_change_signal_user", "schema tracking uses an internal api and does not require a user to be specified") - - fs.Int64("gate_query_cache_size", 0, "gate server query cache size, maximum number of queries to be cached. vtgate analyzes every incoming query and generate a query plan, these plans are being cached in a cache. This config controls the expected amount of unique entries in the cache.") - _ = fs.MarkDeprecated("gate_query_cache_size", "`--gate_query_cache_size` is deprecated and will be removed in `v19.0`. This option only applied to LRU caches, which are now unsupported.") - - fs.Bool("gate_query_cache_lfu", false, "gate server cache algorithm. when set to true, a new cache algorithm based on a TinyLFU admission policy will be used to improve cache behavior and prevent pollution from sparse queries") - _ = fs.MarkDeprecated("gate_query_cache_lfu", "`--gate_query_cache_lfu` is deprecated and will be removed in `v19.0`. The query cache always uses a LFU implementation now.") } + func init() { servenv.OnParseFor("vtgate", registerFlags) servenv.OnParseFor("vtcombo", registerFlags) @@ -193,12 +187,12 @@ var ( // Error counters should be global so they can be set from anywhere errorCounts = stats.NewCountersWithMultiLabels("VtgateApiErrorCounts", "Vtgate API error counts per error type", []string{"Operation", "Keyspace", "DbType", "Code"}) - warnings = stats.NewCountersWithSingleLabel("VtGateWarnings", "Vtgate warnings", "type", "IgnoredSet", "ResultsExceeded", "WarnPayloadSizeExceeded") + warnings = stats.NewCountersWithSingleLabel("VtGateWarnings", "Vtgate warnings", "type", "IgnoredSet", "NonAtomicCommit", "ResultsExceeded", "WarnPayloadSizeExceeded", "WarnUnshardedOnly") vstreamSkewDelayCount = stats.NewCounter("VStreamEventsDelayedBySkewAlignment", "Number of events that had to wait because the skew across shards was too high") - vindexUnknownParams = stats.NewGauge("VindexUnknownParameters", "Number of parameterss unrecognized by Vindexes") + vindexUnknownParams = stats.NewGauge("VindexUnknownParameters", "Number of parameters unrecognized by Vindexes") timings = stats.NewMultiTimings( "VtgateApi", @@ -249,6 +243,7 @@ var RegisterVTGates []RegisterVTGate // Init initializes VTGate server. func Init( ctx context.Context, + env *vtenv.Environment, hc discovery.HealthCheck, serv srvtopo.Server, cell string, @@ -307,7 +302,7 @@ func Init( var si SchemaInfo // default nil var st *vtschema.Tracker if enableSchemaChangeSignal { - st = vtschema.NewTracker(gw.hc.Subscribe(), enableViews) + st = vtschema.NewTracker(gw.hc.Subscribe(), enableViews, env.Parser()) addKeyspacesToTracker(ctx, srvResolver, st, gw) si = st } @@ -316,6 +311,7 @@ func Init( executor := NewExecutor( ctx, + env, serv, cell, resolver, @@ -358,8 +354,10 @@ func Init( st.Start() } srv := initMySQLProtocol(vtgateInst) - servenv.OnTermSync(srv.shutdownMysqlProtocolAndDrain) - servenv.OnClose(srv.rollbackAtShutdown) + if srv != nil { + servenv.OnTermSync(srv.shutdownMysqlProtocolAndDrain) + servenv.OnClose(srv.rollbackAtShutdown) + } }) servenv.OnTerm(func() { if st != nil && enableSchemaChangeSignal { @@ -468,7 +466,7 @@ func (vtg *VTGate) Execute(ctx context.Context, mysqlCtx vtgateservice.MySQLConn "BindVariables": bindVariables, "Session": session, } - err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute) + err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute, vtg.executor.vm.parser) return session, nil, err } @@ -534,7 +532,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, mysqlCtx vtgateservice.MyS "BindVariables": bindVariables, "Session": session, } - return safeSession.Session, recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecute) + return safeSession.Session, recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecute, vtg.executor.vm.parser) } return safeSession.Session, nil } @@ -574,7 +572,7 @@ handleError: "BindVariables": bindVariables, "Session": session, } - err = recordAndAnnotateError(err, statsKey, query, vtg.logPrepare) + err = recordAndAnnotateError(err, statsKey, query, vtg.logPrepare, vtg.executor.vm.parser) return session, nil, err } @@ -593,7 +591,7 @@ func (vtg *VTGate) VSchemaStats() *VSchemaStats { return vtg.executor.VSchemaStats() } -func truncateErrorStrings(data map[string]any) map[string]any { +func truncateErrorStrings(data map[string]any, parser *sqlparser.Parser) map[string]any { ret := map[string]any{} if terseErrors { // request might have PII information. Return an empty map @@ -602,16 +600,16 @@ func truncateErrorStrings(data map[string]any) map[string]any { for key, val := range data { mapVal, ok := val.(map[string]any) if ok { - ret[key] = truncateErrorStrings(mapVal) + ret[key] = truncateErrorStrings(mapVal, parser) } else { strVal := fmt.Sprintf("%v", val) - ret[key] = sqlparser.TruncateForLog(strVal) + ret[key] = parser.TruncateForLog(strVal) } } return ret } -func recordAndAnnotateError(err error, statsKey []string, request map[string]any, logger *logutil.ThrottledLogger) error { +func recordAndAnnotateError(err error, statsKey []string, request map[string]any, logger *logutil.ThrottledLogger, parser *sqlparser.Parser) error { ec := vterrors.Code(err) fullKey := []string{ statsKey[0], @@ -627,7 +625,7 @@ func recordAndAnnotateError(err error, statsKey []string, request map[string]any } // Traverse the request structure and truncate any long values - request = truncateErrorStrings(request) + request = truncateErrorStrings(request, parser) errorCounts.Add(fullKey, 1) @@ -642,7 +640,7 @@ func recordAndAnnotateError(err error, statsKey []string, request map[string]any if !exists { return err } - piiSafeSQL, err2 := sqlparser.RedactSQLQuery(sql.(string)) + piiSafeSQL, err2 := parser.RedactSQLQuery(sql.(string)) if err2 != nil { return err } diff --git a/go/vt/vtorc/collection/collection.go b/go/vt/vtorc/collection/collection.go index 0ef9a71b9a3..4f679286978 100644 --- a/go/vt/vtorc/collection/collection.go +++ b/go/vt/vtorc/collection/collection.go @@ -260,8 +260,8 @@ func (c *Collection) removeBefore(t time.Time) error { // get the interval we need. if first == len(c.collection) { c.collection = nil // remove all entries - } else if first != -1 { - c.collection = c.collection[first:] + } else { + c.collection = c.collection[first+1:] } return nil // no errors } diff --git a/go/vt/vtorc/collection/collection_test.go b/go/vt/vtorc/collection/collection_test.go index 23679245c26..16ea6943dc7 100644 --- a/go/vt/vtorc/collection/collection_test.go +++ b/go/vt/vtorc/collection/collection_test.go @@ -19,6 +19,8 @@ package collection import ( "testing" "time" + + "github.com/stretchr/testify/assert" ) var randomString = []string{ @@ -28,6 +30,7 @@ var randomString = []string{ // some random base timestamp var ts = time.Date(2016, 12, 27, 13, 36, 40, 0, time.Local) +var ts2 = ts.AddDate(-1, 0, 0) // TestCreateOrReturn tests the creation of a named Collection func TestCreateOrReturnCollection(t *testing.T) { @@ -87,6 +90,13 @@ func (tm *testMetric) When() time.Time { return ts } +type testMetric2 struct { +} + +func (tm *testMetric2) When() time.Time { + return ts2 +} + // check that Append() works as expected func TestAppend(t *testing.T) { c := &Collection{} @@ -101,4 +111,121 @@ func TestAppend(t *testing.T) { t.Errorf("TestExpirePeriod: len(Metrics) = %d, expecting %d", len(c.Metrics()), v) } } + + // Test for nil metric + err := c.Append(nil) + assert.Error(t, err) + assert.Equal(t, err.Error(), "Collection.Append: m == nil") +} + +func TestNilCollection(t *testing.T) { + var c *Collection + + metrics := c.Metrics() + assert.Nil(t, metrics) + + err := c.Append(nil) + assert.Error(t, err) + assert.Equal(t, err.Error(), "Collection.Append: c == nil") + + err = c.removeBefore(ts) + assert.Error(t, err) + assert.Equal(t, err.Error(), "Collection.removeBefore: c == nil") + + // Should not throw any error for nil Collection + c.StartAutoExpiration() + c.StopAutoExpiration() +} + +func TestStopAutoExpiration(t *testing.T) { + oldNamedCollection := namedCollection + defer func() { + namedCollection = oldNamedCollection + }() + // Clear Collection map + namedCollection = make(map[string]*Collection) + + name := randomString[0] + c := CreateOrReturnCollection(name) + + c.StopAutoExpiration() + assert.False(t, c.monitoring) + + // Test when c.monitoring == true before calling StartAutoExpiration + c.monitoring = true + c.StartAutoExpiration() + assert.True(t, c.monitoring) +} + +func TestSince(t *testing.T) { + oldNamedCollection := namedCollection + defer func() { + namedCollection = oldNamedCollection + }() + // Clear Collection map + namedCollection = make(map[string]*Collection) + + name := randomString[0] + + var c *Collection + metrics, err := c.Since(ts) + + assert.Nil(t, metrics) + assert.Error(t, err) + assert.Equal(t, err.Error(), "Collection.Since: c == nil") + + c = CreateOrReturnCollection(name) + metrics, err = c.Since(ts) + assert.Nil(t, metrics) + assert.Nil(t, err) + + tm := &testMetric{} + tm2 := &testMetric2{} + _ = c.Append(tm2) + _ = c.Append(tm) + + metrics, err = c.Since(ts2) + assert.Equal(t, []Metric{tm2, tm}, metrics) + assert.Nil(t, err) + + metrics, err = c.Since(ts) + assert.Equal(t, []Metric{tm}, metrics) + assert.Nil(t, err) +} + +func TestRemoveBefore(t *testing.T) { + oldNamedCollection := namedCollection + defer func() { + namedCollection = oldNamedCollection + }() + // Clear Collection map + namedCollection = make(map[string]*Collection) + + name := randomString[0] + c := CreateOrReturnCollection(name) + + tm := &testMetric{} + tm2 := &testMetric2{} + + err := c.Append(tm2) + assert.Nil(t, err) + + err = c.Append(tm) + assert.Nil(t, err) + + err = c.removeBefore(ts) + assert.NoError(t, err) + assert.Equal(t, []Metric{tm}, c.collection) + + ts3 := ts.AddDate(1, 0, 0) + err = c.removeBefore(ts3) + assert.NoError(t, err) + assert.Nil(t, c.collection) + + name = randomString[1] + c = CreateOrReturnCollection(name) + + err = c.removeBefore(ts) + assert.NoError(t, err) + assert.Equal(t, []Metric(nil), c.collection) } diff --git a/go/vt/vtorc/config/config.go b/go/vt/vtorc/config/config.go index 83a39303acb..ba3c41ddc61 100644 --- a/go/vt/vtorc/config/config.go +++ b/go/vt/vtorc/config/config.go @@ -44,7 +44,7 @@ const ( DiscoveryQueueMaxStatisticsSize = 120 DiscoveryCollectionRetentionSeconds = 120 UnseenInstanceForgetHours = 240 // Number of hours after which an unseen instance is forgotten - FailureDetectionPeriodBlockMinutes = 60 // The time for which an instance's failure discovery is kept "active", so as to avoid concurrent "discoveries" of the instance's failure; this preceeds any recovery process, if any. + FailureDetectionPeriodBlockMinutes = 60 // The time for which an instance's failure discovery is kept "active", so as to avoid concurrent "discoveries" of the instance's failure; this precedes any recovery process, if any. ) var ( @@ -59,6 +59,7 @@ var ( recoveryPeriodBlockDuration = 30 * time.Second preventCrossCellFailover = false waitReplicasTimeout = 30 * time.Second + tolerableReplicationLag = 0 * time.Second topoInformationRefreshDuration = 15 * time.Second recoveryPollDuration = 1 * time.Second ersEnabled = true @@ -78,6 +79,7 @@ func RegisterFlags(fs *pflag.FlagSet) { fs.DurationVar(&recoveryPeriodBlockDuration, "recovery-period-block-duration", recoveryPeriodBlockDuration, "Duration for which a new recovery is blocked on an instance after running a recovery") fs.BoolVar(&preventCrossCellFailover, "prevent-cross-cell-failover", preventCrossCellFailover, "Prevent VTOrc from promoting a primary in a different cell than the current primary in case of a failover") fs.DurationVar(&waitReplicasTimeout, "wait-replicas-timeout", waitReplicasTimeout, "Duration for which to wait for replica's to respond when issuing RPCs") + fs.DurationVar(&tolerableReplicationLag, "tolerable-replication-lag", tolerableReplicationLag, "Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS") fs.DurationVar(&topoInformationRefreshDuration, "topo-information-refresh-duration", topoInformationRefreshDuration, "Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topology server") fs.DurationVar(&recoveryPollDuration, "recovery-poll-duration", recoveryPollDuration, "Timer duration on which VTOrc polls its database to run a recovery") fs.BoolVar(&ersEnabled, "allow-emergency-reparent", ersEnabled, "Whether VTOrc should be allowed to run emergency reparent operation when it detects a dead primary") @@ -85,7 +87,7 @@ func RegisterFlags(fs *pflag.FlagSet) { } // Configuration makes for vtorc configuration input, which can be provided by user via JSON formatted file. -// Some of the parameteres have reasonable default values, and some (like database credentials) are +// Some of the parameters have reasonable default values, and some (like database credentials) are // strictly expected from user. // TODO(sougou): change this to yaml parsing, and possible merge with tabletenv. type Configuration struct { @@ -100,6 +102,7 @@ type Configuration struct { RecoveryPeriodBlockSeconds int // (overrides `RecoveryPeriodBlockMinutes`) The time for which an instance's recovery is kept "active", so as to avoid concurrent recoveries on smae instance as well as flapping PreventCrossDataCenterPrimaryFailover bool // When true (default: false), cross-DC primary failover are not allowed, vtorc will do all it can to only fail over within same DC, or else not fail over at all. WaitReplicasTimeoutSeconds int // Timeout on amount of time to wait for the replicas in case of ERS. Should be a small value because we should fail-fast. Should not be larger than LockTimeout since that is the total time we use for an ERS. + TolerableReplicationLagSeconds int // Amount of replication lag that is considered acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary in PRS. TopoInformationRefreshSeconds int // Timer duration on which VTOrc refreshes the keyspace and vttablet records from the topo-server. RecoveryPollSeconds int // Timer duration on which VTOrc recovery analysis runs } @@ -129,6 +132,7 @@ func UpdateConfigValuesFromFlags() { Config.RecoveryPeriodBlockSeconds = int(recoveryPeriodBlockDuration / time.Second) Config.PreventCrossDataCenterPrimaryFailover = preventCrossCellFailover Config.WaitReplicasTimeoutSeconds = int(waitReplicasTimeout / time.Second) + Config.TolerableReplicationLagSeconds = int(tolerableReplicationLag / time.Second) Config.TopoInformationRefreshSeconds = int(topoInformationRefreshDuration / time.Second) Config.RecoveryPollSeconds = int(recoveryPollDuration / time.Second) } diff --git a/go/vt/vtorc/db/db.go b/go/vt/vtorc/db/db.go index d565c9bbdc4..92657eddc3f 100644 --- a/go/vt/vtorc/db/db.go +++ b/go/vt/vtorc/db/db.go @@ -88,7 +88,7 @@ func registerVTOrcDeployment(db *sql.DB) error { } // deployStatements will issue given sql queries that are not already known to be deployed. -// This iterates both lists (to-run and already-deployed) and also verifies no contraditions. +// This iterates both lists (to-run and already-deployed) and also verifies no contradictions. func deployStatements(db *sql.DB, queries []string) error { tx, err := db.Begin() if err != nil { diff --git a/go/vt/vtorc/inst/analysis_dao.go b/go/vt/vtorc/inst/analysis_dao.go index 25082f133da..749827f006c 100644 --- a/go/vt/vtorc/inst/analysis_dao.go +++ b/go/vt/vtorc/inst/analysis_dao.go @@ -521,7 +521,7 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna a.Description = "Primary cannot be reached by vtorc and all of its replicas are lagging" // } else if a.IsPrimary && !a.LastCheckValid && !a.LastCheckPartialSuccess && a.CountValidReplicas > 0 && a.CountValidReplicatingReplicas > 0 { - // partial success is here to redice noise + // partial success is here to reduce noise a.Analysis = UnreachablePrimary a.Description = "Primary cannot be reached by vtorc but it has replicating replicas; possibly a network/host issue" // diff --git a/go/vt/vtorc/inst/audit_dao.go b/go/vt/vtorc/inst/audit_dao.go index 96db7f32ccf..fc1528c9640 100644 --- a/go/vt/vtorc/inst/audit_dao.go +++ b/go/vt/vtorc/inst/audit_dao.go @@ -18,36 +18,22 @@ package inst import ( "fmt" - "log/syslog" "os" "time" - "vitess.io/vitess/go/vt/log" - "github.com/rcrowley/go-metrics" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vtorc/config" "vitess.io/vitess/go/vt/vtorc/db" ) -// syslogWriter is optional, and defaults to nil (disabled) -var syslogWriter *syslog.Writer - var auditOperationCounter = metrics.NewCounter() func init() { _ = metrics.Register("audit.write", auditOperationCounter) } -// EnableSyslogWriter enables, if possible, writes to syslog. These will execute _in addition_ to normal logging -func EnableAuditSyslog() (err error) { - syslogWriter, err = syslog.New(syslog.LOG_ERR, "vtorc") - if err != nil { - syslogWriter = nil - } - return err -} - // AuditOperation creates and writes a new audit entry by given params func AuditOperation(auditType string, tabletAlias string, message string) error { keyspace := "" @@ -94,11 +80,8 @@ func AuditOperation(auditType string, tabletAlias string, message string) error } } logMessage := fmt.Sprintf("auditType:%s alias:%s keyspace:%s shard:%s message:%s", auditType, tabletAlias, keyspace, shard, message) - if syslogWriter != nil { + if syslogMessage(logMessage) { auditWrittenToFile = true - go func() { - _ = syslogWriter.Info(logMessage) - }() } if !auditWrittenToFile { log.Infof(logMessage) diff --git a/go/vt/vtorc/inst/audit_dao_nosyslog.go b/go/vt/vtorc/inst/audit_dao_nosyslog.go new file mode 100644 index 00000000000..a61b3eb8f42 --- /dev/null +++ b/go/vt/vtorc/inst/audit_dao_nosyslog.go @@ -0,0 +1,32 @@ +//go:build windows + +/* + Copyright 2014 Outbrain Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package inst + +import ( + "errors" +) + +// EnableAuditSyslog enables, if possible, writes to syslog. These will execute _in addition_ to normal logging +func EnableAuditSyslog() (err error) { + return errors.New("syslog is not supported on windows") +} + +func syslogMessage(logMessage string) bool { + return false +} diff --git a/go/vt/vtorc/inst/audit_dao_syslog.go b/go/vt/vtorc/inst/audit_dao_syslog.go new file mode 100644 index 00000000000..2567409f03e --- /dev/null +++ b/go/vt/vtorc/inst/audit_dao_syslog.go @@ -0,0 +1,43 @@ +//go:build !windows + +/* + Copyright 2014 Outbrain Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package inst + +import "log/syslog" + +// syslogWriter is optional, and defaults to nil (disabled) +var syslogWriter *syslog.Writer + +// EnableAuditSyslog enables, if possible, writes to syslog. These will execute _in addition_ to normal logging +func EnableAuditSyslog() (err error) { + syslogWriter, err = syslog.New(syslog.LOG_ERR, "vtorc") + if err != nil { + syslogWriter = nil + } + return err +} + +func syslogMessage(logMessage string) bool { + if syslogWriter == nil { + return false + } + go func() { + _ = syslogWriter.Info(logMessage) + }() + return true +} diff --git a/go/vt/vtorc/inst/binlog.go b/go/vt/vtorc/inst/binlog.go index 066c2f5c598..9c115e4e457 100644 --- a/go/vt/vtorc/inst/binlog.go +++ b/go/vt/vtorc/inst/binlog.go @@ -68,7 +68,7 @@ func (binlogCoordinates BinlogCoordinates) String() string { return binlogCoordinates.DisplayString() } -// Equals tests equality of this corrdinate and another one. +// Equals tests equality of this coordinate and another one. func (binlogCoordinates *BinlogCoordinates) Equals(other *BinlogCoordinates) bool { if other == nil { return false @@ -106,8 +106,8 @@ func (binlogCoordinates *BinlogCoordinates) FileSmallerThan(other *BinlogCoordin return binlogCoordinates.LogFile < other.LogFile } -// FileNumberDistance returns the numeric distance between this corrdinate's file number and the other's. -// Effectively it means "how many roatets/FLUSHes would make these coordinates's file reach the other's" +// FileNumberDistance returns the numeric distance between this coordinate's file number and the other's. +// Effectively it means "how many rotates/FLUSHes would make these coordinates's file reach the other's" func (binlogCoordinates *BinlogCoordinates) FileNumberDistance(other *BinlogCoordinates) int { thisNumber, _ := binlogCoordinates.FileNumber() otherNumber, _ := other.FileNumber() @@ -163,7 +163,7 @@ func (binlogCoordinates *BinlogCoordinates) NextFileCoordinates() (BinlogCoordin return result, nil } -// Detach returns a detahced form of coordinates +// Detach returns a detached form of coordinates func (binlogCoordinates *BinlogCoordinates) Detach() (detachedCoordinates BinlogCoordinates) { detachedCoordinates = BinlogCoordinates{LogFile: fmt.Sprintf("//%s:%d", binlogCoordinates.LogFile, binlogCoordinates.LogPos), LogPos: binlogCoordinates.LogPos} return detachedCoordinates diff --git a/go/vt/vtorc/inst/instance.go b/go/vt/vtorc/inst/instance.go index 1216d4c24ae..7b1ef6abc6b 100644 --- a/go/vt/vtorc/inst/instance.go +++ b/go/vt/vtorc/inst/instance.go @@ -63,7 +63,7 @@ type Instance struct { LastSQLError string LastIOError string SecondsBehindPrimary sql.NullInt64 - SQLDelay uint + SQLDelay uint32 ExecutedGtidSet string GtidPurged string GtidErrant string diff --git a/go/vt/vtorc/inst/instance_dao.go b/go/vt/vtorc/inst/instance_dao.go index 211ddce69b1..c396a89ef21 100644 --- a/go/vt/vtorc/inst/instance_dao.go +++ b/go/vt/vtorc/inst/instance_dao.go @@ -17,7 +17,6 @@ package inst import ( - "bytes" "errors" "fmt" "regexp" @@ -52,20 +51,26 @@ const ( backendDBConcurrency = 20 ) -var instanceReadChan = make(chan bool, backendDBConcurrency) -var instanceWriteChan = make(chan bool, backendDBConcurrency) +var ( + instanceReadChan = make(chan bool, backendDBConcurrency) + instanceWriteChan = make(chan bool, backendDBConcurrency) +) var forgetAliases *cache.Cache -var accessDeniedCounter = metrics.NewCounter() -var readTopologyInstanceCounter = metrics.NewCounter() -var readInstanceCounter = metrics.NewCounter() -var writeInstanceCounter = metrics.NewCounter() -var backendWrites = collection.CreateOrReturnCollection("BACKEND_WRITES") -var writeBufferLatency = stopwatch.NewNamedStopwatch() +var ( + accessDeniedCounter = metrics.NewCounter() + readTopologyInstanceCounter = metrics.NewCounter() + readInstanceCounter = metrics.NewCounter() + writeInstanceCounter = metrics.NewCounter() + backendWrites = collection.CreateOrReturnCollection("BACKEND_WRITES") + writeBufferLatency = stopwatch.NewNamedStopwatch() +) -var emptyQuotesRegexp = regexp.MustCompile(`^""$`) -var cacheInitializationCompleted atomic.Bool +var ( + emptyQuotesRegexp = regexp.MustCompile(`^""$`) + cacheInitializationCompleted atomic.Bool +) func init() { _ = metrics.Register("instance.access_denied", accessDeniedCounter) @@ -84,7 +89,7 @@ func initializeInstanceDao() { cacheInitializationCompleted.Store(true) } -// ExecDBWriteFunc chooses how to execute a write onto the database: whether synchronuously or not +// ExecDBWriteFunc chooses how to execute a write onto the database: whether synchronously or not func ExecDBWriteFunc(f func() error) error { m := query.NewMetric() @@ -289,7 +294,7 @@ func ReadTopologyInstanceBufferable(tabletAlias string, latency *stopwatch.Named instance.LastSQLError = emptyQuotesRegexp.ReplaceAllString(strconv.QuoteToASCII(fullStatus.ReplicationStatus.LastSqlError), "") instance.LastIOError = emptyQuotesRegexp.ReplaceAllString(strconv.QuoteToASCII(fullStatus.ReplicationStatus.LastIoError), "") - instance.SQLDelay = uint(fullStatus.ReplicationStatus.SqlDelay) + instance.SQLDelay = fullStatus.ReplicationStatus.SqlDelay instance.UsingOracleGTID = fullStatus.ReplicationStatus.AutoPosition instance.UsingMariaDBGTID = fullStatus.ReplicationStatus.UsingGtid instance.SourceUUID = fullStatus.ReplicationStatus.SourceUuid @@ -522,7 +527,7 @@ func readInstanceRow(m sqlutils.RowMap) *Instance { instance.LastIOError = m.GetString("last_io_error") instance.SecondsBehindPrimary = m.GetNullInt64("replication_lag_seconds") instance.ReplicationLagSeconds = m.GetNullInt64("replica_lag_seconds") - instance.SQLDelay = m.GetUint("sql_delay") + instance.SQLDelay = m.GetUint32("sql_delay") instance.DataCenter = m.GetString("data_center") instance.Region = m.GetString("region") instance.PhysicalEnvironment = m.GetString("physical_environment") @@ -749,12 +754,10 @@ func ReadOutdatedInstanceKeys() ([]string, error) { // We don;t return an error because we want to keep filling the outdated instances list. return nil }) - if err != nil { log.Error(err) } return res, err - } func mkInsertOdku(table string, columns []string, values []string, nrRows int, insertIgnore bool) (string, error) { @@ -768,21 +771,21 @@ func mkInsertOdku(table string, columns []string, values []string, nrRows int, i return "", errors.New("number of values must be equal to number of columns") } - var q bytes.Buffer + var q strings.Builder var ignore string if insertIgnore { ignore = "ignore" } - var valRow = fmt.Sprintf("(%s)", strings.Join(values, ", ")) - var val bytes.Buffer + valRow := fmt.Sprintf("(%s)", strings.Join(values, ", ")) + var val strings.Builder val.WriteString(valRow) for i := 1; i < nrRows; i++ { val.WriteString(",\n ") // indent VALUES, see below val.WriteString(valRow) } - var col = strings.Join(columns, ", ") - var odku bytes.Buffer + col := strings.Join(columns, ", ") + var odku strings.Builder odku.WriteString(fmt.Sprintf("%s=VALUES(%s)", columns[0], columns[0])) for _, c := range columns[1:] { odku.WriteString(", ") @@ -810,7 +813,7 @@ func mkInsertOdkuForInstances(instances []*Instance, instanceWasActuallyFound bo if !instanceWasActuallyFound { insertIgnore = true } - var columns = []string{ + columns := []string{ "alias", "hostname", "port", @@ -876,7 +879,7 @@ func mkInsertOdkuForInstances(instances []*Instance, instanceWasActuallyFound bo "last_discovery_latency", } - var values = make([]string, len(columns)) + values := make([]string, len(columns)) for i := range columns { values[i] = "?" } @@ -1102,7 +1105,7 @@ func ForgetInstance(tabletAlias string) error { return nil } -// ForgetLongUnseenInstances will remove entries of all instacnes that have long since been last seen. +// ForgetLongUnseenInstances will remove entries of all instances that have long since been last seen. func ForgetLongUnseenInstances() error { sqlResult, err := db.ExecVTOrc(` delete diff --git a/go/vt/vtorc/logic/keyspace_shard_discovery.go b/go/vt/vtorc/logic/keyspace_shard_discovery.go index c79ace5bdc3..b1e93fe2a01 100644 --- a/go/vt/vtorc/logic/keyspace_shard_discovery.go +++ b/go/vt/vtorc/logic/keyspace_shard_discovery.go @@ -124,7 +124,12 @@ func refreshKeyspaceHelper(ctx context.Context, keyspaceName string) error { // refreshAllShards refreshes all the shard records in the given keyspace. func refreshAllShards(ctx context.Context, keyspaceName string) error { - shardInfos, err := ts.FindAllShardsInKeyspace(ctx, keyspaceName) + shardInfos, err := ts.FindAllShardsInKeyspace(ctx, keyspaceName, &topo.FindAllShardsInKeyspaceOptions{ + // Fetch shard records concurrently to speed up discovery. A typical + // Vitess cluster will have 1-3 vtorc instances deployed, so there is + // little risk of a thundering herd. + Concurrency: 8, + }) if err != nil { log.Error(err) return err diff --git a/go/vt/vtorc/logic/topology_recovery.go b/go/vt/vtorc/logic/topology_recovery.go index d3e73c00886..e5168fea541 100644 --- a/go/vt/vtorc/logic/topology_recovery.go +++ b/go/vt/vtorc/logic/topology_recovery.go @@ -584,7 +584,7 @@ func runEmergentOperations(analysisEntry *inst.ReplicationAnalysis) { } // executeCheckAndRecoverFunction will choose the correct check & recovery function based on analysis. -// It executes the function synchronuously +// It executes the function synchronously func executeCheckAndRecoverFunction(analysisEntry *inst.ReplicationAnalysis) (err error) { countPendingRecoveries.Add(1) defer countPendingRecoveries.Add(-1) @@ -852,6 +852,7 @@ func electNewPrimary(ctx context.Context, analysisEntry *inst.ReplicationAnalysi analyzedTablet.Shard, reparentutil.PlannedReparentOptions{ WaitReplicasTimeout: time.Duration(config.Config.WaitReplicasTimeoutSeconds) * time.Second, + TolerableReplLag: time.Duration(config.Config.TolerableReplicationLagSeconds) * time.Second, }, ) diff --git a/go/vt/vtorc/logic/topology_recovery_dao.go b/go/vt/vtorc/logic/topology_recovery_dao.go index c835b9ecfe4..4a7a6c77ef1 100644 --- a/go/vt/vtorc/logic/topology_recovery_dao.go +++ b/go/vt/vtorc/logic/topology_recovery_dao.go @@ -369,7 +369,7 @@ func acknowledgeRecoveries(owner string, comment string, markEndRecovery bool, w return rows, err } -// AcknowledgeInstanceCompletedRecoveries marks active and COMPLETED recoveries for given instane as acknowledged. +// AcknowledgeInstanceCompletedRecoveries marks active and COMPLETED recoveries for given instance as acknowledged. // This also implied clearing their active period, which in turn enables further recoveries on those topologies func AcknowledgeInstanceCompletedRecoveries(tabletAlias string, owner string, comment string) (countAcknowledgedEntries int64, err error) { whereClause := ` diff --git a/go/vt/vtorc/logic/topology_recovery_status.go b/go/vt/vtorc/logic/topology_recovery_status.go index d1195963ba1..d128a0637bc 100644 --- a/go/vt/vtorc/logic/topology_recovery_status.go +++ b/go/vt/vtorc/logic/topology_recovery_status.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/vt/vtorc/logic/vtorc.go b/go/vt/vtorc/logic/vtorc.go index 02fb41daa21..f637956fbfd 100644 --- a/go/vt/vtorc/logic/vtorc.go +++ b/go/vt/vtorc/logic/vtorc.go @@ -329,7 +329,7 @@ func onHealthTick() { } } -// ContinuousDiscovery starts an asynchronuous infinite discovery process where instances are +// ContinuousDiscovery starts an asynchronous infinite discovery process where instances are // periodically investigated and their status captured, and long since unseen instances are // purged and forgotten. // nolint SA1015: using time.Tick leaks the underlying ticker diff --git a/go/vt/vtorc/metrics/query/aggregated.go b/go/vt/vtorc/metrics/query/aggregated.go index beece44d53a..a284ca6f74d 100644 --- a/go/vt/vtorc/metrics/query/aggregated.go +++ b/go/vt/vtorc/metrics/query/aggregated.go @@ -1,5 +1,5 @@ -// Package query provdes query metrics with this file providing -// aggregared metrics based on the underlying values. +// Package query provides query metrics with this file providing +// aggregated metrics based on the underlying values. package query import ( diff --git a/go/vt/vtorc/server/api.go b/go/vt/vtorc/server/api.go index f053336e64e..b0112e10add 100644 --- a/go/vt/vtorc/server/api.go +++ b/go/vt/vtorc/server/api.go @@ -117,7 +117,7 @@ func RegisterVTOrcAPIEndpoints() { } } -// returnAsJSON returns the argument received on the resposeWriter as a json object +// returnAsJSON returns the argument received on the responseWriter as a json object func returnAsJSON(response http.ResponseWriter, code int, stuff any) { response.Header().Set("Content-Type", "application/json; charset=utf-8") response.WriteHeader(code) diff --git a/go/vt/vttablet/endtoend/acl_test.go b/go/vt/vttablet/endtoend/acl_test.go index 0894c2838d0..ed9cfa83817 100644 --- a/go/vt/vttablet/endtoend/acl_test.go +++ b/go/vt/vttablet/endtoend/acl_test.go @@ -21,7 +21,7 @@ import ( "encoding/json" "testing" - "gotest.tools/assert" + "github.com/stretchr/testify/assert" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" diff --git a/go/vt/vttablet/endtoend/call_test.go b/go/vt/vttablet/endtoend/call_test.go index 3a42eea3780..a1a2eae792a 100644 --- a/go/vt/vttablet/endtoend/call_test.go +++ b/go/vt/vttablet/endtoend/call_test.go @@ -75,12 +75,16 @@ func TestCallProcedure(t *testing.T) { wantErr bool } tcases := []testcases{{ + query: "call proc_dml()", + }, { query: "call proc_select1()", wantErr: true, }, { query: "call proc_select4()", wantErr: true, }, { + // Again, make sure the connection isn't dirty and does not contain leftover + // result sets from previous tests. query: "call proc_dml()", }} @@ -92,7 +96,6 @@ func TestCallProcedure(t *testing.T) { return } require.NoError(t, err) - }) } } @@ -149,7 +152,7 @@ func TestCallProcedureChangedTx(t *testing.T) { }) } - // This passes as this starts a new transaction by commiting the old transaction implicitly. + // This passes as this starts a new transaction by committing the old transaction implicitly. _, err = client.BeginExecute(`call proc_tx_begin()`, nil, nil) require.NoError(t, err) } diff --git a/go/vt/vttablet/endtoend/compatibility_test.go b/go/vt/vttablet/endtoend/compatibility_test.go index 9b89a602281..4dde4019a99 100644 --- a/go/vt/vttablet/endtoend/compatibility_test.go +++ b/go/vt/vttablet/endtoend/compatibility_test.go @@ -33,7 +33,7 @@ import ( var point12 = "\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@" -func TestCharaterSet(t *testing.T) { +func TestCharacterSet(t *testing.T) { qr, err := framework.NewClient().Execute("select * from vitess_test where intval=1", nil) if err != nil { t.Fatal(err) diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 60303cf4bf5..9eef54bd0bb 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -108,64 +108,88 @@ func TestDisableConsolidator(t *testing.T) { } func TestConsolidatorReplicasOnly(t *testing.T) { - totalConsolidationsTag := "Waits/Histograms/Consolidations/Count" - initial := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) - var wg sync.WaitGroup - wg.Add(2) - go func() { - framework.NewClient().Execute("select sleep(0.5) from dual", nil) - wg.Done() - }() - go func() { - framework.NewClient().Execute("select sleep(0.5) from dual", nil) - wg.Done() - }() - wg.Wait() - afterOne := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) - assert.Equal(t, initial+1, afterOne, "expected one consolidation") - - revert := changeVar(t, "Consolidator", tabletenv.NotOnPrimary) - defer revert() - - // primary should not do query consolidation - var wg2 sync.WaitGroup - wg2.Add(2) - go func() { - framework.NewClient().Execute("select sleep(0.5) from dual", nil) - wg2.Done() - }() - go func() { - framework.NewClient().Execute("select sleep(0.5) from dual", nil) - wg2.Done() - }() - wg2.Wait() - noNewConsolidations := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) - assert.Equal(t, afterOne, noNewConsolidations, "expected no new consolidations") - - // become a replica, where query consolidation should happen - client := framework.NewClientWithTabletType(topodatapb.TabletType_REPLICA) - - err := client.SetServingType(topodatapb.TabletType_REPLICA) - require.NoError(t, err) - defer func() { - err = client.SetServingType(topodatapb.TabletType_PRIMARY) - require.NoError(t, err) - }() + type executeFn func( + query string, bindvars map[string]*querypb.BindVariable, + ) (*sqltypes.Result, error) + + testCases := []struct { + name string + getExecuteFn func(qc *framework.QueryClient) executeFn + totalConsolidationsTag string + }{ + { + name: "Execute", + getExecuteFn: func(qc *framework.QueryClient) executeFn { return qc.Execute }, + totalConsolidationsTag: "Waits/Histograms/Consolidations/Count", + }, + { + name: "StreamExecute", + getExecuteFn: func(qc *framework.QueryClient) executeFn { return qc.StreamExecute }, + totalConsolidationsTag: "Waits/Histograms/StreamConsolidations/Count", + }, + } - initial = framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) - var wg3 sync.WaitGroup - wg3.Add(2) - go func() { - client.Execute("select sleep(0.5) from dual", nil) - wg3.Done() - }() - go func() { - client.Execute("select sleep(0.5) from dual", nil) - wg3.Done() - }() - wg3.Wait() - afterOne = framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) - assert.Equal(t, initial+1, afterOne, "expected another consolidation") + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + initial := framework.FetchInt(framework.DebugVars(), testCase.totalConsolidationsTag) + var wg sync.WaitGroup + wg.Add(2) + go func() { + testCase.getExecuteFn(framework.NewClient())("select sleep(0.5) from dual", nil) + wg.Done() + }() + go func() { + testCase.getExecuteFn(framework.NewClient())("select sleep(0.5) from dual", nil) + wg.Done() + }() + wg.Wait() + afterOne := framework.FetchInt(framework.DebugVars(), testCase.totalConsolidationsTag) + assert.Equal(t, initial+1, afterOne, "expected one consolidation") + + revert := changeVar(t, "Consolidator", tabletenv.NotOnPrimary) + defer revert() + + // primary should not do query consolidation + var wg2 sync.WaitGroup + wg2.Add(2) + go func() { + testCase.getExecuteFn(framework.NewClient())("select sleep(0.5) from dual", nil) + wg2.Done() + }() + go func() { + testCase.getExecuteFn(framework.NewClient())("select sleep(0.5) from dual", nil) + wg2.Done() + }() + wg2.Wait() + noNewConsolidations := framework.FetchInt(framework.DebugVars(), testCase.totalConsolidationsTag) + assert.Equal(t, afterOne, noNewConsolidations, "expected no new consolidations") + + // become a replica, where query consolidation should happen + client := framework.NewClientWithTabletType(topodatapb.TabletType_REPLICA) + + err := client.SetServingType(topodatapb.TabletType_REPLICA) + require.NoError(t, err) + defer func() { + err = client.SetServingType(topodatapb.TabletType_PRIMARY) + require.NoError(t, err) + }() + + initial = framework.FetchInt(framework.DebugVars(), testCase.totalConsolidationsTag) + var wg3 sync.WaitGroup + wg3.Add(2) + go func() { + testCase.getExecuteFn(client)("select sleep(0.5) from dual", nil) + wg3.Done() + }() + go func() { + testCase.getExecuteFn(client)("select sleep(0.5) from dual", nil) + wg3.Done() + }() + wg3.Wait() + afterOne = framework.FetchInt(framework.DebugVars(), testCase.totalConsolidationsTag) + assert.Equal(t, initial+1, afterOne, "expected another consolidation") + }) + } } func TestQueryPlanCache(t *testing.T) { diff --git a/go/vt/vttablet/endtoend/connkilling/main_test.go b/go/vt/vttablet/endtoend/connkilling/main_test.go index e7486c397eb..3d0ec344715 100644 --- a/go/vt/vttablet/endtoend/connkilling/main_test.go +++ b/go/vt/vttablet/endtoend/connkilling/main_test.go @@ -23,6 +23,7 @@ import ( "fmt" "os" "testing" + "time" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/tableacl" @@ -81,7 +82,7 @@ func TestMain(m *testing.M) { connParams = cluster.MySQLConnParams() connAppDebugParams = cluster.MySQLAppDebugConnParams() config := tabletenv.NewDefaultConfig() - _ = config.Oltp.TxTimeoutSeconds.Set("3s") + config.Oltp.TxTimeout = 3 * time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() err := framework.StartCustomServer(ctx, connParams, connAppDebugParams, cluster.DbName(), config) diff --git a/go/vt/vttablet/endtoend/framework/client.go b/go/vt/vttablet/endtoend/framework/client.go index 3c06f9b465c..eb70eaeb9cb 100644 --- a/go/vt/vttablet/endtoend/framework/client.go +++ b/go/vt/vttablet/endtoend/framework/client.go @@ -57,6 +57,19 @@ func NewClient() *QueryClient { } } +// NewClientWithServer creates a new client for a given server. +func NewClientWithServer(server *tabletserver.TabletServer) *QueryClient { + return &QueryClient{ + ctx: callerid.NewContext( + context.Background(), + &vtrpcpb.CallerID{}, + &querypb.VTGateCallerID{Username: "dev"}, + ), + target: Target, + server: server, + } +} + // NewClientWithTabletType creates a new client for Server with the provided tablet type. func NewClientWithTabletType(tabletType topodatapb.TabletType) *QueryClient { targetCopy := Target.CloneVT() @@ -134,7 +147,7 @@ func (client *QueryClient) CommitPrepared(dtid string) error { return client.server.CommitPrepared(client.ctx, client.target, dtid) } -// RollbackPrepared rollsback a prepared transaction. +// RollbackPrepared rolls back a prepared transaction. func (client *QueryClient) RollbackPrepared(dtid string, originalID int64) error { return client.server.RollbackPrepared(client.ctx, client.target, dtid, originalID) } diff --git a/go/vt/vttablet/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go index 4f8043fba5a..0258dee9186 100644 --- a/go/vt/vttablet/endtoend/framework/server.go +++ b/go/vt/vttablet/endtoend/framework/server.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/yaml2" "vitess.io/vitess/go/vt/topo/memorytopo" @@ -58,7 +59,7 @@ var ( // StartCustomServer starts the server and initializes // all the global variables. This function should only be called // once at the beginning of the test. -func StartCustomServer(ctx context.Context, connParams, connAppDebugParams mysql.ConnParams, dbName string, config *tabletenv.TabletConfig) error { +func StartCustomServer(ctx context.Context, connParams, connAppDebugParams mysql.ConnParams, dbName string, cfg *tabletenv.TabletConfig) error { // Setup a fake vtgate server. protocol := "resolveTest" vtgateconn.SetVTGateProtocol(protocol) @@ -77,7 +78,7 @@ func StartCustomServer(ctx context.Context, connParams, connAppDebugParams mysql } TopoServer = memorytopo.NewServer(ctx, "") - Server = tabletserver.NewTabletServer(ctx, "", config, TopoServer, &topodatapb.TabletAlias{}) + Server = tabletserver.NewTabletServer(ctx, vtenv.NewTestEnv(), "", cfg, TopoServer, &topodatapb.TabletAlias{}) Server.Register() err := Server.StartService(Target, dbcfgs, nil /* mysqld */) if err != nil { @@ -118,11 +119,11 @@ func StartServer(ctx context.Context, connParams, connAppDebugParams mysql.ConnP config.TwoPCCoordinatorAddress = "fake" config.HotRowProtection.Mode = tabletenv.Enable config.TrackSchemaVersions = true - _ = config.GracePeriods.ShutdownSeconds.Set("2s") + config.GracePeriods.Shutdown = 2 * time.Second config.SignalWhenSchemaChange = true - _ = config.Healthcheck.IntervalSeconds.Set("100ms") - _ = config.Oltp.TxTimeoutSeconds.Set("5s") - _ = config.Olap.TxTimeoutSeconds.Set("5s") + config.Healthcheck.Interval = 100 * time.Millisecond + config.Oltp.TxTimeout = 5 * time.Second + config.Olap.TxTimeout = 5 * time.Second config.EnableViews = true config.QueryCacheDoorkeeper = false gotBytes, _ := yaml2.Marshal(config) @@ -135,7 +136,7 @@ func StopServer() { Server.StopService() } -// txReolver transmits dtids to be resolved through ResolveChan. +// txResolver transmits dtids to be resolved through ResolveChan. type txResolver struct { fakerpcvtgateconn.FakeVTGateConn } diff --git a/go/vt/vttablet/endtoend/misc_test.go b/go/vt/vttablet/endtoend/misc_test.go index 5c37a5d9bb0..568036f672e 100644 --- a/go/vt/vttablet/endtoend/misc_test.go +++ b/go/vt/vttablet/endtoend/misc_test.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "io" - "math" "net/http" "reflect" "strings" @@ -28,20 +27,17 @@ import ( "testing" "time" - "google.golang.org/protobuf/proto" - - "vitess.io/vitess/go/test/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" ) @@ -628,66 +624,6 @@ func (tl *testLogger) getLog(i int) string { return fmt.Sprintf("ERROR: log %d/%d does not exist", i, len(tl.logs)) } -func TestLogTruncation(t *testing.T) { - client := framework.NewClient() - tl := newTestLogger() - defer tl.Close() - - // Test that a long error string is not truncated by default - _, err := client.Execute( - "insert into vitess_test values(123, null, :data, null)", - map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, - ) - wantLog := `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\""}` - wantErr := wantLog - if err == nil { - t.Errorf("query unexpectedly succeeded") - } - if tl.getLog(0) != wantLog { - t.Errorf("log was unexpectedly truncated: got\n'%s', want\n'%s'", tl.getLog(0), wantLog) - } - - if err.Error() != wantErr { - t.Errorf("error was unexpectedly truncated: got\n'%s', want\n'%s'", err.Error(), wantErr) - } - - // Test that the data too long error is truncated once the option is set - sqlparser.SetTruncateErrLen(30) - _, err = client.Execute( - "insert into vitess_test values(123, null, :data, null)", - map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, - ) - wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED]", BindVars: {data: " [TRUNCATED]` - wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\""}` - if err == nil { - t.Errorf("query unexpectedly succeeded") - } - if tl.getLog(1) != wantLog { - t.Errorf("log was not truncated properly: got\n'%s', want\n'%s'", tl.getLog(1), wantLog) - } - if err.Error() != wantErr { - t.Errorf("error was unexpectedly truncated: got\n'%s', want\n'%s'", err.Error(), wantErr) - } - - // Test that trailing comments are preserved data too long error is truncated once the option is set - sqlparser.SetTruncateErrLen(30) - _, err = client.Execute( - "insert into vitess_test values(123, null, :data, null) /* KEEP ME */", - map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, - ) - wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED] /* KEEP ME */", BindVars: {data: " [TRUNCATED]` - wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null) /* KEEP ME */", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\""}` - if err == nil { - t.Errorf("query unexpectedly succeeded") - } - if tl.getLog(2) != wantLog { - t.Errorf("log was not truncated properly: got\n'%s', want\n'%s'", tl.getLog(2), wantLog) - } - if err.Error() != wantErr { - t.Errorf("error was unexpectedly truncated: got\n'%s', want\n'%s'", err.Error(), wantErr) - } -} - func TestClientFoundRows(t *testing.T) { client := framework.NewClient() if _, err := client.Execute("insert into vitess_test(intval, charval) values(124, 'aa')", nil); err != nil { @@ -976,7 +912,7 @@ func TestShowTablesWithSizes(t *testing.T) { "show_tables_with_sizes_employees": {"BASE TABLE", ""}, } - rs, err := conn.ExecuteFetch(conn.BaseShowTablesWithSizes(), math.MaxInt, false) + rs, err := conn.ExecuteFetch(conn.BaseShowTablesWithSizes(), -1, false) require.NoError(t, err) require.NotEmpty(t, rs.Rows) diff --git a/go/vt/vttablet/endtoend/reserve_test.go b/go/vt/vttablet/endtoend/reserve_test.go index 591512d44c6..d3fb685dd49 100644 --- a/go/vt/vttablet/endtoend/reserve_test.go +++ b/go/vt/vttablet/endtoend/reserve_test.go @@ -28,8 +28,6 @@ import ( "vitess.io/vitess/go/vt/vttablet/endtoend/framework" ) -//TODO: Add Counter checks in all the tests. - func TestMultipleReserveHaveDifferentConnection(t *testing.T) { framework.Server.Config().EnableSettingsPool = false defer func() { @@ -777,9 +775,9 @@ func TestReserveBeginExecuteWithPreQueriesAndCheckConnectionState(t *testing.T) require.NoError(t, err) assert.NotEqual(t, qr1.Rows, qr2.Rows) - // As the transaction is read commited it is not able to see #5. + // As the transaction is read committed it is not able to see #5. assert.Equal(t, `[[INT32(1)] [INT32(2)] [INT32(3)] [INT32(4)]]`, fmt.Sprintf("%v", qr1.Rows)) - // As the transaction is read uncommited it is able to see #4. + // As the transaction is read uncommitted it is able to see #4. assert.Equal(t, `[[INT32(1)] [INT32(2)] [INT32(3)] [INT32(4)] [INT32(5)]]`, fmt.Sprintf("%v", qr2.Rows)) err = rucClient.Commit() @@ -804,7 +802,7 @@ func TestReserveBeginExecuteWithPreQueriesAndCheckConnectionState(t *testing.T) qr2, err = rucClient.Execute(selQuery, nil) require.NoError(t, err) - // As the transaction on read committed client got rollbacked back, table will forget #4. + // As the transaction on read committed client got rolled back, table will forget #4. assert.Equal(t, qr1.Rows, qr2.Rows) assert.Equal(t, `[[INT32(1)] [INT32(2)] [INT32(3)] [INT32(5)]]`, fmt.Sprintf("%v", qr2.Rows)) @@ -1190,3 +1188,23 @@ func TestReserveQueryTimeout(t *testing.T) { assert.NoError(t, client.Release()) } + +// TestReserveFlushTables checks that `flush table with read lock` works only with reserve api. +func TestReserveFlushTables(t *testing.T) { + client := framework.NewClient() + + _, err := client.Execute("flush tables with read lock", nil) + assert.ErrorContains(t, err, "Flush not allowed without reserved connection") + + _, err = client.Execute("unlock tables", nil) + assert.ErrorContains(t, err, "unlock tables should be executed with an existing connection") + + _, err = client.ReserveExecute("flush tables with read lock", nil, nil) + assert.NoError(t, err) + + _, err = client.Execute("unlock tables", nil) + assert.NoError(t, err) + + assert.NoError(t, + client.Release()) +} diff --git a/go/vt/vttablet/endtoend/rpc_test.go b/go/vt/vttablet/endtoend/rpc_test.go index a186d444f8d..e24137e1340 100644 --- a/go/vt/vttablet/endtoend/rpc_test.go +++ b/go/vt/vttablet/endtoend/rpc_test.go @@ -169,6 +169,25 @@ func TestGetSchemaRPC(t *testing.T) { }, getSchemaQueryType: querypb.SchemaTableType_ALL, getSchemaTables: []string{"vitess_temp1", "vitess_temp3", "unknown_table", "vitess_view3", "vitess_view1", "unknown_view"}, + }, { + name: "Create some internal tables", + queries: []string{ + "create table if not exists _vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410(id bigint primary key);", + "create table vitess_temp1 (eid int);", + "create view vitess_view1 as select eid from vitess_a", + }, + deferQueries: []string{ + "drop table _vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "drop table vitess_temp1", + "drop view vitess_view1", + }, + mapToExpect: map[string]string{ + "vitess_view1": "CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view1` AS select `vitess_a`.`eid` AS `eid` from `vitess_a`", + "vitess_temp1": "CREATE TABLE `vitess_temp1` (\n `eid` int DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + // These shouldn't be part of the result, so we verify it is empty. + "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410": "", + }, + getSchemaQueryType: querypb.SchemaTableType_ALL, }, } diff --git a/go/vt/vttablet/endtoend/streamtimeout/healthstream_test.go b/go/vt/vttablet/endtoend/streamtimeout/healthstream_test.go index d69ce193ef9..9890efd427d 100644 --- a/go/vt/vttablet/endtoend/streamtimeout/healthstream_test.go +++ b/go/vt/vttablet/endtoend/streamtimeout/healthstream_test.go @@ -74,7 +74,7 @@ func TestSchemaChangeTimedout(t *testing.T) { // This is because the query timeout triggers the *DBConn.Kill() method, which in turn holds the mutex lock on the health_streamer. // Although not indefinitely, this can result in longer wait times. // It's worth noting that the behavior of *DBConn.Kill() is outside the scope of this test. - reloadInterval := config.SignalSchemaChangeReloadIntervalSeconds.Get() + reloadInterval := config.SignalSchemaChangeReloadInterval time.Sleep(reloadInterval) // pause simulating the mysql stall to allow the health_streamer to resume. diff --git a/go/vt/vttablet/endtoend/streamtimeout/main_test.go b/go/vt/vttablet/endtoend/streamtimeout/main_test.go index 68851bf901b..0b2f37a987c 100644 --- a/go/vt/vttablet/endtoend/streamtimeout/main_test.go +++ b/go/vt/vttablet/endtoend/streamtimeout/main_test.go @@ -84,7 +84,7 @@ func TestMain(m *testing.M) { connParams := cluster.MySQLConnParams() connAppDebugParams := cluster.MySQLAppDebugConnParams() config = tabletenv.NewDefaultConfig() - _ = config.SchemaReloadIntervalSeconds.Set("2100ms") + config.SchemaReloadInterval = (2 * time.Second) + (100 * time.Millisecond) config.SchemaChangeReloadTimeout = 10 * time.Second config.SignalWhenSchemaChange = true diff --git a/go/vt/vttablet/endtoend/views_test.go b/go/vt/vttablet/endtoend/views_test.go index 4ef70345180..99a28b0f215 100644 --- a/go/vt/vttablet/endtoend/views_test.go +++ b/go/vt/vttablet/endtoend/views_test.go @@ -70,7 +70,7 @@ func TestCreateViewDDL(t *testing.T) { qr, err := client.Execute(qSelAllRows, nil) require.NoError(t, err) require.Equal(t, - "[[VARCHAR(\"vttest\") VARCHAR(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`eid` AS `eid`,`vitess_a`.`id` AS `id`,`vitess_a`.`name` AS `name`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", + "[[VARBINARY(\"vttest\") VARBINARY(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`eid` AS `eid`,`vitess_a`.`id` AS `id`,`vitess_a`.`name` AS `name`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", fmt.Sprintf("%v", qr.Rows)) // view already exists. This should fail. @@ -86,7 +86,7 @@ func TestCreateViewDDL(t *testing.T) { qr, err = client.Execute(qSelAllRows, nil) require.NoError(t, err) require.Equal(t, - "[[VARCHAR(\"vttest\") VARCHAR(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`id` AS `id`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", + "[[VARBINARY(\"vttest\") VARBINARY(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`id` AS `id`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", fmt.Sprintf("%v", qr.Rows)) } @@ -132,7 +132,7 @@ func TestAlterViewDDL(t *testing.T) { qr, err := client.Execute(qSelAllRows, nil) require.NoError(t, err) require.Equal(t, - "[[VARCHAR(\"vttest\") VARCHAR(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`eid` AS `eid`,`vitess_a`.`id` AS `id`,`vitess_a`.`name` AS `name`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", + "[[VARBINARY(\"vttest\") VARBINARY(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`eid` AS `eid`,`vitess_a`.`id` AS `id`,`vitess_a`.`name` AS `name`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", fmt.Sprintf("%v", qr.Rows)) // view exists, should PASS @@ -144,7 +144,7 @@ func TestAlterViewDDL(t *testing.T) { qr, err = client.Execute(qSelAllRows, nil) require.NoError(t, err) require.Equal(t, - "[[VARCHAR(\"vttest\") VARCHAR(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`id` AS `id`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", + "[[VARBINARY(\"vttest\") VARBINARY(\"vitess_view\") TEXT(\"CREATE ALGORITHM=UNDEFINED DEFINER=`vt_dba`@`localhost` SQL SECURITY DEFINER VIEW `vitess_view` AS select `vitess_a`.`id` AS `id`,`vitess_a`.`foo` AS `foo` from `vitess_a`\")]]", fmt.Sprintf("%v", qr.Rows)) } diff --git a/go/vt/vttablet/flags.go b/go/vt/vttablet/flags.go index 3ce2cd3b378..994080b95a5 100644 --- a/go/vt/vttablet/flags.go +++ b/go/vt/vttablet/flags.go @@ -25,11 +25,14 @@ import ( ) const ( + // VReplicationExperimentalFlags is a bitmask of experimental features in vreplication. VReplicationExperimentalFlagOptimizeInserts = int64(1) VReplicationExperimentalFlagAllowNoBlobBinlogRowImage = int64(2) + VReplicationExperimentalFlagVPlayerBatching = int64(4) ) var ( + // Default flags. VReplicationExperimentalFlags = VReplicationExperimentalFlagOptimizeInserts | VReplicationExperimentalFlagAllowNoBlobBinlogRowImage VReplicationNetReadTimeout = 300 VReplicationNetWriteTimeout = 600 diff --git a/go/vt/vttablet/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go index cb97abcbbae..8bb8a466b21 100644 --- a/go/vt/vttablet/grpctabletconn/conn.go +++ b/go/vt/vttablet/grpctabletconn/conn.go @@ -188,7 +188,7 @@ func (conn *gRPCQueryClient) StreamExecute(ctx context.Context, target *querypb. fields = ser.Result.Fields } if err := callback(sqltypes.CustomProto3ToResult(fields, ser.Result)); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return nil } return err @@ -417,7 +417,7 @@ func (conn *gRPCQueryClient) ConcludeTransaction(ctx context.Context, target *qu return nil } -// ReadTransaction returns the metadata for the sepcified dtid. +// ReadTransaction returns the metadata for the specified dtid. func (conn *gRPCQueryClient) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (*querypb.TransactionMetadata, error) { conn.mu.RLock() defer conn.mu.RUnlock() @@ -473,6 +473,10 @@ func (conn *gRPCQueryClient) BeginExecute(ctx context.Context, target *querypb.T // BeginStreamExecute starts a transaction and runs an Execute. func (conn *gRPCQueryClient) BeginStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, query string, bindVars map[string]*querypb.BindVariable, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (state queryservice.TransactionState, err error) { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() + conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -537,7 +541,7 @@ func (conn *gRPCQueryClient) BeginStreamExecute(ctx context.Context, target *que fields = ser.Result.Fields } if err := callback(sqltypes.CustomProto3ToResult(fields, ser.Result)); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return state, nil } return state, err @@ -583,7 +587,7 @@ func (conn *gRPCQueryClient) MessageStream(ctx context.Context, target *querypb. fields = msr.Result.Fields } if err := callback(sqltypes.CustomProto3ToResult(fields, msr.Result)); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return nil } return err @@ -640,7 +644,7 @@ func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*qu return tabletconn.ErrorFromGRPC(err) } if err := callback(shr); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return nil } return err @@ -650,6 +654,9 @@ func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*qu // VStream starts a VReplication stream. func (conn *gRPCQueryClient) VStream(ctx context.Context, request *binlogdatapb.VStreamRequest, send func([]*binlogdatapb.VEvent) error) error { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() stream, err := func() (queryservicepb.Query_VStreamClient, error) { conn.mu.RLock() defer conn.mu.RUnlock() @@ -695,6 +702,9 @@ func (conn *gRPCQueryClient) VStream(ctx context.Context, request *binlogdatapb. // VStreamRows streams rows of a query from the specified starting point. func (conn *gRPCQueryClient) VStreamRows(ctx context.Context, request *binlogdatapb.VStreamRowsRequest, send func(*binlogdatapb.VStreamRowsResponse) error) error { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() stream, err := func() (queryservicepb.Query_VStreamRowsClient, error) { conn.mu.RLock() defer conn.mu.RUnlock() @@ -737,6 +747,9 @@ func (conn *gRPCQueryClient) VStreamRows(ctx context.Context, request *binlogdat // VStreamTables streams rows of a query from the specified starting point. func (conn *gRPCQueryClient) VStreamTables(ctx context.Context, request *binlogdatapb.VStreamTablesRequest, send func(*binlogdatapb.VStreamTablesResponse) error) error { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() stream, err := func() (queryservicepb.Query_VStreamTablesClient, error) { conn.mu.RLock() defer conn.mu.RUnlock() @@ -777,6 +790,9 @@ func (conn *gRPCQueryClient) VStreamTables(ctx context.Context, request *binlogd // VStreamResults streams rows of a query from the specified starting point. func (conn *gRPCQueryClient) VStreamResults(ctx context.Context, target *querypb.Target, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() stream, err := func() (queryservicepb.Query_VStreamResultsClient, error) { conn.mu.RLock() defer conn.mu.RUnlock() @@ -856,6 +872,9 @@ func (conn *gRPCQueryClient) ReserveBeginExecute(ctx context.Context, target *qu // ReserveBeginStreamExecute implements the queryservice interface func (conn *gRPCQueryClient) ReserveBeginStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, postBeginQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (state queryservice.ReservedTransactionState, err error) { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -924,7 +943,7 @@ func (conn *gRPCQueryClient) ReserveBeginStreamExecute(ctx context.Context, targ fields = ser.Result.Fields } if err := callback(sqltypes.CustomProto3ToResult(fields, ser.Result)); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return state, nil } return state, err @@ -967,6 +986,9 @@ func (conn *gRPCQueryClient) ReserveExecute(ctx context.Context, target *querypb // ReserveStreamExecute implements the queryservice interface func (conn *gRPCQueryClient) ReserveStreamExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (state queryservice.ReservedState, err error) { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -1029,7 +1051,7 @@ func (conn *gRPCQueryClient) ReserveStreamExecute(ctx context.Context, target *q fields = ser.Result.Fields } if err := callback(sqltypes.CustomProto3ToResult(fields, ser.Result)); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return state, nil } return state, err @@ -1060,6 +1082,9 @@ func (conn *gRPCQueryClient) Release(ctx context.Context, target *querypb.Target // GetSchema implements the queryservice interface func (conn *gRPCQueryClient) GetSchema(ctx context.Context, target *querypb.Target, tableType querypb.SchemaTableType, tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { + // Please see comments in StreamExecute to see how this works. + ctx, cancel := context.WithCancel(ctx) + defer cancel() conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { @@ -1092,7 +1117,7 @@ func (conn *gRPCQueryClient) GetSchema(ctx context.Context, target *querypb.Targ return tabletconn.ErrorFromGRPC(err) } if err := callback(shr); err != nil { - if err == nil || err == io.EOF { + if err == io.EOF { return nil } return err diff --git a/go/vt/vttablet/grpctabletconn/conn_test.go b/go/vt/vttablet/grpctabletconn/conn_test.go index fb182bfe2e4..70e30e337bc 100644 --- a/go/vt/vttablet/grpctabletconn/conn_test.go +++ b/go/vt/vttablet/grpctabletconn/conn_test.go @@ -17,13 +17,21 @@ limitations under the License. package grpctabletconn import ( + "context" + "fmt" "io" "net" "os" + "sync" "testing" + "github.com/stretchr/testify/require" "google.golang.org/grpc" + "vitess.io/vitess/go/sqltypes" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + queryservicepb "vitess.io/vitess/go/vt/proto/queryservice" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vttablet/grpcqueryservice" "vitess.io/vitess/go/vt/vttablet/tabletconntest" @@ -113,3 +121,111 @@ func TestGRPCTabletAuthConn(t *testing.T) { }, }, service, f) } + +// mockQueryClient is a mock query client that returns an error from Streaming calls, +// but only after storing the context that was passed to the RPC. +type mockQueryClient struct { + lastCallCtx context.Context + queryservicepb.QueryClient +} + +func (m *mockQueryClient) StreamExecute(ctx context.Context, in *querypb.StreamExecuteRequest, opts ...grpc.CallOption) (queryservicepb.Query_StreamExecuteClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) BeginStreamExecute(ctx context.Context, in *querypb.BeginStreamExecuteRequest, opts ...grpc.CallOption) (queryservicepb.Query_BeginStreamExecuteClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) ReserveStreamExecute(ctx context.Context, in *querypb.ReserveStreamExecuteRequest, opts ...grpc.CallOption) (queryservicepb.Query_ReserveStreamExecuteClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) ReserveBeginStreamExecute(ctx context.Context, in *querypb.ReserveBeginStreamExecuteRequest, opts ...grpc.CallOption) (queryservicepb.Query_ReserveBeginStreamExecuteClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) VStream(ctx context.Context, in *binlogdatapb.VStreamRequest, opts ...grpc.CallOption) (queryservicepb.Query_VStreamClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) VStreamRows(ctx context.Context, in *binlogdatapb.VStreamRowsRequest, opts ...grpc.CallOption) (queryservicepb.Query_VStreamRowsClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) VStreamTables(ctx context.Context, in *binlogdatapb.VStreamTablesRequest, opts ...grpc.CallOption) (queryservicepb.Query_VStreamTablesClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) VStreamResults(ctx context.Context, in *binlogdatapb.VStreamResultsRequest, opts ...grpc.CallOption) (queryservicepb.Query_VStreamResultsClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +func (m *mockQueryClient) GetSchema(ctx context.Context, in *querypb.GetSchemaRequest, opts ...grpc.CallOption) (queryservicepb.Query_GetSchemaClient, error) { + m.lastCallCtx = ctx + return nil, fmt.Errorf("A general error") +} + +var _ queryservicepb.QueryClient = (*mockQueryClient)(nil) + +// TestGoRoutineLeakPrevention tests that after all the RPCs that stream queries, we end up closing the context that was passed to it, to prevent go routines from being leaked. +func TestGoRoutineLeakPrevention(t *testing.T) { + mqc := &mockQueryClient{} + qc := &gRPCQueryClient{ + mu: sync.RWMutex{}, + cc: &grpc.ClientConn{}, + c: mqc, + } + _ = qc.StreamExecute(context.Background(), nil, "", nil, 0, 0, nil, func(result *sqltypes.Result) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _, _ = qc.BeginStreamExecute(context.Background(), nil, nil, "", nil, 0, nil, func(result *sqltypes.Result) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _, _ = qc.ReserveBeginStreamExecute(context.Background(), nil, nil, nil, "", nil, nil, func(result *sqltypes.Result) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _, _ = qc.ReserveStreamExecute(context.Background(), nil, nil, "", nil, 0, nil, func(result *sqltypes.Result) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _ = qc.VStream(context.Background(), &binlogdatapb.VStreamRequest{}, func(events []*binlogdatapb.VEvent) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _ = qc.VStreamRows(context.Background(), &binlogdatapb.VStreamRowsRequest{}, func(response *binlogdatapb.VStreamRowsResponse) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _ = qc.VStreamResults(context.Background(), nil, "", func(response *binlogdatapb.VStreamResultsResponse) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _ = qc.VStreamTables(context.Background(), &binlogdatapb.VStreamTablesRequest{}, func(response *binlogdatapb.VStreamTablesResponse) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) + + _ = qc.GetSchema(context.Background(), nil, querypb.SchemaTableType_TABLES, nil, func(schemaRes *querypb.GetSchemaResponse) error { + return nil + }) + require.Error(t, mqc.lastCallCtx.Err()) +} diff --git a/go/vt/vttablet/grpctmclient/client.go b/go/vt/vttablet/grpctmclient/client.go index 0068ed74706..089b7c014dd 100644 --- a/go/vt/vttablet/grpctmclient/client.go +++ b/go/vt/vttablet/grpctmclient/client.go @@ -55,7 +55,7 @@ var ( ) func registerFlags(fs *pflag.FlagSet) { - fs.IntVar(&concurrency, "tablet_manager_grpc_concurrency", concurrency, "concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App})") + fs.IntVar(&concurrency, "tablet_manager_grpc_concurrency", concurrency, "concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,App} and CheckThrottler)") fs.StringVar(&cert, "tablet_manager_grpc_cert", cert, "the cert to use to connect") fs.StringVar(&key, "tablet_manager_grpc_key", key, "the key to use to connect") fs.StringVar(&ca, "tablet_manager_grpc_ca", ca, "the server ca to use to validate servers when connecting") @@ -94,10 +94,9 @@ type tmc struct { // grpcClient implements both dialer and poolDialer. type grpcClient struct { - // This cache of connections is to maximize QPS for ExecuteFetch. - // Note we'll keep the clients open and close them upon Close() only. - // But that's OK because usually the tasks that use them are - // one-purpose only. + // This cache of connections is to maximize QPS for ExecuteFetchAs{Dba,App} and + // CheckThrottler. Note we'll keep the clients open and close them upon Close() only. + // But that's OK because usually the tasks that use them are one-purpose only. // The map is protected by the mutex. mu sync.Mutex rpcClientMap map[string]chan *tmc @@ -115,16 +114,17 @@ type poolDialer interface { // Client implements tmclient.TabletManagerClient. // // Connections are produced by the dialer implementation, which is either the -// grpcClient implementation, which reuses connections only for ExecuteFetch and -// otherwise makes single-purpose connections that are closed after use. +// grpcClient implementation, which reuses connections only for ExecuteFetchAs{Dba,App} +// and CheckThrottler, otherwise making single-purpose connections that are closed +// after use. // // In order to more efficiently use the underlying tcp connections, you can // instead use the cachedConnDialer implementation by specifying // -// -tablet_manager_protocol "grpc-cached" +// --tablet_manager_protocol "grpc-cached" // -// The cachedConnDialer keeps connections to up to -tablet_manager_grpc_connpool_size distinct -// tablets open at any given time, for faster per-RPC call time, and less +// The cachedConnDialer keeps connections to up to --tablet_manager_grpc_connpool_size +// distinct tablets open at any given time, for faster per-RPC call time, and less // connection churn. type Client struct { dialer dialer @@ -488,11 +488,12 @@ func (client *Client) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb. } response, err := c.ExecuteFetchAsDba(ctx, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: req.Query, - DbName: topoproto.TabletDbName(tablet), - MaxRows: req.MaxRows, - DisableBinlogs: req.DisableBinlogs, - ReloadSchema: req.DisableBinlogs, + Query: req.Query, + DbName: topoproto.TabletDbName(tablet), + MaxRows: req.MaxRows, + DisableBinlogs: req.DisableBinlogs, + ReloadSchema: req.DisableBinlogs, + DisableForeignKeyChecks: req.DisableForeignKeyChecks, }) if err != nil { return nil, err @@ -1002,12 +1003,29 @@ func (client *Client) Backup(ctx context.Context, tablet *topodatapb.Tablet, req } // CheckThrottler is part of the tmclient.TabletManagerClient interface. +// It always tries to use a cached client via the dialer pool as this is +// called very frequently between tablets when the throttler is enabled in +// a keyspace and the overhead of creating a new gRPC connection/channel +// and dialing the other tablet every time is not practical. func (client *Client) CheckThrottler(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.CheckThrottlerRequest) (*tabletmanagerdatapb.CheckThrottlerResponse, error) { - c, closer, err := client.dialer.dial(ctx, tablet) - if err != nil { - return nil, err + var c tabletmanagerservicepb.TabletManagerClient + var err error + if poolDialer, ok := client.dialer.(poolDialer); ok { + c, err = poolDialer.dialPool(ctx, tablet) + if err != nil { + return nil, err + } } - defer closer.Close() + + if c == nil { + var closer io.Closer + c, closer, err = client.dialer.dial(ctx, tablet) + if err != nil { + return nil, err + } + defer closer.Close() + } + response, err := c.CheckThrottler(ctx, req) if err != nil { return nil, err diff --git a/go/vt/vttablet/grpctmserver/server.go b/go/vt/vttablet/grpctmserver/server.go index d0fe5a2cbe1..d7d70194cec 100644 --- a/go/vt/vttablet/grpctmserver/server.go +++ b/go/vt/vttablet/grpctmserver/server.go @@ -300,7 +300,7 @@ func (s *server) PrimaryPosition(ctx context.Context, request *tabletmanagerdata } func (s *server) WaitForPosition(ctx context.Context, request *tabletmanagerdatapb.WaitForPositionRequest) (response *tabletmanagerdatapb.WaitForPositionResponse, err error) { - defer s.tm.HandleRPCPanic(ctx, "WaitForPosition", request, response, false /*verbose*/, &err) + defer s.tm.HandleRPCPanic(ctx, "WaitForPosition", request, response, true /*verbose*/, &err) ctx = callinfo.GRPCCallInfo(ctx) response = &tabletmanagerdatapb.WaitForPositionResponse{} return response, s.tm.WaitForPosition(ctx, request.Position) @@ -426,7 +426,7 @@ func (s *server) InitPrimary(ctx context.Context, request *tabletmanagerdatapb.I } func (s *server) PopulateReparentJournal(ctx context.Context, request *tabletmanagerdatapb.PopulateReparentJournalRequest) (response *tabletmanagerdatapb.PopulateReparentJournalResponse, err error) { - defer s.tm.HandleRPCPanic(ctx, "PopulateReparentJournal", request, response, false /*verbose*/, &err) + defer s.tm.HandleRPCPanic(ctx, "PopulateReparentJournal", request, response, true /*verbose*/, &err) ctx = callinfo.GRPCCallInfo(ctx) response = &tabletmanagerdatapb.PopulateReparentJournalResponse{} return response, s.tm.PopulateReparentJournal(ctx, request.TimeCreatedNs, request.ActionName, request.PrimaryAlias, request.ReplicationPosition) diff --git a/go/vt/vttablet/onlineddl/analysis.go b/go/vt/vttablet/onlineddl/analysis.go index 987f09124a1..1dc073bb7d0 100644 --- a/go/vt/vttablet/onlineddl/analysis.go +++ b/go/vt/vttablet/onlineddl/analysis.go @@ -19,11 +19,11 @@ package onlineddl import ( "context" "encoding/json" - "strings" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/schema" + "vitess.io/vitess/go/vt/schemadiff" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" ) @@ -75,7 +75,7 @@ func (e *Executor) getCreateTableStatement(ctx context.Context, tableName string if err != nil { return nil, vterrors.Wrapf(err, "in Executor.getCreateTableStatement()") } - stmt, err := sqlparser.ParseStrictDDL(showCreateTable) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(showCreateTable) if err != nil { return nil, err } @@ -175,181 +175,24 @@ func analyzeAddRangePartition(alterTable *sqlparser.AlterTable, createTable *sql return op } -// alterOptionAvailableViaInstantDDL chcks if the specific alter option is eligible to run via ALGORITHM=INSTANT -// reference: https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html -func alterOptionAvailableViaInstantDDL(alterOption sqlparser.AlterOption, createTable *sqlparser.CreateTable, capableOf mysql.CapableOf) (bool, error) { - findColumn := func(colName string) *sqlparser.ColumnDefinition { - if createTable == nil { - return nil - } - for _, col := range createTable.TableSpec.Columns { - if strings.EqualFold(colName, col.Name.String()) { - return col - } - } - return nil - } - findTableOption := func(optName string) *sqlparser.TableOption { - if createTable == nil { - return nil - } - for _, opt := range createTable.TableSpec.Options { - if strings.EqualFold(optName, opt.Name) { - return opt - } - } - return nil - } - isVirtualColumn := func(colName string) bool { - col := findColumn(colName) - if col == nil { - return false - } - if col.Type.Options == nil { - return false - } - if col.Type.Options.As == nil { - return false - } - return col.Type.Options.Storage == sqlparser.VirtualStorage - } - colStringStrippedDown := func(col *sqlparser.ColumnDefinition, stripDefault bool, stripEnum bool) string { - strippedCol := sqlparser.CloneRefOfColumnDefinition(col) - if stripDefault { - strippedCol.Type.Options.Default = nil - strippedCol.Type.Options.DefaultLiteral = false - } - if stripEnum { - strippedCol.Type.EnumValues = nil - } - return sqlparser.CanonicalString(strippedCol) - } - hasPrefix := func(vals []string, prefix []string) bool { - if len(vals) < len(prefix) { - return false - } - for i := range prefix { - if vals[i] != prefix[i] { - return false - } - } - return true - } - // Up to 8.0.26 we could only ADD COLUMN as last column - switch opt := alterOption.(type) { - case *sqlparser.ChangeColumn: - // We do not support INSTANT for renaming a column (ALTER TABLE ...CHANGE) because: - // 1. We discourage column rename - // 2. We do not produce CHANGE statements in declarative diff - // 3. The success of the operation depends on whether the column is referenced by a foreign key - // in another table. Which is a bit too much to compute here. - return false, nil - case *sqlparser.AddColumns: - if opt.First || opt.After != nil { - // not a "last" column. Only supported as of 8.0.29 - return capableOf(mysql.InstantAddDropColumnFlavorCapability) - } - // Adding a *last* column is supported in 8.0 - return capableOf(mysql.InstantAddLastColumnFlavorCapability) - case *sqlparser.DropColumn: - // not supported in COMPRESSED tables - if opt := findTableOption("ROW_FORMAT"); opt != nil { - if strings.EqualFold(opt.String, "COMPRESSED") { - return false, nil - } - } - if isVirtualColumn(opt.Name.Name.String()) { - // supported by all 8.0 versions - return capableOf(mysql.InstantAddDropVirtualColumnFlavorCapability) - } - return capableOf(mysql.InstantAddDropColumnFlavorCapability) - case *sqlparser.ModifyColumn: - if col := findColumn(opt.NewColDefinition.Name.String()); col != nil { - // Check if only diff is change of default - // we temporarily remove the DEFAULT expression (if any) from both - // table and ALTER statement, and compare the columns: if they're otherwise equal, - // then the only change can be an addition/change/removal of DEFAULT, which - // is instant-table. - tableColDefinition := colStringStrippedDown(col, true, false) - newColDefinition := colStringStrippedDown(opt.NewColDefinition, true, false) - if tableColDefinition == newColDefinition { - return capableOf(mysql.InstantChangeColumnDefaultFlavorCapability) - } - // Check if: - // 1. this an ENUM/SET - // 2. and the change is to append values to the end of the list - // 3. and the number of added values does not increase the storage size for the enum/set - // 4. while still not caring about a change in the default value - if len(col.Type.EnumValues) > 0 && len(opt.NewColDefinition.Type.EnumValues) > 0 { - // both are enum or set - if !hasPrefix(opt.NewColDefinition.Type.EnumValues, col.Type.EnumValues) { - return false, nil - } - // we know the new column definition is identical to, or extends, the old definition. - // Now validate storage: - if strings.EqualFold(col.Type.Type, "enum") { - if len(col.Type.EnumValues) <= 255 && len(opt.NewColDefinition.Type.EnumValues) > 255 { - // this increases the SET storage size (1 byte for up to 8 values, 2 bytes beyond) - return false, nil - } - } - if strings.EqualFold(col.Type.Type, "set") { - if (len(col.Type.EnumValues)+7)/8 != (len(opt.NewColDefinition.Type.EnumValues)+7)/8 { - // this increases the SET storage size (1 byte for up to 8 values, 2 bytes for 8-15, etc.) - return false, nil - } - } - // Now don't care about change of default: - tableColDefinition := colStringStrippedDown(col, true, true) - newColDefinition := colStringStrippedDown(opt.NewColDefinition, true, true) - if tableColDefinition == newColDefinition { - return capableOf(mysql.InstantExpandEnumCapability) - } - } - } - return false, nil - default: - return false, nil - } -} - -// AnalyzeInstantDDL takes declarative CreateTable and AlterTable, as well as a server version, and checks whether it is possible to run the ALTER -// using ALGORITM=INSTANT for that version. -// This function is INTENTIONALLY public, even though we do not guarantee that it will remain so. -func AnalyzeInstantDDL(alterTable *sqlparser.AlterTable, createTable *sqlparser.CreateTable, capableOf mysql.CapableOf) (*SpecialAlterPlan, error) { - capable, err := capableOf(mysql.InstantDDLFlavorCapability) +// analyzeInstantDDL takes declarative CreateTable and AlterTable, as well as a server version, and checks whether it is possible to run the ALTER +// using ALGORITHM=INSTANT for that version. +func analyzeInstantDDL(alterTable *sqlparser.AlterTable, createTable *sqlparser.CreateTable, capableOf capabilities.CapableOf) (*SpecialAlterPlan, error) { + capable, err := schemadiff.AlterTableCapableOfInstantDDL(alterTable, createTable, capableOf) if err != nil { return nil, err } if !capable { return nil, nil } - if alterTable.PartitionOption != nil { - // no INSTANT for partitions - return nil, nil - } - if alterTable.PartitionSpec != nil { - // no INSTANT for partitions - return nil, nil - } - // For the ALTER statement to qualify for ALGORITHM=INSTANT, all alter options must each qualify. - for _, alterOption := range alterTable.AlterOptions { - instantOK, err := alterOptionAvailableViaInstantDDL(alterOption, createTable, capableOf) - if err != nil { - return nil, err - } - if !instantOK { - return nil, nil - } - } op := NewSpecialAlterOperation(instantDDLSpecialOperation, alterTable, createTable) return op, nil } // analyzeSpecialAlterPlan checks if the given ALTER onlineDDL, and for the current state of affected table, // can be executed in a special way. If so, it returns with a "special plan" -func (e *Executor) analyzeSpecialAlterPlan(ctx context.Context, onlineDDL *schema.OnlineDDL, capableOf mysql.CapableOf) (*SpecialAlterPlan, error) { - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) +func (e *Executor) analyzeSpecialAlterPlan(ctx context.Context, onlineDDL *schema.OnlineDDL, capableOf capabilities.CapableOf) (*SpecialAlterPlan, error) { + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return nil, err } @@ -379,7 +222,7 @@ func (e *Executor) analyzeSpecialAlterPlan(ctx context.Context, onlineDDL *schem } } if onlineDDL.StrategySetting().IsPreferInstantDDL() { - op, err := AnalyzeInstantDDL(alterTable, createTable, capableOf) + op, err := analyzeInstantDDL(alterTable, createTable, capableOf) if err != nil { return nil, err } diff --git a/go/vt/vttablet/onlineddl/analysis_test.go b/go/vt/vttablet/onlineddl/analysis_test.go index afaa3e8aa1f..b37a24d3bbc 100644 --- a/go/vt/vttablet/onlineddl/analysis_test.go +++ b/go/vt/vttablet/onlineddl/analysis_test.go @@ -208,21 +208,22 @@ func TestAnalyzeInstantDDL(t *testing.T) { instant: false, }, } + parser := sqlparser.NewTestParser() for _, tc := range tt { name := tc.version + " " + tc.create t.Run(name, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(tc.create) + stmt, err := parser.ParseStrictDDL(tc.create) require.NoError(t, err) createTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - stmt, err = sqlparser.ParseStrictDDL(tc.alter) + stmt, err = parser.ParseStrictDDL(tc.alter) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) - _, capableOf, _ := mysql.GetFlavor(tc.version, nil) - plan, err := AnalyzeInstantDDL(alterTable, createTable, capableOf) + capableOf := mysql.ServerVersionCapableOf(tc.version) + plan, err := analyzeInstantDDL(alterTable, createTable, capableOf) if tc.expectError { assert.Error(t, err) } else { diff --git a/go/vt/vttablet/onlineddl/executor.go b/go/vt/vttablet/onlineddl/executor.go index 8a3cf61348b..30bb465a1f0 100644 --- a/go/vt/vttablet/onlineddl/executor.go +++ b/go/vt/vttablet/onlineddl/executor.go @@ -24,7 +24,6 @@ import ( "context" "errors" "fmt" - "math" "os" "path" "strconv" @@ -39,10 +38,12 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/syscallutil" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -76,7 +77,7 @@ var ( ) var ( - // fixCompletedTimestampDone fixes a nil `completed_tiemstamp` columns, see + // fixCompletedTimestampDone fixes a nil `completed_timestamp` columns, see // https://github.com/vitessio/vitess/issues/13927 // The fix is in release-18.0 // TODO: remove in release-19.0 @@ -94,6 +95,10 @@ var ( retainOnlineDDLTables = 24 * time.Hour defaultCutOverThreshold = 10 * time.Second maxConcurrentOnlineDDLs = 256 + + migrationNextCheckIntervals = []time.Duration{1 * time.Second, 5 * time.Second, 10 * time.Second, 20 * time.Second} + maxConstraintNameLength = 64 + cutoverIntervals = []time.Duration{0, 1 * time.Minute, 5 * time.Minute, 10 * time.Minute, 30 * time.Minute} ) func init() { @@ -109,9 +114,6 @@ func registerOnlineDDLFlags(fs *pflag.FlagSet) { fs.IntVar(&maxConcurrentOnlineDDLs, "max_concurrent_online_ddl", maxConcurrentOnlineDDLs, "Maximum number of online DDL changes that may run concurrently") } -var migrationNextCheckIntervals = []time.Duration{1 * time.Second, 5 * time.Second, 10 * time.Second, 20 * time.Second} -var maxConstraintNameLength = 64 - const ( maxPasswordLength = 32 // MySQL's *replication* password may not exceed 32 characters staleMigrationMinutes = 180 @@ -176,6 +178,7 @@ type Executor struct { ts *topo.Server lagThrottler *throttle.Throttler toggleBufferTableFunc func(cancelCtx context.Context, tableName string, timeout time.Duration, bufferQueries bool) + requestGCChecksFunc func() tabletAlias *topodatapb.TabletAlias keyspace string @@ -238,7 +241,7 @@ func newGCTableRetainTime() time.Time { } // getMigrationCutOverThreshold returns the cut-over threshold for the given migration. The migration's -// DDL Strategy may excplicitly set the threshold; otherwise, we return the default cut-over threshold. +// DDL Strategy may explicitly set the threshold; otherwise, we return the default cut-over threshold. func getMigrationCutOverThreshold(onlineDDL *schema.OnlineDDL) time.Duration { if threshold, _ := onlineDDL.StrategySetting().CutOverThreshold(); threshold != 0 { return threshold @@ -251,6 +254,7 @@ func NewExecutor(env tabletenv.Env, tabletAlias *topodatapb.TabletAlias, ts *top lagThrottler *throttle.Throttler, tabletTypeFunc func() topodatapb.TabletType, toggleBufferTableFunc func(cancelCtx context.Context, tableName string, timeout time.Duration, bufferQueries bool), + requestGCChecksFunc func(), ) *Executor { // sanitize flags if maxConcurrentOnlineDDLs < 1 { @@ -261,13 +265,14 @@ func NewExecutor(env tabletenv.Env, tabletAlias *topodatapb.TabletAlias, ts *top tabletAlias: tabletAlias.CloneVT(), pool: connpool.NewPool(env, "OnlineDDLExecutorPool", tabletenv.ConnPoolConfig{ - Size: databasePoolSize, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: databasePoolSize, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }), tabletTypeFunc: tabletTypeFunc, ts: ts, lagThrottler: lagThrottler, toggleBufferTableFunc: toggleBufferTableFunc, + requestGCChecksFunc: requestGCChecksFunc, ticks: timer.NewTimer(migrationCheckInterval), // Gracefully return an error if any caller tries to execute // a query before the executor has been fully opened. @@ -286,7 +291,7 @@ func (e *Executor) executeQuery(ctx context.Context, query string) (result *sqlt } defer conn.Recycle() - return conn.Conn.Exec(ctx, query, math.MaxInt32, true) + return conn.Conn.Exec(ctx, query, -1, true) } func (e *Executor) executeQueryWithSidecarDBReplacement(ctx context.Context, query string) (result *sqltypes.Result, err error) { @@ -299,11 +304,11 @@ func (e *Executor) executeQueryWithSidecarDBReplacement(ctx context.Context, que defer conn.Recycle() // Replace any provided sidecar DB qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) + uq, err := e.env.Environment().Parser().ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } - return conn.Conn.Exec(ctx, uq, math.MaxInt32, true) + return conn.Conn.Exec(ctx, uq, -1, true) } // TabletAliasString returns tablet alias as string (duh) @@ -387,7 +392,7 @@ func (e *Executor) matchesShards(commaDelimitedShards string) bool { } // countOwnedRunningMigrations returns an estimate of current count of running migrations; this is -// normally an accurate number, but can be inexact because the exdcutor peridocially reviews +// normally an accurate number, but can be inexact because the executor periodically reviews // e.ownedRunningMigrations and adds/removes migrations based on actual migration state. func (e *Executor) countOwnedRunningMigrations() (count int) { e.ownedRunningMigrations.Range(func(_, val any) bool { @@ -408,7 +413,7 @@ func (e *Executor) allowConcurrentMigration(onlineDDL *schema.OnlineDDL) (action } var err error - action, err = onlineDDL.GetAction() + action, err = onlineDDL.GetAction(e.env.Environment().Parser()) if err != nil { return action, false } @@ -546,7 +551,7 @@ func (e *Executor) readMySQLVariables(ctx context.Context) (variables *mysqlVari } // createOnlineDDLUser creates a gh-ost or pt-osc user account with all -// neccessary privileges and with a random password +// necessary privileges and with a random password func (e *Executor) createOnlineDDLUser(ctx context.Context) (password string, err error) { conn, err := dbconnpool.NewDBConnection(ctx, e.env.Config().DB.DbaConnector()) if err != nil { @@ -765,8 +770,100 @@ func (e *Executor) terminateVReplMigration(ctx context.Context, uuid string) err return nil } +// killTableLockHoldersAndAccessors kills any active queries using the given table, and also kills +// connections with open transactions, holding locks on the table. +// This is done on a best-effort basis, by issuing `KILL` and `KILL QUERY` commands. As MySQL goes, +// it is not guaranteed that the queries/transactions will terminate in a timely manner. +func (e *Executor) killTableLockHoldersAndAccessors(ctx context.Context, tableName string) error { + log.Infof("killTableLockHoldersAndAccessors: %v", tableName) + conn, err := dbconnpool.NewDBConnection(ctx, e.env.Config().DB.DbaWithDB()) + if err != nil { + return err + } + defer conn.Close() + + { + // First, let's look at PROCESSLIST for queries that _might_ be operating on our table. This may have + // plenty false positives as we're simply looking for the table name as a query substring. + likeVariable := "%" + tableName + "%" + query, err := sqlparser.ParseAndBind(sqlFindProcessByInfo, sqltypes.StringBindVariable(likeVariable)) + if err != nil { + return err + } + rs, err := conn.Conn.ExecuteFetch(query, -1, true) + if err != nil { + return err + } + + log.Infof("killTableLockHoldersAndAccessors: found %v potential queries", len(rs.Rows)) + // Now that we have some list of queries, we actually parse them to find whether the query actually references our table: + for _, row := range rs.Named().Rows { + threadId := row.AsInt64("id", 0) + infoQuery := row.AsString("info", "") + stmt, err := e.env.Environment().Parser().Parse(infoQuery) + if err != nil { + log.Error(vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unable to parse processlist Info query: %v", infoQuery)) + continue + } + queryUsesTable := false + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + switch node := node.(type) { + case *sqlparser.TableName: + if node.Name.String() == tableName { + queryUsesTable = true + return false, nil + } + case *sqlparser.AliasedTableExpr: + if alasedTableName, ok := node.Expr.(sqlparser.TableName); ok { + if alasedTableName.Name.String() == tableName { + queryUsesTable = true + return false, nil + } + } + } + return true, nil + }, stmt) + + if queryUsesTable { + log.Infof("killTableLockHoldersAndAccessors: killing query %v: %.100s", threadId, infoQuery) + killQuery := fmt.Sprintf("KILL QUERY %d", threadId) + if _, err := conn.Conn.ExecuteFetch(killQuery, 1, false); err != nil { + log.Error(vterrors.Errorf(vtrpcpb.Code_ABORTED, "could not kill query %v. Ignoring", threadId)) + } + } + } + } + capableOf := mysql.ServerVersionCapableOf(conn.ServerVersion) + capable, err := capableOf(capabilities.PerformanceSchemaDataLocksTableCapability) + if err != nil { + return err + } + if capable { + { + // Kill connections that have open transactions locking the table. These potentially (probably?) are not + // actively running a query on our table. They're doing other things while holding locks on our table. + query, err := sqlparser.ParseAndBind(sqlProcessWithLocksOnTable, sqltypes.StringBindVariable(tableName)) + if err != nil { + return err + } + rs, err := conn.Conn.ExecuteFetch(query, -1, true) + if err != nil { + return err + } + log.Infof("killTableLockHoldersAndAccessors: found %v locking transactions", len(rs.Rows)) + for _, row := range rs.Named().Rows { + threadId := row.AsInt64("trx_mysql_thread_id", 0) + log.Infof("killTableLockHoldersAndAccessors: killing connection %v with transaction on table", threadId) + killConnection := fmt.Sprintf("KILL %d", threadId) + _, _ = conn.Conn.ExecuteFetch(killConnection, 1, false) + } + } + } + return nil +} + // cutOverVReplMigration stops vreplication, then removes the _vt.vreplication entry for the given migration -func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) error { +func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream, shouldForceCutOver bool) error { if err := e.incrementCutoverAttempts(ctx, s.workflow); err != nil { return err } @@ -775,7 +872,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) er defer tmClient.Close() // sanity checks: - vreplTable, err := getVreplTable(ctx, s) + vreplTable, err := getVreplTable(s) if err != nil { return err } @@ -844,7 +941,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) er } // This was a best effort optimization. Possibly the error is not nil. Which means we // still have a record of the sentry table, and gcArtifacts() will still be able to take - // care of it in the futre. + // care of it in the future. }() parsed := sqlparser.BuildParsedQuery(sqlCreateSentryTable, sentryTableName) if _, err := e.execQuery(ctx, parsed.Query); err != nil { @@ -890,13 +987,11 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) er if preserveFKSupported { // This code is only applicable when MySQL supports the 'rename_table_preserve_foreign_key' variable. This variable // does not exist in vanilla MySQL. - // See https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced - // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. - if _, err := renameConn.Conn.Exec(ctx, sqlEnablePreserveForeignKey, 1, false); err != nil { - return err - } - log.Infof("@@rename_table_preserve_foreign_key enabled") - defer renameConn.Conn.Exec(ctx, sqlDisablePreserveForeignKey, 1, false) + // See + // - https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced + // - https://github.com/planetscale/mysql-server/commit/c2f1344a6863518d749f2eb01a4c74ca08a5b889 + // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps3. + log.Infof("@@rename_table_preserve_foreign_key supported") } renameQuery := sqlparser.BuildParsedQuery(sqlSwapTables, onlineDDL.Table, sentryTableName, vreplTable, onlineDDL.Table, sentryTableName, vreplTable) @@ -904,7 +999,7 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) er waitForRenameProcess := func() error { // This function waits until it finds the RENAME TABLE... query running in MySQL's PROCESSLIST, or until timeout // The function assumes that one of the renamed tables is locked, thus causing the RENAME to block. If nothing - // is locked, then the RENAME will be near-instantaneious and it's unlikely that the function will find it. + // is locked, then the RENAME will be near-instantaneous and it's unlikely that the function will find it. renameWaitCtx, cancel := context.WithTimeout(ctx, migrationCutOverThreshold) defer cancel() @@ -974,6 +1069,12 @@ func (e *Executor) cutOverVReplMigration(ctx context.Context, s *VReplStream) er e.updateMigrationStage(ctx, onlineDDL.UUID, "graceful wait for buffering") time.Sleep(100 * time.Millisecond) + if shouldForceCutOver { + if err := e.killTableLockHoldersAndAccessors(ctx, onlineDDL.Table); err != nil { + return err + } + } + if isVreplicationTestSuite { // The testing suite may inject queries internally from the server via a recurring EVENT. // Those queries are unaffected by query rules (ACLs) because they don't go through Vitess. @@ -1280,7 +1381,7 @@ func (e *Executor) duplicateCreateTable(ctx context.Context, onlineDDL *schema.O constraintMap map[string]string, err error, ) { - stmt, err := sqlparser.ParseStrictDDL(originalShowCreateTable) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(originalShowCreateTable) if err != nil { return nil, nil, nil, err } @@ -1301,7 +1402,7 @@ func (e *Executor) duplicateCreateTable(ctx context.Context, onlineDDL *schema.O // createDuplicateTableLike creates the table named by `newTableName` in the likeness of onlineDDL.Table // This function emulates MySQL's `CREATE TABLE LIKE ...` statement. The difference is that this function takes control over the generated CONSTRAINT names, -// if any, such that they are detrministic across shards, as well as preserve original names where possible. +// if any, such that they are deterministic across shards, as well as preserve original names where possible. func (e *Executor) createDuplicateTableLike(ctx context.Context, newTableName string, onlineDDL *schema.OnlineDDL, conn *dbconnpool.DBConnection) ( originalShowCreateTable string, constraintMap map[string]string, @@ -1337,7 +1438,10 @@ func (e *Executor) initVreplicationOriginalMigration(ctx context.Context, online return v, err } - vreplTableName := fmt.Sprintf("_%s_%s_vrepl", onlineDDL.UUID, ReadableTimestamp()) + vreplTableName, err := schema.GenerateInternalTableName(schema.InternalTableVreplicationHint.String(), onlineDDL.UUID, time.Now()) + if err != nil { + return v, err + } if err := e.updateArtifacts(ctx, onlineDDL.UUID, vreplTableName); err != nil { return v, err } @@ -1346,7 +1450,7 @@ func (e *Executor) initVreplicationOriginalMigration(ctx context.Context, online return nil, err } - stmt, err := sqlparser.ParseStrictDDL(onlineDDL.SQL) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(onlineDDL.SQL) if err != nil { return nil, err } @@ -1373,7 +1477,7 @@ func (e *Executor) initVreplicationOriginalMigration(ctx context.Context, online return v, err } - v = NewVRepl(onlineDDL.UUID, e.keyspace, e.shard, e.dbName, onlineDDL.Table, vreplTableName, originalShowCreateTable, vreplShowCreateTable, onlineDDL.SQL, onlineDDL.StrategySetting().IsAnalyzeTableFlag()) + v = NewVRepl(e.env.Environment(), onlineDDL.UUID, e.keyspace, e.shard, e.dbName, onlineDDL.Table, vreplTableName, originalShowCreateTable, vreplShowCreateTable, onlineDDL.SQL, onlineDDL.StrategySetting().IsAnalyzeTableFlag()) return v, nil } @@ -1419,7 +1523,7 @@ func (e *Executor) initVreplicationRevertMigration(ctx context.Context, onlineDD return nil, err } - vreplTableName, err := getVreplTable(ctx, revertStream) + vreplTableName, err := getVreplTable(revertStream) if err != nil { return nil, err } @@ -1427,7 +1531,7 @@ func (e *Executor) initVreplicationRevertMigration(ctx context.Context, onlineDD if err := e.updateArtifacts(ctx, onlineDDL.UUID, vreplTableName); err != nil { return v, err } - v = NewVRepl(onlineDDL.UUID, e.keyspace, e.shard, e.dbName, onlineDDL.Table, vreplTableName, "", "", "", false) + v = NewVRepl(e.env.Environment(), onlineDDL.UUID, e.keyspace, e.shard, e.dbName, onlineDDL.Table, vreplTableName, "", "", "", false) v.pos = revertStream.pos return v, nil } @@ -1656,7 +1760,7 @@ exit $exit_code runGhost := func(execute bool) error { alterOptions := e.parseAlterOptions(ctx, onlineDDL) - forceTableNames := fmt.Sprintf("%s_%s", onlineDDL.UUID, ReadableTimestamp()) + forceTableNames := fmt.Sprintf("%s_%s", onlineDDL.UUID, schema.ReadableTimestamp()) if err := e.updateArtifacts(ctx, onlineDDL.UUID, fmt.Sprintf("_%s_gho", forceTableNames), @@ -1880,14 +1984,14 @@ export MYSQL_PWD // The following sleep() is temporary and artificial. Because we create a new user for this // migration, and because we throttle by replicas, we need to wait for the replicas to be // caught up with the new user creation. Otherwise, the OSC tools will fail connecting to the replicas... - // Once we have a built in throttling service , we will no longe rneed to have the OSC tools probe the + // Once we have a built in throttling service , we will no longer need to have the OSC tools probe the // replicas. Instead, they will consult with our throttling service. // TODO(shlomi): replace/remove this when we have a proper throttling solution time.Sleep(time.Second) runPTOSC := func(execute bool) error { os.Setenv("MYSQL_PWD", onlineDDLPassword) - newTableName := fmt.Sprintf("_%s_%s_new", onlineDDL.UUID, ReadableTimestamp()) + newTableName := fmt.Sprintf("_%s_%s_new", onlineDDL.UUID, schema.ReadableTimestamp()) if err := e.updateArtifacts(ctx, onlineDDL.UUID, fmt.Sprintf("_%s_old", onlineDDL.Table), @@ -2041,10 +2145,10 @@ func (e *Executor) terminateMigration(ctx context.Context, onlineDDL *schema.Onl foundRunning = true // Because pt-osc doesn't offer much control, we take a brute force approach to killing it, // revoking its privileges, and cleaning up its triggers. - if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { + if err := syscallutil.Kill(pid, syscall.SIGTERM); err != nil { return foundRunning, nil } - if err := syscall.Kill(pid, syscall.SIGKILL); err != nil { + if err := syscallutil.Kill(pid, syscall.SIGKILL); err != nil { return foundRunning, nil } if err := e.dropOnlineDDLUser(ctx); err != nil { @@ -2229,7 +2333,7 @@ func (e *Executor) UnthrottleAllMigrations(ctx context.Context) (result *sqltype return emptyResult, nil } -// scheduleNextMigration attemps to schedule a single migration to run next. +// scheduleNextMigration attempts to schedule a single migration to run next. // possibly there are migrations to run. // The effect of this function is to move a migration from 'queued' state to 'ready' state, is all. func (e *Executor) scheduleNextMigration(ctx context.Context) error { @@ -2257,7 +2361,7 @@ func (e *Executor) scheduleNextMigration(ctx context.Context) error { if !readyToComplete { // see if we need to update ready_to_complete if isImmediateOperation { - // Whether postponsed or not, CREATE and DROP operations, as well as VIEW operations, + // Whether postponed or not, CREATE and DROP operations, as well as VIEW operations, // are inherently "ready to complete" because their operation is immediate. if err := e.updateMigrationReadyToComplete(ctx, uuid, true); err != nil { return err @@ -2297,7 +2401,7 @@ func (e *Executor) reviewEmptyTableRevertMigrations(ctx context.Context, onlineD // Try to update table name and ddl_action // Failure to do so fails the migration - revertUUID, err := onlineDDL.GetRevertUUID() + revertUUID, err := onlineDDL.GetRevertUUID(e.env.Environment().Parser()) if err != nil { return false, e.failMigration(ctx, onlineDDL, fmt.Errorf("cannot analyze revert UUID for revert migration %s: %v", onlineDDL.UUID, err)) } @@ -2340,7 +2444,14 @@ func (e *Executor) reviewEmptyTableRevertMigrations(ctx context.Context, onlineD // Non immediate operations are: // - A gh-ost migration // - A vitess (vreplication) migration -func (e *Executor) reviewImmediateOperations(ctx context.Context, capableOf mysql.CapableOf, onlineDDL *schema.OnlineDDL, ddlAction string, isRevert bool, isView bool) (bool, error) { +func (e *Executor) reviewImmediateOperations( + ctx context.Context, + capableOf capabilities.CapableOf, + onlineDDL *schema.OnlineDDL, + ddlAction string, + isRevert bool, + isView bool, +) (bool, error) { switch ddlAction { case sqlparser.CreateStr, sqlparser.DropStr: return true, nil @@ -2364,9 +2475,9 @@ func (e *Executor) reviewImmediateOperations(ctx context.Context, capableOf mysq // reviewQueuedMigration investigates a single migration found in `queued` state. // It analyzes whether the migration can & should be fulfilled immediately (e.g. via INSTANT DDL or just because it's a CREATE or DROP), -// or backfils necessary information if it's a REVERT. +// or backfills necessary information if it's a REVERT. // If all goes well, it sets `reviewed_timestamp` which then allows the state machine to schedule the migration. -func (e *Executor) reviewQueuedMigration(ctx context.Context, uuid string, capableOf mysql.CapableOf) error { +func (e *Executor) reviewQueuedMigration(ctx context.Context, uuid string, capableOf capabilities.CapableOf) error { onlineDDL, row, err := e.readMigration(ctx, uuid) if err != nil { return err @@ -2430,7 +2541,7 @@ func (e *Executor) reviewQueuedMigrations(ctx context.Context) error { return err } defer conn.Close() - _, capableOf, _ := mysql.GetFlavor(conn.ServerVersion, nil) + capableOf := mysql.ServerVersionCapableOf(conn.ServerVersion) e.migrationMutex.Lock() defer e.migrationMutex.Unlock() @@ -2451,7 +2562,7 @@ func (e *Executor) reviewQueuedMigrations(ctx context.Context) error { func (e *Executor) validateMigrationRevertible(ctx context.Context, revertMigration *schema.OnlineDDL, revertingMigrationUUID string) (err error) { // Validation: migration to revert exists and is in complete state - action, actionStr, err := revertMigration.GetActionStr() + action, actionStr, err := revertMigration.GetActionStr(e.env.Environment().Parser()) if err != nil { return err } @@ -2520,7 +2631,7 @@ func (e *Executor) validateMigrationRevertible(ctx context.Context, revertMigrat // - what type of migration we're reverting? (CREATE/DROP/ALTER) // - revert appropriately to the type of migration func (e *Executor) executeRevert(ctx context.Context, onlineDDL *schema.OnlineDDL) (err error) { - revertUUID, err := onlineDDL.GetRevertUUID() + revertUUID, err := onlineDDL.GetRevertUUID(e.env.Environment().Parser()) if err != nil { return fmt.Errorf("cannot run a revert migration %v: %+v", onlineDDL.UUID, err) } @@ -2633,7 +2744,7 @@ func (e *Executor) executeRevert(ctx context.Context, onlineDDL *schema.OnlineDD func (e *Executor) evaluateDeclarativeDiff(ctx context.Context, onlineDDL *schema.OnlineDDL) (diff schemadiff.EntityDiff, err error) { // Modify the CREATE TABLE statement to indicate a different, made up table name, known as the "comparison table" - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return nil, err } @@ -2687,12 +2798,16 @@ func (e *Executor) evaluateDeclarativeDiff(ctx context.Context, onlineDDL *schem if newShowCreateTable == "" { return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected: cannot find table or view even as it was just created: %v", onlineDDL.Table) } - hints := &schemadiff.DiffHints{AutoIncrementStrategy: schemadiff.AutoIncrementApplyHigher} + senv := schemadiff.NewEnv(e.env.Environment(), e.env.Environment().CollationEnv().DefaultConnectionCharset()) + hints := &schemadiff.DiffHints{ + AutoIncrementStrategy: schemadiff.AutoIncrementApplyHigher, + EnumReorderStrategy: schemadiff.EnumReorderStrategyAllow, + } switch ddlStmt.(type) { case *sqlparser.CreateTable: - diff, err = schemadiff.DiffCreateTablesQueries(existingShowCreateTable, newShowCreateTable, hints) + diff, err = schemadiff.DiffCreateTablesQueries(senv, existingShowCreateTable, newShowCreateTable, hints) case *sqlparser.CreateView: - diff, err = schemadiff.DiffCreateViewsQueries(existingShowCreateTable, newShowCreateTable, hints) + diff, err = schemadiff.DiffCreateViewsQueries(senv, existingShowCreateTable, newShowCreateTable, hints) default: return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expected CREATE TABLE or CREATE VIEW in online DDL statement: %v", onlineDDL.SQL) } @@ -2702,7 +2817,7 @@ func (e *Executor) evaluateDeclarativeDiff(ctx context.Context, onlineDDL *schem return diff, nil } -// getCompletedMigrationByContextAndSQL chceks if there exists a completed migration with exact same +// getCompletedMigrationByContextAndSQL checks if there exists a completed migration with exact same // context and SQL as given migration. If so, it returns its UUID. func (e *Executor) getCompletedMigrationByContextAndSQL(ctx context.Context, onlineDDL *schema.OnlineDDL) (completedUUID string, err error) { if onlineDDL.MigrationContext == "" { @@ -2753,7 +2868,7 @@ func (e *Executor) analyzeDropDDLActionMigration(ctx context.Context, onlineDDL } } } - stmt, err := sqlparser.ParseStrictDDL(originalShowCreateTable) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(originalShowCreateTable) if err != nil { return err } @@ -2799,7 +2914,7 @@ func (e *Executor) executeDropDDLActionMigration(ctx context.Context, onlineDDL // We transform a DROP TABLE into a RENAME TABLE statement, so as to remove the table safely and asynchronously. - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -2842,7 +2957,7 @@ func (e *Executor) executeCreateDDLActionMigration(ctx context.Context, onlineDD e.migrationMutex.Lock() defer e.migrationMutex.Unlock() - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -2863,6 +2978,17 @@ func (e *Executor) executeCreateDDLActionMigration(ctx context.Context, onlineDD } } } + if originalCreateTable, ok := ddlStmt.(*sqlparser.CreateTable); ok { + newCreateTable := sqlparser.CloneRefOfCreateTable(originalCreateTable) + // Rewrite this CREATE TABLE statement such that CONSTRAINT names are edited, + // specifically removing any prefix. + if _, err := e.validateAndEditCreateTableStatement(ctx, onlineDDL, newCreateTable); err != nil { + return failMigration(err) + } + ddlStmt = newCreateTable + onlineDDL.SQL = sqlparser.String(newCreateTable) + } + // from now on, whether a VIEW or a TABLE, they get the same treatment sentryArtifactTableName, err := schema.GenerateGCTableName(schema.HoldTableGCState, newGCTableRetainTime()) @@ -2918,7 +3044,7 @@ func (e *Executor) executeAlterViewOnline(ctx context.Context, onlineDDL *schema if err != nil { return err } - stmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + stmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return err } @@ -3006,7 +3132,7 @@ func (e *Executor) executeSpecialAlterDDLActionMigrationIfApplicable(ctx context return false, err } defer conn.Close() - _, capableOf, _ := mysql.GetFlavor(conn.ServerVersion, nil) + capableOf := mysql.ServerVersionCapableOf(conn.ServerVersion) specialPlan, err := e.analyzeSpecialAlterPlan(ctx, onlineDDL, capableOf) if err != nil { @@ -3077,7 +3203,7 @@ func (e *Executor) executeAlterDDLActionMigration(ctx context.Context, onlineDDL failMigration := func(err error) error { return e.failMigration(ctx, onlineDDL, err) } - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -3150,7 +3276,7 @@ func (e *Executor) executeMigration(ctx context.Context, onlineDDL *schema.Onlin return e.failMigration(ctx, onlineDDL, err) } - ddlAction, err := onlineDDL.GetAction() + ddlAction, err := onlineDDL.GetAction(e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -3184,7 +3310,7 @@ func (e *Executor) executeMigration(ctx context.Context, onlineDDL *schema.Onlin // - Implicitly do nothing, if the table does not exist { // Sanity: reject IF NOT EXISTS statements, because they don't make sense (or are ambiguous) in declarative mode - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -3199,7 +3325,7 @@ func (e *Executor) executeMigration(ctx context.Context, onlineDDL *schema.Onlin if exists { // table does exist, so this declarative DROP turns out to really be an actual DROP. No further action is needed here } else { - // table does not exist. We mark this DROP as implicitly sucessful + // table does not exist. We mark this DROP as implicitly successful _ = e.onSchemaMigrationStatus(ctx, onlineDDL.UUID, schema.OnlineDDLStatusComplete, false, progressPctFull, etaSecondsNow, rowsCopiedUnknown, emptyHint) _ = e.updateMigrationMessage(ctx, onlineDDL.UUID, "no change") return nil @@ -3211,7 +3337,7 @@ func (e *Executor) executeMigration(ctx context.Context, onlineDDL *schema.Onlin // - Implicitly do nothing, if the table exists and is identical to CREATE statement // Sanity: reject IF NOT EXISTS statements, because they don't make sense (or are ambiguous) in declarative mode - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err != nil { return failMigration(err) } @@ -3232,7 +3358,7 @@ func (e *Executor) executeMigration(ctx context.Context, onlineDDL *schema.Onlin return failMigration(err) } if diff == nil || diff.IsEmpty() { - // No diff! We mark this CREATE as implicitly sucessful + // No diff! We mark this CREATE as implicitly successful _ = e.onSchemaMigrationStatus(ctx, onlineDDL.UUID, schema.OnlineDDLStatusComplete, false, progressPctFull, etaSecondsNow, rowsCopiedUnknown, emptyHint) _ = e.updateMigrationMessage(ctx, onlineDDL.UUID, "no change") return nil @@ -3352,7 +3478,7 @@ func (e *Executor) runNextMigration(ctx context.Context) error { } { // We strip out any VT query comments because our simplified parser doesn't work well with comments - ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL) + ddlStmt, _, err := schema.ParseOnlineDDLStatement(onlineDDL.SQL, e.env.Environment().Parser()) if err == nil { ddlStmt.SetComments(sqlparser.Comments{}) onlineDDL.SQL = sqlparser.String(ddlStmt) @@ -3471,8 +3597,10 @@ func (e *Executor) readVReplStream(ctx context.Context, uuid string, okIfMissing // isPreserveForeignKeySupported checks if the underlying MySQL server supports 'rename_table_preserve_foreign_key' // Online DDL is not possible on vanilla MySQL 8.0 for reasons described in https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/. -// However, Online DDL is made possible in via these changes: https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced -// as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. +// However, Online DDL is made possible in via these changes: +// - https://github.com/planetscale/mysql-server/commit/bb777e3e86387571c044fb4a2beb4f8c60462ced +// - https://github.com/planetscale/mysql-server/commit/c2f1344a6863518d749f2eb01a4c74ca08a5b889 +// as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps3. // Said changes introduce a new global/session boolean variable named 'rename_table_preserve_foreign_key'. It defaults 'false'/0 for backwards compatibility. // When enabled, a `RENAME TABLE` to a FK parent "pins" the children's foreign keys to the table name rather than the table pointer. Which means after the RENAME, // the children will point to the newly instated table rather than the original, renamed table. @@ -3496,7 +3624,7 @@ func (e *Executor) isVReplMigrationReadyToCutOver(ctx context.Context, onlineDDL } } { - // Both time_updated and transaction_timestamp must be in close priximity to each + // Both time_updated and transaction_timestamp must be in close proximity to each // other and to the time now, otherwise that means we're lagging and it's not a good time // to cut-over durationDiff := func(t1, t2 time.Time) time.Duration { @@ -3543,6 +3671,46 @@ func (e *Executor) isVReplMigrationReadyToCutOver(ctx context.Context, onlineDDL return true, nil } +// shouldCutOverAccordingToBackoff is called when a vitess migration (ALTER TABLE) is generally ready to cut-over. +// This function further determines whether the migration should cut-over or not, by considering: +// - backoff: we cut-over by increasing intervals, see `cutoverIntervals` +// - forced cut-over: either via `--force-cut-over-after` DDL strategy, or via user command, we override +// any backoff (and will also potentially KILL queries and connections holding locks on the migrated tabl) +func shouldCutOverAccordingToBackoff( + shouldForceCutOverIndicator bool, + forceCutOverAfter time.Duration, + sinceReadyToComplete time.Duration, + sinceLastCutoverAttempt time.Duration, + cutoverAttempts int64, +) ( + shouldCutOver bool, shouldForceCutOver bool, +) { + if shouldForceCutOverIndicator { + // That's very simple: the user indicated they want to force cut over. + return true, true + } + // shouldForceCutOver means the time since migration was ready to complete + // is beyond the --force-cut-over-after setting, or the column `force_cutover` is "1", and this means: + // - we do not want to backoff, we want to cutover asap + // - we agree to brute-force KILL any pending queries on the migrated table so as to ensure it's unlocked. + if forceCutOverAfter > 0 && sinceReadyToComplete > forceCutOverAfter { + // time since migration was ready to complete is beyond the --force-cut-over-after setting + return true, true + } + + // Backoff mechanism. Do not attempt to cut-over every single minute. Check how much time passed since last cut-over attempt + desiredTimeSinceLastCutover := cutoverIntervals[len(cutoverIntervals)-1] + if int(cutoverAttempts) < len(cutoverIntervals) { + desiredTimeSinceLastCutover = cutoverIntervals[cutoverAttempts] + } + if sinceLastCutoverAttempt >= desiredTimeSinceLastCutover { + // Yes! Time since last cut-over complies with our expected cut-over interval + return true, false + } + // Don't cut-over yet + return false, false +} + // reviewRunningMigrations iterates migrations in 'running' state. Normally there's only one running, which was // spawned by this tablet; but vreplication migrations could also resume from failure. func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning int, cancellable []*cancellableMigration, err error) { @@ -3574,30 +3742,42 @@ func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning i uuidsFoundRunning := map[string]bool{} for _, row := range r.Named().Rows { uuid := row["migration_uuid"].ToString() + cutoverAttempts := row.AsInt64("cutover_attempts", 0) + sinceLastCutoverAttempt := time.Second * time.Duration(row.AsInt64("seconds_since_last_cutover_attempt", 0)) + sinceReadyToComplete := time.Second * time.Duration(row.AsInt64("seconds_since_ready_to_complete", 0)) onlineDDL, migrationRow, err := e.readMigration(ctx, uuid) if err != nil { return countRunnning, cancellable, err } postponeCompletion := row.AsBool("postpone_completion", false) + shouldForceCutOver := row.AsBool("force_cutover", false) elapsedSeconds := row.AsInt64("elapsed_seconds", 0) + strategySetting := onlineDDL.StrategySetting() + // --force-cut-over-after flag is validated when DDL strategy is first parsed. + // There should never be an error here. But if there is, we choose to skip it, + // otherwise migrations will never complete. + forceCutOverAfter, errForceCutOverAfter := strategySetting.ForceCutOverAfter() + if errForceCutOverAfter != nil { + forceCutOverAfter = 0 + } uuidsFoundRunning[uuid] = true _ = e.updateMigrationUserThrottleRatio(ctx, uuid, currentUserThrottleRatio) - switch onlineDDL.StrategySetting().Strategy { + switch strategySetting.Strategy { case schema.DDLStrategyOnline, schema.DDLStrategyVitess: - { + reviewVReplRunningMigration := func() error { // We check the _vt.vreplication table s, err := e.readVReplStream(ctx, uuid, true) if err != nil { - return countRunnning, cancellable, err + return err } - isVreplicationTestSuite := onlineDDL.StrategySetting().IsVreplicationTestSuite() + isVreplicationTestSuite := strategySetting.IsVreplicationTestSuite() if isVreplicationTestSuite { e.triggerNextCheckInterval() } if s == nil { - continue + return nil } // Let's see if vreplication indicates an error. Many errors are recoverable, and // we do not wish to fail on first sight. We will use LastError to repeatedly @@ -3614,65 +3794,77 @@ func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning i if isTerminal || !lastError.ShouldRetry() { cancellable = append(cancellable, newCancellableMigration(uuid, s.message)) } - if s.isRunning() { - // This VRepl migration may have started from outside this tablet, so - // this executor may not own the migration _yet_. We make sure to own it. - // VReplication migrations are unique in this respect: we are able to complete - // a vreplicaiton migration started by another tablet. - e.ownedRunningMigrations.Store(uuid, onlineDDL) - if lastVitessLivenessIndicator := migrationRow.AsInt64("vitess_liveness_indicator", 0); lastVitessLivenessIndicator < s.livenessTimeIndicator() { - _ = e.updateMigrationTimestamp(ctx, "liveness_timestamp", uuid) - _ = e.updateVitessLivenessIndicator(ctx, uuid, s.livenessTimeIndicator()) - } - if onlineDDL.TabletAlias != e.TabletAliasString() { - _ = e.updateMigrationTablet(ctx, uuid) - log.Infof("migration %s adopted by tablet %s", uuid, e.TabletAliasString()) - } - _ = e.updateRowsCopied(ctx, uuid, s.rowsCopied) - _ = e.updateMigrationProgressByRowsCopied(ctx, uuid, s.rowsCopied) - _ = e.updateMigrationETASecondsByProgress(ctx, uuid) - _ = e.updateMigrationLastThrottled(ctx, uuid, time.Unix(s.timeThrottled, 0), s.componentThrottled) + if !s.isRunning() { + return nil + } + // This VRepl migration may have started from outside this tablet, so + // this executor may not own the migration _yet_. We make sure to own it. + // VReplication migrations are unique in this respect: we are able to complete + // a vreplication migration started by another tablet. + e.ownedRunningMigrations.Store(uuid, onlineDDL) + if lastVitessLivenessIndicator := migrationRow.AsInt64("vitess_liveness_indicator", 0); lastVitessLivenessIndicator < s.livenessTimeIndicator() { + _ = e.updateMigrationTimestamp(ctx, "liveness_timestamp", uuid) + _ = e.updateVitessLivenessIndicator(ctx, uuid, s.livenessTimeIndicator()) + } + if onlineDDL.TabletAlias != e.TabletAliasString() { + _ = e.updateMigrationTablet(ctx, uuid) + log.Infof("migration %s adopted by tablet %s", uuid, e.TabletAliasString()) + } + _ = e.updateRowsCopied(ctx, uuid, s.rowsCopied) + _ = e.updateMigrationProgressByRowsCopied(ctx, uuid, s.rowsCopied) + _ = e.updateMigrationETASecondsByProgress(ctx, uuid) + _ = e.updateMigrationLastThrottled(ctx, uuid, time.Unix(s.timeThrottled, 0), s.componentThrottled) - isReady, err := e.isVReplMigrationReadyToCutOver(ctx, onlineDDL, s) - if err != nil { - _ = e.updateMigrationMessage(ctx, uuid, err.Error()) - return countRunnning, cancellable, err - } - if isReady && isVreplicationTestSuite { - // This is a endtoend test suite execution. We intentionally delay it by at least - // vreplicationTestSuiteWaitSeconds - if elapsedSeconds < vreplicationTestSuiteWaitSeconds { - isReady = false - } - } - // Indicate to outside observers whether the migration is generally ready to complete. - // In the case of a postponed migration, we will not complete it, but the user will - // understand whether "now is a good time" or "not there yet" - _ = e.updateMigrationReadyToComplete(ctx, uuid, isReady) - if postponeCompletion { - // override. Even if migration is ready, we do not complete it. + isReady, err := e.isVReplMigrationReadyToCutOver(ctx, onlineDDL, s) + if err != nil { + _ = e.updateMigrationMessage(ctx, uuid, err.Error()) + return err + } + if isReady && isVreplicationTestSuite { + // This is a endtoend test suite execution. We intentionally delay it by at least + // vreplicationTestSuiteWaitSeconds + if elapsedSeconds < vreplicationTestSuiteWaitSeconds { isReady = false } - if isReady && onlineDDL.StrategySetting().IsInOrderCompletion() { - if len(pendingMigrationsUUIDs) > 0 && pendingMigrationsUUIDs[0] != onlineDDL.UUID { - // wait for earlier pending migrations to complete - isReady = false - } + } + // Indicate to outside observers whether the migration is generally ready to complete. + // In the case of a postponed migration, we will not complete it, but the user will + // understand whether "now is a good time" or "not there yet" + _ = e.updateMigrationReadyToComplete(ctx, uuid, isReady) + if !isReady { + return nil + } + if postponeCompletion { + // override. Even if migration is ready, we do not complete it. + return nil + } + if strategySetting.IsInOrderCompletion() { + if len(pendingMigrationsUUIDs) > 0 && pendingMigrationsUUIDs[0] != onlineDDL.UUID { + // wait for earlier pending migrations to complete + return nil } - if isReady { - if err := e.cutOverVReplMigration(ctx, s); err != nil { - _ = e.updateMigrationMessage(ctx, uuid, err.Error()) - log.Errorf("cutOverVReplMigration failed: err=%v", err) - if merr, ok := err.(*sqlerror.SQLError); ok { - switch merr.Num { - case sqlerror.ERTooLongIdent: - go e.CancelMigration(ctx, uuid, err.Error(), false) - } - } - return countRunnning, cancellable, err + } + shouldCutOver, shouldForceCutOver := shouldCutOverAccordingToBackoff( + shouldForceCutOver, forceCutOverAfter, sinceReadyToComplete, sinceLastCutoverAttempt, cutoverAttempts, + ) + if !shouldCutOver { + return nil + } + if err := e.cutOverVReplMigration(ctx, s, shouldForceCutOver); err != nil { + _ = e.updateMigrationMessage(ctx, uuid, err.Error()) + log.Errorf("cutOverVReplMigration failed: err=%v", err) + if merr, ok := err.(*sqlerror.SQLError); ok { + switch merr.Num { + case sqlerror.ERTooLongIdent: + go e.CancelMigration(ctx, uuid, err.Error(), false) } } + return err } + return nil + } + if err := reviewVReplRunningMigration(); err != nil { + return countRunnning, cancellable, err } case schema.DDLStrategyPTOSC: { @@ -3709,7 +3901,7 @@ func (e *Executor) reviewRunningMigrations(ctx context.Context) (countRunnning i countRunnning++ } { - // now, let's look at UUIDs we own and _think_ should be running, and see which of tham _isn't_ actually running or pending... + // now, let's look at UUIDs we own and _think_ should be running, and see which of them _isn't_ actually running or pending... uuidsFoundPending := map[string]bool{} for _, uuid := range pendingMigrationsUUIDs { uuidsFoundPending[uuid] = true @@ -3862,7 +4054,7 @@ func (e *Executor) gcArtifactTable(ctx context.Context, artifactTable, uuid stri // The fact we're here means the table is not needed anymore. We can throw it away. // We do so by renaming it into a GC table. We use the HOLD state and with a timestamp that is // in the past. So as we rename the table: - // - The Online DDL executor compeltely loses it and has no more access to its data + // - The Online DDL executor completely loses it and has no more access to its data // - TableGC will find it on next iteration, see that it's been on HOLD "long enough", and will // take it from there to transition it into PURGE or EVAC, or DROP, and eventually drop it. renameStatement, toTableName, err := schema.GenerateRenameStatementWithUUID(artifactTable, schema.HoldTableGCState, schema.OnlineDDLToGCUUID(uuid), t) @@ -3921,6 +4113,7 @@ func (e *Executor) gcArtifacts(ctx context.Context) error { if err == nil { // artifact was renamed away and is gone. There' no need to list it in `artifacts` column. e.clearSingleArtifact(ctx, uuid, artifactTable) + e.requestGCChecksFunc() } else { return vterrors.Wrapf(err, "in gcArtifacts() for %s", artifactTable) } @@ -4491,9 +4684,69 @@ func (e *Executor) CleanupMigration(ctx context.Context, uuid string) (result *s return nil, err } log.Infof("CleanupMigration: migration %s marked as ready to clean up", uuid) + defer e.triggerNextCheckInterval() return rs, nil } +// ForceCutOverMigration markes the given migration for forced cut-over. This has two implications: +// - No backoff for the given migration's cut-over (cut-over will be attempted at the next scheduler cycle, +// irrespective of how many cut-over attempts have been made and when these attempts have been made). +// - During the cut-over, Online DDL will try and temrinate all existing queries on the migrated table, and +// transactions (killing their connections) holding a lock on the migrated table. This is likely to cause the +// cut-over to succeed. Of course, it's not guaranteed, and it's possible that next cut-over will fail. +// The force_cutover flag, once set, remains set, and so all future cut-over attempts will again KILL interfering +// queries and connections. +func (e *Executor) ForceCutOverMigration(ctx context.Context, uuid string) (result *sqltypes.Result, err error) { + if atomic.LoadInt64(&e.isOpen) == 0 { + return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, schema.ErrOnlineDDLDisabled.Error()) + } + if !schema.IsOnlineDDLUUID(uuid) { + return nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "Not a valid migration ID in FORCE_CUTOVER: %s", uuid) + } + log.Infof("ForceCutOverMigration: request to force cut-over migration %s", uuid) + e.migrationMutex.Lock() + defer e.migrationMutex.Unlock() + + query, err := sqlparser.ParseAndBind(sqlUpdateForceCutOver, + sqltypes.StringBindVariable(uuid), + ) + if err != nil { + return nil, err + } + rs, err := e.execQuery(ctx, query) + if err != nil { + return nil, err + } + e.triggerNextCheckInterval() + log.Infof("ForceCutOverMigration: migration %s marked for forced cut-over", uuid) + return rs, nil +} + +// ForceCutOverPendingMigrations sets force_cutover flag for all pending migrations +func (e *Executor) ForceCutOverPendingMigrations(ctx context.Context) (result *sqltypes.Result, err error) { + if atomic.LoadInt64(&e.isOpen) == 0 { + return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, schema.ErrOnlineDDLDisabled.Error()) + } + + uuids, err := e.readPendingMigrationsUUIDs(ctx) + if err != nil { + return result, err + } + log.Infof("ForceCutOverPendingMigrations: iterating %v migrations %s", len(uuids)) + + result = &sqltypes.Result{} + for _, uuid := range uuids { + log.Infof("ForceCutOverPendingMigrations: applying to %s", uuid) + res, err := e.ForceCutOverMigration(ctx, uuid) + if err != nil { + return result, err + } + result.AppendResult(res) + } + log.Infof("ForceCutOverPendingMigrations: done iterating %v migrations %s", len(uuids)) + return result, nil +} + // CompleteMigration clears the postpone_completion flag for a given migration, assuming it was set in the first place func (e *Executor) CompleteMigration(ctx context.Context, uuid string) (result *sqltypes.Result, err error) { if atomic.LoadInt64(&e.isOpen) == 0 { @@ -4507,7 +4760,7 @@ func (e *Executor) CompleteMigration(ctx context.Context, uuid string) (result * e.migrationMutex.Lock() defer e.migrationMutex.Unlock() - query, err := sqlparser.ParseAndBind(sqlUpdateCompleteMigration, + query, err := sqlparser.ParseAndBind(sqlClearPostponeCompletion, sqltypes.StringBindVariable(uuid), ) if err != nil { @@ -4565,7 +4818,7 @@ func (e *Executor) LaunchMigration(ctx context.Context, uuid string, shardsArg s // Does not apply to this shard! return &sqltypes.Result{}, nil } - log.Infof("LaunchMigration: request to execute migration %s", uuid) + log.Infof("LaunchMigration: request to launch migration %s", uuid) e.migrationMutex.Lock() defer e.migrationMutex.Unlock() @@ -4623,7 +4876,7 @@ func (e *Executor) submittedMigrationConflictsWithPendingMigrationInSingletonCon return false } // Let's see if the pending migration is a revert: - if _, err := pendingOnlineDDL.GetRevertUUID(); err != nil { + if _, err := pendingOnlineDDL.GetRevertUUID(e.env.Environment().Parser()); err != nil { // Not a revert. So the pending migration definitely conflicts with our migration. return true } @@ -4637,7 +4890,7 @@ func (e *Executor) submittedMigrationConflictsWithPendingMigrationInSingletonCon return true } -// submitCallbackIfNonConflicting is called internally by SubmitMigration, and is given a callack to execute +// submitCallbackIfNonConflicting is called internally by SubmitMigration, and is given a callback to execute // if the given migration does not conflict any terms. Specifically, this function looks for singleton or // singleton-context conflicts. // The call back can be an insertion of a new migration, or a retry of an existing migration, or whatnot. @@ -4743,10 +4996,10 @@ func (e *Executor) SubmitMigration( // So we will _mostly_ ignore the request: we will not submit a new migration. However, we will do // these things: - // 1. Check that the requested submmited migration macthes the existing one's migration-context, otherwise + // 1. Check that the requested submitted migration matches the existing one's migration-context, otherwise // this doesn't seem right, not the idempotency we were looking for if storedMigration.MigrationContext != onlineDDL.MigrationContext { - return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "migration rejected: found migration %s with different context: %s than submmitted migration's context: %s", onlineDDL.UUID, storedMigration.MigrationContext, onlineDDL.MigrationContext) + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "migration rejected: found migration %s with different context: %s than submitted migration's context: %s", onlineDDL.UUID, storedMigration.MigrationContext, onlineDDL.MigrationContext) } // 2. Possibly, the existing migration is in 'failed' or 'cancelled' state, in which case this // resubmission should retry the migration. @@ -4758,13 +5011,13 @@ func (e *Executor) SubmitMigration( // OK, this is a new UUID - _, actionStr, err := onlineDDL.GetActionStr() + _, actionStr, err := onlineDDL.GetActionStr(e.env.Environment().Parser()) if err != nil { return nil, err } log.Infof("SubmitMigration: request to submit migration %s; action=%s, table=%s", onlineDDL.UUID, actionStr, onlineDDL.Table) - revertedUUID, _ := onlineDDL.GetRevertUUID() // Empty value if the migration is not actually a REVERT. Safe to ignore error. + revertedUUID, _ := onlineDDL.GetRevertUUID(e.env.Environment().Parser()) // Empty value if the migration is not actually a REVERT. Safe to ignore error. retainArtifactsSeconds := int64((retainOnlineDDLTables).Seconds()) if retainArtifacts, _ := onlineDDL.StrategySetting().RetainArtifactsDuration(); retainArtifacts != 0 { // Explicit retention indicated by `--retain-artifact` DDL strategy flag for this migration. Override! @@ -4790,7 +5043,7 @@ func (e *Executor) SubmitMigration( sqltypes.BoolBindVariable(onlineDDL.StrategySetting().IsPostponeCompletion()), sqltypes.BoolBindVariable(allowConcurrentMigration), sqltypes.StringBindVariable(revertedUUID), - sqltypes.BoolBindVariable(onlineDDL.IsView()), + sqltypes.BoolBindVariable(onlineDDL.IsView(e.env.Environment().Parser())), ) if err != nil { return nil, err diff --git a/go/vt/vttablet/onlineddl/executor_test.go b/go/vt/vttablet/onlineddl/executor_test.go index 4eb0d54a418..c6fc0044c91 100644 --- a/go/vt/vttablet/onlineddl/executor_test.go +++ b/go/vt/vttablet/onlineddl/executor_test.go @@ -23,10 +23,14 @@ package onlineddl import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" ) @@ -43,7 +47,9 @@ func TestGetConstraintType(t *testing.T) { } func TestValidateAndEditCreateTableStatement(t *testing.T) { - e := Executor{} + e := Executor{ + env: tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "ValidateAndEditCreateTableStatementTest"), + } tt := []struct { name string query string @@ -155,7 +161,7 @@ func TestValidateAndEditCreateTableStatement(t *testing.T) { } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(tc.query) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(tc.query) require.NoError(t, err) createTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) @@ -185,7 +191,9 @@ func TestValidateAndEditCreateTableStatement(t *testing.T) { } func TestValidateAndEditAlterTableStatement(t *testing.T) { - e := Executor{} + e := Executor{ + env: tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestValidateAndEditAlterTableStatementTest"), + } tt := []struct { alter string m map[string]string @@ -255,7 +263,7 @@ func TestValidateAndEditAlterTableStatement(t *testing.T) { } for _, tc := range tt { t.Run(tc.alter, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(tc.alter) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(tc.alter) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) @@ -267,7 +275,7 @@ func TestValidateAndEditAlterTableStatement(t *testing.T) { onlineDDL := &schema.OnlineDDL{UUID: "a5a563da_dc1a_11ec_a416_0a43f95f28a3", Table: "t", Options: "--unsafe-allow-foreign-keys"} alters, err := e.validateAndEditAlterTableStatement(context.Background(), onlineDDL, alterTable, m) assert.NoError(t, err) - altersStrings := []string{} + var altersStrings []string for _, alter := range alters { altersStrings = append(altersStrings, sqlparser.String(alter)) } @@ -277,7 +285,9 @@ func TestValidateAndEditAlterTableStatement(t *testing.T) { } func TestAddInstantAlgorithm(t *testing.T) { - e := Executor{} + e := Executor{ + env: tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "AddInstantAlgorithmTest"), + } tt := []struct { alter string expect string @@ -301,7 +311,7 @@ func TestAddInstantAlgorithm(t *testing.T) { } for _, tc := range tt { t.Run(tc.alter, func(t *testing.T) { - stmt, err := sqlparser.ParseStrictDDL(tc.alter) + stmt, err := e.env.Environment().Parser().ParseStrictDDL(tc.alter) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) @@ -311,7 +321,7 @@ func TestAddInstantAlgorithm(t *testing.T) { assert.Equal(t, tc.expect, alterInstant) - stmt, err = sqlparser.ParseStrictDDL(alterInstant) + stmt, err = e.env.Environment().Parser().ParseStrictDDL(alterInstant) require.NoError(t, err) _, ok = stmt.(*sqlparser.AlterTable) require.True(t, ok) @@ -320,7 +330,9 @@ func TestAddInstantAlgorithm(t *testing.T) { } func TestDuplicateCreateTable(t *testing.T) { - e := Executor{} + e := Executor{ + env: tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "DuplicateCreateTableTest"), + } ctx := context.Background() onlineDDL := &schema.OnlineDDL{UUID: "a5a563da_dc1a_11ec_a416_0a43f95f28a3", Table: "something", Strategy: "vitess", Options: "--unsafe-allow-foreign-keys"} @@ -356,3 +368,109 @@ func TestDuplicateCreateTable(t *testing.T) { }) } } + +func TestShouldCutOverAccordingToBackoff(t *testing.T) { + tcases := []struct { + name string + + shouldForceCutOverIndicator bool + forceCutOverAfter time.Duration + sinceReadyToComplete time.Duration + sinceLastCutoverAttempt time.Duration + cutoverAttempts int64 + + expectShouldCutOver bool + expectShouldForceCutOver bool + }{ + { + name: "no reason why not, normal cutover", + expectShouldCutOver: true, + }, + { + name: "backoff", + cutoverAttempts: 1, + expectShouldCutOver: false, + }, + { + name: "more backoff", + cutoverAttempts: 3, + expectShouldCutOver: false, + }, + { + name: "more backoff, since last cutover", + cutoverAttempts: 3, + sinceLastCutoverAttempt: time.Second, + expectShouldCutOver: false, + }, + { + name: "no backoff, long since last cutover", + cutoverAttempts: 3, + sinceLastCutoverAttempt: time.Hour, + expectShouldCutOver: true, + }, + { + name: "many attempts, long since last cutover", + cutoverAttempts: 3000, + sinceLastCutoverAttempt: time.Hour, + expectShouldCutOver: true, + }, + { + name: "force cutover", + shouldForceCutOverIndicator: true, + expectShouldCutOver: true, + expectShouldForceCutOver: true, + }, + { + name: "force cutover overrides backoff", + cutoverAttempts: 3, + shouldForceCutOverIndicator: true, + expectShouldCutOver: true, + expectShouldForceCutOver: true, + }, + { + name: "backoff; cutover-after not in effect yet", + cutoverAttempts: 3, + forceCutOverAfter: time.Second, + expectShouldCutOver: false, + expectShouldForceCutOver: false, + }, + { + name: "backoff; cutover-after still not in effect yet", + cutoverAttempts: 3, + forceCutOverAfter: time.Second, + sinceReadyToComplete: time.Millisecond, + expectShouldCutOver: false, + expectShouldForceCutOver: false, + }, + { + name: "cutover-after overrides backoff", + cutoverAttempts: 3, + forceCutOverAfter: time.Second, + sinceReadyToComplete: time.Second * 2, + expectShouldCutOver: true, + expectShouldForceCutOver: true, + }, + { + name: "cutover-after overrides backoff, realistic value", + cutoverAttempts: 300, + sinceLastCutoverAttempt: time.Minute, + forceCutOverAfter: time.Hour, + sinceReadyToComplete: time.Hour * 2, + expectShouldCutOver: true, + expectShouldForceCutOver: true, + }, + } + for _, tcase := range tcases { + t.Run(tcase.name, func(t *testing.T) { + shouldCutOver, shouldForceCutOver := shouldCutOverAccordingToBackoff( + tcase.shouldForceCutOverIndicator, + tcase.forceCutOverAfter, + tcase.sinceReadyToComplete, + tcase.sinceLastCutoverAttempt, + tcase.cutoverAttempts, + ) + assert.Equal(t, tcase.expectShouldCutOver, shouldCutOver) + assert.Equal(t, tcase.expectShouldForceCutOver, shouldForceCutOver) + }) + } +} diff --git a/go/vt/vttablet/onlineddl/schema.go b/go/vt/vttablet/onlineddl/schema.go index 1cef44e08d3..2ba566703e5 100644 --- a/go/vt/vttablet/onlineddl/schema.go +++ b/go/vt/vttablet/onlineddl/schema.go @@ -98,7 +98,7 @@ const ( ` sqlSetMigrationReadyToComplete = `UPDATE _vt.schema_migrations SET ready_to_complete=1, - ready_to_complete_timestamp=NOW(6) + ready_to_complete_timestamp=IFNULL(ready_to_complete_timestamp, NOW(6)) WHERE migration_uuid=%a ` @@ -159,7 +159,8 @@ const ( migration_uuid=%a ` sqlIncrementCutoverAttempts = `UPDATE _vt.schema_migrations - SET cutover_attempts=cutover_attempts+1 + SET cutover_attempts=cutover_attempts+1, + last_cutover_attempt_timestamp=NOW() WHERE migration_uuid=%a ` @@ -168,13 +169,18 @@ const ( WHERE migration_uuid=%a ` + sqlUpdateForceCutOver = `UPDATE _vt.schema_migrations + SET force_cutover=1 + WHERE + migration_uuid=%a + ` sqlUpdateLaunchMigration = `UPDATE _vt.schema_migrations SET postpone_launch=0 WHERE migration_uuid=%a AND postpone_launch != 0 ` - sqlUpdateCompleteMigration = `UPDATE _vt.schema_migrations + sqlClearPostponeCompletion = `UPDATE _vt.schema_migrations SET postpone_completion=0 WHERE migration_uuid=%a @@ -254,6 +260,7 @@ const ( liveness_timestamp=NULL, cancelled_timestamp=NULL, completed_timestamp=NULL, + last_cutover_attempt_timestamp=NULL, cleanup_timestamp=NULL WHERE migration_status IN ('failed', 'cancelled') @@ -274,6 +281,7 @@ const ( liveness_timestamp=NULL, cancelled_timestamp=NULL, completed_timestamp=NULL, + last_cutover_attempt_timestamp=NULL, cleanup_timestamp=NULL WHERE migration_status IN ('failed', 'cancelled') @@ -287,6 +295,10 @@ const ( sqlSelectRunningMigrations = `SELECT migration_uuid, postpone_completion, + force_cutover, + cutover_attempts, + ifnull(timestampdiff(second, ready_to_complete_timestamp, now()), 0) as seconds_since_ready_to_complete, + ifnull(timestampdiff(second, last_cutover_attempt_timestamp, now()), 0) as seconds_since_last_cutover_attempt, timestampdiff(second, started_timestamp, now()) as elapsed_seconds FROM _vt.schema_migrations WHERE @@ -526,8 +538,9 @@ const ( sqlAnalyzeTable = "ANALYZE NO_WRITE_TO_BINLOG TABLE `%a`" sqlShowCreateTable = "SHOW CREATE TABLE `%a`" sqlShowVariablesLikePreserveForeignKey = "show global variables like 'rename_table_preserve_foreign_key'" - sqlEnablePreserveForeignKey = "set @@rename_table_preserve_foreign_key = 1" - sqlDisablePreserveForeignKey = "set @@rename_table_preserve_foreign_key = 0" + sqlShowVariablesLikeFastAnalyzeTable = "show global variables like 'fast_analyze_table'" + sqlEnableFastAnalyzeTable = "set @@fast_analyze_table = 1" + sqlDisableFastAnalyzeTable = "set @@fast_analyze_table = 0" sqlGetAutoIncrement = ` SELECT AUTO_INCREMENT @@ -567,12 +580,22 @@ const ( _vt.copy_state WHERE vrepl_id=%a ` - sqlSwapTables = "RENAME TABLE `%a` TO `%a`, `%a` TO `%a`, `%a` TO `%a`" - sqlRenameTable = "RENAME TABLE `%a` TO `%a`" - sqlLockTwoTablesWrite = "LOCK TABLES `%a` WRITE, `%a` WRITE" - sqlUnlockTables = "UNLOCK TABLES" - sqlCreateSentryTable = "CREATE TABLE IF NOT EXISTS `%a` (id INT PRIMARY KEY)" - sqlFindProcess = "SELECT id, Info as info FROM information_schema.processlist WHERE id=%a AND Info LIKE %a" + sqlSwapTables = "RENAME TABLE `%a` TO `%a`, `%a` TO `%a`, `%a` TO `%a`" + sqlRenameTable = "RENAME TABLE `%a` TO `%a`" + sqlLockTwoTablesWrite = "LOCK TABLES `%a` WRITE, `%a` WRITE" + sqlUnlockTables = "UNLOCK TABLES" + sqlCreateSentryTable = "CREATE TABLE IF NOT EXISTS `%a` (id INT PRIMARY KEY)" + sqlFindProcess = "SELECT id, Info as info FROM information_schema.processlist WHERE id=%a AND Info LIKE %a" + sqlFindProcessByInfo = "SELECT id, Info as info FROM information_schema.processlist WHERE Info LIKE %a and id != connection_id()" + sqlProcessWithLocksOnTable = ` + SELECT + DISTINCT innodb_trx.trx_mysql_thread_id + from + performance_schema.data_locks + join information_schema.innodb_trx on (data_locks.ENGINE_TRANSACTION_ID=innodb_trx.trx_id) + where + data_locks.OBJECT_SCHEMA=database() AND data_locks.OBJECT_NAME=%a + ` ) var ( diff --git a/go/vt/vttablet/onlineddl/util.go b/go/vt/vttablet/onlineddl/util.go index 305b01c057f..3d06e6df60e 100644 --- a/go/vt/vttablet/onlineddl/util.go +++ b/go/vt/vttablet/onlineddl/util.go @@ -26,15 +26,10 @@ import ( "os/exec" "path/filepath" "strings" - "time" "vitess.io/vitess/go/vt/log" ) -const ( - readableTimeFormat = "20060102150405" -) - // execCmd searches the PATH for a command and runs it, logging the output. // If input is not nil, pipe it to the command's stdin. func execCmd(name string, args, env []string, dir string, input io.Reader, output io.Writer) (cmd *exec.Cmd, err error) { @@ -89,17 +84,3 @@ func RandomHash() string { hasher.Write(rb) return hex.EncodeToString(hasher.Sum(nil)) } - -// ToReadableTimestamp returns a timestamp, in seconds resolution, that is human readable -// (as opposed to unix timestamp which is just a number) -// Example: for Aug 25 2020, 16:04:25 we return "20200825160425" -func ToReadableTimestamp(t time.Time) string { - return t.Format(readableTimeFormat) -} - -// ReadableTimestamp returns a timestamp, in seconds resolution, that is human readable -// (as opposed to unix timestamp which is just a number), and which corresponds to the time now. -// Example: for Aug 25 2020, 16:04:25 we return "20200825160425" -func ReadableTimestamp() string { - return ToReadableTimestamp(time.Now()) -} diff --git a/go/vt/vttablet/onlineddl/util_test.go b/go/vt/vttablet/onlineddl/util_test.go index 707e321c6f5..4beb154c0ae 100644 --- a/go/vt/vttablet/onlineddl/util_test.go +++ b/go/vt/vttablet/onlineddl/util_test.go @@ -18,7 +18,6 @@ package onlineddl import ( "testing" - "time" "github.com/stretchr/testify/assert" ) @@ -31,11 +30,3 @@ func TestRandomHash(t *testing.T) { assert.Equal(t, len(h2), 64) assert.NotEqual(t, h1, h2) } - -func TestToReadableTimestamp(t *testing.T) { - ti, err := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015") - assert.NoError(t, err) - - readableTimestamp := ToReadableTimestamp(ti) - assert.Equal(t, readableTimestamp, "20150225110639") -} diff --git a/go/vt/vttablet/onlineddl/vrepl.go b/go/vt/vttablet/onlineddl/vrepl.go index 8432f79b506..ef38fb7d012 100644 --- a/go/vt/vttablet/onlineddl/vrepl.go +++ b/go/vt/vttablet/onlineddl/vrepl.go @@ -27,7 +27,7 @@ import ( "context" "errors" "fmt" - "math" + "net/url" "strconv" "strings" @@ -37,8 +37,10 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/vt/dbconnpool" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/onlineddl/vrepl" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" @@ -135,10 +137,14 @@ type VRepl struct { parser *vrepl.AlterTableParser convertCharset map[string](*binlogdatapb.CharsetConversion) + + env *vtenv.Environment } // NewVRepl creates a VReplication handler for Online DDL -func NewVRepl(workflow string, +func NewVRepl( + env *vtenv.Environment, + workflow string, keyspace string, shard string, dbName string, @@ -150,6 +156,7 @@ func NewVRepl(workflow string, analyzeTable bool, ) *VRepl { return &VRepl{ + env: env, workflow: workflow, keyspace: keyspace, shard: shard, @@ -167,7 +174,7 @@ func NewVRepl(workflow string, } } -// readAutoIncrement reads the AUTO_INCREMENT vlaue, if any, for a give ntable +// readAutoIncrement reads the AUTO_INCREMENT value, if any, for a give ntable func (v *VRepl) readAutoIncrement(ctx context.Context, conn *dbconnpool.DBConnection, tableName string) (autoIncrement uint64, err error) { query, err := sqlparser.ParseAndBind(sqlGetAutoIncrement, sqltypes.StringBindVariable(v.dbName), @@ -177,7 +184,7 @@ func (v *VRepl) readAutoIncrement(ctx context.Context, conn *dbconnpool.DBConnec return 0, err } - rs, err := conn.ExecuteFetch(query, math.MaxInt64, true) + rs, err := conn.ExecuteFetch(query, -1, true) if err != nil { return 0, err } @@ -191,7 +198,7 @@ func (v *VRepl) readAutoIncrement(ctx context.Context, conn *dbconnpool.DBConnec // readTableColumns reads column list from given table func (v *VRepl) readTableColumns(ctx context.Context, conn *dbconnpool.DBConnection, tableName string) (columns *vrepl.ColumnList, virtualColumns *vrepl.ColumnList, pkColumns *vrepl.ColumnList, err error) { parsed := sqlparser.BuildParsedQuery(sqlShowColumnsFrom, tableName) - rs, err := conn.ExecuteFetch(parsed.Query, math.MaxInt64, true) + rs, err := conn.ExecuteFetch(parsed.Query, -1, true) if err != nil { return nil, nil, nil, err } @@ -229,7 +236,7 @@ func (v *VRepl) readTableUniqueKeys(ctx context.Context, conn *dbconnpool.DBConn if err != nil { return nil, err } - rs, err := conn.ExecuteFetch(query, math.MaxInt64, true) + rs, err := conn.ExecuteFetch(query, -1, true) if err != nil { return nil, err } @@ -247,17 +254,47 @@ func (v *VRepl) readTableUniqueKeys(ctx context.Context, conn *dbconnpool.DBConn return uniqueKeys, nil } +// isFastAnalyzeTableSupported checks if the underlying MySQL server supports 'fast_analyze_table', +// introduced by a fork of MySQL: https://github.com/planetscale/mysql-server/commit/c8a9d93686358dabfba8f3dc5cc0621e3149fe78 +// When `fast_analyze_table=1`, an `ANALYZE TABLE` command only analyzes the clustering index (normally the `PRIMARY KEY`). +// This is useful when you want to get a better estimate of the number of table rows, as fast as possible. +func (v *VRepl) isFastAnalyzeTableSupported(ctx context.Context, conn *dbconnpool.DBConnection) (isSupported bool, err error) { + rs, err := conn.ExecuteFetch(sqlShowVariablesLikeFastAnalyzeTable, -1, true) + if err != nil { + return false, err + } + return len(rs.Rows) > 0, nil +} + // executeAnalyzeTable runs an ANALYZE TABLE command func (v *VRepl) executeAnalyzeTable(ctx context.Context, conn *dbconnpool.DBConnection, tableName string) error { + fastAnalyzeTableSupported, err := v.isFastAnalyzeTableSupported(ctx, conn) + if err != nil { + return err + } + if fastAnalyzeTableSupported { + // This code is only applicable when MySQL supports the 'fast_analyze_table' variable. This variable + // does not exist in vanilla MySQL. + // See https://github.com/planetscale/mysql-server/commit/c8a9d93686358dabfba8f3dc5cc0621e3149fe78 + // as part of https://github.com/planetscale/mysql-server/releases/tag/8.0.34-ps1. + if _, err := conn.ExecuteFetch(sqlEnableFastAnalyzeTable, 1, false); err != nil { + return err + } + log.Infof("@@fast_analyze_table enabled") + defer conn.ExecuteFetch(sqlDisableFastAnalyzeTable, 1, false) + } + parsed := sqlparser.BuildParsedQuery(sqlAnalyzeTable, tableName) - _, err := conn.ExecuteFetch(parsed.Query, 1, false) - return err + if _, err := conn.ExecuteFetch(parsed.Query, 1, false); err != nil { + return err + } + return nil } // readTableStatus reads table status information func (v *VRepl) readTableStatus(ctx context.Context, conn *dbconnpool.DBConnection, tableName string) (tableRows int64, err error) { parsed := sqlparser.BuildParsedQuery(sqlShowTableStatus, tableName) - rs, err := conn.ExecuteFetch(parsed.Query, math.MaxInt64, true) + rs, err := conn.ExecuteFetch(parsed.Query, -1, true) if err != nil { return 0, err } @@ -278,7 +315,7 @@ func (v *VRepl) applyColumnTypes(ctx context.Context, conn *dbconnpool.DBConnect if err != nil { return err } - rs, err := conn.ExecuteFetch(query, math.MaxInt64, true) + rs, err := conn.ExecuteFetch(query, -1, true) if err != nil { return err } @@ -353,7 +390,7 @@ func (v *VRepl) analyzeAlter(ctx context.Context) error { // Happens for REVERT return nil } - if err := v.parser.ParseAlterStatement(v.alterQuery); err != nil { + if err := v.parser.ParseAlterStatement(v.alterQuery, v.env.Parser()); err != nil { return err } if v.parser.IsRenameTable() { @@ -424,7 +461,7 @@ func (v *VRepl) analyzeTables(ctx context.Context, conn *dbconnpool.DBConnection } v.addedUniqueKeys = vrepl.AddedUniqueKeys(sourceUniqueKeys, targetUniqueKeys, v.parser.ColumnRenameMap()) v.removedUniqueKeys = vrepl.RemovedUniqueKeys(sourceUniqueKeys, targetUniqueKeys, v.parser.ColumnRenameMap()) - v.removedForeignKeyNames, err = vrepl.RemovedForeignKeyNames(v.originalShowCreateTable, v.vreplShowCreateTable) + v.removedForeignKeyNames, err = vrepl.RemovedForeignKeyNames(v.env, v.originalShowCreateTable, v.vreplShowCreateTable) if err != nil { return err } @@ -522,11 +559,11 @@ func (v *VRepl) generateFilterQuery(ctx context.Context) error { case sourceCol.Type == vrepl.StringColumnType: // Check source and target charset/encoding. If needed, create // a binlogdatapb.CharsetConversion entry (later written to vreplication) - fromCollation := collations.Local().DefaultCollationForCharset(sourceCol.Charset) + fromCollation := v.env.CollationEnv().DefaultCollationForCharset(sourceCol.Charset) if fromCollation == collations.Unknown { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Character set %s not supported for column %s", sourceCol.Charset, sourceCol.Name) } - toCollation := collations.Local().DefaultCollationForCharset(targetCol.Charset) + toCollation := v.env.CollationEnv().DefaultCollationForCharset(targetCol.Charset) // Let's see if target col is at all textual if targetCol.Type == vrepl.StringColumnType && toCollation == collations.Unknown { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Character set %s not supported for column %s", targetCol.Charset, targetCol.Name) @@ -582,6 +619,7 @@ func (v *VRepl) analyzeBinlogSource(ctx context.Context) { SourceUniqueKeyColumns: encodeColumns(&v.chosenSourceUniqueKey.Columns), TargetUniqueKeyColumns: encodeColumns(&v.chosenTargetUniqueKey.Columns), SourceUniqueKeyTargetColumns: encodeColumns(v.chosenSourceUniqueKey.Columns.MappedNamesColumnList(v.sharedColumnsMap)), + ForceUniqueKey: url.QueryEscape(v.chosenSourceUniqueKey.Name), } if len(v.convertCharset) > 0 { rule.ConvertCharset = v.convertCharset @@ -611,7 +649,7 @@ func (v *VRepl) analyze(ctx context.Context, conn *dbconnpool.DBConnection) erro return nil } -// generateInsertStatement generates the INSERT INTO _vt.replication stataement that creates the vreplication workflow +// generateInsertStatement generates the INSERT INTO _vt.replication statement that creates the vreplication workflow func (v *VRepl) generateInsertStatement(ctx context.Context) (string, error) { ig := vreplication.NewInsertGenerator(binlogdatapb.VReplicationWorkflowState_Stopped, v.dbName) ig.AddRow(v.workflow, v.bls, v.pos, "", "in_order:REPLICA,PRIMARY", @@ -628,16 +666,16 @@ func (v *VRepl) generateStartStatement(ctx context.Context) (string, error) { ) } -func getVreplTable(ctx context.Context, s *VReplStream) (string, error) { +func getVreplTable(s *VReplStream) (string, error) { // sanity checks: if s == nil { - return "", vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "No vreplication stream migration %s", s.workflow) + return "", vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "No vreplication stream migration") } if s.bls.Filter == nil { return "", vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "No binlog source filter for migration %s", s.workflow) } if len(s.bls.Filter.Rules) != 1 { - return "", vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "Cannot detect filter rules for migration/vreplication %+v", s.workflow) + return "", vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "Cannot detect filter rules for migration/vreplication %s", s.workflow) } vreplTable := s.bls.Filter.Rules[0].Match return vreplTable, nil diff --git a/go/vt/vttablet/onlineddl/vrepl/foreign_key.go b/go/vt/vttablet/onlineddl/vrepl/foreign_key.go index f0925594ec0..8671badadc0 100644 --- a/go/vt/vttablet/onlineddl/vrepl/foreign_key.go +++ b/go/vt/vttablet/onlineddl/vrepl/foreign_key.go @@ -23,18 +23,24 @@ package vrepl import ( "vitess.io/vitess/go/vt/schemadiff" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" ) // RemovedForeignKeyNames returns the names of removed foreign keys, ignoring mere name changes func RemovedForeignKeyNames( + venv *vtenv.Environment, originalCreateTable string, vreplCreateTable string, ) (names []string, err error) { if originalCreateTable == "" || vreplCreateTable == "" { return nil, nil } - diffHints := schemadiff.DiffHints{ConstraintNamesStrategy: schemadiff.ConstraintNamesIgnoreAll} - diff, err := schemadiff.DiffCreateTablesQueries(originalCreateTable, vreplCreateTable, &diffHints) + env := schemadiff.NewEnv(venv, venv.CollationEnv().DefaultConnectionCharset()) + diffHints := schemadiff.DiffHints{ + ConstraintNamesStrategy: schemadiff.ConstraintNamesIgnoreAll, + EnumReorderStrategy: schemadiff.EnumReorderStrategyAllow, + } + diff, err := schemadiff.DiffCreateTablesQueries(env, originalCreateTable, vreplCreateTable, &diffHints) if err != nil { return nil, err } diff --git a/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go b/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go index 619ba4847d9..95b2c84e66e 100644 --- a/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go +++ b/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go @@ -24,6 +24,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/vt/vtenv" ) func TestRemovedForeignKeyNames(t *testing.T) { @@ -66,7 +68,7 @@ func TestRemovedForeignKeyNames(t *testing.T) { } for _, tcase := range tcases { t.Run(tcase.before, func(t *testing.T) { - names, err := RemovedForeignKeyNames(tcase.before, tcase.after) + names, err := RemovedForeignKeyNames(vtenv.NewTestEnv(), tcase.before, tcase.after) assert.NoError(t, err) assert.Equal(t, tcase.names, names) }) diff --git a/go/vt/vttablet/onlineddl/vrepl/parser.go b/go/vt/vttablet/onlineddl/vrepl/parser.go index 87f82cb8096..b5648adeabe 100644 --- a/go/vt/vttablet/onlineddl/vrepl/parser.go +++ b/go/vt/vttablet/onlineddl/vrepl/parser.go @@ -78,8 +78,8 @@ func (p *AlterTableParser) analyzeAlter(alterTable *sqlparser.AlterTable) { } // ParseAlterStatement is the main function of th eparser, and parses an ALTER TABLE statement -func (p *AlterTableParser) ParseAlterStatement(alterQuery string) (err error) { - stmt, err := sqlparser.ParseStrictDDL(alterQuery) +func (p *AlterTableParser) ParseAlterStatement(alterQuery string, parser *sqlparser.Parser) (err error) { + stmt, err := parser.ParseStrictDDL(alterQuery) if err != nil { return err } @@ -112,7 +112,7 @@ func (p *AlterTableParser) DroppedColumnsMap() map[string]bool { return p.droppedColumns } -// IsRenameTable returns true when the ALTER TABLE statement inclusdes renaming the table +// IsRenameTable returns true when the ALTER TABLE statement includes renaming the table func (p *AlterTableParser) IsRenameTable() bool { return p.isRenameTable } diff --git a/go/vt/vttablet/onlineddl/vrepl/parser_test.go b/go/vt/vttablet/onlineddl/vrepl/parser_test.go index f849b1d741d..2a7031f3a98 100644 --- a/go/vt/vttablet/onlineddl/vrepl/parser_test.go +++ b/go/vt/vttablet/onlineddl/vrepl/parser_test.go @@ -24,12 +24,14 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/vt/sqlparser" ) func TestParseAlterStatement(t *testing.T) { statement := "alter table t add column t int, engine=innodb" parser := NewAlterTableParser() - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.False(t, parser.HasNonTrivialRenames()) assert.False(t, parser.IsAutoIncrementDefined()) @@ -38,7 +40,7 @@ func TestParseAlterStatement(t *testing.T) { func TestParseAlterStatementTrivialRename(t *testing.T) { statement := "alter table t add column t int, change ts ts timestamp, engine=innodb" parser := NewAlterTableParser() - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.False(t, parser.HasNonTrivialRenames()) assert.False(t, parser.IsAutoIncrementDefined()) @@ -66,7 +68,7 @@ func TestParseAlterStatementWithAutoIncrement(t *testing.T) { for _, statement := range statements { parser := NewAlterTableParser() statement := "alter table t " + statement - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.True(t, parser.IsAutoIncrementDefined()) } @@ -75,7 +77,7 @@ func TestParseAlterStatementWithAutoIncrement(t *testing.T) { func TestParseAlterStatementTrivialRenames(t *testing.T) { statement := "alter table t add column t int, change ts ts timestamp, CHANGE f `f` float, engine=innodb" parser := NewAlterTableParser() - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.False(t, parser.HasNonTrivialRenames()) assert.False(t, parser.IsAutoIncrementDefined()) @@ -98,7 +100,7 @@ func TestParseAlterStatementNonTrivial(t *testing.T) { for _, statement := range statements { statement := "alter table t " + statement parser := NewAlterTableParser() - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.False(t, parser.IsAutoIncrementDefined()) renames := parser.GetNonTrivialRenames() @@ -113,7 +115,7 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) { { parser := NewAlterTableParser() statement := "alter table t drop column b" - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, len(parser.droppedColumns), 1) assert.True(t, parser.droppedColumns["b"]) @@ -121,7 +123,7 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) { { parser := NewAlterTableParser() statement := "alter table t drop column b, drop key c_idx, drop column `d`" - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, len(parser.droppedColumns), 2) assert.True(t, parser.droppedColumns["b"]) @@ -130,7 +132,7 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) { { parser := NewAlterTableParser() statement := "alter table t drop column b, drop key c_idx, drop column `d`, drop `e`, drop primary key, drop foreign key fk_1" - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, len(parser.droppedColumns), 3) assert.True(t, parser.droppedColumns["b"]) @@ -140,7 +142,7 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) { { parser := NewAlterTableParser() statement := "alter table t drop column b, drop bad statement, add column i int" - err := parser.ParseAlterStatement(statement) + err := parser.ParseAlterStatement(statement, sqlparser.NewTestParser()) assert.Error(t, err) } } @@ -177,7 +179,7 @@ func TestParseAlterStatementRenameTable(t *testing.T) { for _, tc := range tt { t.Run(tc.alter, func(t *testing.T) { parser := NewAlterTableParser() - err := parser.ParseAlterStatement(tc.alter) + err := parser.ParseAlterStatement(tc.alter, sqlparser.NewTestParser()) assert.NoError(t, err) assert.Equal(t, tc.isRename, parser.isRenameTable) }) diff --git a/go/vt/vttablet/onlineddl/vrepl/types.go b/go/vt/vttablet/onlineddl/vrepl/types.go index e4ddff6d58e..0ca834ffdf0 100644 --- a/go/vt/vttablet/onlineddl/vrepl/types.go +++ b/go/vt/vttablet/onlineddl/vrepl/types.go @@ -207,7 +207,7 @@ func (l *ColumnList) Equals(other *ColumnList) bool { return reflect.DeepEqual(l.Columns, other.Columns) } -// EqualsByNames chcks if the names in this list equals the names of another list, in order. Type is ignored. +// EqualsByNames checks if the names in this list equals the names of another list, in order. Type is ignored. func (l *ColumnList) EqualsByNames(other *ColumnList) bool { return reflect.DeepEqual(l.Names(), other.Names()) } @@ -252,7 +252,7 @@ func (l *ColumnList) MappedNamesColumnList(columnNamesMap map[string]string) *Co return NewColumnList(names) } -// SetEnumToTextConversion tells this column list that an enum is conveted to text +// SetEnumToTextConversion tells this column list that an enum is converted to text func (l *ColumnList) SetEnumToTextConversion(columnName string, enumValues string) { l.GetColumn(columnName).EnumToTextConversion = true l.GetColumn(columnName).EnumValues = enumValues diff --git a/go/vt/vttablet/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go index 0c8485f97e5..ad9c1b3702f 100644 --- a/go/vt/vttablet/sandboxconn/sandboxconn.go +++ b/go/vt/vttablet/sandboxconn/sandboxconn.go @@ -128,6 +128,8 @@ type SandboxConn struct { NotServing bool getSchemaResult []map[string]string + + parser *sqlparser.Parser } var _ queryservice.QueryService = (*SandboxConn)(nil) // compile-time interface check @@ -139,6 +141,7 @@ func NewSandboxConn(t *topodatapb.Tablet) *SandboxConn { MustFailCodes: make(map[vtrpcpb.Code]int), MustFailExecute: make(map[sqlparser.StatementType]int), txIDToRID: make(map[int64]int64), + parser: sqlparser.NewTestParser(), } } @@ -225,7 +228,7 @@ func (sbc *SandboxConn) Execute(ctx context.Context, target *querypb.Target, que return nil, err } - stmt, _ := sqlparser.Parse(query) // knowingly ignoring the error + stmt, _ := sbc.parser.Parse(query) // knowingly ignoring the error if sbc.MustFailExecute[sqlparser.ASTToStatementType(stmt)] > 0 { sbc.MustFailExecute[sqlparser.ASTToStatementType(stmt)] = sbc.MustFailExecute[sqlparser.ASTToStatementType(stmt)] - 1 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "failed query: %v", query) @@ -251,7 +254,7 @@ func (sbc *SandboxConn) StreamExecute(ctx context.Context, target *querypb.Targe sbc.sExecMu.Unlock() return err } - parse, _ := sqlparser.Parse(query) + parse, _ := sbc.parser.Parse(query) if sbc.results == nil { nextRs := sbc.getNextResult(parse) @@ -391,7 +394,7 @@ func (sbc *SandboxConn) ConcludeTransaction(ctx context.Context, target *querypb return sbc.getError() } -// ReadTransaction returns the metadata for the sepcified dtid. +// ReadTransaction returns the metadata for the specified dtid. func (sbc *SandboxConn) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (metadata *querypb.TransactionMetadata, err error) { sbc.ReadTransactionCount.Add(1) if err := sbc.getError(); err != nil { diff --git a/go/vt/vttablet/sysloglogger/sysloglogger.go b/go/vt/vttablet/sysloglogger/sysloglogger.go index e56d47bd902..37672911e23 100644 --- a/go/vt/vttablet/sysloglogger/sysloglogger.go +++ b/go/vt/vttablet/sysloglogger/sysloglogger.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. @@ -18,8 +20,8 @@ limitations under the License. package sysloglogger import ( - "bytes" "log/syslog" + "strings" "github.com/spf13/pflag" @@ -76,8 +78,10 @@ func run() { } formatParams := map[string][]string{"full": {}} + + var b strings.Builder for stats := range ch { - var b bytes.Buffer + b.Reset() if err := stats.Logf(&b, formatParams); err != nil { log.Errorf("Error formatting logStats: %v", err) continue diff --git a/go/vt/vttablet/sysloglogger/sysloglogger_test.go b/go/vt/vttablet/sysloglogger/sysloglogger_test.go index c62a4396ac6..7a1678638ca 100644 --- a/go/vt/vttablet/sysloglogger/sysloglogger_test.go +++ b/go/vt/vttablet/sysloglogger/sysloglogger_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2019 The Vitess Authors. diff --git a/go/vt/vttablet/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go index cfe540ead42..d3adff022e4 100644 --- a/go/vt/vttablet/tabletconntest/fakequeryservice.go +++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go @@ -173,7 +173,7 @@ func (f *FakeQueryService) Commit(ctx context.Context, target *querypb.Target, t return 0, nil } -// rollbackTransactionID is a test transactin id for Rollback. +// rollbackTransactionID is a test transaction id for Rollback. const rollbackTransactionID int64 = 999044 // Rollback is part of the queryservice.QueryService interface diff --git a/go/vt/vttablet/tabletmanager/restore.go b/go/vt/vttablet/tabletmanager/restore.go index 4512b546f2c..6b37edb5244 100644 --- a/go/vt/vttablet/tabletmanager/restore.go +++ b/go/vt/vttablet/tabletmanager/restore.go @@ -131,7 +131,8 @@ func (tm *TabletManager) RestoreData( deleteBeforeRestore bool, backupTime time.Time, restoreToTimetamp time.Time, - restoreToPos string) error { + restoreToPos string, + mysqlShutdownTimeout time.Duration) error { if err := tm.lock(ctx); err != nil { return err } @@ -180,14 +181,14 @@ func (tm *TabletManager) RestoreData( RestoreToPos: restoreToPos, RestoreToTimestamp: protoutil.TimeToProto(restoreToTimetamp), } - err = tm.restoreDataLocked(ctx, logger, waitForBackupInterval, deleteBeforeRestore, req) + err = tm.restoreDataLocked(ctx, logger, waitForBackupInterval, deleteBeforeRestore, req, mysqlShutdownTimeout) if err != nil { return err } return nil } -func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.Logger, waitForBackupInterval time.Duration, deleteBeforeRestore bool, request *tabletmanagerdatapb.RestoreFromBackupRequest) error { +func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.Logger, waitForBackupInterval time.Duration, deleteBeforeRestore bool, request *tabletmanagerdatapb.RestoreFromBackupRequest, mysqlShutdownTimeout time.Duration) error { tablet := tm.Tablet() originalType := tablet.Type @@ -217,18 +218,19 @@ func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.L } params := mysqlctl.RestoreParams{ - Cnf: tm.Cnf, - Mysqld: tm.MysqlDaemon, - Logger: logger, - Concurrency: restoreConcurrency, - HookExtraEnv: tm.hookExtraEnv(), - DeleteBeforeRestore: deleteBeforeRestore, - DbName: topoproto.TabletDbName(tablet), - Keyspace: keyspace, - Shard: tablet.Shard, - StartTime: startTime, - DryRun: request.DryRun, - Stats: backupstats.RestoreStats(), + Cnf: tm.Cnf, + Mysqld: tm.MysqlDaemon, + Logger: logger, + Concurrency: restoreConcurrency, + HookExtraEnv: tm.hookExtraEnv(), + DeleteBeforeRestore: deleteBeforeRestore, + DbName: topoproto.TabletDbName(tablet), + Keyspace: keyspace, + Shard: tablet.Shard, + StartTime: startTime, + DryRun: request.DryRun, + Stats: backupstats.RestoreStats(), + MysqlShutdownTimeout: mysqlShutdownTimeout, } restoreToTimestamp := protoutil.TimeFromProto(request.RestoreToTimestamp).UTC() if request.RestoreToPos != "" && !restoreToTimestamp.IsZero() { @@ -424,7 +426,7 @@ func (tm *TabletManager) getGTIDFromTimestamp(ctx context.Context, pos replicati Port: connParams.Port, } dbCfgs.SetDbParams(*connParams, *connParams, *connParams) - vsClient := vreplication.NewReplicaConnector(connParams) + vsClient := vreplication.NewReplicaConnector(tm.Env, connParams) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -475,7 +477,7 @@ func (tm *TabletManager) getGTIDFromTimestamp(ctx context.Context, pos replicati gtidsChan <- []string{"", ""} } }() - defer vsClient.Close(ctx) + defer vsClient.Close() select { case val := <-gtidsChan: return val[0], val[1], nil @@ -581,7 +583,7 @@ func (tm *TabletManager) catchupToGTID(ctx context.Context, afterGTIDPos string, } } -// disableReplication stopes and resets replication on the mysql server. It moreover sets impossible replication +// disableReplication stops and resets replication on the mysql server. It moreover sets impossible replication // source params, so that the replica can't possibly reconnect. It would take a `CHANGE [MASTER|REPLICATION SOURCE] TO ...` to // make the mysql server replicate again (available via tm.MysqlDaemon.SetReplicationPosition) func (tm *TabletManager) disableReplication(ctx context.Context) error { diff --git a/go/vt/vttablet/tabletmanager/rpc_backup.go b/go/vt/vttablet/tabletmanager/rpc_backup.go index b3d2e2794f6..9c361eac400 100644 --- a/go/vt/vttablet/tabletmanager/rpc_backup.go +++ b/go/vt/vttablet/tabletmanager/rpc_backup.go @@ -149,19 +149,20 @@ func (tm *TabletManager) Backup(ctx context.Context, logger logutil.Logger, req // Now we can run the backup. backupParams := mysqlctl.BackupParams{ - Cnf: tm.Cnf, - Mysqld: tm.MysqlDaemon, - Logger: l, - Concurrency: int(req.Concurrency), - IncrementalFromPos: req.IncrementalFromPos, - HookExtraEnv: tm.hookExtraEnv(), - TopoServer: tm.TopoServer, - Keyspace: tablet.Keyspace, - Shard: tablet.Shard, - TabletAlias: topoproto.TabletAliasString(tablet.Alias), - BackupTime: time.Now(), - Stats: backupstats.BackupStats(), - UpgradeSafe: req.UpgradeSafe, + Cnf: tm.Cnf, + Mysqld: tm.MysqlDaemon, + Logger: l, + Concurrency: int(req.Concurrency), + IncrementalFromPos: req.IncrementalFromPos, + HookExtraEnv: tm.hookExtraEnv(), + TopoServer: tm.TopoServer, + Keyspace: tablet.Keyspace, + Shard: tablet.Shard, + TabletAlias: topoproto.TabletAliasString(tablet.Alias), + BackupTime: time.Now(), + Stats: backupstats.BackupStats(), + UpgradeSafe: req.UpgradeSafe, + MysqlShutdownTimeout: mysqlShutdownTimeout, } returnErr := mysqlctl.Backup(ctx, backupParams) @@ -189,7 +190,7 @@ func (tm *TabletManager) RestoreFromBackup(ctx context.Context, logger logutil.L l := logutil.NewTeeLogger(logutil.NewConsoleLogger(), logger) // Now we can run restore. - err = tm.restoreDataLocked(ctx, l, 0 /* waitForBackupInterval */, true /* deleteBeforeRestore */, request) + err = tm.restoreDataLocked(ctx, l, 0 /* waitForBackupInterval */, true /* deleteBeforeRestore */, request, mysqlShutdownTimeout) // Re-run health check to be sure to capture any replication delay. tm.QueryServiceControl.BroadcastHealth() diff --git a/go/vt/vttablet/tabletmanager/rpc_query.go b/go/vt/vttablet/tabletmanager/rpc_query.go index 8b8ac605893..00e2ef3f325 100644 --- a/go/vt/vttablet/tabletmanager/rpc_query.go +++ b/go/vt/vttablet/tabletmanager/rpc_query.go @@ -24,21 +24,63 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + "vitess.io/vitess/go/vt/proto/vtrpc" ) +// analyzeExecuteFetchAsDbaMultiQuery reutrns 'true' when at least one of the queries +// in the given SQL has a `/*vt+ allowZeroInDate=true */` directive. +func analyzeExecuteFetchAsDbaMultiQuery(sql string, parser *sqlparser.Parser) (queries []string, parseable bool, countCreate int, allowZeroInDate bool, err error) { + queries, err = parser.SplitStatementToPieces(sql) + if err != nil { + return nil, false, 0, false, err + } + if len(queries) == 0 { + return nil, false, 0, false, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "no statements found in query: %s", sql) + } + parseable = true + for _, query := range queries { + // Some of the queries we receive here are legitimately non-parseable by our + // current parser, such as `CHANGE REPLICATION SOURCE TO...`. We must allow + // them and so we skip parsing errors. + stmt, err := parser.Parse(query) + if err != nil { + parseable = false + continue + } + switch stmt.(type) { + case *sqlparser.CreateTable, *sqlparser.CreateView: + countCreate++ + default: + } + + if cmnt, ok := stmt.(sqlparser.Commented); ok { + directives := cmnt.GetParsedComments().Directives() + if directives.IsSet("allowZeroInDate") { + allowZeroInDate = true + } + } + + } + return queries, parseable, countCreate, allowZeroInDate, nil +} + // ExecuteFetchAsDba will execute the given query, possibly disabling binlogs and reload schema. func (tm *TabletManager) ExecuteFetchAsDba(ctx context.Context, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) { - // get a connection + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } + // Get a connection. conn, err := tm.MysqlDaemon.GetDbaConnection(ctx) if err != nil { return nil, err } defer conn.Close() - // disable binlogs if necessary + // Disable binlogs if necessary. if req.DisableBinlogs { _, err := conn.ExecuteFetch("SET sql_log_bin = OFF", 0, false) if err != nil { @@ -46,33 +88,67 @@ func (tm *TabletManager) ExecuteFetchAsDba(ctx context.Context, req *tabletmanag } } + // Disable FK checks if requested. + if req.DisableForeignKeyChecks { + _, err := conn.ExecuteFetch("SET SESSION foreign_key_checks = OFF", 0, false) + if err != nil { + return nil, err + } + } + if req.DbName != "" { // This execute might fail if db does not exist. // Error is ignored because given query might create this database. _, _ = conn.ExecuteFetch("USE "+sqlescape.EscapeID(req.DbName), 1, false) } - // Handle special possible directives - var directives *sqlparser.CommentDirectives - if stmt, err := sqlparser.Parse(string(req.Query)); err == nil { - if cmnt, ok := stmt.(sqlparser.Commented); ok { - directives = cmnt.GetParsedComments().Directives() + statements, _, countCreate, allowZeroInDate, err := analyzeExecuteFetchAsDbaMultiQuery(string(req.Query), tm.Env.Parser()) + if err != nil { + return nil, err + } + if len(statements) > 1 { + // Up to v19, we allow multi-statement SQL in ExecuteFetchAsDba, but only for the specific case + // where all statements are CREATE TABLE or CREATE VIEW. This is to support `ApplySchema --batch-size`. + // In v20, we will not support multi statements whatsoever. + // v20 will throw an error by virtua of using ExecuteFetch instead of ExecuteFetchMulti. + if countCreate != len(statements) { + return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "multi statement queries are not supported in ExecuteFetchAsDba unless all are CREATE TABLE or CREATE VIEW") } } - if directives.IsSet("allowZeroInDate") { + if allowZeroInDate { if _, err := conn.ExecuteFetch("set @@session.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')", 1, false); err != nil { return nil, err } } - // Replace any provided sidecar database qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) + // TODO(shlomi): we use ReplaceTableQualifiersMultiQuery for backwards compatibility. In v20 we will not accept + // multi statement queries in ExecuteFetchAsDBA. This will be rewritten as ReplaceTableQualifiers() + uq, err := tm.Env.Parser().ReplaceTableQualifiersMultiQuery(string(req.Query), sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } - result, err := conn.ExecuteFetch(uq, int(req.MaxRows), true /*wantFields*/) + // TODO(shlomi): we use ExecuteFetchMulti for backwards compatibility. In v20 we will not accept + // multi statement queries in ExecuteFetchAsDBA. This will be rewritten as: + // (in v20): result, err := ExecuteFetch(uq, int(req.MaxRows), true /*wantFields*/) + result, more, err := conn.ExecuteFetchMulti(uq, int(req.MaxRows), true /*wantFields*/) + for more { + _, more, _, err = conn.ReadQueryResult(0, false) + if err != nil { + return nil, err + } + } - // re-enable binlogs if necessary + // Re-enable FK checks if necessary. + if req.DisableForeignKeyChecks && !conn.IsClosed() { + _, err := conn.ExecuteFetch("SET SESSION foreign_key_checks = ON", 0, false) + if err != nil { + // If we can't reset the FK checks flag, + // let's just close the connection. + conn.Close() + } + } + + // Re-enable binlogs if necessary. if req.DisableBinlogs && !conn.IsClosed() { _, err := conn.ExecuteFetch("SET sql_log_bin = ON", 0, false) if err != nil { @@ -93,6 +169,9 @@ func (tm *TabletManager) ExecuteFetchAsDba(ctx context.Context, req *tabletmanag // ExecuteFetchAsAllPrivs will execute the given query, possibly reloading schema. func (tm *TabletManager) ExecuteFetchAsAllPrivs(ctx context.Context, req *tabletmanagerdatapb.ExecuteFetchAsAllPrivsRequest) (*querypb.QueryResult, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } // get a connection conn, err := tm.MysqlDaemon.GetAllPrivsConnection(ctx) if err != nil { @@ -107,7 +186,7 @@ func (tm *TabletManager) ExecuteFetchAsAllPrivs(ctx context.Context, req *tablet } // Replace any provided sidecar database qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) + uq, err := tm.Env.Parser().ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } @@ -124,6 +203,9 @@ func (tm *TabletManager) ExecuteFetchAsAllPrivs(ctx context.Context, req *tablet // ExecuteFetchAsApp will execute the given query. func (tm *TabletManager) ExecuteFetchAsApp(ctx context.Context, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } // get a connection conn, err := tm.MysqlDaemon.GetAppConnection(ctx) if err != nil { @@ -131,7 +213,7 @@ func (tm *TabletManager) ExecuteFetchAsApp(ctx context.Context, req *tabletmanag } defer conn.Recycle() // Replace any provided sidecar database qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) + uq, err := tm.Env.Parser().ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } @@ -141,11 +223,14 @@ func (tm *TabletManager) ExecuteFetchAsApp(ctx context.Context, req *tabletmanag // ExecuteQuery submits a new online DDL request func (tm *TabletManager) ExecuteQuery(ctx context.Context, req *tabletmanagerdatapb.ExecuteQueryRequest) (*querypb.QueryResult, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } // get the db name from the tablet tablet := tm.Tablet() target := &querypb.Target{Keyspace: tablet.Keyspace, Shard: tablet.Shard, TabletType: tablet.Type} // Replace any provided sidecar database qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) + uq, err := tm.Env.Parser().ReplaceTableQualifiers(string(req.Query), sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/rpc_query_test.go b/go/vt/vttablet/tabletmanager/rpc_query_test.go index 87a64b2d8b7..e30f63b362b 100644 --- a/go/vt/vttablet/tabletmanager/rpc_query_test.go +++ b/go/vt/vttablet/tabletmanager/rpc_query_test.go @@ -21,6 +21,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" @@ -28,11 +29,91 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletservermock" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" ) +func TestAnalyzeExecuteFetchAsDbaMultiQuery(t *testing.T) { + tcases := []struct { + query string + count int + parseable bool + allowZeroInDate bool + allCreate bool + expectErr bool + }{ + { + query: "", + expectErr: true, + }, + { + query: "select * from t1 ; select * from t2", + count: 2, + parseable: true, + }, + { + query: "create table t(id int)", + count: 1, + allCreate: true, + parseable: true, + }, + { + query: "create table t(id int); create view v as select 1 from dual", + count: 2, + allCreate: true, + parseable: true, + }, + { + query: "create table t(id int); create view v as select 1 from dual; drop table t3", + count: 3, + allCreate: false, + parseable: true, + }, + { + query: "create /*vt+ allowZeroInDate=true */ table t (id int)", + count: 1, + allCreate: true, + allowZeroInDate: true, + parseable: true, + }, + { + query: "create table a (id int) ; create /*vt+ allowZeroInDate=true */ table b (id int)", + count: 2, + allCreate: true, + allowZeroInDate: true, + parseable: true, + }, + { + query: "stop replica; start replica", + count: 2, + parseable: false, + }, + { + query: "create table a (id int) ; --comment ; what", + count: 3, + parseable: false, + }, + } + for _, tcase := range tcases { + t.Run(tcase.query, func(t *testing.T) { + parser := sqlparser.NewTestParser() + queries, parseable, countCreate, allowZeroInDate, err := analyzeExecuteFetchAsDbaMultiQuery(tcase.query, parser) + if tcase.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tcase.count, len(queries)) + assert.Equal(t, tcase.parseable, parseable) + assert.Equal(t, tcase.allCreate, (countCreate == len(queries))) + assert.Equal(t, tcase.allowZeroInDate, allowZeroInDate) + } + }) + } +} + func TestTabletManager_ExecuteFetchAsDba(t *testing.T) { ctx := context.Background() cp := mysql.ConnParams{} @@ -42,10 +123,13 @@ func TestTabletManager_ExecuteFetchAsDba(t *testing.T) { dbName := " escap`e me " tm := &TabletManager{ - MysqlDaemon: daemon, - DBConfigs: dbconfigs.NewTestDBConfigs(cp, cp, dbName), - QueryServiceControl: tabletservermock.NewController(), + MysqlDaemon: daemon, + DBConfigs: dbconfigs.NewTestDBConfigs(cp, cp, dbName), + QueryServiceControl: tabletservermock.NewController(), + _waitForGrantsComplete: make(chan struct{}), + Env: vtenv.NewTestEnv(), } + close(tm._waitForGrantsComplete) _, err := tm.ExecuteFetchAsDba(ctx, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ Query: []byte("select 42"), diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go index 9981219e4a2..ff8cb3a9b57 100644 --- a/go/vt/vttablet/tabletmanager/rpc_replication.go +++ b/go/vt/vttablet/tabletmanager/rpc_replication.go @@ -39,6 +39,9 @@ import ( // ReplicationStatus returns the replication status func (tm *TabletManager) ReplicationStatus(ctx context.Context) (*replicationdatapb.Status, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } status, err := tm.MysqlDaemon.ReplicationStatus() if err != nil { return nil, err @@ -48,6 +51,9 @@ func (tm *TabletManager) ReplicationStatus(ctx context.Context) (*replicationdat // FullStatus returns the full status of MySQL including the replication information, semi-sync information, GTID information among others func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.FullStatus, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } // Server ID - "select @@global.server_id" serverID, err := tm.MysqlDaemon.GetServerID(ctx) if err != nil { @@ -166,6 +172,9 @@ func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.Ful // PrimaryStatus returns the replication status for a primary tablet. func (tm *TabletManager) PrimaryStatus(ctx context.Context) (*replicationdatapb.PrimaryStatus, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } status, err := tm.MysqlDaemon.PrimaryStatus(ctx) if err != nil { return nil, err @@ -175,6 +184,9 @@ func (tm *TabletManager) PrimaryStatus(ctx context.Context) (*replicationdatapb. // PrimaryPosition returns the position of a primary database func (tm *TabletManager) PrimaryPosition(ctx context.Context) (string, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return "", err + } pos, err := tm.MysqlDaemon.PrimaryPosition() if err != nil { return "", err @@ -185,6 +197,9 @@ func (tm *TabletManager) PrimaryPosition(ctx context.Context) (string, error) { // WaitForPosition waits until replication reaches the desired position func (tm *TabletManager) WaitForPosition(ctx context.Context, pos string) error { log.Infof("WaitForPosition: %v", pos) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } mpos, err := replication.DecodePosition(pos) if err != nil { return err @@ -196,6 +211,9 @@ func (tm *TabletManager) WaitForPosition(ctx context.Context, pos string) error // replication or not (using hook if not). func (tm *TabletManager) StopReplication(ctx context.Context) error { log.Infof("StopReplication") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -217,6 +235,9 @@ func (tm *TabletManager) stopIOThreadLocked(ctx context.Context) error { // replication or not (using hook if not). func (tm *TabletManager) StopReplicationMinimum(ctx context.Context, position string, waitTime time.Duration) (string, error) { log.Infof("StopReplicationMinimum: position: %v waitTime: %v", position, waitTime) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return "", err + } if err := tm.lock(ctx); err != nil { return "", err } @@ -245,6 +266,9 @@ func (tm *TabletManager) StopReplicationMinimum(ctx context.Context, position st // replication or not (using hook if not). func (tm *TabletManager) StartReplication(ctx context.Context, semiSync bool) error { log.Infof("StartReplication") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -265,6 +289,9 @@ func (tm *TabletManager) StartReplication(ctx context.Context, semiSync bool) er // until and including the transactions in `position` func (tm *TabletManager) StartReplicationUntilAfter(ctx context.Context, position string, waitTime time.Duration) error { log.Infof("StartReplicationUntilAfter: position: %v waitTime: %v", position, waitTime) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -283,6 +310,9 @@ func (tm *TabletManager) StartReplicationUntilAfter(ctx context.Context, positio // GetReplicas returns the address of all the replicas func (tm *TabletManager) GetReplicas(ctx context.Context) ([]string, error) { + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } return mysqlctl.FindReplicas(tm.MysqlDaemon) } @@ -290,6 +320,9 @@ func (tm *TabletManager) GetReplicas(ctx context.Context) ([]string, error) { // All binary and relay logs are flushed. All replication positions are reset. func (tm *TabletManager) ResetReplication(ctx context.Context) error { log.Infof("ResetReplication") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -301,6 +334,9 @@ func (tm *TabletManager) ResetReplication(ctx context.Context) error { // InitPrimary enables writes and returns the replication position. func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string, error) { log.Infof("InitPrimary with semiSync as %t", semiSync) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return "", err + } if err := tm.lock(ctx); err != nil { return "", err } @@ -352,6 +388,9 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string func (tm *TabletManager) PopulateReparentJournal(ctx context.Context, timeCreatedNS int64, actionName string, primaryAlias *topodatapb.TabletAlias, position string) error { log.Infof("PopulateReparentJournal: action: %v parent: %v position: %v timeCreatedNS: %d actionName: %s primaryAlias: %s", actionName, primaryAlias, position, timeCreatedNS, actionName, primaryAlias) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } pos, err := replication.DecodePosition(position) if err != nil { return err @@ -366,6 +405,9 @@ func (tm *TabletManager) PopulateReparentJournal(ctx context.Context, timeCreate // reparent_journal table entry up to context timeout func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.TabletAlias, position string, timeCreatedNS int64, semiSync bool) error { log.Infof("InitReplica: parent: %v position: %v timeCreatedNS: %d semisync: %t", parent, position, timeCreatedNS, semiSync) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -418,7 +460,7 @@ func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.Tab // DemotePrimary prepares a PRIMARY tablet to give up leadership to another tablet. // -// It attemps to idempotently ensure the following guarantees upon returning +// It attempts to idempotently ensure the following guarantees upon returning // successfully: // - No future writes will be accepted. // - No writes are in-flight. @@ -433,6 +475,9 @@ func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.Tab // If a step fails in the middle, it will try to undo any changes it made. func (tm *TabletManager) DemotePrimary(ctx context.Context) (*replicationdatapb.PrimaryStatus, error) { log.Infof("DemotePrimary") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return nil, err + } // The public version always reverts on partial failure. return tm.demotePrimary(ctx, true /* revertPartialFailure */) } @@ -530,6 +575,9 @@ func (tm *TabletManager) demotePrimary(ctx context.Context, revertPartialFailure // and returns its primary position. func (tm *TabletManager) UndoDemotePrimary(ctx context.Context, semiSync bool) error { log.Infof("UndoDemotePrimary") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -562,6 +610,9 @@ func (tm *TabletManager) UndoDemotePrimary(ctx context.Context, semiSync bool) e // ReplicaWasPromoted promotes a replica to primary, no questions asked. func (tm *TabletManager) ReplicaWasPromoted(ctx context.Context) error { log.Infof("ReplicaWasPromoted") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -572,6 +623,9 @@ func (tm *TabletManager) ReplicaWasPromoted(ctx context.Context) error { // ResetReplicationParameters resets the replica replication parameters func (tm *TabletManager) ResetReplicationParameters(ctx context.Context) error { log.Infof("ResetReplicationParameters") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -593,6 +647,9 @@ func (tm *TabletManager) ResetReplicationParameters(ctx context.Context) error { // reparent_journal table entry up to context timeout func (tm *TabletManager) SetReplicationSource(ctx context.Context, parentAlias *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool, semiSync bool) error { log.Infof("SetReplicationSource: parent: %v position: %s force: %v semiSync: %v timeCreatedNS: %d", parentAlias, waitPosition, forceStartReplication, semiSync, timeCreatedNS) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -732,6 +789,9 @@ func (tm *TabletManager) setReplicationSourceLocked(ctx context.Context, parentA // ReplicaWasRestarted updates the parent record for a tablet. func (tm *TabletManager) ReplicaWasRestarted(ctx context.Context, parent *topodatapb.TabletAlias) error { log.Infof("ReplicaWasRestarted: parent: %v", parent) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return err + } if err := tm.lock(ctx); err != nil { return err } @@ -750,6 +810,9 @@ func (tm *TabletManager) ReplicaWasRestarted(ctx context.Context, parent *topoda // current status. func (tm *TabletManager) StopReplicationAndGetStatus(ctx context.Context, stopReplicationMode replicationdatapb.StopReplicationMode) (StopReplicationAndGetStatusResponse, error) { log.Infof("StopReplicationAndGetStatus: mode: %v", stopReplicationMode) + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return StopReplicationAndGetStatusResponse{}, err + } if err := tm.lock(ctx); err != nil { return StopReplicationAndGetStatusResponse{}, err } @@ -833,6 +896,9 @@ type StopReplicationAndGetStatusResponse struct { // PromoteReplica makes the current tablet the primary func (tm *TabletManager) PromoteReplica(ctx context.Context, semiSync bool) (string, error) { log.Infof("PromoteReplica") + if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { + return "", err + } if err := tm.lock(ctx); err != nil { return "", err } @@ -958,3 +1024,13 @@ func (tm *TabletManager) handleRelayLogError(err error) error { } return err } + +// waitForGrantsToHaveApplied wait for the grants to have applied for. +func (tm *TabletManager) waitForGrantsToHaveApplied(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-tm._waitForGrantsComplete: + } + return nil +} diff --git a/go/vt/vttablet/tabletmanager/rpc_replication_test.go b/go/vt/vttablet/tabletmanager/rpc_replication_test.go new file mode 100644 index 00000000000..c587f1e24b8 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/rpc_replication_test.go @@ -0,0 +1,44 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tabletmanager + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// TestWaitForGrantsToHaveApplied tests that waitForGrantsToHaveApplied only succeeds after waitForDBAGrants has been called. +func TestWaitForGrantsToHaveApplied(t *testing.T) { + tm := &TabletManager{ + _waitForGrantsComplete: make(chan struct{}), + } + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + err := tm.waitForGrantsToHaveApplied(ctx) + require.ErrorContains(t, err, "deadline exceeded") + + err = tm.waitForDBAGrants(nil, 0) + require.NoError(t, err) + + secondContext, secondCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer secondCancel() + err = tm.waitForGrantsToHaveApplied(secondContext) + require.NoError(t, err) +} diff --git a/go/vt/vttablet/tabletmanager/rpc_server.go b/go/vt/vttablet/tabletmanager/rpc_server.go index da4d4e0b042..78beed43dad 100644 --- a/go/vt/vttablet/tabletmanager/rpc_server.go +++ b/go/vt/vttablet/tabletmanager/rpc_server.go @@ -70,7 +70,7 @@ func (tm *TabletManager) HandleRPCPanic(ctx context.Context, name string, args, if *err != nil { // error case log.Warningf("TabletManager.%v(%v)(on %v from %v) error: %v", name, args, topoproto.TabletAliasString(tm.tabletAlias), from, (*err).Error()) - *err = vterrors.Wrapf(*err, "TabletManager.%v on %v error: %v", name, topoproto.TabletAliasString(tm.tabletAlias), (*err).Error()) + *err = vterrors.Wrapf(*err, "TabletManager.%v on %v", name, topoproto.TabletAliasString(tm.tabletAlias)) } else { // success case log.Infof("TabletManager.%v(%v)(on %v from %v): %#v", name, args, topoproto.TabletAliasString(tm.tabletAlias), from, reply) diff --git a/go/vt/vttablet/tabletmanager/rpc_throttler.go b/go/vt/vttablet/tabletmanager/rpc_throttler.go index dfdc0d230fb..c961761c5f2 100644 --- a/go/vt/vttablet/tabletmanager/rpc_throttler.go +++ b/go/vt/vttablet/tabletmanager/rpc_throttler.go @@ -19,6 +19,7 @@ package tabletmanager import ( "context" + "vitess.io/vitess/go/stats" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" @@ -28,6 +29,7 @@ import ( // CheckThrottler executes a throttler check func (tm *TabletManager) CheckThrottler(ctx context.Context, req *tabletmanagerdatapb.CheckThrottlerRequest) (*tabletmanagerdatapb.CheckThrottlerResponse, error) { + go stats.GetOrNewCounter("ThrottlerCheckRequest", "CheckThrottler requests").Add(1) if req.AppName == "" { req.AppName = throttlerapp.VitessName.String() } diff --git a/go/vt/vttablet/tabletmanager/rpc_vreplication.go b/go/vt/vttablet/tabletmanager/rpc_vreplication.go index b18caa1063f..ee1907005a8 100644 --- a/go/vt/vttablet/tabletmanager/rpc_vreplication.go +++ b/go/vt/vttablet/tabletmanager/rpc_vreplication.go @@ -23,6 +23,7 @@ import ( "google.golang.org/protobuf/encoding/prototext" "vitess.io/vitess/go/constants/sidecar" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/vt/discovery" @@ -57,6 +58,7 @@ func (tm *TabletManager) CreateVReplicationWorkflow(ctx context.Context, req *ta } res := &sqltypes.Result{} for _, bls := range req.BinlogSource { + protoutil.SortBinlogSourceTables(bls) source, err := prototext.Marshal(bls) if err != nil { return nil, err @@ -328,8 +330,8 @@ func (tm *TabletManager) UpdateVReplicationWorkflow(ctx context.Context, req *ta // VReplicationExec executes a vreplication command. func (tm *TabletManager) VReplicationExec(ctx context.Context, query string) (*querypb.QueryResult, error) { - // Replace any provided sidecar databsae qualifiers with the correct one. - uq, err := sqlparser.ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) + // Replace any provided sidecar database qualifiers with the correct one. + uq, err := tm.Env.Parser().ReplaceTableQualifiers(query, sidecar.DefaultName, sidecar.GetName()) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go index a471750da19..1db4e02b67b 100644 --- a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go +++ b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet" "github.com/stretchr/testify/require" @@ -44,7 +45,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" - "vitess.io/vitess/go/vt/proto/vttime" + vttimepb "vitess.io/vitess/go/vt/proto/vttime" ) const ( @@ -54,11 +55,11 @@ const ( checkForFrozenWorkflow = "select 1 from _vt.vreplication where db_name='vt_%s' and message='FROZEN' and workflow_sub_type != 1" freezeWorkflow = "update _vt.vreplication set message = 'FROZEN' where db_name='vt_%s' and workflow='%s'" checkForJournal = "/select val from _vt.resharding_journal where id=" - getWorkflowStatus = "select id, workflow, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, message, tags, workflow_type, workflow_sub_type, time_heartbeat, defer_secondary_keys, component_throttled, time_throttled, rows_copied from _vt.vreplication where workflow = '%s' and db_name = 'vt_%s'" + getWorkflowStatus = "select id, workflow, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, message, tags, workflow_type, workflow_sub_type, time_heartbeat, defer_secondary_keys, component_throttled, time_throttled, rows_copied, tablet_types, cell from _vt.vreplication where workflow = '%s' and db_name = 'vt_%s'" getWorkflowState = "select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1" getCopyState = "select distinct table_name from _vt.copy_state cs, _vt.vreplication vr where vr.id = cs.vrepl_id and vr.id = 1" getNumCopyStateTable = "select count(distinct table_name) from _vt.copy_state where vrepl_id=1" - getLatestCopyState = "select table_name, lastpk from _vt.copy_state where vrepl_id = 1 and id in (select max(id) from _vt.copy_state where vrepl_id = 1 group by vrepl_id, table_name)" + getLatestCopyState = "select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (1) and id in (select max(id) from _vt.copy_state where vrepl_id in (1) group by vrepl_id, table_name)" getAutoIncrementStep = "select @@session.auto_increment_increment" setSessionTZ = "set @@session.time_zone = '+00:00'" setNames = "set names 'binary'" @@ -111,7 +112,7 @@ func TestCreateVReplicationWorkflow(t *testing.T) { targetTablet := tenv.addTablet(t, targetTabletUID, targetKs, shard) defer tenv.deleteTablet(targetTablet.tablet) - ws := workflow.NewServer(tenv.ts, tenv.tmc) + ws := workflow.NewServer(vtenv.NewTestEnv(), tenv.ts, tenv.tmc) tests := []struct { name string @@ -166,6 +167,86 @@ func TestCreateVReplicationWorkflow(t *testing.T) { query: fmt.Sprintf(`%s values ('%s', 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"t1\" filter:\"select * from t1\"}} on_ddl:EXEC stop_after_copy:true source_time_zone:\"EDT\" target_time_zone:\"UTC\"', '', 0, 0, '%s', '', now(), 0, 'Stopped', '%s', 1, 0, 1)`, insertVReplicationPrefix, wf, sourceKs, shard, tenv.cells[0], tenv.dbName), }, + { + name: "binlog source order with include", + schema: &tabletmanagerdatapb.SchemaDefinition{ + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "zt", + Columns: []string{"id"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id", "int64"), + }, + { + Name: "t1", + Columns: []string{"id", "c2"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id|c2", "int64|int64"), + }, + { + Name: "wut", + Columns: []string{"id"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id", "int64"), + }, + }, + }, + req: &vtctldatapb.MoveTablesCreateRequest{ + SourceKeyspace: sourceKs, + TargetKeyspace: targetKs, + Workflow: wf, + Cells: tenv.cells, + IncludeTables: []string{"zt", "wut", "t1"}, + SourceTimeZone: "EDT", + OnDdl: binlogdatapb.OnDDLAction_EXEC.String(), + StopAfterCopy: true, + DropForeignKeys: true, + DeferSecondaryKeys: true, + AutoStart: true, + }, + query: fmt.Sprintf(`%s values ('%s', 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"t1\" filter:\"select * from t1\"} rules:{match:\"wut\" filter:\"select * from wut\"} rules:{match:\"zt\" filter:\"select * from zt\"}} on_ddl:EXEC stop_after_copy:true source_time_zone:\"EDT\" target_time_zone:\"UTC\"', '', 0, 0, '%s', '', now(), 0, 'Stopped', '%s', 1, 0, 1)`, + insertVReplicationPrefix, wf, sourceKs, shard, tenv.cells[0], tenv.dbName), + }, + { + name: "binlog source order with all-tables", + schema: &tabletmanagerdatapb.SchemaDefinition{ + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "zt", + Columns: []string{"id"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id", "int64"), + }, + { + Name: "t1", + Columns: []string{"id", "c2"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id|c2", "int64|int64"), + }, + { + Name: "wut", + Columns: []string{"id"}, + PrimaryKeyColumns: []string{"id"}, + Fields: sqltypes.MakeTestFields("id", "int64"), + }, + }, + }, + req: &vtctldatapb.MoveTablesCreateRequest{ + SourceKeyspace: sourceKs, + TargetKeyspace: targetKs, + Workflow: wf, + Cells: tenv.cells, + AllTables: true, + SourceTimeZone: "EDT", + OnDdl: binlogdatapb.OnDDLAction_EXEC.String(), + StopAfterCopy: true, + DropForeignKeys: true, + DeferSecondaryKeys: true, + AutoStart: true, + }, + query: fmt.Sprintf(`%s values ('%s', 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"t1\" filter:\"select * from t1\"} rules:{match:\"wut\" filter:\"select * from wut\"} rules:{match:\"zt\" filter:\"select * from zt\"}} on_ddl:EXEC stop_after_copy:true source_time_zone:\"EDT\" target_time_zone:\"UTC\"', '', 0, 0, '%s', '', now(), 0, 'Stopped', '%s', 1, 0, 1)`, + insertVReplicationPrefix, wf, sourceKs, shard, tenv.cells[0], tenv.dbName), + }, } tenv.tmc.setVReplicationExecResults(targetTablet.tablet, fmt.Sprintf("select 1 from _vt.vreplication where db_name='vt_%s' and workflow='%s'", @@ -268,7 +349,7 @@ func TestMoveTables(t *testing.T) { }, }) - ws := workflow.NewServer(tenv.ts, tenv.tmc) + ws := workflow.NewServer(vtenv.NewTestEnv(), tenv.ts, tenv.tmc) tenv.mysqld.Schema = defaultSchema tenv.mysqld.Schema.DatabaseSchema = tenv.dbName @@ -425,7 +506,7 @@ func TestMoveTables(t *testing.T) { Keyspace: targetKs, Workflow: wf, Cells: tenv.cells, - MaxReplicationLagAllowed: &vttime.Duration{Seconds: 922337203}, + MaxReplicationLagAllowed: &vttimepb.Duration{Seconds: 922337203}, EnableReverseReplication: true, InitializeTargetSequences: true, Direction: int32(workflow.DirectionForward), @@ -446,7 +527,7 @@ func TestMoveTables(t *testing.T) { Keyspace: targetKs, Workflow: wf, Cells: tenv.cells, - MaxReplicationLagAllowed: &vttime.Duration{Seconds: 922337203}, + MaxReplicationLagAllowed: &vttimepb.Duration{Seconds: 922337203}, EnableReverseReplication: true, Direction: int32(workflow.DirectionBackward), }) @@ -475,7 +556,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { } selectQuery, err := parsed.GenerateQuery(bindVars, nil) require.NoError(t, err) - blsStr := fmt.Sprintf(`keyspace:"%s" shard:"%s" filter:{rules:{match:"customer" filter:"select * from customer"} rules:{match:"corder" filter:"select * from corder"}}`, + blsStr := fmt.Sprintf(`keyspace:"%s" shard:"%s" filter:{rules:{match:"corder" filter:"select * from corder"} rules:{match:"customer" filter:"select * from customer"}}`, keyspace, shard) selectRes := sqltypes.MakeTestResult( sqltypes.MakeTestFields( @@ -508,7 +589,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { Cells: []string{"zone2"}, // TabletTypes is an empty value, so the current value should be cleared }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}}', cell = '%s', tablet_types = '' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}}', cell = '%s', tablet_types = '' where id in (%d)`, keyspace, shard, "zone2", vreplID), }, { @@ -519,7 +600,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { Cells: []string{"zone3"}, TabletTypes: []topodatapb.TabletType{topodatapb.TabletType(textutil.SimulatedNullInt)}, // So keep the current value of replica }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, keyspace, shard, "zone3", tabletTypes[0], vreplID), }, { @@ -530,7 +611,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { TabletSelectionPreference: tabletmanagerdatapb.TabletSelectionPreference_INORDER, TabletTypes: []topodatapb.TabletType{topodatapb.TabletType_RDONLY, topodatapb.TabletType_REPLICA}, }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}}', cell = '', tablet_types = '%s' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}}', cell = '', tablet_types = '%s' where id in (%d)`, keyspace, shard, "in_order:rdonly,replica", vreplID), }, { @@ -541,7 +622,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { Cells: textutil.SimulatedNullStringSlice, // So keep the current value of zone1 TabletTypes: []topodatapb.TabletType{topodatapb.TabletType_RDONLY}, }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, keyspace, shard, cells[0], "rdonly", vreplID), }, { @@ -551,7 +632,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { State: binlogdatapb.VReplicationWorkflowState(textutil.SimulatedNullInt), OnDdl: binlogdatapb.OnDDLAction_EXEC, }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}} on_ddl:%s', cell = '', tablet_types = '' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}} on_ddl:%s', cell = '', tablet_types = '' where id in (%d)`, keyspace, shard, binlogdatapb.OnDDLAction_EXEC.String(), vreplID), }, { @@ -563,7 +644,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { TabletTypes: []topodatapb.TabletType{topodatapb.TabletType_RDONLY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_PRIMARY}, OnDdl: binlogdatapb.OnDDLAction_EXEC_IGNORE, }, - query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}} on_ddl:%s', cell = '%s', tablet_types = '%s' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = 'Running', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}} on_ddl:%s', cell = '%s', tablet_types = '%s' where id in (%d)`, keyspace, shard, binlogdatapb.OnDDLAction_EXEC_IGNORE.String(), "zone1,zone2,zone3", "rdonly,replica,primary", vreplID), }, { @@ -575,7 +656,7 @@ func TestUpdateVReplicationWorkflow(t *testing.T) { TabletTypes: []topodatapb.TabletType{topodatapb.TabletType(textutil.SimulatedNullInt)}, OnDdl: binlogdatapb.OnDDLAction(textutil.SimulatedNullInt), }, - query: fmt.Sprintf(`update _vt.vreplication set state = '%s', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"customer\" filter:\"select * from customer\"} rules:{match:\"corder\" filter:\"select * from corder\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, + query: fmt.Sprintf(`update _vt.vreplication set state = '%s', source = 'keyspace:\"%s\" shard:\"%s\" filter:{rules:{match:\"corder\" filter:\"select * from corder\"} rules:{match:\"customer\" filter:\"select * from customer\"}}', cell = '%s', tablet_types = '%s' where id in (%d)`, binlogdatapb.VReplicationWorkflowState_Stopped.String(), keyspace, shard, cells[0], tabletTypes[0], vreplID), }, } @@ -656,7 +737,7 @@ func TestSourceShardSelection(t *testing.T) { defer tenv.deleteTablet(tt.tablet) } - ws := workflow.NewServer(tenv.ts, tenv.tmc) + ws := workflow.NewServer(vtenv.NewTestEnv(), tenv.ts, tenv.tmc) tenv.ts.SaveVSchema(ctx, sourceKs, &vschemapb.Keyspace{ Sharded: true, @@ -855,7 +936,7 @@ func TestFailedMoveTablesCreateCleanup(t *testing.T) { sourceKs, shard, table, table) tenv := newTestEnv(t, ctx, sourceKs, []string{shard}) defer tenv.close() - ws := workflow.NewServer(tenv.ts, tenv.tmc) + ws := workflow.NewServer(vtenv.NewTestEnv(), tenv.ts, tenv.tmc) sourceTablet := tenv.addTablet(t, sourceTabletUID, sourceKs, shard) defer tenv.deleteTablet(sourceTablet.tablet) @@ -993,7 +1074,7 @@ func TestFailedMoveTablesCreateCleanup(t *testing.T) { ) // We expect the workflow creation to fail due to the invalid time - // zone and thus the workflow iteslf to be cleaned up. + // zone and thus the workflow itself to be cleaned up. tenv.tmc.setVReplicationExecResults(sourceTablet.tablet, fmt.Sprintf(deleteWorkflow, sourceKs, workflow.ReverseWorkflowName(wf)), &sqltypes.Result{RowsAffected: 1}, diff --git a/go/vt/vttablet/tabletmanager/tm_init.go b/go/vt/vttablet/tabletmanager/tm_init.go index 2cd21c09a21..c4ad332a470 100644 --- a/go/vt/vttablet/tabletmanager/tm_init.go +++ b/go/vt/vttablet/tabletmanager/tm_init.go @@ -36,6 +36,7 @@ package tabletmanager import ( "context" "encoding/hex" + "errors" "fmt" "math/rand" "regexp" @@ -67,14 +68,19 @@ import ( "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtctl/reparentutil" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vdiff" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" "vitess.io/vitess/go/vt/vttablet/tabletserver" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) -// Query rules from denylist -const denyListQueryList string = "DenyListQueryRules" +const ( + // Query rules from denylist + denyListQueryList string = "DenyListQueryRules" + dbaGrantWaitTime = 10 * time.Second +) var ( // The following flags initialize the tablet record. @@ -86,8 +92,8 @@ var ( skipBuildInfoTags = "/.*/" initTags flagutil.StringMapValue - initPopulateMetadata bool initTimeout = 1 * time.Minute + mysqlShutdownTimeout = 5 * time.Minute ) func registerInitFlags(fs *pflag.FlagSet) { @@ -99,6 +105,7 @@ func registerInitFlags(fs *pflag.FlagSet) { fs.StringVar(&skipBuildInfoTags, "vttablet_skip_buildinfo_tags", skipBuildInfoTags, "comma-separated list of buildinfo tags to skip from merging with --init_tags. each tag is either an exact match or a regular expression of the form '/regexp/'.") fs.Var(&initTags, "init_tags", "(init parameter) comma separated list of key:value pairs used to tag the tablet") fs.DurationVar(&initTimeout, "init_timeout", initTimeout, "(init parameter) timeout to use for the init phase.") + fs.DurationVar(&mysqlShutdownTimeout, "mysql-shutdown-timeout", mysqlShutdownTimeout, "timeout to use when MySQL is being shut down.") } var ( @@ -149,6 +156,7 @@ type TabletManager struct { UpdateStream binlog.UpdateStreamControl VREngine *vreplication.Engine VDiffEngine *vdiff.Engine + Env *vtenv.Environment // tmState manages the TabletManager state. tmState *tmState @@ -170,6 +178,10 @@ type TabletManager struct { // only hold the mutex to update the fields, nothing else. mutex sync.Mutex + // _waitForGrantsComplete is a channel for waiting until the grants for all the mysql + // users have been verified. + _waitForGrantsComplete chan struct{} + // _shardSyncChan is a channel for informing the shard sync goroutine that // it should wake up and recheck the tablet state, to make sure it and the // shard record are in sync. @@ -200,7 +212,7 @@ type TabletManager struct { } // BuildTabletFromInput builds a tablet record from input parameters. -func BuildTabletFromInput(alias *topodatapb.TabletAlias, port, grpcPort int32, db *dbconfigs.DBConfigs) (*topodatapb.Tablet, error) { +func BuildTabletFromInput(alias *topodatapb.TabletAlias, port, grpcPort int32, db *dbconfigs.DBConfigs, collationEnv *collations.Environment) (*topodatapb.Tablet, error) { hostname := tabletHostname if hostname == "" { var err error @@ -238,14 +250,14 @@ func BuildTabletFromInput(alias *topodatapb.TabletAlias, port, grpcPort int32, d return nil, err } - var charset uint8 + var charset collations.ID if db != nil && db.Charset != "" { - charset, err = collations.Local().ParseConnectionCharset(db.Charset) + charset, err = collationEnv.ParseConnectionCharset(db.Charset) if err != nil { return nil, err } } else { - charset = collations.Local().DefaultConnectionCharset() + charset = collationEnv.DefaultConnectionCharset() } return &topodatapb.Tablet{ @@ -333,7 +345,7 @@ func mergeTags(a, b map[string]string) map[string]string { } // Start starts the TabletManager. -func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval time.Duration) error { +func (tm *TabletManager) Start(tablet *topodatapb.Tablet, config *tabletenv.TabletConfig) error { defer func() { log.Infof("TabletManager Start took ~%d ms", time.Since(servenv.GetInitStartTime()).Milliseconds()) }() @@ -342,6 +354,7 @@ func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval ti tm.tabletAlias = tablet.Alias tm.tmState = newTMState(tm, tablet) tm.actionSema = semaphore.NewWeighted(1) + tm._waitForGrantsComplete = make(chan struct{}) tm.baseTabletType = tablet.Type @@ -354,7 +367,7 @@ func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval ti if err := tm.checkPrimaryShip(ctx, si); err != nil { return err } - if err := tm.checkMysql(ctx); err != nil { + if err := tm.checkMysql(); err != nil { return err } if err := tm.initTablet(ctx); err != nil { @@ -393,7 +406,7 @@ func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval ti tm.exportStats() servenv.OnRun(tm.registerTabletManager) - restoring, err := tm.handleRestore(tm.BatchCtx) + restoring, err := tm.handleRestore(tm.BatchCtx, config) if err != nil { return err } @@ -406,8 +419,17 @@ func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval ti // We shouldn't use the base tablet type directly, since the type could have changed to PRIMARY // earlier in tm.checkPrimaryShip code. _, err = tm.initializeReplication(ctx, tm.Tablet().Type) + if err != nil { + return err + } + + // Make sure we have the correct privileges for the DBA user before we start the state manager. + err = tm.waitForDBAGrants(config, dbaGrantWaitTime) + if err != nil { + return err + } tm.tmState.Open() - return err + return nil } // Close prepares a tablet for shutdown. First we check our tablet ownership and @@ -444,7 +466,7 @@ func (tm *TabletManager) Close() { // Stop shuts down the tm. Normally this is not necessary, since we use // servenv OnTerm and OnClose hooks to coordinate shutdown automatically, // while taking lameduck into account. However, this may be useful for tests, -// when you want to clean up an tm immediately. +// when you want to clean up a tm immediately. func (tm *TabletManager) Stop() { // Stop the shard sync loop and wait for it to exit. This needs to be done // here in addition to in Close() because tests do not call Close(). @@ -536,7 +558,7 @@ func (tm *TabletManager) createKeyspaceShard(ctx context.Context) (*topo.ShardIn tm._rebuildKeyspaceDone = make(chan struct{}) go tm.rebuildKeyspace(rebuildKsCtx, tm._rebuildKeyspaceDone, tablet.Keyspace, rebuildKeyspaceRetryInterval) default: - return nil, vterrors.Wrap(err, "initeKeyspaceShardTopo: failed to read SrvKeyspace") + return nil, vterrors.Wrap(err, "initKeyspaceShardTopo: failed to read SrvKeyspace") } // Rebuild vschema graph if this is the first tablet in this keyspace/cell. @@ -546,16 +568,16 @@ func (tm *TabletManager) createKeyspaceShard(ctx context.Context) (*topo.ShardIn // Check if vschema was rebuilt after the initial creation of the keyspace. if _, keyspaceExists := srvVSchema.GetKeyspaces()[tablet.Keyspace]; !keyspaceExists { if err := tm.TopoServer.RebuildSrvVSchema(ctx, []string{tm.tabletAlias.Cell}); err != nil { - return nil, vterrors.Wrap(err, "initeKeyspaceShardTopo: failed to RebuildSrvVSchema") + return nil, vterrors.Wrap(err, "initKeyspaceShardTopo: failed to RebuildSrvVSchema") } } case topo.IsErrType(err, topo.NoNode): // There is no SrvSchema in this cell at all, so we definitely need to rebuild. if err := tm.TopoServer.RebuildSrvVSchema(ctx, []string{tm.tabletAlias.Cell}); err != nil { - return nil, vterrors.Wrap(err, "initeKeyspaceShardTopo: failed to RebuildSrvVSchema") + return nil, vterrors.Wrap(err, "initKeyspaceShardTopo: failed to RebuildSrvVSchema") } default: - return nil, vterrors.Wrap(err, "initeKeyspaceShardTopo: failed to read SrvVSchema") + return nil, vterrors.Wrap(err, "initKeyspaceShardTopo: failed to read SrvVSchema") } return shardInfo, nil } @@ -681,7 +703,7 @@ func (tm *TabletManager) checkPrimaryShip(ctx context.Context, si *topo.ShardInf return nil } -func (tm *TabletManager) checkMysql(ctx context.Context) error { +func (tm *TabletManager) checkMysql() error { appConfig, err := tm.DBConfigs.AppWithDB().MysqlParams() if err != nil { return err @@ -762,7 +784,7 @@ func (tm *TabletManager) initTablet(ctx context.Context) error { return nil } -func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { +func (tm *TabletManager) handleRestore(ctx context.Context, config *tabletenv.TabletConfig) (bool, error) { // Sanity check for inconsistent flags if tm.Cnf == nil && restoreFromBackup { return false, fmt.Errorf("you cannot enable --restore_from_backup without a my.cnf file") @@ -774,9 +796,6 @@ func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { // Restore in the background if restoreFromBackup { go func() { - // Open the state manager after restore is done. - defer tm.tmState.Open() - // Zero date will cause us to use the latest, which is the default backupTime := time.Time{} // Or if a backup timestamp was specified then we use the last backup taken at or before that time @@ -798,9 +817,18 @@ func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { } // restoreFromBackup will just be a regular action // (same as if it was triggered remotely) - if err := tm.RestoreData(ctx, logutil.NewConsoleLogger(), waitForBackupInterval, false /* deleteBeforeRestore */, backupTime, restoreToTimestamp, restoreToPos); err != nil { + if err := tm.RestoreData(ctx, logutil.NewConsoleLogger(), waitForBackupInterval, false /* deleteBeforeRestore */, backupTime, restoreToTimestamp, restoreToPos, mysqlShutdownTimeout); err != nil { log.Exitf("RestoreFromBackup failed: %v", err) } + + // Make sure we have the correct privileges for the DBA user before we start the state manager. + err := tm.waitForDBAGrants(config, dbaGrantWaitTime) + if err != nil { + log.Exitf("Failed waiting for DBA grants: %v", err) + } + + // Open the state manager after restore is done. + tm.tmState.Open() }() return true, nil } @@ -808,6 +836,48 @@ func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { return false, nil } +// waitForDBAGrants waits for DBA user to have the required privileges to function properly. +func (tm *TabletManager) waitForDBAGrants(config *tabletenv.TabletConfig, waitTime time.Duration) (err error) { + // We should close the _waitForGrantsComplete channel in the end to signify that the wait for dba grants has completed. + defer func() { + if err == nil { + close(tm._waitForGrantsComplete) + } + }() + // We don't wait for grants if the tablet is externally managed. Permissions + // are then the responsibility of the DBA. + if config == nil || config.DB.HasGlobalSettings() || waitTime == 0 { + return nil + } + timer := time.NewTimer(waitTime) + ctx, cancel := context.WithTimeout(context.Background(), waitTime) + defer cancel() + for { + conn, connErr := dbconnpool.NewDBConnection(ctx, config.DB.DbaConnector()) + if connErr == nil { + res, fetchErr := conn.ExecuteFetch("SHOW GRANTS", 1000, false) + conn.Close() + if fetchErr != nil { + log.Errorf("Error running SHOW GRANTS - %v", fetchErr) + } + if fetchErr == nil && res != nil && len(res.Rows) > 0 && len(res.Rows[0]) > 0 { + privileges := res.Rows[0][0].ToString() + // In MySQL 8.0, all the privileges are listed out explicitly, so we can search for SUPER in the output. + // In MySQL 5.7, all the privileges are not listed explicitly, instead ALL PRIVILEGES is written, so we search for that too. + if strings.Contains(privileges, "SUPER") || strings.Contains(privileges, "ALL PRIVILEGES") { + return nil + } + } + } + select { + case <-timer.C: + return fmt.Errorf("timed out after %v waiting for the dba user to have the required permissions", waitTime) + default: + time.Sleep(100 * time.Millisecond) + } + } +} + func (tm *TabletManager) exportStats() { tablet := tm.Tablet() statsKeyspace.Set(tablet.Keyspace) @@ -830,7 +900,7 @@ func (tm *TabletManager) withRetry(ctx context.Context, description string, work backoff := 1 * time.Second for { err := work() - if err == nil || err == context.Canceled || err == context.DeadlineExceeded { + if err == nil || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return err } diff --git a/go/vt/vttablet/tabletmanager/tm_init_test.go b/go/vt/vttablet/tabletmanager/tm_init_test.go index 148042bd6b1..c44bb846eb3 100644 --- a/go/vt/vttablet/tabletmanager/tm_init_test.go +++ b/go/vt/vttablet/tabletmanager/tm_init_test.go @@ -34,11 +34,14 @@ import ( "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl" + vttestpb "vitess.io/vitess/go/vt/proto/vttest" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletservermock" + "vitess.io/vitess/go/vt/vttest" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" @@ -71,16 +74,16 @@ func TestStartBuildTabletFromInput(t *testing.T) { Type: topodatapb.TabletType_REPLICA, Tags: map[string]string{}, DbNameOverride: "aa", - DefaultConnCollation: uint32(collations.Default()), + DefaultConnCollation: uint32(collations.MySQL8().DefaultConnectionCharset()), } - gotTablet, err := BuildTabletFromInput(alias, port, grpcport, nil) + gotTablet, err := BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) require.NoError(t, err) // Hostname should be resolved. assert.Equal(t, wantTablet, gotTablet) tabletHostname = "" - gotTablet, err = BuildTabletFromInput(alias, port, grpcport, nil) + gotTablet, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) require.NoError(t, err) assert.NotEqual(t, "", gotTablet.Hostname) @@ -92,7 +95,7 @@ func TestStartBuildTabletFromInput(t *testing.T) { Start: []byte(""), End: []byte("\xc0"), } - gotTablet, err = BuildTabletFromInput(alias, port, grpcport, nil) + gotTablet, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) require.NoError(t, err) // KeyRange check is explicit because the next comparison doesn't // show the diff well enough. @@ -102,25 +105,25 @@ func TestStartBuildTabletFromInput(t *testing.T) { // Invalid inputs. initKeyspace = "" initShard = "0" - _, err = BuildTabletFromInput(alias, port, grpcport, nil) + _, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) assert.Contains(t, err.Error(), "init_keyspace and init_shard must be specified") initKeyspace = "test_keyspace" initShard = "" - _, err = BuildTabletFromInput(alias, port, grpcport, nil) + _, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) assert.Contains(t, err.Error(), "init_keyspace and init_shard must be specified") initShard = "x-y" - _, err = BuildTabletFromInput(alias, port, grpcport, nil) + _, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) assert.Contains(t, err.Error(), "cannot validate shard name") initShard = "0" initTabletType = "bad" - _, err = BuildTabletFromInput(alias, port, grpcport, nil) + _, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) assert.Contains(t, err.Error(), "unknown TabletType bad") initTabletType = "primary" - _, err = BuildTabletFromInput(alias, port, grpcport, nil) + _, err = BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) assert.Contains(t, err.Error(), "invalid init_tablet_type PRIMARY") } @@ -153,10 +156,10 @@ func TestBuildTabletFromInputWithBuildTags(t *testing.T) { Type: topodatapb.TabletType_REPLICA, Tags: servenv.AppVersion.ToStringMap(), DbNameOverride: "aa", - DefaultConnCollation: uint32(collations.Default()), + DefaultConnCollation: uint32(collations.MySQL8().DefaultConnectionCharset()), } - gotTablet, err := BuildTabletFromInput(alias, port, grpcport, nil) + gotTablet, err := BuildTabletFromInput(alias, port, grpcport, nil, collations.MySQL8()) require.NoError(t, err) assert.Equal(t, wantTablet, gotTablet) } @@ -282,7 +285,7 @@ func TestCheckPrimaryShip(t *testing.T) { return nil }) require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -297,7 +300,7 @@ func TestCheckPrimaryShip(t *testing.T) { // correct and start as PRIMARY. err = ts.DeleteTablet(ctx, alias) require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -311,7 +314,7 @@ func TestCheckPrimaryShip(t *testing.T) { ti.Type = topodatapb.TabletType_PRIMARY err = ts.UpdateTablet(ctx, ti) require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -321,7 +324,7 @@ func TestCheckPrimaryShip(t *testing.T) { tm.Stop() // 5. Subsequent inits will still start the vttablet as PRIMARY. - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -353,7 +356,7 @@ func TestCheckPrimaryShip(t *testing.T) { return nil }) require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -380,7 +383,7 @@ func TestCheckPrimaryShip(t *testing.T) { "FAKE SET MASTER", "START SLAVE", } - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) ti, err = ts.GetTablet(ctx, alias) require.NoError(t, err) @@ -407,7 +410,7 @@ func TestStartCheckMysql(t *testing.T) { DBConfigs: dbconfigs.NewTestDBConfigs(cp, cp, ""), QueryServiceControl: tabletservermock.NewController(), } - err := tm.Start(tablet, 0) + err := tm.Start(tablet, nil) require.NoError(t, err) defer tm.Stop() @@ -435,7 +438,7 @@ func TestStartFindMysqlPort(t *testing.T) { DBConfigs: &dbconfigs.DBConfigs{}, QueryServiceControl: tabletservermock.NewController(), } - err := tm.Start(tablet, 0) + err := tm.Start(tablet, nil) require.NoError(t, err) defer tm.Stop() @@ -511,7 +514,7 @@ func TestStartDoesNotUpdateReplicationDataForTabletInWrongShard(t *testing.T) { tablet := newTestTablet(t, 1, "ks", "-d0") require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) assert.Contains(t, err.Error(), "existing tablet keyspace and shard ks/0 differ") tablets, err := ts.FindAllTabletAliasesInShard(ctx, "ks", "-d0") @@ -548,7 +551,7 @@ func TestCheckTabletTypeResets(t *testing.T) { return nil }) require.NoError(t, err) - err = tm.Start(tablet, 0) + err = tm.Start(tablet, nil) require.NoError(t, err) assert.Equal(t, tm.tmState.tablet.Type, tm.tmState.displayState.tablet.Type) ti, err = ts.GetTablet(ctx, alias) @@ -630,7 +633,6 @@ func TestGetBuildTags(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.skipCSV, func(t *testing.T) { t.Parallel() @@ -671,7 +673,7 @@ func newTestTM(t *testing.T, ts *topo.Server, uid int, keyspace, shard string) * DBConfigs: &dbconfigs.DBConfigs{}, QueryServiceControl: tabletservermock.NewController(), } - err := tm.Start(tablet, 0) + err := tm.Start(tablet, nil) require.NoError(t, err) // Wait for SrvKeyspace to be rebuilt. We know that it has been built @@ -733,3 +735,193 @@ func ensureSrvKeyspace(t *testing.T, ctx context.Context, ts *topo.Server, cell, } assert.True(t, found) } + +func TestWaitForDBAGrants(t *testing.T) { + tests := []struct { + name string + waitTime time.Duration + errWanted string + setupFunc func(t *testing.T) (*tabletenv.TabletConfig, func()) + }{ + { + name: "Success without any wait", + waitTime: 1 * time.Second, + errWanted: "", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + // Create a new mysql instance, and the dba user with required grants. + // Since all the grants already exist, this should pass without any waiting to be needed. + testUser := "vt_test_dba" + cluster, err := startMySQLAndCreateUser(t, testUser) + require.NoError(t, err) + grantAllPrivilegesToUser(t, cluster.MySQLConnParams(), testUser) + tc := &tabletenv.TabletConfig{ + DB: &dbconfigs.DBConfigs{}, + } + connParams := cluster.MySQLConnParams() + connParams.Uname = testUser + tc.DB.SetDbParams(connParams, mysql.ConnParams{}, mysql.ConnParams{}) + return tc, func() { + cluster.TearDown() + } + }, + }, + { + name: "Success with wait", + waitTime: 1 * time.Second, + errWanted: "", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + // Create a new mysql instance, but delay granting the privileges to the dba user. + // This makes the waitForDBAGrants function retry the grant check. + testUser := "vt_test_dba" + cluster, err := startMySQLAndCreateUser(t, testUser) + require.NoError(t, err) + + go func() { + time.Sleep(500 * time.Millisecond) + grantAllPrivilegesToUser(t, cluster.MySQLConnParams(), testUser) + }() + + tc := &tabletenv.TabletConfig{ + DB: &dbconfigs.DBConfigs{}, + } + connParams := cluster.MySQLConnParams() + connParams.Uname = testUser + tc.DB.SetDbParams(connParams, mysql.ConnParams{}, mysql.ConnParams{}) + return tc, func() { + cluster.TearDown() + } + }, + }, { + name: "Failure due to timeout", + waitTime: 300 * time.Millisecond, + errWanted: "timed out after 300ms waiting for the dba user to have the required permissions", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + // Create a new mysql but don't give the grants to the vt_dba user at all. + // This should cause a timeout after waiting, since the privileges are never granted. + testUser := "vt_test_dba" + cluster, err := startMySQLAndCreateUser(t, testUser) + require.NoError(t, err) + + tc := &tabletenv.TabletConfig{ + DB: &dbconfigs.DBConfigs{}, + } + connParams := cluster.MySQLConnParams() + connParams.Uname = testUser + tc.DB.SetDbParams(connParams, mysql.ConnParams{}, mysql.ConnParams{}) + return tc, func() { + cluster.TearDown() + } + }, + }, { + name: "Success for externally managed tablet", + waitTime: 300 * time.Millisecond, + errWanted: "", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + // Create a new mysql but don't give the grants to the vt_dba user at all. + // This should cause a timeout after waiting, since the privileges are never granted. + testUser := "vt_test_dba" + cluster, err := startMySQLAndCreateUser(t, testUser) + require.NoError(t, err) + + tc := &tabletenv.TabletConfig{ + DB: &dbconfigs.DBConfigs{ + Host: "some.unknown.host", + }, + } + connParams := cluster.MySQLConnParams() + connParams.Uname = testUser + tc.DB.SetDbParams(connParams, mysql.ConnParams{}, mysql.ConnParams{}) + return tc, func() { + cluster.TearDown() + } + }, + }, { + name: "Empty timeout", + waitTime: 0, + errWanted: "", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + tc := &tabletenv.TabletConfig{ + DB: &dbconfigs.DBConfigs{}, + } + return tc, func() {} + }, + }, { + name: "Empty config", + waitTime: 300 * time.Millisecond, + errWanted: "", + setupFunc: func(t *testing.T) (*tabletenv.TabletConfig, func()) { + return nil, func() {} + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, cleanup := tt.setupFunc(t) + defer cleanup() + tm := TabletManager{ + _waitForGrantsComplete: make(chan struct{}), + } + err := tm.waitForDBAGrants(config, tt.waitTime) + if tt.errWanted == "" { + require.NoError(t, err) + // Verify the channel has been closed. + _, isOpen := <-tm._waitForGrantsComplete + require.False(t, isOpen) + } else { + require.EqualError(t, err, tt.errWanted) + } + }) + } +} + +// startMySQLAndCreateUser starts a MySQL instance and creates the given user +func startMySQLAndCreateUser(t *testing.T, testUser string) (vttest.LocalCluster, error) { + // Launch MySQL. + // We need a Keyspace in the topology, so the DbName is set. + // We need a Shard too, so the database 'vttest' is created. + cfg := vttest.Config{ + Topology: &vttestpb.VTTestTopology{ + Keyspaces: []*vttestpb.Keyspace{ + { + Name: "vttest", + Shards: []*vttestpb.Shard{ + { + Name: "0", + DbNameOverride: "vttest", + }, + }, + }, + }, + }, + OnlyMySQL: true, + Charset: "utf8mb4", + } + cluster := vttest.LocalCluster{ + Config: cfg, + } + err := cluster.Setup() + if err != nil { + return cluster, nil + } + + connParams := cluster.MySQLConnParams() + conn, err := mysql.Connect(context.Background(), &connParams) + require.NoError(t, err) + _, err = conn.ExecuteFetch(fmt.Sprintf(`CREATE USER '%v'@'localhost'`, testUser), 1000, false) + conn.Close() + + return cluster, err +} + +// grantAllPrivilegesToUser grants all the privileges to the user specified. +func grantAllPrivilegesToUser(t *testing.T, connParams mysql.ConnParams, testUser string) { + conn, err := mysql.Connect(context.Background(), &connParams) + require.NoError(t, err) + _, err = conn.ExecuteFetch(fmt.Sprintf(`GRANT ALL ON *.* TO '%v'@'localhost'`, testUser), 1000, false) + require.NoError(t, err) + _, err = conn.ExecuteFetch(fmt.Sprintf(`GRANT GRANT OPTION ON *.* TO '%v'@'localhost'`, testUser), 1000, false) + require.NoError(t, err) + _, err = conn.ExecuteFetch("FLUSH PRIVILEGES", 1000, false) + require.NoError(t, err) + conn.Close() +} diff --git a/go/vt/vttablet/tabletmanager/vdiff/action.go b/go/vt/vttablet/tabletmanager/vdiff/action.go index 59ee79077f7..0b9dd6f45ed 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/action.go +++ b/go/vt/vttablet/tabletmanager/vdiff/action.go @@ -63,7 +63,15 @@ var ( } ) -func (vde *Engine) PerformVDiffAction(ctx context.Context, req *tabletmanagerdatapb.VDiffRequest) (*tabletmanagerdatapb.VDiffResponse, error) { +func (vde *Engine) PerformVDiffAction(ctx context.Context, req *tabletmanagerdatapb.VDiffRequest) (resp *tabletmanagerdatapb.VDiffResponse, err error) { + defer func() { + if err != nil { + globalStats.ErrorCount.Add(1) + } + }() + if req == nil { + return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "nil vdiff request") + } if !vde.isOpen { return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "vdiff engine is closed") } @@ -71,7 +79,7 @@ func (vde *Engine) PerformVDiffAction(ctx context.Context, req *tabletmanagerdat return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "vdiff engine is still trying to open") } - resp := &tabletmanagerdatapb.VDiffResponse{ + resp = &tabletmanagerdatapb.VDiffResponse{ Id: 0, Output: nil, } @@ -232,9 +240,6 @@ func (vde *Engine) handleCreateResumeAction(ctx context.Context, dbClient binlog if qr.RowsAffected == 0 { msg := fmt.Sprintf("no completed or stopped vdiff found for UUID %s on tablet %v", req.VdiffUuid, vde.thisTablet.Alias) - if err != nil { - msg = fmt.Sprintf("%s (%v)", msg, err) - } return fmt.Errorf(msg) } } @@ -371,6 +376,9 @@ func (vde *Engine) handleDeleteAction(ctx context.Context, dbClient binlogplayer } controller.Stop() delete(vde.controllers, controller.id) + globalStats.mu.Lock() + defer globalStats.mu.Unlock() + delete(globalStats.controllers, controller.id) } switch req.ActionArg { diff --git a/go/vt/vttablet/tabletmanager/vdiff/action_test.go b/go/vt/vttablet/tabletmanager/vdiff/action_test.go index 1049bc8607d..4676238cf69 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/action_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/action_test.go @@ -56,8 +56,13 @@ func TestPerformVDiffAction(t *testing.T) { expectQueries []queryAndResult wantErr error }{ + { + name: "nil request", + wantErr: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "nil vdiff request"), + }, { name: "engine not open", + req: &tabletmanagerdatapb.VDiffRequest{}, vde: &Engine{isOpen: false}, wantErr: vterrors.New(vtrpcpb.Code_UNAVAILABLE, "vdiff engine is closed"), }, @@ -208,6 +213,7 @@ func TestPerformVDiffAction(t *testing.T) { }, }, } + errCount := int64(0) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.preFunc != nil { @@ -224,6 +230,9 @@ func TestPerformVDiffAction(t *testing.T) { vdiffenv.dbClient.ExpectRequest(queryResult.query, queryResult.result, nil) } got, err := tt.vde.PerformVDiffAction(ctx, tt.req) + if err != nil { + errCount++ + } vdiffenv.dbClient.Wait() if tt.wantErr != nil && !vterrors.Equals(err, tt.wantErr) { t.Errorf("Engine.PerformVDiffAction() error = %v, wantErr %v", err, tt.wantErr) @@ -239,6 +248,8 @@ func TestPerformVDiffAction(t *testing.T) { // No VDiffs should be running anymore. require.Equal(t, 0, len(vdiffenv.vde.controllers), "expected no controllers to be running, but found %d", len(vdiffenv.vde.controllers)) + require.Equal(t, int64(0), globalStats.numControllers(), "expected no controllers, but found %d") }) + require.Equal(t, errCount, globalStats.ErrorCount.Get(), "expected error count %d, got %d", errCount, globalStats.ErrorCount.Get()) } } diff --git a/go/vt/vttablet/tabletmanager/vdiff/controller.go b/go/vt/vttablet/tabletmanager/vdiff/controller.go index 22b1d3f5374..0265e8a0a35 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/controller.go +++ b/go/vt/vttablet/tabletmanager/vdiff/controller.go @@ -27,6 +27,7 @@ import ( "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -39,10 +40,8 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -/* -vdiff operation states: pending/started/stopped/completed/error/unknown -vdiff table states: pending/started/stopped/completed/error/unknown -*/ +// VDiff operation and table states: +// pending/started/stopped/completed/error/unknown type VDiffState string //nolint const ( PendingState VDiffState = "pending" @@ -55,28 +54,33 @@ const ( ) type controller struct { - id int64 // id from row in _vt.vdiff + id int64 // id from the row in _vt.vdiff uuid string workflow string workflowType binlogdatapb.VReplicationWorkflowType cancel context.CancelFunc dbClientFactory func() binlogplayer.DBClient ts *topo.Server - vde *Engine // the singleton vdiff engine + vde *Engine // The singleton vdiff engine done chan struct{} - sources map[string]*migrationSource // currently picked source tablets for this shard's data + sources map[string]*migrationSource // Currently picked source tablets for this shard's data workflowFilter string sourceKeyspace string tmc tmclient.TabletManagerClient targetShardStreamer *shardStreamer - filter *binlogdatapb.Filter // vreplication row filter - options *tabletmanagerdata.VDiffOptions // options initially from vtctld command and later from _vt.vdiff + filter *binlogdatapb.Filter // VReplication row filter + options *tabletmanagerdata.VDiffOptions // Options initially from vtctld command and later from _vt.vdiff + + sourceTimeZone, targetTimeZone string // Named time zones if conversions are necessary for datetime values - sourceTimeZone, targetTimeZone string // named time zones if conversions are necessary for datetime values + externalCluster string // For Mount+Migrate - externalCluster string // for Mount+Migrate + // Information used in vdiff stats/metrics. + Errors *stats.CountersWithMultiLabels + TableDiffRowCounts *stats.CountersWithMultiLabels + TableDiffPhaseTimings *stats.Timings } func newController(ctx context.Context, row sqltypes.RowNamedValues, dbClientFactory func() binlogplayer.DBClient, @@ -86,16 +90,19 @@ func newController(ctx context.Context, row sqltypes.RowNamedValues, dbClientFac id, _ := row["id"].ToInt64() ct := &controller{ - id: id, - uuid: row["vdiff_uuid"].ToString(), - workflow: row["workflow"].ToString(), - dbClientFactory: dbClientFactory, - ts: ts, - vde: vde, - done: make(chan struct{}), - tmc: vde.tmClientFactory(), - sources: make(map[string]*migrationSource), - options: options, + id: id, + uuid: row["vdiff_uuid"].ToString(), + workflow: row["workflow"].ToString(), + dbClientFactory: dbClientFactory, + ts: ts, + vde: vde, + done: make(chan struct{}), + tmc: vde.tmClientFactory(), + sources: make(map[string]*migrationSource), + options: options, + Errors: stats.NewCountersWithMultiLabels("", "", []string{"Error"}), + TableDiffRowCounts: stats.NewCountersWithMultiLabels("", "", []string{"Rows"}), + TableDiffPhaseTimings: stats.NewTimings("", "", "", "TablePhase"), } ctx, ct.cancel = context.WithCancel(ctx) go ct.run(ctx) @@ -185,7 +192,7 @@ func (ct *controller) start(ctx context.Context, dbClient binlogplayer.DBClient) case <-ctx.Done(): return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") case <-ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser default: } ct.workflowFilter = fmt.Sprintf("where workflow = %s and db_name = %s", encodeString(ct.workflow), @@ -201,7 +208,7 @@ func (ct *controller) start(ctx context.Context, dbClient binlogplayer.DBClient) case <-ctx.Done(): return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") case <-ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser default: } source := newMigrationSource() @@ -240,7 +247,7 @@ func (ct *controller) start(ctx context.Context, dbClient binlogplayer.DBClient) return err } - wd, err := newWorkflowDiffer(ct, ct.options) + wd, err := newWorkflowDiffer(ct, ct.options, ct.vde.collationEnv) if err != nil { return err } @@ -328,7 +335,7 @@ func (ct *controller) saveErrorState(ctx context.Context, saveErr error) error { case <-ctx.Done(): return vterrors.Errorf(vtrpcpb.Code_CANCELED, "engine is shutting down") case <-ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser case <-time.After(retryDelay): if retryDelay < maxRetryDelay { retryDelay = time.Duration(float64(retryDelay) * 1.5) diff --git a/go/vt/vttablet/tabletmanager/vdiff/engine.go b/go/vt/vttablet/tabletmanager/vdiff/engine.go index 72098eb52be..b2285a070fa 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/engine.go +++ b/go/vt/vttablet/tabletmanager/vdiff/engine.go @@ -24,19 +24,18 @@ import ( "sync" "time" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" - "vitess.io/vitess/go/vt/vttablet/tmclient" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" + "vitess.io/vitess/go/vt/vttablet/tmclient" ) type Engine struct { @@ -69,14 +68,19 @@ type Engine struct { // modified behavior for that env, e.g. not starting the retry goroutine. This should // NOT be set in production. fortests bool + + collationEnv *collations.Environment + parser *sqlparser.Parser } -func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, tablet *topodata.Tablet) *Engine { +func NewEngine(ts *topo.Server, tablet *topodata.Tablet, collationEnv *collations.Environment, parser *sqlparser.Parser) *Engine { vde := &Engine{ controllers: make(map[int64]*controller), ts: ts, thisTablet: tablet, tmClientFactory: func() tmclient.TabletManagerClient { return tmclient.NewTabletManagerClient() }, + collationEnv: collationEnv, + parser: parser, } return vde } @@ -94,20 +98,22 @@ func NewTestEngine(ts *topo.Server, tablet *topodata.Tablet, dbn string, dbcf fu dbClientFactoryDba: dbcf, tmClientFactory: tmcf, fortests: true, + collationEnv: collations.MySQL8(), + parser: sqlparser.NewTestParser(), } return vde } func (vde *Engine) InitDBConfig(dbcfgs *dbconfigs.DBConfigs) { - // If it's a test engine and we're already initilized then do nothing. + // If it's a test engine and we're already initialized then do nothing. if vde.fortests && vde.dbClientFactoryFiltered != nil && vde.dbClientFactoryDba != nil { return } vde.dbClientFactoryFiltered = func() binlogplayer.DBClient { - return binlogplayer.NewDBClient(dbcfgs.FilteredWithDB()) + return binlogplayer.NewDBClient(dbcfgs.FilteredWithDB(), vde.parser) } vde.dbClientFactoryDba = func() binlogplayer.DBClient { - return binlogplayer.NewDBClient(dbcfgs.DbaWithDB()) + return binlogplayer.NewDBClient(dbcfgs.DbaWithDB(), vde.parser) } vde.dbName = dbcfgs.DBName } @@ -152,8 +158,9 @@ func (vde *Engine) openLocked(ctx context.Context) error { if err := vde.initControllers(rows); err != nil { return err } + vde.updateStats() - // At this point we've fully and succesfully opened so begin + // At this point we've fully and successfully opened so begin // retrying error'd VDiffs until the engine is closed. vde.wg.Add(1) go func() { @@ -193,7 +200,7 @@ func (vde *Engine) retry(ctx context.Context, err error) { if err := vde.openLocked(ctx); err == nil { log.Infof("VDiff engine: opened successfully") // Don't invoke cancelRetry because openLocked - // will hold on to this context for later cancelation. + // will hold on to this context for later cancellation. vde.cancelRetry = nil vde.mu.Unlock() return @@ -211,6 +218,9 @@ func (vde *Engine) addController(row sqltypes.RowNamedValues, options *tabletman row, vde.thisTablet.Alias) } vde.controllers[ct.id] = ct + globalStats.mu.Lock() + defer globalStats.mu.Unlock() + globalStats.controllers[ct.id] = ct return nil } @@ -385,4 +395,16 @@ func (vde *Engine) resetControllers() { ct.Stop() } vde.controllers = make(map[int64]*controller) + vde.updateStats() +} + +// updateStats must only be called while holding the engine lock. +func (vre *Engine) updateStats() { + globalStats.mu.Lock() + defer globalStats.mu.Unlock() + + globalStats.controllers = make(map[int64]*controller, len(vre.controllers)) + for id, ct := range vre.controllers { + globalStats.controllers[id] = ct + } } diff --git a/go/vt/vttablet/tabletmanager/vdiff/engine_test.go b/go/vt/vttablet/tabletmanager/vdiff/engine_test.go index 75b0e37d630..e6c9a84d9e2 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/engine_test.go @@ -27,7 +27,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -148,7 +147,7 @@ func TestVDiff(t *testing.T) { ), "NULL", ), nil) - vdenv.dbClient.ExpectRequest(fmt.Sprintf("select table_name as table_name, table_rows as table_rows from INFORMATION_SCHEMA.TABLES where table_schema = '%s' and table_name in ('t1')", vdiffDBName), sqltypes.MakeTestResult(sqltypes.MakeTestFields( + vdenv.dbClient.ExpectRequest(fmt.Sprintf("select table_name as table_name, table_rows as table_rows from INFORMATION_SCHEMA.TABLES where table_schema = '%s' and table_name in ('t1') order by table_name", vdiffDBName), sqltypes.MakeTestResult(sqltypes.MakeTestFields( "table_name|table_rows", "varchar|int64", ), @@ -193,7 +192,7 @@ func TestVDiff(t *testing.T) { vdenv.dbClient.ExpectRequest(`insert into _vt.vdiff_log(vdiff_id, message) values (1, 'completed: table \'t1\'')`, singleRowAffected, nil) vdenv.dbClient.ExpectRequest("update _vt.vdiff_table set state = 'completed' where vdiff_id = 1 and table_name = 't1'", singleRowAffected, nil) vdenv.dbClient.ExpectRequest(`insert into _vt.vdiff_log(vdiff_id, message) values (1, 'completed: table \'t1\'')`, singleRowAffected, nil) - vdenv.dbClient.ExpectRequest("select table_name as table_name from _vt.vdiff_table where vdiff_id = 1 and state != 'completed'", singleRowAffected, nil) + vdenv.dbClient.ExpectRequest("select table_name as table_name from _vt.vdiff_table where vdiff_id = 1 and state != 'completed' order by table_name", singleRowAffected, nil) vdenv.dbClient.ExpectRequest("update _vt.vdiff set state = 'completed', last_error = left('', 1024) , completed_at = utc_timestamp() where id = 1", singleRowAffected, nil) vdenv.dbClient.ExpectRequest("insert into _vt.vdiff_log(vdiff_id, message) values (1, 'State changed to: completed')", singleRowAffected, nil) @@ -270,7 +269,7 @@ func TestEngineRetryErroredVDiffs(t *testing.T) { fmt.Sprintf("%s|%s|%s|%s||9223372036854775807|9223372036854775807||PRIMARY,REPLICA|1669511347|0|Running||%s|200||1669511347|1|0||1", id, vdiffenv.workflow, vreplSource, vdiffSourceGtid, vdiffDBName), ), nil) - // At this point we know that we kicked off the expected retry so we can short circit the vdiff. + // At this point we know that we kicked off the expected retry so we can short circuit the vdiff. shortCircuitTestAfterQuery(fmt.Sprintf("update _vt.vdiff set state = 'started', last_error = left('', 1024) , started_at = utc_timestamp() where id = %s", id), vdiffenv.dbClient) expectedControllerCnt++ diff --git a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go index d5e8c134814..0676c5204be 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go @@ -100,16 +100,26 @@ var ( Columns: []string{"id", "dt"}, PrimaryKeyColumns: []string{"id"}, Fields: sqltypes.MakeTestFields("id|dt", "int64|datetime"), + }, { + Name: "nopk", + Columns: []string{"c1", "c2", "c3"}, + Fields: sqltypes.MakeTestFields("c1|c2|c3", "int64|int64|int64"), + }, { + Name: "nopkwithpke", + Columns: []string{"c1", "c2", "c3"}, + Fields: sqltypes.MakeTestFields("c1|c2|c3", "int64|int64|int64"), }, }, } tableDefMap = map[string]int{ - "t1": 0, - "nonpktext": 1, - "pktext": 2, - "multipk": 3, - "aggr": 4, - "datze": 5, + "t1": 0, + "nonpktext": 1, + "pktext": 2, + "multipk": 3, + "aggr": 4, + "datze": 5, + "nopk": 6, + "nopkwithpke": 7, } ) @@ -396,6 +406,22 @@ func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Resu return qr, err } +func (dbc *realDBClient) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + queries, err := sqlparser.NewTestParser().SplitStatementToPieces(query) + if err != nil { + return nil, err + } + results := make([]*sqltypes.Result, 0, len(queries)) + for _, query := range queries { + qr, err := dbc.ExecuteFetch(query, maxrows) + if err != nil { + return nil, err + } + results = append(results, qr) + } + return results, nil +} + //---------------------------------------------- // fakeTMClient diff --git a/go/vt/vttablet/tabletmanager/vdiff/report.go b/go/vt/vttablet/tabletmanager/vdiff/report.go index 4f9b264cddd..62ce6d24585 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/report.go +++ b/go/vt/vttablet/tabletmanager/vdiff/report.go @@ -26,9 +26,7 @@ import ( ) const ( - // At most how many samples we should show for row differences in the final report - maxVDiffReportSampleRows = 10 - truncatedNotation = "...[TRUNCATED]" + truncatedNotation = "...[TRUNCATED]" ) // DiffReport is the summary of differences for one table. @@ -68,7 +66,7 @@ type RowDiff struct { func (td *tableDiffer) genRowDiff(queryStmt string, row []sqltypes.Value, debug, onlyPks bool) (*RowDiff, error) { drp := &RowDiff{} drp.Row = make(map[string]string) - statement, err := sqlparser.Parse(queryStmt) + statement, err := td.wd.ct.vde.parser.Parse(queryStmt) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/vdiff/schema.go b/go/vt/vttablet/tabletmanager/vdiff/schema.go index a63e60d9434..afb79b4e4b3 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/schema.go +++ b/go/vt/vttablet/tabletmanager/vdiff/schema.go @@ -37,19 +37,19 @@ const ( vd.started_at as started_at, vdt.rows_compared as rows_compared, vd.completed_at as completed_at, IF(vdt.mismatch = 1, 1, 0) as has_mismatch, vdt.report as report from _vt.vdiff as vd left join _vt.vdiff_table as vdt on (vd.id = vdt.vdiff_id) - where vd.id = %a` + where vd.id = %a order by table_name` // sqlUpdateVDiffState has a penultimate placeholder for any additional columns you want to update, e.g. `, foo = 1`. // It also truncates the error if needed to ensure that we can save the state when the error text is very long. sqlUpdateVDiffState = "update _vt.vdiff set state = %s, last_error = left(%s, 1024) %s where id = %d" sqlUpdateVDiffStopped = `update _vt.vdiff as vd, _vt.vdiff_table as vdt set vd.state = 'stopped', vdt.state = 'stopped', vd.last_error = '' where vd.id = vdt.vdiff_id and vd.id = %a and vd.state != 'completed'` - sqlGetVReplicationEntry = "select * from _vt.vreplication %s" + sqlGetVReplicationEntry = "select * from _vt.vreplication %s" // A filter/where is added by the caller sqlGetVDiffsToRun = "select * from _vt.vdiff where state in ('started','pending')" // what VDiffs have not been stopped or completed sqlGetVDiffsToRetry = "select * from _vt.vdiff where state = 'error' and json_unquote(json_extract(options, '$.core_options.auto_retry')) = 'true'" sqlGetVDiffID = "select id as id from _vt.vdiff where vdiff_uuid = %a" sqlGetVDiffIDsByKeyspaceWorkflow = "select id as id from _vt.vdiff where keyspace = %a and workflow = %a" sqlGetTableRows = "select table_rows as table_rows from INFORMATION_SCHEMA.TABLES where table_schema = %a and table_name = %a" - sqlGetAllTableRows = "select table_name as table_name, table_rows as table_rows from INFORMATION_SCHEMA.TABLES where table_schema = %s and table_name in (%s)" + sqlGetAllTableRows = "select table_name as table_name, table_rows as table_rows from INFORMATION_SCHEMA.TABLES where table_schema = %s and table_name in (%s) order by table_name" sqlNewVDiffTable = "insert into _vt.vdiff_table(vdiff_id, table_name, state, table_rows) values(%a, %a, 'pending', %a)" sqlGetVDiffTable = `select vdt.lastpk as lastpk, vdt.mismatch as mismatch, vdt.report as report @@ -62,5 +62,5 @@ const ( sqlUpdateTableStateAndReport = "update _vt.vdiff_table set state = %a, rows_compared = %a, report = %a where vdiff_id = %a and table_name = %a" sqlUpdateTableMismatch = "update _vt.vdiff_table set mismatch = true where vdiff_id = %a and table_name = %a" - sqlGetIncompleteTables = "select table_name as table_name from _vt.vdiff_table where vdiff_id = %a and state != 'completed'" + sqlGetIncompleteTables = "select table_name as table_name from _vt.vdiff_table where vdiff_id = %a and state != 'completed' order by table_name" ) diff --git a/go/vt/vttablet/tabletmanager/vdiff/stats.go b/go/vt/vttablet/tabletmanager/vdiff/stats.go new file mode 100644 index 00000000000..b68e1f86556 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vdiff/stats.go @@ -0,0 +1,149 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vdiff + +import ( + "fmt" + "sync" + + "vitess.io/vitess/go/stats" +) + +var ( + globalStats = &vdiffStats{} +) + +func init() { + globalStats.register() +} + +// This is a singleton. +// vdiffStats exports the stats for Engine. It's a separate structure to +// prevent potential deadlocks with the mutex in Engine. +type vdiffStats struct { + mu sync.Mutex + controllers map[int64]*controller + + Count *stats.Gauge + ErrorCount *stats.Counter + RestartedTableDiffs *stats.CountersWithSingleLabel + RowsDiffedCount *stats.Counter +} + +func (vds *vdiffStats) register() { + globalStats.Count = stats.NewGauge("", "") + globalStats.ErrorCount = stats.NewCounter("", "") + globalStats.RestartedTableDiffs = stats.NewCountersWithSingleLabel("", "", "Table", "") + globalStats.RowsDiffedCount = stats.NewCounter("", "") + + stats.NewGaugeFunc("VDiffCount", "Number of current vdiffs", vds.numControllers) + + stats.NewCounterFunc( + "VDiffErrorCountTotal", + "Number of errors encountered across all vdiff actions", + func() int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + return globalStats.ErrorCount.Get() + }, + ) + + stats.NewGaugesFuncWithMultiLabels( + "VDiffRestartedTableDiffsCount", + "Table diffs restarted due to --max-diff-duration counts by table", + []string{"table_name"}, + func() map[string]int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + result := make(map[string]int64) + for label, count := range globalStats.RestartedTableDiffs.Counts() { + if label == "" { + continue + } + result[label] = count + } + return result + }, + ) + + stats.NewCounterFunc( + "VDiffRowsComparedTotal", + "Number of rows compared across all vdiffs", + func() int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + return globalStats.RowsDiffedCount.Get() + }, + ) + + stats.NewGaugesFuncWithMultiLabels( + "VDiffRowsCompared", + "Live number of rows compared per vdiff by table", + []string{"workflow", "uuid", "table"}, + func() map[string]int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + result := make(map[string]int64, len(vds.controllers)) + for _, ct := range vds.controllers { + for key, val := range ct.TableDiffRowCounts.Counts() { + result[fmt.Sprintf("%s.%s.%s", ct.workflow, ct.uuid, key)] = val + } + } + return result + }, + ) + + stats.NewCountersFuncWithMultiLabels( + "VDiffErrors", + "Count of specific errors seen during the lifetime of a vdiff", + []string{"workflow", "uuid", "error"}, + func() map[string]int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + result := make(map[string]int64, len(vds.controllers)) + for _, ct := range vds.controllers { + for key, val := range ct.Errors.Counts() { + result[fmt.Sprintf("%s.%s.%s", ct.workflow, ct.uuid, key)] = val + } + } + return result + }, + ) + + stats.NewGaugesFuncWithMultiLabels( + "VDiffPhaseTimings", + "VDiff phase timings", + []string{"workflow", "uuid", "table", "phase"}, + func() map[string]int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + result := make(map[string]int64, len(vds.controllers)) + for _, ct := range vds.controllers { + for tablePhase, h := range ct.TableDiffPhaseTimings.Histograms() { + result[fmt.Sprintf("%s.%s.%s", ct.workflow, ct.uuid, tablePhase)] = h.Total() + } + } + return result + }, + ) +} + +func (vds *vdiffStats) numControllers() int64 { + vds.mu.Lock() + defer vds.mu.Unlock() + return int64(len(vds.controllers)) +} diff --git a/go/vt/vttablet/tabletmanager/vdiff/stats_test.go b/go/vt/vttablet/tabletmanager/vdiff/stats_test.go new file mode 100644 index 00000000000..b4f02d7b192 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vdiff/stats_test.go @@ -0,0 +1,76 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vdiff + +import ( + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/stats" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +func TestVDiffStats(t *testing.T) { + testStats := &vdiffStats{ + ErrorCount: stats.NewCounter("", ""), + RestartedTableDiffs: stats.NewCountersWithSingleLabel("", "", "Table", ""), + RowsDiffedCount: stats.NewCounter("", ""), + } + id := int64(1) + testStats.controllers = map[int64]*controller{ + id: { + id: id, + workflow: "testwf", + workflowType: binlogdatapb.VReplicationWorkflowType_MoveTables, + uuid: uuid.New().String(), + Errors: stats.NewCountersWithMultiLabels("", "", []string{"Error"}), + TableDiffRowCounts: stats.NewCountersWithMultiLabels("", "", []string{"Rows"}), + TableDiffPhaseTimings: stats.NewTimings("", "", "", "TablePhase"), + }, + } + + require.Equal(t, int64(1), testStats.numControllers()) + + sleepTime := 1 * time.Millisecond + record := func(phase string) { + defer testStats.controllers[id].TableDiffPhaseTimings.Record(phase, time.Now()) + time.Sleep(sleepTime) + } + want := 10 * sleepTime // Allow 10x overhead for recording timing on flaky test hosts + record(string(initializing)) + require.Greater(t, want, testStats.controllers[id].TableDiffPhaseTimings.Histograms()[string(initializing)].Total()) + record(string(pickingTablets)) + require.Greater(t, want, testStats.controllers[id].TableDiffPhaseTimings.Histograms()[string(pickingTablets)].Total()) + record(string(diffingTable)) + require.Greater(t, want, testStats.controllers[id].TableDiffPhaseTimings.Histograms()[string(diffingTable)].Total()) + + testStats.ErrorCount.Set(11) + require.Equal(t, int64(11), testStats.ErrorCount.Get()) + + testStats.controllers[id].Errors.Add([]string{"test error"}, int64(12)) + require.Equal(t, int64(12), testStats.controllers[id].Errors.Counts()["test error"]) + + testStats.RestartedTableDiffs.Add("t1", int64(5)) + require.Equal(t, int64(5), testStats.RestartedTableDiffs.Counts()["t1"]) + + testStats.RowsDiffedCount.Add(512) + require.Equal(t, int64(512), testStats.RowsDiffedCount.Get()) +} diff --git a/go/vt/vttablet/tabletmanager/vdiff/table_differ.go b/go/vt/vttablet/tabletmanager/vdiff/table_differ.go index c0cba599bdd..7ad83a3ad4b 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/table_differ.go +++ b/go/vt/vttablet/tabletmanager/vdiff/table_differ.go @@ -49,9 +49,25 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +type tableDiffPhase string + +const ( + initializing = tableDiffPhase("initializing") + pickingTablets = tableDiffPhase("picking_streaming_tablets") + syncingSources = tableDiffPhase("syncing_source_streams") + syncingTargets = tableDiffPhase("syncing_target_streams") + startingSources = tableDiffPhase("starting_source_data_streams") + startingTargets = tableDiffPhase("starting_target_data_streams") + restartingVreplication = tableDiffPhase("restarting_vreplication_streams") + diffingTable = tableDiffPhase("diffing_table") +) + // how long to wait for background operations to complete var BackgroundOperationTimeout = topo.RemoteOperationTimeout * 4 +var ErrMaxDiffDurationExceeded = vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "table diff was stopped due to exceeding the max-diff-duration time") +var ErrVDiffStoppedByUser = vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped by user") + // compareColInfo contains the metadata for a column of the table being diffed type compareColInfo struct { colIndex int // index of the column in the filter's select @@ -87,6 +103,7 @@ func newTableDiffer(wd *workflowDiffer, table *tabletmanagerdatapb.TableDefiniti // initialize func (td *tableDiffer) initialize(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, initializing), time.Now()) vdiffEngine := td.wd.ct.vde vdiffEngine.snapshotMu.Lock() defer vdiffEngine.snapshotMu.Unlock() @@ -109,7 +126,7 @@ func (td *tableDiffer) initialize(ctx context.Context) error { defer func() { unlock(&err) if err != nil { - log.Errorf("UnlockKeyspace %s failed: %v", targetKeyspace, lockErr) + log.Errorf("UnlockKeyspace %s failed: %v", targetKeyspace, err) } }() @@ -209,6 +226,7 @@ func (td *tableDiffer) forEachSource(cb func(source *migrationSource) error) err } func (td *tableDiffer) selectTablets(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, pickingTablets), time.Now()) var ( wg sync.WaitGroup sourceErr, targetErr error @@ -284,6 +302,7 @@ func (td *tableDiffer) pickTablet(ctx context.Context, ts *topo.Server, cells [] } func (td *tableDiffer) syncSourceStreams(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, syncingSources), time.Now()) // source can be replica, wait for them to at least reach max gtid of all target streams ct := td.wd.ct waitCtx, cancel := context.WithTimeout(ctx, time.Duration(ct.options.CoreOptions.TimeoutSeconds*int64(time.Second))) @@ -302,6 +321,7 @@ func (td *tableDiffer) syncSourceStreams(ctx context.Context) error { } func (td *tableDiffer) syncTargetStreams(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, syncingTargets), time.Now()) ct := td.wd.ct waitCtx, cancel := context.WithTimeout(ctx, time.Duration(ct.options.CoreOptions.TimeoutSeconds*int64(time.Second))) defer cancel() @@ -324,6 +344,7 @@ func (td *tableDiffer) syncTargetStreams(ctx context.Context) error { } func (td *tableDiffer) startTargetDataStream(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, startingTargets), time.Now()) ct := td.wd.ct gtidch := make(chan string, 1) ct.targetShardStreamer.result = make(chan *sqltypes.Result, 1) @@ -338,6 +359,7 @@ func (td *tableDiffer) startTargetDataStream(ctx context.Context) error { } func (td *tableDiffer) startSourceDataStreams(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, startingSources), time.Now()) if err := td.forEachSource(func(source *migrationSource) error { gtidch := make(chan string, 1) source.result = make(chan *sqltypes.Result, 1) @@ -356,6 +378,7 @@ func (td *tableDiffer) startSourceDataStreams(ctx context.Context) error { } func (td *tableDiffer) restartTargetVReplicationStreams(ctx context.Context) error { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, restartingVreplication), time.Now()) ct := td.wd.ct query := fmt.Sprintf("update _vt.vreplication set state='Running', message='', stop_pos='' where db_name=%s and workflow=%s", encodeString(ct.vde.dbName), encodeString(ct.workflow)) @@ -431,7 +454,7 @@ func (td *tableDiffer) streamOneShard(ctx context.Context, participant *shardStr case <-ctx.Done(): return vterrors.Wrap(ctx.Err(), "VStreamRows") case <-td.wd.ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser } return nil }) @@ -444,25 +467,27 @@ func (td *tableDiffer) setupRowSorters() { for shard, source := range td.wd.ct.sources { sources[shard] = source.shardStreamer } - td.sourcePrimitive = newMergeSorter(sources, td.tablePlan.comparePKs) + td.sourcePrimitive = newMergeSorter(sources, td.tablePlan.comparePKs, td.wd.collationEnv) // Create a merge sorter for the target. targets := make(map[string]*shardStreamer) targets[td.wd.ct.targetShardStreamer.shard] = td.wd.ct.targetShardStreamer - td.targetPrimitive = newMergeSorter(targets, td.tablePlan.comparePKs) + td.targetPrimitive = newMergeSorter(targets, td.tablePlan.comparePKs, td.wd.collationEnv) // If there were aggregate expressions, we have to re-aggregate // the results, which engine.OrderedAggregate can do. if len(td.tablePlan.aggregates) != 0 { td.sourcePrimitive = &engine.OrderedAggregate{ - Aggregates: td.tablePlan.aggregates, - GroupByKeys: pkColsToGroupByParams(td.tablePlan.pkCols), - Input: td.sourcePrimitive, + Aggregates: td.tablePlan.aggregates, + GroupByKeys: pkColsToGroupByParams(td.tablePlan.pkCols, td.wd.collationEnv), + Input: td.sourcePrimitive, + CollationEnv: td.wd.collationEnv, } } } -func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onlyPks bool, maxExtraRowsToCompare int64) (*DiffReport, error) { +func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onlyPks bool, maxExtraRowsToCompare int64, maxReportSampleRows int64, stop <-chan time.Time) (*DiffReport, error) { + defer td.wd.ct.TableDiffPhaseTimings.Record(fmt.Sprintf("%s.%s", td.table.Name, diffingTable), time.Now()) dbClient := td.wd.ct.dbClientFactory() if err := dbClient.Connect(); err != nil { return nil, err @@ -506,11 +531,12 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl advanceSource := true advanceTarget := true - // Save our progress when we finish the run + // Save our progress when we finish the run. defer func() { if err := td.updateTableProgress(dbClient, dr, lastProcessedRow); err != nil { log.Errorf("Failed to update vdiff progress on %s table: %v", td.table.Name, err) } + globalStats.RowsDiffedCount.Add(dr.ProcessedRows) }() for { @@ -520,7 +546,10 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl case <-ctx.Done(): return nil, vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") case <-td.wd.ct.done: - return nil, vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return nil, ErrVDiffStoppedByUser + case <-stop: + globalStats.RestartedTableDiffs.Add(td.table.Name, 1) + return nil, ErrMaxDiffDurationExceeded default: } @@ -533,7 +562,7 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl } rowsToCompare-- if rowsToCompare < 0 { - log.Infof("Stopping vdiff, specified limit reached") + log.Infof("Stopping vdiff, specified row limit reached") return dr, nil } if advanceSource { @@ -564,7 +593,7 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl } dr.ExtraRowsTargetDiffs = append(dr.ExtraRowsTargetDiffs, diffRow) - // drain target, update count + // Drain target, update count. count, err := targetExecutor.drain(ctx) if err != nil { return nil, err @@ -574,8 +603,8 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl return dr, nil } if targetRow == nil { - // no more rows from the target - // we know we have rows from source, drain, update count + // No more rows from the target but we know we have more rows from + // source, so drain them and update the counts. diffRow, err := td.genRowDiff(td.tablePlan.sourceQuery, sourceRow, debug, onlyPks) if err != nil { return nil, vterrors.Wrap(err, "unexpected error generating diff") @@ -628,8 +657,8 @@ func (td *tableDiffer) diff(ctx context.Context, rowsToCompare int64, debug, onl case err != nil: return nil, err case c != 0: - // We don't do a second pass to compare mismatched rows so we can cap the slice here - if dr.MismatchedRows < maxVDiffReportSampleRows { + // We don't do a second pass to compare mismatched rows so we can cap the slice here. + if maxReportSampleRows == 0 || dr.MismatchedRows < maxReportSampleRows { sourceDiffRow, err := td.genRowDiff(td.tablePlan.targetQuery, sourceRow, debug, onlyPks) if err != nil { return nil, vterrors.Wrap(err, "unexpected error generating diff") @@ -672,7 +701,7 @@ func (td *tableDiffer) compare(sourceRow, targetRow []sqltypes.Value, cols []com if collationID == collations.Unknown { collationID = collations.CollationBinaryID } - c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], collationID) + c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], td.wd.collationEnv, collationID) if err != nil { return 0, err } @@ -700,6 +729,16 @@ func (td *tableDiffer) updateTableProgress(dbClient binlogplayer.DBClient, dr *D return err } + if td.wd.opts.CoreOptions.MaxDiffSeconds > 0 { + // Update the in-memory lastPK as well so that we can restart the table + // diff if needed. + lastpkpb := &querypb.QueryResult{} + if err := prototext.Unmarshal(lastPK, lastpkpb); err != nil { + return err + } + td.lastPK = lastpkpb + } + query, err = sqlparser.ParseAndBind(sqlUpdateTableProgress, sqltypes.Int64BindVariable(dr.ProcessedRows), sqltypes.StringBindVariable(string(lastPK)), @@ -724,6 +763,7 @@ func (td *tableDiffer) updateTableProgress(dbClient binlogplayer.DBClient, dr *D if _, err := dbClient.ExecuteFetch(query, 1); err != nil { return err } + td.wd.ct.TableDiffRowCounts.Add([]string{td.table.Name}, dr.ProcessedRows) return nil } diff --git a/go/vt/vttablet/tabletmanager/vdiff/table_plan.go b/go/vt/vttablet/tabletmanager/vdiff/table_plan.go index e669dbd9a33..548f902e9ac 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/table_plan.go +++ b/go/vt/vttablet/tabletmanager/vdiff/table_plan.go @@ -17,21 +17,23 @@ limitations under the License. package vdiff import ( + "context" "fmt" "strings" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/log" - querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/engine/opcode" + + querypb "vitess.io/vitess/go/vt/proto/query" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) const sqlSelectColumnCollations = "select column_name as column_name, collation_name as collation_name from information_schema.columns where table_schema=%a and table_name=%a and column_name in %a" @@ -59,12 +61,12 @@ type tablePlan struct { aggregates []*engine.AggregateParams } -func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName string) (*tablePlan, error) { +func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName string, collationEnv *collations.Environment) (*tablePlan, error) { tp := &tablePlan{ table: td.table, dbName: dbName, } - statement, err := sqlparser.Parse(td.sourceQuery) + statement, err := td.wd.ct.vde.parser.Parse(td.sourceQuery) if err != nil { return nil, err } @@ -75,7 +77,7 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str sourceSelect := &sqlparser.Select{} targetSelect := &sqlparser.Select{} - // aggregates is the list of Aggregate functions, if any. + // Aggregates is the list of Aggregate functions, if any. var aggregates []*engine.AggregateParams for _, selExpr := range sel.SelectExprs { switch selExpr := selExpr.(type) { @@ -88,14 +90,14 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str } case *sqlparser.AliasedExpr: var targetCol *sqlparser.ColName - if !selExpr.As.IsEmpty() { - targetCol = &sqlparser.ColName{Name: selExpr.As} - } else { + if selExpr.As.IsEmpty() { if colAs, ok := selExpr.Expr.(*sqlparser.ColName); ok { targetCol = colAs } else { return nil, fmt.Errorf("expression needs an alias: %v", sqlparser.String(selExpr)) } + } else { + targetCol = &sqlparser.ColName{Name: selExpr.As} } // If the input was "select a as b", then source will use "a" and target will use "b". sourceSelect.SelectExprs = append(sourceSelect.SelectExprs, selExpr) @@ -112,7 +114,8 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str aggregates = append(aggregates, engine.NewAggregateParam( /*opcode*/ opcode.AggregateSum, /*offset*/ len(sourceSelect.SelectExprs)-1, - /*alias*/ "")) + /*alias*/ "", collationEnv), + ) } } default: @@ -152,12 +155,27 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str }, } - err = tp.findPKs(dbClient, targetSelect) + if len(tp.table.PrimaryKeyColumns) == 0 { + // We use the columns from a PKE if there is one. + pkeCols, err := tp.getPKEquivalentColumns(dbClient) + if err != nil { + return nil, vterrors.Wrapf(err, "error getting PK equivalent columns for table %s", tp.table.Name) + } + if len(pkeCols) > 0 { + tp.table.PrimaryKeyColumns = append(tp.table.PrimaryKeyColumns, pkeCols...) + } else { + // We use every column together as a substitute PK. + tp.table.PrimaryKeyColumns = append(tp.table.PrimaryKeyColumns, tp.table.Columns...) + } + } + + err = tp.findPKs(dbClient, targetSelect, collationEnv) if err != nil { return nil, err } + // Remove in_keyrange. It's not understood by mysql. - sourceSelect.Where = sel.Where //removeKeyrange(sel.Where) + sourceSelect.Where = sel.Where // removeKeyrange(sel.Where) // The source should also perform the group by. sourceSelect.GroupBy = sel.GroupBy sourceSelect.OrderBy = tp.orderBy @@ -167,8 +185,8 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str tp.sourceQuery = sqlparser.String(sourceSelect) tp.targetQuery = sqlparser.String(targetSelect) - log.Info("VDiff query on source: %v", tp.sourceQuery) - log.Info("VDiff query on target: %v", tp.targetQuery) + log.Infof("VDiff query on source: %v", tp.sourceQuery) + log.Infof("VDiff query on target: %v", tp.targetQuery) tp.aggregates = aggregates td.tablePlan = tp @@ -176,7 +194,10 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str } // findPKs identifies PKs and removes them from the columns to do data comparison. -func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlparser.Select) error { +func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlparser.Select, collationEnv *collations.Environment) error { + if len(tp.table.PrimaryKeyColumns) == 0 { + return nil + } var orderby sqlparser.OrderBy for _, pk := range tp.table.PrimaryKeyColumns { found := false @@ -186,8 +207,8 @@ func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlpa switch ct := expr.(type) { case *sqlparser.ColName: colname = ct.Name.String() - case *sqlparser.FuncExpr: //eg. weight_string() - //no-op + case *sqlparser.FuncExpr: // eg. weight_string() + // no-op default: log.Warningf("Not considering column %v for PK, type %v not handled", selExpr, ct) } @@ -195,7 +216,7 @@ func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlpa tp.compareCols[i].isPK = true tp.comparePKs = append(tp.comparePKs, tp.compareCols[i]) tp.selectPks = append(tp.selectPks, i) - // We'll be comparing pks separately. So, remove them from compareCols. + // We'll be comparing PKs separately. So, remove them from compareCols. tp.pkCols = append(tp.pkCols, i) found = true break @@ -210,7 +231,7 @@ func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlpa Direction: sqlparser.AscOrder, }) } - if err := tp.getPKColumnCollations(dbClient); err != nil { + if err := tp.getPKColumnCollations(dbClient, collationEnv); err != nil { return vterrors.Wrapf(err, "error getting PK column collations for table %s", tp.table.Name) } tp.orderBy = orderby @@ -222,7 +243,10 @@ func (tp *tablePlan) findPKs(dbClient binlogplayer.DBClient, targetSelect *sqlpa // sorting when we do the merge sort and for the comparisons. It then // saves the collations in the tablePlan's comparePKs column info // structs for those subsequent operations. -func (tp *tablePlan) getPKColumnCollations(dbClient binlogplayer.DBClient) error { +func (tp *tablePlan) getPKColumnCollations(dbClient binlogplayer.DBClient, collationEnv *collations.Environment) error { + if len(tp.comparePKs) == 0 { + return nil + } columnList := make([]string, len(tp.comparePKs)) for i := range tp.comparePKs { columnList[i] = tp.comparePKs[i].colName @@ -246,7 +270,6 @@ func (tp *tablePlan) getPKColumnCollations(dbClient binlogplayer.DBClient) error if qr == nil || len(qr.Rows) != len(tp.comparePKs) { return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unexpected result for query %s: %+v", query, qr) } - collationEnv := collations.Local() for _, row := range qr.Named().Rows { columnName := row["column_name"].ToString() collateName := strings.ToLower(row["collation_name"].ToString()) @@ -259,3 +282,17 @@ func (tp *tablePlan) getPKColumnCollations(dbClient binlogplayer.DBClient) error } return nil } + +func (tp *tablePlan) getPKEquivalentColumns(dbClient binlogplayer.DBClient) ([]string, error) { + ctx, cancel := context.WithTimeout(context.Background(), BackgroundOperationTimeout/2) + defer cancel() + executeFetch := func(query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { + // This sets wantfields to true. + return dbClient.ExecuteFetch(query, maxrows) + } + pkeCols, _, err := mysqlctl.GetPrimaryKeyEquivalentColumns(ctx, executeFetch, tp.dbName, tp.table.Name) + if err != nil { + return nil, err + } + return pkeCols, nil +} diff --git a/go/vt/vttablet/tabletmanager/vdiff/utils.go b/go/vt/vttablet/tabletmanager/vdiff/utils.go index 5904fd41795..07e070976a9 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/utils.go +++ b/go/vt/vttablet/tabletmanager/vdiff/utils.go @@ -33,7 +33,7 @@ import ( ) // newMergeSorter creates an engine.MergeSort based on the shard streamers and pk columns -func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compareColInfo) *engine.MergeSort { +func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compareColInfo, collationEnv *collations.Environment) *engine.MergeSort { prims := make([]engine.StreamExecutor, 0, len(participants)) for _, participant := range participants { prims = append(prims, participant) @@ -42,14 +42,11 @@ func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compare for i, cpk := range comparePKs { weightStringCol := -1 // if the collation is nil or unknown, use binary collation to compare as bytes - t := evalengine.Type{ - Type: sqltypes.Unknown, - Coll: collations.CollationBinaryID, - } + var collation collations.ID = collations.CollationBinaryID if cpk.collation != collations.Unknown { - t.Coll = cpk.collation + collation = cpk.collation } - ob[i] = evalengine.OrderByParams{Col: cpk.colIndex, WeightStringCol: weightStringCol, Type: t} + ob[i] = evalengine.OrderByParams{Col: cpk.colIndex, WeightStringCol: weightStringCol, Type: evalengine.NewType(sqltypes.Unknown, collation), CollationEnv: collationEnv} } return &engine.MergeSort{ Primitives: prims, @@ -66,10 +63,10 @@ func encodeString(in string) string { return buf.String() } -func pkColsToGroupByParams(pkCols []int) []*engine.GroupByParams { +func pkColsToGroupByParams(pkCols []int, collationEnv *collations.Environment) []*engine.GroupByParams { var res []*engine.GroupByParams for _, col := range pkCols { - res = append(res, &engine.GroupByParams{KeyCol: col, WeightStringCol: -1, Type: evalengine.UnknownType()}) + res = append(res, &engine.GroupByParams{KeyCol: col, WeightStringCol: -1, CollationEnv: collationEnv}) } return res } diff --git a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go index d7d2583a5d3..97d2bd387cb 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go +++ b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ.go @@ -18,28 +18,30 @@ package vdiff import ( "context" + "errors" "fmt" "reflect" "strings" + "time" "google.golang.org/protobuf/encoding/prototext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/vtgate/vindexes" - - "vitess.io/vitess/go/vt/schema" - "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtctl/schematools" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // workflowDiffer has metadata and state for the vdiff of a single workflow on this tablet @@ -49,13 +51,16 @@ type workflowDiffer struct { tableDiffers map[string]*tableDiffer // key is table name opts *tabletmanagerdatapb.VDiffOptions + + collationEnv *collations.Environment } -func newWorkflowDiffer(ct *controller, opts *tabletmanagerdatapb.VDiffOptions) (*workflowDiffer, error) { +func newWorkflowDiffer(ct *controller, opts *tabletmanagerdatapb.VDiffOptions, collationEnv *collations.Environment) (*workflowDiffer, error) { wd := &workflowDiffer{ ct: ct, opts: opts, tableDiffers: make(map[string]*tableDiffer, 1), + collationEnv: collationEnv, } return wd, nil } @@ -64,7 +69,7 @@ func newWorkflowDiffer(ct *controller, opts *tabletmanagerdatapb.VDiffOptions) ( // by MySQL on each side then we'll have the same number of extras on // both sides. If that's the case, then let's see if the extra rows on // both sides are actually different. -func (wd *workflowDiffer) reconcileExtraRows(dr *DiffReport, maxExtraRowsToCompare int64) error { +func (wd *workflowDiffer) reconcileExtraRows(dr *DiffReport, maxExtraRowsToCompare int64, maxReportSampleRows int64) error { if dr.MismatchedRows == 0 { // Get the VSchema on the target and source keyspaces. We can then use this // for handling additional edge cases, such as adjusting results for reference @@ -122,69 +127,121 @@ func (wd *workflowDiffer) reconcileExtraRows(dr *DiffReport, maxExtraRowsToCompa } } } - // We can now trim the extra rows diffs on both sides to the maxVDiffReportSampleRows value - if len(dr.ExtraRowsSourceDiffs) > maxVDiffReportSampleRows { - dr.ExtraRowsSourceDiffs = dr.ExtraRowsSourceDiffs[:maxVDiffReportSampleRows-1] + // We can now trim the extra rows diffs on both sides to the maxReportSampleRows value + if int64(len(dr.ExtraRowsSourceDiffs)) > maxReportSampleRows && maxReportSampleRows > 0 { + dr.ExtraRowsSourceDiffs = dr.ExtraRowsSourceDiffs[:maxReportSampleRows-1] } - if len(dr.ExtraRowsTargetDiffs) > maxVDiffReportSampleRows { - dr.ExtraRowsTargetDiffs = dr.ExtraRowsTargetDiffs[:maxVDiffReportSampleRows-1] + if int64(len(dr.ExtraRowsTargetDiffs)) > maxReportSampleRows && maxReportSampleRows > 0 { + dr.ExtraRowsTargetDiffs = dr.ExtraRowsTargetDiffs[:maxReportSampleRows-1] } return nil } func (wd *workflowDiffer) diffTable(ctx context.Context, dbClient binlogplayer.DBClient, td *tableDiffer) error { - defer func() { + cancelShardStreams := func() { if td.shardStreamsCancel != nil { td.shardStreamsCancel() } // Wait for all the shard streams to finish before returning. td.wgShardStreamers.Wait() + } + defer func() { + cancelShardStreams() }() - select { - case <-ctx.Done(): - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") - case <-wd.ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") - default: + var ( + diffTimer *time.Timer + diffReport *DiffReport + diffErr error + ) + defer func() { + if diffTimer != nil { + if !diffTimer.Stop() { + select { + case <-diffTimer.C: + default: + } + } + } + }() + + maxDiffRuntime := time.Duration(24 * time.Hour * 365) // 1 year (effectively forever) + if wd.ct.options.CoreOptions.MaxDiffSeconds > 0 { + // Restart the diff if it takes longer than the specified max diff time. + maxDiffRuntime = time.Duration(wd.ct.options.CoreOptions.MaxDiffSeconds) * time.Second } log.Infof("Starting differ on table %s for vdiff %s", td.table.Name, wd.ct.uuid) if err := td.updateTableState(ctx, dbClient, StartedState); err != nil { return err } - if err := td.initialize(ctx); err != nil { - return err - } - log.Infof("Table initialization done on table %s for vdiff %s", td.table.Name, wd.ct.uuid) - dr, err := td.diff(ctx, wd.opts.CoreOptions.MaxRows, wd.opts.ReportOptions.DebugQuery, wd.opts.ReportOptions.OnlyPks, wd.opts.CoreOptions.MaxExtraRowsToCompare) - if err != nil { - log.Errorf("Encountered an error diffing table %s for vdiff %s: %v", td.table.Name, wd.ct.uuid, err) - return err + + for { + select { + case <-ctx.Done(): + return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") + case <-wd.ct.done: + return ErrVDiffStoppedByUser + default: + } + + if diffTimer != nil { // We're restarting the diff + if !diffTimer.Stop() { + select { + case <-diffTimer.C: + default: + } + } + diffTimer = nil + cancelShardStreams() + // Give the underlying resources (mainly MySQL) a moment to catch up + // before we pick up where we left off (but with new database snapshots). + time.Sleep(30 * time.Second) + } + if err := td.initialize(ctx); err != nil { // Setup the consistent snapshots + return err + } + log.Infof("Table initialization done on table %s for vdiff %s", td.table.Name, wd.ct.uuid) + diffTimer = time.NewTimer(maxDiffRuntime) + diffReport, diffErr = td.diff(ctx, wd.opts.CoreOptions.MaxRows, wd.opts.ReportOptions.DebugQuery, wd.opts.ReportOptions.OnlyPks, wd.opts.CoreOptions.MaxExtraRowsToCompare, wd.opts.ReportOptions.MaxSampleRows, diffTimer.C) + if diffErr == nil { // We finished the diff successfully + break + } + log.Errorf("Encountered an error diffing table %s for vdiff %s: %v", td.table.Name, wd.ct.uuid, diffErr) + if !errors.Is(diffErr, ErrMaxDiffDurationExceeded) { // We only want to retry if we hit the max-diff-duration + return diffErr + } } - log.Infof("Table diff done on table %s for vdiff %s with report: %+v", td.table.Name, wd.ct.uuid, dr) - if dr.ExtraRowsSource > 0 || dr.ExtraRowsTarget > 0 { - if err := wd.reconcileExtraRows(dr, wd.opts.CoreOptions.MaxExtraRowsToCompare); err != nil { + log.Infof("Table diff done on table %s for vdiff %s with report: %+v", td.table.Name, wd.ct.uuid, diffReport) + + if diffReport.ExtraRowsSource > 0 || diffReport.ExtraRowsTarget > 0 { + if err := wd.reconcileExtraRows(diffReport, wd.opts.CoreOptions.MaxExtraRowsToCompare, wd.opts.ReportOptions.MaxSampleRows); err != nil { log.Errorf("Encountered an error reconciling extra rows found for table %s for vdiff %s: %v", td.table.Name, wd.ct.uuid, err) return vterrors.Wrap(err, "failed to reconcile extra rows") } } - if dr.MismatchedRows > 0 || dr.ExtraRowsTarget > 0 || dr.ExtraRowsSource > 0 { + if diffReport.MismatchedRows > 0 || diffReport.ExtraRowsTarget > 0 || diffReport.ExtraRowsSource > 0 { if err := updateTableMismatch(dbClient, wd.ct.id, td.table.Name); err != nil { return err } } - log.Infof("Completed reconciliation on table %s for vdiff %s with updated report: %+v", td.table.Name, wd.ct.uuid, dr) - if err := td.updateTableStateAndReport(ctx, dbClient, CompletedState, dr); err != nil { + log.Infof("Completed reconciliation on table %s for vdiff %s with updated report: %+v", td.table.Name, wd.ct.uuid, diffReport) + if err := td.updateTableStateAndReport(ctx, dbClient, CompletedState, diffReport); err != nil { return err } return nil } -func (wd *workflowDiffer) diff(ctx context.Context) error { +func (wd *workflowDiffer) diff(ctx context.Context) (err error) { + defer func() { + if err != nil { + globalStats.ErrorCount.Add(1) + wd.ct.Errors.Add([]string{err.Error()}, 1) + } + }() dbClient := wd.ct.dbClientFactory() if err := dbClient.Connect(); err != nil { return err @@ -195,7 +252,7 @@ func (wd *workflowDiffer) diff(ctx context.Context) error { case <-ctx.Done(): return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") case <-wd.ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser default: } @@ -216,7 +273,7 @@ func (wd *workflowDiffer) diff(ctx context.Context) error { case <-ctx.Done(): return vterrors.Errorf(vtrpcpb.Code_CANCELED, "context has expired") case <-wd.ct.done: - return vterrors.Errorf(vtrpcpb.Code_CANCELED, "vdiff was stopped") + return ErrVDiffStoppedByUser default: } query, err := sqlparser.ParseAndBind(sqlGetVDiffTable, @@ -315,7 +372,7 @@ func (wd *workflowDiffer) buildPlan(dbClient binlogplayer.DBClient, filter *binl } td.lastPK = lastpkpb wd.tableDiffers[table.Name] = td - if _, err := td.buildTablePlan(dbClient, wd.ct.vde.dbName); err != nil { + if _, err := td.buildTablePlan(dbClient, wd.ct.vde.dbName, wd.collationEnv); err != nil { return err } } diff --git a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go index 10c6406f046..a460b87a4f6 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go @@ -67,8 +67,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -87,8 +87,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where in_keyrange('-80') order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -107,8 +107,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -127,8 +127,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c2, c1 from t1 order by c1 asc", targetQuery: "select c2, c1 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, - comparePKs: []compareColInfo{{1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + comparePKs: []compareColInfo{{1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{1}, selectPks: []int{1}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -147,8 +147,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c0 as c1, c2 from t2 order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -157,7 +157,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // non-pk text column. + // Non-PK text column. input: &binlogdatapb.Rule{ Match: "nonpktext", Filter: "select c1, textcol from nonpktext", @@ -168,8 +168,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["nonpktext"]], sourceQuery: "select c1, textcol from nonpktext order by c1 asc", targetQuery: "select c1, textcol from nonpktext order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "textcol"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "textcol"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -178,7 +178,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // non-pk text column, different order. + // Non-PK text column, different order. input: &binlogdatapb.Rule{ Match: "nonpktext", Filter: "select textcol, c1 from nonpktext", @@ -189,8 +189,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["nonpktext"]], sourceQuery: "select textcol, c1 from nonpktext order by c1 asc", targetQuery: "select textcol, c1 from nonpktext order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), false, "textcol"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, - comparePKs: []compareColInfo{{1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "textcol"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + comparePKs: []compareColInfo{{1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{1}, selectPks: []int{1}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -199,7 +199,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // pk text column. + // PK text column. input: &binlogdatapb.Rule{ Match: "pktext", Filter: "select textcol, c2 from pktext", @@ -210,8 +210,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["pktext"]], sourceQuery: "select textcol, c2 from pktext order by textcol asc", targetQuery: "select textcol, c2 from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -220,7 +220,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // pk text column, different order. + // PK text column, different order. input: &binlogdatapb.Rule{ Match: "pktext", Filter: "select c2, textcol from pktext", @@ -231,8 +231,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["pktext"]], sourceQuery: "select c2, textcol from pktext order by textcol asc", targetQuery: "select c2, textcol from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, - comparePKs: []compareColInfo{{1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, + comparePKs: []compareColInfo{{1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, pkCols: []int{1}, selectPks: []int{1}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -241,7 +241,61 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // text column as expression. + // No PK. Use all columns as a substitute. + input: &binlogdatapb.Rule{ + Match: "nopk", + Filter: "select * from nopk", + }, + table: "nopk", + tablePlan: &tablePlan{ + dbName: vdiffDBName, + table: testSchema.TableDefinitions[tableDefMap["nopk"]], + sourceQuery: "select c1, c2, c3 from nopk order by c1 asc, c2 asc, c3 asc", + targetQuery: "select c1, c2, c3 from nopk order by c1 asc, c2 asc, c3 asc", + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c2"}, {2, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c3"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c2"}, {2, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c3"}}, + pkCols: []int{0, 1, 2}, + selectPks: []int{0, 1, 2}, + orderBy: sqlparser.OrderBy{ + &sqlparser.Order{ + Expr: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("c1")}, + Direction: sqlparser.AscOrder, + }, + &sqlparser.Order{ + Expr: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("c2")}, + Direction: sqlparser.AscOrder, + }, + &sqlparser.Order{ + Expr: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("c3")}, + Direction: sqlparser.AscOrder, + }, + }, + }, + }, { + // No PK, but a PKE on c3. + input: &binlogdatapb.Rule{ + Match: "nopkwithpke", + Filter: "select * from nopkwithpke", + }, + table: "nopkwithpke", + tablePlan: &tablePlan{ + dbName: vdiffDBName, + table: testSchema.TableDefinitions[tableDefMap["nopkwithpke"]], + sourceQuery: "select c1, c2, c3 from nopkwithpke order by c3 asc", + targetQuery: "select c1, c2, c3 from nopkwithpke order by c3 asc", + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}, {2, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c3"}}, + comparePKs: []compareColInfo{{2, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c3"}}, + pkCols: []int{2}, + selectPks: []int{2}, + orderBy: sqlparser.OrderBy{ + &sqlparser.Order{ + Expr: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("c3")}, + Direction: sqlparser.AscOrder, + }, + }, + }, + }, { + // Text column as expression. input: &binlogdatapb.Rule{ Match: "pktext", Filter: "select c2, a+b as textcol from pktext", @@ -252,8 +306,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["pktext"]], sourceQuery: "select c2, a + b as textcol from pktext order by textcol asc", targetQuery: "select c2, textcol from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, - comparePKs: []compareColInfo{{1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, + comparePKs: []compareColInfo{{1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "textcol"}}, pkCols: []int{1}, selectPks: []int{1}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -262,7 +316,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // multiple pk columns. + // Multiple PK columns. input: &binlogdatapb.Rule{ Match: "multipk", }, @@ -272,8 +326,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["multipk"]], sourceQuery: "select c1, c2 from multipk order by c1 asc, c2 asc", targetQuery: "select c1, c2 from multipk order by c1 asc, c2 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c2"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c2"}}, pkCols: []int{0, 1}, selectPks: []int{0, 1}, orderBy: sqlparser.OrderBy{ @@ -299,8 +353,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where in_keyrange('-80') order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -321,8 +375,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and in_keyrange('-80') order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -343,8 +397,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where in_keyrange('-80') and c2 = 2 order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -365,8 +419,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and c1 = 1 and in_keyrange('-80') order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -387,8 +441,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and in_keyrange('-80') order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -397,7 +451,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // group by + // Group by. input: &binlogdatapb.Rule{ Match: "t1", Filter: "select * from t1 group by c1", @@ -408,8 +462,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 group by c1 order by c1 asc", targetQuery: "select c1, c2 from t1 order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -418,7 +472,7 @@ func TestBuildPlanSuccess(t *testing.T) { }}, }, }, { - // aggregations + // Aggregations. input: &binlogdatapb.Rule{ Match: "aggr", Filter: "select c1, c2, count(*) as c3, sum(c4) as c4 from t1 group by c1", @@ -429,8 +483,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["aggr"]], sourceQuery: "select c1, c2, count(*) as c3, sum(c4) as c4 from t1 group by c1 order by c1 asc", targetQuery: "select c1, c2, c3, c4 from aggr order by c1 asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c2"}, {2, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c3"}, {3, collations.Local().LookupByName(sqltypes.NULL.String()), false, "c4"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "c1"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}, {2, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c3"}, {3, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c4"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -438,12 +492,12 @@ func TestBuildPlanSuccess(t *testing.T) { Direction: sqlparser.AscOrder, }}, aggregates: []*engine.AggregateParams{ - engine.NewAggregateParam(opcode.AggregateSum, 2, ""), - engine.NewAggregateParam(opcode.AggregateSum, 3, ""), + engine.NewAggregateParam(opcode.AggregateSum, 2, "", collations.MySQL8()), + engine.NewAggregateParam(opcode.AggregateSum, 3, "", collations.MySQL8()), }, }, }, { - // date conversion on import. + // Date conversion on import. input: &binlogdatapb.Rule{ Match: "datze", }, @@ -454,8 +508,8 @@ func TestBuildPlanSuccess(t *testing.T) { table: testSchema.TableDefinitions[tableDefMap["datze"]], sourceQuery: "select id, dt from datze order by id asc", targetQuery: "select id, convert_tz(dt, 'UTC', 'US/Pacific') as dt from datze order by id asc", - compareCols: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "id"}, {1, collations.Local().LookupByName(sqltypes.NULL.String()), false, "dt"}}, - comparePKs: []compareColInfo{{0, collations.Local().LookupByName(sqltypes.NULL.String()), true, "id"}}, + compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "id"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "dt"}}, + comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "id"}}, pkCols: []int{0}, selectPks: []int{0}, orderBy: sqlparser.OrderBy{&sqlparser.Order{ @@ -478,34 +532,49 @@ func TestBuildPlanSuccess(t *testing.T) { dbc := binlogplayer.NewMockDBClient(t) filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} vdiffenv.opts.CoreOptions.Tables = tcase.table - wd, err := newWorkflowDiffer(ct, vdiffenv.opts) + wd, err := newWorkflowDiffer(ct, vdiffenv.opts, collations.MySQL8()) require.NoError(t, err) dbc.ExpectRequestRE("select vdt.lastpk as lastpk, vdt.mismatch as mismatch, vdt.report as report", noResults, nil) - columnList := make([]string, len(tcase.tablePlan.comparePKs)) - collationList := make([]string, len(tcase.tablePlan.comparePKs)) - env := collations.Local() - for i := range tcase.tablePlan.comparePKs { - columnList[i] = tcase.tablePlan.comparePKs[i].colName - if tcase.tablePlan.comparePKs[i].collation != collations.Unknown { - collationList[i] = env.LookupName(tcase.tablePlan.comparePKs[i].collation) - } else { - collationList[i] = sqltypes.NULL.String() + if len(tcase.tablePlan.table.PrimaryKeyColumns) == 0 { + result := noResults + if tcase.table == "nopkwithpke" { // This has a PKE column: c3 + result = sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "column_name|index_name", + "varchar|varchar", + ), + "c3|c3", + ) } + dbc.ExpectRequestRE("SELECT index_cols.COLUMN_NAME AS column_name, index_cols.INDEX_NAME as index_name FROM information_schema.STATISTICS", result, nil) + } + if len(tcase.tablePlan.comparePKs) > 0 { + columnList := make([]string, len(tcase.tablePlan.comparePKs)) + collationList := make([]string, len(tcase.tablePlan.comparePKs)) + env := collations.MySQL8() + for i := range tcase.tablePlan.comparePKs { + columnList[i] = tcase.tablePlan.comparePKs[i].colName + if tcase.tablePlan.comparePKs[i].collation != collations.Unknown { + collationList[i] = env.LookupName(tcase.tablePlan.comparePKs[i].collation) + } else { + collationList[i] = sqltypes.NULL.String() + } + } + columnBV, err := sqltypes.BuildBindVariable(columnList) + require.NoError(t, err) + query, err := sqlparser.ParseAndBind(sqlSelectColumnCollations, + sqltypes.StringBindVariable(vdiffDBName), + sqltypes.StringBindVariable(tcase.tablePlan.table.Name), + columnBV, + ) + require.NoError(t, err) + dbc.ExpectRequest(query, sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "collation_name", + "varchar", + ), + collationList..., + ), nil) } - columnBV, err := sqltypes.BuildBindVariable(columnList) - require.NoError(t, err) - query, err := sqlparser.ParseAndBind(sqlSelectColumnCollations, - sqltypes.StringBindVariable(vdiffDBName), - sqltypes.StringBindVariable(tcase.tablePlan.table.Name), - columnBV, - ) - require.NoError(t, err) - dbc.ExpectRequest(query, sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "collation_name", - "varchar", - ), - collationList..., - ), nil) err = wd.buildPlan(dbc, filter, testSchema) require.NoError(t, err, tcase.input) require.Equal(t, 1, len(wd.tableDiffers), tcase.input) @@ -577,7 +646,7 @@ func TestBuildPlanInclude(t *testing.T) { for _, tcase := range testcases { dbc := binlogplayer.NewMockDBClient(t) vdiffenv.opts.CoreOptions.Tables = strings.Join(tcase.tables, ",") - wd, err := newWorkflowDiffer(ct, vdiffenv.opts) + wd, err := newWorkflowDiffer(ct, vdiffenv.opts, collations.MySQL8()) require.NoError(t, err) for _, table := range tcase.tables { query := fmt.Sprintf(`select vdt.lastpk as lastpk, vdt.mismatch as mismatch, vdt.report as report @@ -650,7 +719,7 @@ func TestBuildPlanFailure(t *testing.T) { dbc := binlogplayer.NewMockDBClient(t) filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} vdiffenv.opts.CoreOptions.Tables = tcase.input.Match - wd, err := newWorkflowDiffer(ct, vdiffenv.opts) + wd, err := newWorkflowDiffer(ct, vdiffenv.opts, collations.MySQL8()) require.NoError(t, err) dbc.ExpectRequestRE("select vdt.lastpk as lastpk, vdt.mismatch as mismatch, vdt.report as report", noResults, nil) err = wd.buildPlan(dbc, filter, testSchema) diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index b9aad39fe6c..d7a6f5a31b6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -49,7 +49,7 @@ const ( ) // controller is created by Engine. Members are initialized upfront. -// There is no mutex within a controller becaust its members are +// There is no mutex within a controller because its members are // either read-only or self-synchronized. type controller struct { vre *Engine diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go b/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go index b168625d20a..0273f05ab4e 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller_plan.go @@ -18,6 +18,7 @@ package vreplication import ( "fmt" + "strings" "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/vt/sqlparser" @@ -50,9 +51,83 @@ const ( reshardingJournalQuery ) +// A comment directive that you can include in your VReplication write +// statements if you want to bypass the safety checks that ensure you are +// being selective. The full comment directive looks like this: +// delete /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ from _vt.vreplication +const AllowUnsafeWriteCommentDirective = "ALLOW_UNSAFE_VREPLICATION_WRITE" + +// Check that the given WHERE clause is using at least one of the specified +// columns with an equality or in operator to ensure that it is being +// properly selective and not unintentionally going to potentially affect +// multiple workflows. +// The engine's exec function -- used by the VReplicationExec RPC -- should +// provide guardrails for data changing statements and if the user wants get +// around them they can e.g. use the ExecuteFetchAsDba RPC. +// If you as a developer truly do want to affect multiple workflows, you can +// add a comment directive using the AllowUnsafeWriteCommentDirective constant. +var isSelective = func(where *sqlparser.Where, columns ...*sqlparser.ColName) bool { + if where == nil { + return false + } + if len(columns) == 0 { + return true + } + selective := false + _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + switch node := node.(type) { + case *sqlparser.ComparisonExpr: + column, ok := node.Left.(*sqlparser.ColName) + if !ok { + return true, nil + } + wantedColumn := false + for i := range columns { + if columns[i].Equal(column) { + wantedColumn = true + break + } + } + // If we found a desired column, check that it is being used with an + // equality operator OR an in clause, logically being equal to any + // of N things. + if wantedColumn && + (node.Operator == sqlparser.EqualOp || node.Operator == sqlparser.InOp) { + selective = true // This is a safe statement + return false, nil // We can stop walking + } + default: + } + return true, nil + }, where) + return selective +} + +// tableSelectiveColumns is a map that can be used to declare +// what selective columns should be used (one or more) in queries +// against a table. +var tableSelectiveColumns = map[string][]*sqlparser.ColName{ + vreplicationTableName: { + {Name: sqlparser.NewIdentifierCI("id")}, + {Name: sqlparser.NewIdentifierCI("workflow")}, + }, +} + +// columnsAsCSV returns a comma-separated list of column names. +func columnsAsCSV(columns []*sqlparser.ColName) string { + if len(columns) == 0 { + return "" + } + colsForError := make([]string, len(columns)) + for i := range columns { + colsForError[i] = columns[i].Name.String() + } + return strings.Join(colsForError, ", ") +} + // buildControllerPlan parses the input query and returns an appropriate plan. -func buildControllerPlan(query string) (*controllerPlan, error) { - stmt, err := sqlparser.Parse(query) +func buildControllerPlan(query string, parser *sqlparser.Parser) (*controllerPlan, error) { + stmt, err := parser.Parse(query) if err != nil { return nil, err } @@ -163,7 +238,12 @@ func buildUpdatePlan(upd *sqlparser.Update) (*controllerPlan, error) { opcode: reshardingJournalQuery, }, nil case vreplicationTableName: - // no-op + if upd.Comments == nil || upd.Comments.Directives() == nil || !upd.Comments.Directives().IsSet(AllowUnsafeWriteCommentDirective) { + if safe := isSelective(upd.Where, tableSelectiveColumns[vreplicationTableName]...); !safe { + return nil, fmt.Errorf("unsafe WHERE clause in update without the /*vt+ %s */ comment directive: %s; should be using = or in with at least one of the following columns: %s", + AllowUnsafeWriteCommentDirective, sqlparser.String(upd.Where), columnsAsCSV(tableSelectiveColumns[vreplicationTableName])) + } + } default: return nil, fmt.Errorf("invalid table name: %s", tableName.Name.String()) } @@ -220,7 +300,12 @@ func buildDeletePlan(del *sqlparser.Delete) (*controllerPlan, error) { opcode: reshardingJournalQuery, }, nil case vreplicationTableName: - // no-op + if del.Comments == nil || del.Comments.Directives() == nil || !del.Comments.Directives().IsSet(AllowUnsafeWriteCommentDirective) { + if safe := isSelective(del.Where, tableSelectiveColumns[vreplicationTableName]...); !safe { + return nil, fmt.Errorf("unsafe WHERE clause in delete without the /*vt+ %s */ comment directive: %s; should be using = or in with at least one of the following columns: %s", + AllowUnsafeWriteCommentDirective, sqlparser.String(del.Where), columnsAsCSV(tableSelectiveColumns[vreplicationTableName])) + } + } default: return nil, fmt.Errorf("invalid table name: %s", tableName.Name.String()) } diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller_plan_test.go b/go/vt/vttablet/tabletmanager/vreplication/controller_plan_test.go index 391b8d9c67e..4d4383a79f1 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller_plan_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller_plan_test.go @@ -21,6 +21,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/sqlparser" ) type testControllerPlan struct { @@ -111,13 +113,25 @@ func TestControllerPlan(t *testing.T) { applier: "update _vt.vreplication set state = 'Running' where id in ::ids", }, }, { - in: "update _vt.vreplication set state='Running'", + in: "update _vt.vreplication set state='Running'", + err: "unsafe WHERE clause in update without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: ; should be using = or in with at least one of the following columns: id, workflow", + }, { + in: "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running'", plan: &testControllerPlan{ - query: "update _vt.vreplication set state='Running'", + query: "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running'", opcode: updateQuery, selector: "select id from _vt.vreplication", - applier: "update _vt.vreplication set state = 'Running' where id in ::ids", + applier: "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state = 'Running' where id in ::ids", }, + }, { + in: "update _vt.vreplication set state='Running', message='' where id >= 1", + err: "unsafe WHERE clause in update without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where id >= 1; should be using = or in with at least one of the following columns: id, workflow", + }, { + in: "update _vt.vreplication set state = 'Running' where state in ('Stopped', 'Error')", + err: "unsafe WHERE clause in update without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where state in ('Stopped', 'Error'); should be using = or in with at least one of the following columns: id, workflow", + }, { + in: "update _vt.vreplication set state='Running', message='' where state='Stopped'", + err: "unsafe WHERE clause in update without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where state = 'Stopped'; should be using = or in with at least one of the following columns: id, workflow", }, { in: "update _vt.vreplication set state='Running' where a = 1", plan: &testControllerPlan{ @@ -126,6 +140,7 @@ func TestControllerPlan(t *testing.T) { selector: "select id from _vt.vreplication where a = 1", applier: "update _vt.vreplication set state = 'Running' where id in ::ids", }, + err: "unsafe WHERE clause in update without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where a = 1; should be using = or in with at least one of the following columns: id, workflow", }, { in: "update _vt.resharding_journal set col = 1", plan: &testControllerPlan{ @@ -157,15 +172,21 @@ func TestControllerPlan(t *testing.T) { delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids", }, }, { - in: "delete from _vt.vreplication", + in: "delete from _vt.vreplication", + err: "unsafe WHERE clause in delete without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: ; should be using = or in with at least one of the following columns: id, workflow", + }, { + in: "delete /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ from _vt.vreplication", plan: &testControllerPlan{ - query: "delete from _vt.vreplication", + query: "delete /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ from _vt.vreplication", opcode: deleteQuery, selector: "select id from _vt.vreplication", - applier: "delete from _vt.vreplication where id in ::ids", + applier: "delete /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ from _vt.vreplication where id in ::ids", delCopyState: "delete from _vt.copy_state where vrepl_id in ::ids", delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids", }, + }, { + in: "delete from _vt.vreplication where state='Stopped'", + err: "unsafe WHERE clause in delete without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where state = 'Stopped'; should be using = or in with at least one of the following columns: id, workflow", }, { in: "delete from _vt.vreplication where a = 1", plan: &testControllerPlan{ @@ -176,6 +197,7 @@ func TestControllerPlan(t *testing.T) { delCopyState: "delete from _vt.copy_state where vrepl_id in ::ids", delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids", }, + err: "unsafe WHERE clause in delete without the /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ comment directive: where a = 1; should be using = or in with at least one of the following columns: id, workflow", }, { in: "delete from _vt.resharding_journal where id = 1", plan: &testControllerPlan{ @@ -240,7 +262,7 @@ func TestControllerPlan(t *testing.T) { }} for _, tcase := range tcases { t.Run(tcase.in, func(t *testing.T) { - pl, err := buildControllerPlan(tcase.in) + pl, err := buildControllerPlan(tcase.in, sqlparser.NewTestParser()) if tcase.err != "" { require.EqualError(t, err, tcase.err) return diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 8b81dd722c6..2b2937056ac 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -28,22 +28,22 @@ import ( "time" "vitess.io/vitess/go/constants/sidecar" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) const ( @@ -72,7 +72,7 @@ var waitRetryTime = 1 * time.Second // How frequently vcopier will update _vt.vreplication rows_copied var rowsCopiedUpdateInterval = 30 * time.Second -// How frequntly vcopier will garbage collect old copy_state rows. +// How frequently vcopier will garbage collect old copy_state rows. // By default, do it in between every 2nd and 3rd rows copied update. var copyStateGCInterval = (rowsCopiedUpdateInterval * 3) - (rowsCopiedUpdateInterval / 2) @@ -107,10 +107,12 @@ type Engine struct { throttlerClient *throttle.Client // This should only be set in Test Engines in order to short - // curcuit functions as needed in unit tests. It's automatically + // circuit functions as needed in unit tests. It's automatically // enabled in NewSimpleTestEngine. This should NOT be used in // production. shortcircuit bool + + env *vtenv.Environment } type journalEvent struct { @@ -127,14 +129,15 @@ type PostCopyAction struct { // NewEngine creates a new Engine. // A nil ts means that the Engine is disabled. -func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, lagThrottler *throttle.Throttler) *Engine { +func NewEngine(env *vtenv.Environment, config *tabletenv.TabletConfig, ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, lagThrottler *throttle.Throttler) *Engine { vre := &Engine{ + env: env, controllers: make(map[int32]*controller), ts: ts, cell: cell, mysqld: mysqld, journaler: make(map[string]*journalEvent), - ec: newExternalConnector(config.ExternalConnections), + ec: newExternalConnector(env, config.ExternalConnections), throttlerClient: throttle.NewBackgroundClient(lagThrottler, throttlerapp.VReplicationName, throttle.ThrottleCheckPrimaryWrite), } @@ -143,22 +146,24 @@ func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, cell string, mys // InitDBConfig should be invoked after the db name is computed. func (vre *Engine) InitDBConfig(dbcfgs *dbconfigs.DBConfigs) { - // If we're already initilized, it's a test engine. Ignore the call. + // If we're already initialized, it's a test engine. Ignore the call. if vre.dbClientFactoryFiltered != nil && vre.dbClientFactoryDba != nil { return } vre.dbClientFactoryFiltered = func() binlogplayer.DBClient { - return binlogplayer.NewDBClient(dbcfgs.FilteredWithDB()) + return binlogplayer.NewDBClient(dbcfgs.FilteredWithDB(), vre.env.Parser()) } vre.dbClientFactoryDba = func() binlogplayer.DBClient { - return binlogplayer.NewDBClient(dbcfgs.DbaWithDB()) + return binlogplayer.NewDBClient(dbcfgs.DbaWithDB(), vre.env.Parser()) } vre.dbName = dbcfgs.DBName } // NewTestEngine creates a new Engine for testing. func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactoryFiltered func() binlogplayer.DBClient, dbClientFactoryDba func() binlogplayer.DBClient, dbname string, externalConfig map[string]*dbconfigs.DBConfigs) *Engine { + env := vtenv.NewTestEnv() vre := &Engine{ + env: env, controllers: make(map[int32]*controller), ts: ts, cell: cell, @@ -167,15 +172,17 @@ func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, db dbClientFactoryDba: dbClientFactoryDba, dbName: dbname, journaler: make(map[string]*journalEvent), - ec: newExternalConnector(externalConfig), + ec: newExternalConnector(env, externalConfig), } return vre } // NewSimpleTestEngine creates a new Engine for testing that can -// also short curcuit functions as needed. +// also short circuit functions as needed. func NewSimpleTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactoryFiltered func() binlogplayer.DBClient, dbClientFactoryDba func() binlogplayer.DBClient, dbname string, externalConfig map[string]*dbconfigs.DBConfigs) *Engine { + env := vtenv.NewTestEnv() vre := &Engine{ + env: env, controllers: make(map[int32]*controller), ts: ts, cell: cell, @@ -184,7 +191,7 @@ func NewSimpleTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaem dbClientFactoryDba: dbClientFactoryDba, dbName: dbname, journaler: make(map[string]*journalEvent), - ec: newExternalConnector(externalConfig), + ec: newExternalConnector(env, externalConfig), shortcircuit: true, } return vre @@ -262,7 +269,7 @@ func (vre *Engine) retry(ctx context.Context, err error) { } if err := vre.openLocked(ctx); err == nil { // Don't invoke cancelRetry because openLocked - // will hold on to this context for later cancelation. + // will hold on to this context for later cancellation. vre.cancelRetry = nil vre.mu.Unlock() return @@ -362,7 +369,7 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) } defer vre.updateStats() - plan, err := buildControllerPlan(query) + plan, err := buildControllerPlan(query, vre.env.Parser()) if err != nil { return nil, err } @@ -524,7 +531,7 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) } return qr, nil case selectQuery, reshardingJournalQuery: - // select and resharding journal queries are passed through. + // Selects and resharding journal queries are passed through. return dbClient.ExecuteFetch(plan.query, maxRows) } panic("unreachable") @@ -847,7 +854,7 @@ func (vre *Engine) readAllRows(ctx context.Context) ([]map[string]string, error) return nil, err } defer dbClient.Close() - qr, err := dbClient.ExecuteFetch(fmt.Sprintf("select * from _vt.vreplication where db_name=%v", encodeString(vre.dbName)), maxRows) + qr, err := dbClient.ExecuteFetch(fmt.Sprintf("select * from _vt.vreplication where db_name=%s", encodeString(vre.dbName)), maxRows) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 32add04c8e0..6d34c52df4e 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -277,23 +277,7 @@ func TestEngineExec(t *testing.T) { t.Errorf("stats are mismatched: %v, want %v", globalStats.controllers, vre.controllers) } - // Test Delete of multiple rows - - dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) - dbClient.ExpectRequest("select id from _vt.vreplication where id > 1", testSelectorResponse2, nil) - dbClient.ExpectRequest("begin", nil, nil) - dbClient.ExpectRequest("delete from _vt.vreplication where id in (1, 2)", testDMLResponse, nil) - dbClient.ExpectRequest("delete from _vt.copy_state where vrepl_id in (1, 2)", nil, nil) - dbClient.ExpectRequest("delete from _vt.post_copy_action where vrepl_id in (1, 2)", nil, nil) - dbClient.ExpectRequest("commit", nil, nil) - - _, err = vre.Exec("delete from _vt.vreplication where id > 1") - if err != nil { - t.Fatal(err) - } - dbClient.Wait() - - // Test no delete + // Test simple delete. dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) dbClient.ExpectRequest("select id from _vt.vreplication where id = 3", &sqltypes.Result{}, nil) _, err = vre.Exec("delete from _vt.vreplication where id = 3") @@ -301,6 +285,21 @@ func TestEngineExec(t *testing.T) { t.Fatal(err) } dbClient.Wait() + + // Test unsafe writes of multiple rows, which we want to prevent. + unsafeQueries := []string{ + "delete from _vt.vreplication", + "delete from _vt.vreplication where id > 1", + "delete from _vt.vreplication where message != 'FROZEN'", + "update _vt.vreplication set workflow = 'bad'", + "update _vt.vreplication set state = 'Stopped' where id > 1", + "update _vt.vreplication set message = '' where state == 'Running'", + } + for _, unsafeQuery := range unsafeQueries { + _, err = vre.Exec(unsafeQuery) + require.Error(t, err, "%s should fail", unsafeQuery) + dbClient.Wait() + } } func TestEngineBadInsert(t *testing.T) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 1c20e2054be..873bf498c14 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -17,9 +17,8 @@ limitations under the License. package vreplication import ( - "sync" - "context" + "sync" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" @@ -28,6 +27,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -58,13 +58,15 @@ type VStreamerClient interface { } type externalConnector struct { + env *vtenv.Environment mu sync.Mutex dbconfigs map[string]*dbconfigs.DBConfigs connectors map[string]*mysqlConnector } -func newExternalConnector(dbcfgs map[string]*dbconfigs.DBConfigs) *externalConnector { +func newExternalConnector(env *vtenv.Environment, dbcfgs map[string]*dbconfigs.DBConfigs) *externalConnector { return &externalConnector{ + env: env, dbconfigs: dbcfgs, connectors: make(map[string]*mysqlConnector), } @@ -91,7 +93,7 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name) } c := &mysqlConnector{} - c.env = tabletenv.NewEnv(config, name) + c.env = tabletenv.NewEnv(ec.env, config, name) c.se = schema.NewEngine(c.env) c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se, nil, "") c.vstreamer.InitDBConfig("", "") diff --git a/go/vt/vttablet/tabletmanager/vreplication/flags.go b/go/vt/vttablet/tabletmanager/vreplication/flags.go index 44f07f87a0f..9f328f48a68 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/flags.go +++ b/go/vt/vttablet/tabletmanager/vreplication/flags.go @@ -26,9 +26,9 @@ import ( var ( retryDelay = 5 * time.Second - maxTimeToRetryError time.Duration // default behavior is to keep retrying, for backward compatibility + maxTimeToRetryError time.Duration // Default behavior is to keep retrying, for backward compatibility - tabletTypesStr = "in_order:REPLICA,PRIMARY" + tabletTypesStr = "in_order:REPLICA,PRIMARY" // Default value relayLogMaxSize = 250000 relayLogMaxItems = 5000 @@ -45,10 +45,6 @@ func registerVReplicationFlags(fs *pflag.FlagSet) { fs.DurationVar(&retryDelay, "vreplication_retry_delay", retryDelay, "delay before retrying a failed workflow event in the replication phase") fs.DurationVar(&maxTimeToRetryError, "vreplication_max_time_to_retry_on_error", maxTimeToRetryError, "stop automatically retrying when we've had consecutive failures with the same error for this long after the first occurrence") - // these are the default tablet_types that will be used by the tablet picker to find source tablets for a vreplication stream - // it can be overridden by passing a different list to the MoveTables or Reshard commands - fs.StringVar(&tabletTypesStr, "vreplication_tablet_type", tabletTypesStr, "comma separated list of tablet types used as a source") - fs.IntVar(&relayLogMaxSize, "relay_log_max_size", relayLogMaxSize, "Maximum buffer size (in bytes) for VReplication target buffering. If single rows are larger than this, a single row is buffered at a time.") fs.IntVar(&relayLogMaxItems, "relay_log_max_items", relayLogMaxItems, "Maximum number of rows for VReplication target buffering.") @@ -62,12 +58,11 @@ func registerVReplicationFlags(fs *pflag.FlagSet) { fs.IntVar(&vreplicationHeartbeatUpdateInterval, "vreplication_heartbeat_update_interval", vreplicationHeartbeatUpdateInterval, "Frequency (in seconds, default 1, max 60) at which the time_updated column of a vreplication stream when idling") fs.BoolVar(&vreplicationStoreCompressedGTID, "vreplication_store_compressed_gtid", vreplicationStoreCompressedGTID, "Store compressed gtids in the pos column of the sidecar database's vreplication table") - // deprecated flags (7.0), however there are several e2e tests that still depend on them - fs.Duration("vreplication_healthcheck_topology_refresh", 30*time.Second, "refresh interval for re-reading the topology") - fs.Duration("vreplication_healthcheck_retry_delay", 5*time.Second, "healthcheck retry delay") - fs.Duration("vreplication_healthcheck_timeout", 1*time.Minute, "healthcheck retry delay") - fs.IntVar(&vreplicationParallelInsertWorkers, "vreplication-parallel-insert-workers", vreplicationParallelInsertWorkers, "Number of parallel insertion workers to use during copy phase. Set <= 1 to disable parallelism, or > 1 to enable concurrent insertion during copy phase.") + + // Deprecated and ignored in v19. + fs.String("vreplication_tablet_type", tabletTypesStr, "Comma-separated list of tablet types used as a source.") + fs.MarkDeprecated("vreplication_tablet_type", "As of v19 this is ignored and will be removed in a future release.") } func init() { diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 576ce4c22a8..262ba28187d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -28,25 +28,26 @@ import ( "testing" "time" - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/vt/vttablet" - - "vitess.io/vitess/go/test/utils" - "vitess.io/vitess/go/vt/dbconfigs" - "github.com/spf13/pflag" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/vt/sqlparser" + _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/binlog/binlogplayer" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/dbconnpool" "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vttablet" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -69,6 +70,7 @@ var ( globalFBC = &fakeBinlogClient{} vrepldb = "vrepl" globalDBQueries = make(chan string, 1000) + lastMultiExecQuery = "" testForeignKeyQueries = false testSetForeignKeyQueries = false doNotLogDBQueries = false @@ -225,6 +227,15 @@ func execStatements(t *testing.T, queries []string) { } } +func execConnStatements(t *testing.T, conn *dbconnpool.DBConnection, queries []string) { + t.Helper() + for _, query := range queries { + if _, err := conn.ExecuteFetch(query, 10000, false); err != nil { + t.Fatalf("ExecuteFetch(%v) failed: %v", query, err) + } + } +} + //-------------------------------------- // Topos and tablets @@ -484,6 +495,23 @@ func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Resu return qr, err } +func (dc *realDBClient) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + queries, err := sqlparser.NewTestParser().SplitStatementToPieces(query) + if err != nil { + return nil, err + } + results := make([]*sqltypes.Result, 0, len(queries)) + for _, query := range queries { + qr, err := dc.ExecuteFetch(query, maxrows) + if err != nil { + return nil, err + } + results = append(results, qr) + } + lastMultiExecQuery = query + return results, nil +} + func expectDeleteQueries(t *testing.T) { t.Helper() if doNotLogDBQueries { @@ -496,6 +524,19 @@ func expectDeleteQueries(t *testing.T) { )) } +func deleteAllVReplicationStreams(t *testing.T) { + t.Helper() + res, err := playerEngine.Exec("select id from _vt.vreplication") + require.NoError(t, err, "could not select ids from _vt.vreplication: %v", err) + ids := make([]string, len(res.Rows)) + for i, row := range res.Rows { + id := row[0].ToString() + ids[i] = id + } + _, err = playerEngine.Exec(fmt.Sprintf("delete from _vt.vreplication where id in (%s)", strings.Join(ids, ","))) + require.NoError(t, err, "failed to delete vreplication rows: %v", err) +} + func expectLogsAndUnsubscribe(t *testing.T, logs []LogExpectation, logCh chan *VrLogStats) { t.Helper() defer vrLogStatsLogger.Unsubscribe(logCh) @@ -540,6 +581,9 @@ func shouldIgnoreQuery(query string) bool { ", component_throttled=", // update of last throttle time, can happen out-of-band, so can't test for it "context cancel", "SELECT rows_copied FROM _vt.vreplication WHERE id=", + // This is only executed if the table has no defined Primary Key, which we don't know in the lower level + // code. + "SELECT index_cols.COLUMN_NAME AS column_name, index_cols.INDEX_NAME as index_name FROM information_schema.STATISTICS", } if sidecardb.MatchesInitQuery(query) { return true diff --git a/go/vt/vttablet/tabletmanager/vreplication/insert_generator.go b/go/vt/vttablet/tabletmanager/vreplication/insert_generator.go index da1753a8444..62af8c9396d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/insert_generator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/insert_generator.go @@ -21,8 +21,10 @@ import ( "strings" "time" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/vt/throttler" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) // InsertGenerator generates a vreplication insert statement. @@ -50,6 +52,7 @@ func NewInsertGenerator(state binlogdatapb.VReplicationWorkflowState, dbname str // AddRow adds a row to the insert statement. func (ig *InsertGenerator) AddRow(workflow string, bls *binlogdatapb.BinlogSource, pos, cell, tabletTypes string, workflowType binlogdatapb.VReplicationWorkflowType, workflowSubType binlogdatapb.VReplicationWorkflowSubType, deferSecondaryKeys bool) { + protoutil.SortBinlogSourceTables(bls) fmt.Fprintf(ig.buf, "%s(%v, %v, %v, %v, %v, %v, %v, %v, 0, '%v', %v, %d, %d, %v)", ig.prefix, encodeString(workflow), diff --git a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go index 9dfdee766d1..b9dc716d56f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go @@ -81,9 +81,7 @@ func TestJournalOneToOne(t *testing.T) { )) // Delete all vreplication streams. There should be only one, but we don't know its id. - if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { - t.Fatal(err) - } + deleteAllVReplicationStreams(t) expectDeleteQueries(t) } @@ -150,9 +148,7 @@ func TestJournalOneToMany(t *testing.T) { )) // Delete all vreplication streams. There should be only one, but we don't know its id. - if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { - t.Fatal(err) - } + deleteAllVReplicationStreams(t) expectDeleteQueries(t) } @@ -211,9 +207,7 @@ func TestJournalTablePresent(t *testing.T) { )) // Delete all vreplication streams. There should be only one, but we don't know its id. - if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { - t.Fatal(err) - } + deleteAllVReplicationStreams(t) expectDeleteQueries(t) } @@ -264,9 +258,7 @@ func TestJournalTableNotPresent(t *testing.T) { defer execStatements(t, []string{"delete from _vt.resharding_journal"}) // Delete all vreplication streams. There should be only one, but we don't know its id. - if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { - t.Fatal(err) - } + deleteAllVReplicationStreams(t) expectDeleteQueries(t) } @@ -326,8 +318,6 @@ func TestJournalTableMixed(t *testing.T) { )) // Delete all vreplication streams. There should be only one, but we don't know its id. - if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { - t.Fatal(err) - } + deleteAllVReplicationStreams(t) expectDeleteQueries(t) } diff --git a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/sequenced_expectation_set.go b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/sequenced_expectation_set.go index 9ab0bf99043..95b2c3e4f67 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/sequenced_expectation_set.go +++ b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/sequenced_expectation_set.go @@ -12,7 +12,7 @@ type sequencedExpectationSet map[SequencedExpectation]any func (ses *sequencedExpectationSet) Add(expectation SequencedExpectation) { if ses == nil { - *ses = make(sequencedExpectationSet) + ses = new(sequencedExpectationSet) } (*ses)[expectation] = true } @@ -27,7 +27,7 @@ func (ses *sequencedExpectationSet) Contains(expectation SequencedExpectation) b func (ses *sequencedExpectationSet) Slice() []SequencedExpectation { s := make([]SequencedExpectation, 0) - if len(*ses) == 0 { + if ses == nil || len(*ses) == 0 { return s } for se := range *ses { diff --git a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go index a7015b0daf5..ebe145461d7 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go @@ -41,7 +41,7 @@ func NewVerifier(sequence ExpectationSequence) *Verifier { } // AcceptQuery verifies that the provided query is valid according to the -// internal ExpectationSequence and the internal History of preceeding queries. +// internal ExpectationSequence and the internal History of preceding queries. // Returns a *Result indicating whether the query was accepted and, if not, // diagnostic details indicating why not. func (v *Verifier) AcceptQuery(query string) *Result { @@ -159,7 +159,7 @@ func (v *Verifier) checkQueryAgainstExpectation(query string, expectation Sequen // Query passed expectation. result.Accepted = true result.Matched = true - result.Message = "matched expectated query and expected order" + result.Message = "matched expected query and expected order" return true } diff --git a/go/vt/vttablet/tabletmanager/vreplication/replica_connector.go b/go/vt/vttablet/tabletmanager/vreplication/replica_connector.go index 9c6f427b418..c3cd073f0bf 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replica_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replica_connector.go @@ -19,16 +19,13 @@ package vreplication import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" "context" - "vitess.io/vitess/go/sqltypes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" ) @@ -38,8 +35,7 @@ import ( // This is used by binlog server to make vstream connection // using the vstream connection, it will parse the events from binglog // to fetch the corresponding GTID for required recovery time -func NewReplicaConnector(connParams *mysql.ConnParams) *ReplicaConnector { - +func NewReplicaConnector(venv *vtenv.Environment, connParams *mysql.ConnParams) *ReplicaConnector { // Construct config := tabletenv.NewDefaultConfig() dbCfg := &dbconfigs.DBConfigs{ @@ -49,7 +45,7 @@ func NewReplicaConnector(connParams *mysql.ConnParams) *ReplicaConnector { dbCfg.SetDbParams(*connParams, *connParams, *connParams) config.DB = dbCfg c := &ReplicaConnector{conn: connParams} - env := tabletenv.NewEnv(config, "source") + env := tabletenv.NewEnv(venv, config, "source") c.se = schema.NewEngine(env) c.se.SkipMetaCheck = true c.vstreamer = vstreamer.NewEngine(env, nil, c.se, nil, "") @@ -70,33 +66,12 @@ type ReplicaConnector struct { vstreamer *vstreamer.Engine } -func (c *ReplicaConnector) shutdown() { +func (c *ReplicaConnector) Close() error { c.vstreamer.Close() c.se.Close() -} - -func (c *ReplicaConnector) Open(ctx context.Context) error { - return nil -} - -func (c *ReplicaConnector) Close(ctx context.Context) error { - c.shutdown() return nil } func (c *ReplicaConnector) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { return c.vstreamer.Stream(ctx, startPos, nil, filter, throttlerapp.ReplicaConnectorName, send) } - -// VStreamRows streams rows from query result -func (c *ReplicaConnector) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error { - var row []sqltypes.Value - if lastpk != nil { - r := sqltypes.Proto3ToResult(lastpk) - if len(r.Rows) != 1 { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected lastpk input: %v", lastpk) - } - row = r.Rows[0] - } - return c.vstreamer.StreamRows(ctx, query, row, send) -} diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go index 39ffdef04ae..a328249d0e0 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go @@ -29,13 +29,14 @@ import ( vjson "vitess.io/vitess/go/mysql/json" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // ReplicatorPlan is the execution plan for the replicator. It contains @@ -58,6 +59,7 @@ type ReplicatorPlan struct { ColInfoMap map[string][]*ColumnInfo stats *binlogplayer.Stats Source *binlogdatapb.BinlogSource + collationEnv *collations.Environment } // buildExecution plan uses the field info as input and the partially built @@ -97,11 +99,12 @@ func (rp *ReplicatorPlan) buildExecutionPlan(fieldEvent *binlogdatapb.FieldEvent // requires us to wait for the field info sent by the source. func (rp *ReplicatorPlan) buildFromFields(tableName string, lastpk *sqltypes.Result, fields []*querypb.Field) (*TablePlan, error) { tpb := &tablePlanBuilder{ - name: sqlparser.NewIdentifierCS(tableName), - lastpk: lastpk, - colInfos: rp.ColInfoMap[tableName], - stats: rp.stats, - source: rp.Source, + name: sqlparser.NewIdentifierCS(tableName), + lastpk: lastpk, + colInfos: rp.ColInfoMap[tableName], + stats: rp.stats, + source: rp.Source, + collationEnv: rp.collationEnv, } for _, field := range fields { colName := sqlparser.NewIdentifierCI(field.Name) @@ -195,6 +198,7 @@ type TablePlan struct { Insert *sqlparser.ParsedQuery Update *sqlparser.ParsedQuery Delete *sqlparser.ParsedQuery + MultiDelete *sqlparser.ParsedQuery Fields []*querypb.Field EnumValuesMap map[string](map[string]string) ConvertIntToEnum map[string]bool @@ -215,6 +219,8 @@ type TablePlan struct { PartialInserts map[string]*sqlparser.ParsedQuery // PartialUpdates are same as PartialInserts, but for update statements PartialUpdates map[string]*sqlparser.ParsedQuery + + CollationEnv *collations.Environment } // MarshalJSON performs a custom JSON Marshalling. @@ -252,7 +258,7 @@ func (tp *TablePlan) applyBulkInsert(sqlbuffer *bytes2.Buffer, rows []*querypb.R if i > 0 { sqlbuffer.WriteString(", ") } - if err := tp.BulkInsertValues.AppendFromRow(sqlbuffer, tp.Fields, row, tp.FieldsToSkip); err != nil { + if err := appendFromRow(tp.BulkInsertValues, sqlbuffer, tp.Fields, row, tp.FieldsToSkip); err != nil { return nil, err } } @@ -297,7 +303,7 @@ func (tp *TablePlan) isOutsidePKRange(bindvars map[string]*querypb.BindVariable, rowVal, _ := sqltypes.BindVariableToValue(bindvar) // TODO(king-11) make collation aware - result, err := evalengine.NullsafeCompare(rowVal, tp.Lastpk.Rows[0][0], collations.Unknown) + result, err := evalengine.NullsafeCompare(rowVal, tp.Lastpk.Rows[0][0], tp.CollationEnv, collations.Unknown) // If rowVal is > last pk, transaction will be a noop, so don't apply this statement if err == nil && result > 0 { tp.Stats.NoopQueryCount.Add(stmtType, 1) @@ -315,7 +321,7 @@ func (tp *TablePlan) isOutsidePKRange(bindvars map[string]*querypb.BindVariable, func (tp *TablePlan) bindFieldVal(field *querypb.Field, val *sqltypes.Value) (*querypb.BindVariable, error) { if conversion, ok := tp.ConvertCharset[field.Name]; ok && !val.IsNull() { // Non-null string value, for which we have a charset conversion instruction - fromCollation := collations.Local().DefaultCollationForCharset(conversion.FromCharset) + fromCollation := tp.CollationEnv.DefaultCollationForCharset(conversion.FromCharset) if fromCollation == collations.Unknown { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Character set %s not supported for column %s", conversion.FromCharset, field.Name) } @@ -444,6 +450,126 @@ func (tp *TablePlan) applyChange(rowChange *binlogdatapb.RowChange, executor fun return nil, nil } +// applyBulkDeleteChanges applies a bulk DELETE statement from the row changes +// to the target table -- which resulted from a DELETE statement executed on the +// source that deleted N rows -- using an IN clause with the primary key values +// of the rows to be deleted. This currently only supports tables with single +// column primary keys. This limitation is in place for now as we know that case +// will still be efficient. When using large multi-column IN or OR group clauses +// in DELETES we could end up doing large (table) scans that actually make things +// slower. +// TODO: Add support for multi-column primary keys. +func (tp *TablePlan) applyBulkDeleteChanges(rowDeletes []*binlogdatapb.RowChange, executor func(string) (*sqltypes.Result, error), maxQuerySize int64) (*sqltypes.Result, error) { + if len(rowDeletes) == 0 { + return &sqltypes.Result{}, nil + } + if (len(tp.TablePlanBuilder.pkCols) + len(tp.TablePlanBuilder.extraSourcePkCols)) != 1 { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "bulk delete is only supported for tables with a single primary key column") + } + if tp.MultiDelete == nil { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "plan has no bulk delete query") + } + + baseQuerySize := int64(len(tp.MultiDelete.Query)) + querySize := baseQuerySize + + execQuery := func(pkVals *[]sqltypes.Value) (*sqltypes.Result, error) { + pksBV, err := sqltypes.BuildBindVariable(*pkVals) + if err != nil { + return nil, err + } + query, err := tp.MultiDelete.GenerateQuery(map[string]*querypb.BindVariable{"bulk_pks": pksBV}, nil) + if err != nil { + return nil, err + } + tp.TablePlanBuilder.stats.BulkQueryCount.Add("delete", 1) + return executor(query) + } + + pkIndex := -1 + pkVals := make([]sqltypes.Value, 0, len(rowDeletes)) + for _, rowDelete := range rowDeletes { + vals := sqltypes.MakeRowTrusted(tp.Fields, rowDelete.Before) + if pkIndex == -1 { + for i := range vals { + if tp.PKIndices[i] { + pkIndex = i + break + } + } + } + addedSize := int64(len(vals[pkIndex].Raw()) + 2) // Plus 2 for the comma and space + if querySize+addedSize > maxQuerySize { + if _, err := execQuery(&pkVals); err != nil { + return nil, err + } + pkVals = nil + querySize = baseQuerySize + } + pkVals = append(pkVals, vals[pkIndex]) + querySize += addedSize + } + + return execQuery(&pkVals) +} + +// applyBulkInsertChanges generates a multi-row INSERT statement from the row +// changes generated from a multi-row INSERT statement executed on the source. +func (tp *TablePlan) applyBulkInsertChanges(rowInserts []*binlogdatapb.RowChange, executor func(string) (*sqltypes.Result, error), maxQuerySize int64) (*sqltypes.Result, error) { + if len(rowInserts) == 0 { + return &sqltypes.Result{}, nil + } + if tp.BulkInsertFront == nil { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "plan has no bulk insert query") + } + + prefix := &strings.Builder{} + prefix.WriteString(tp.BulkInsertFront.Query) + prefix.WriteString(" values ") + insertPrefix := prefix.String() + maxQuerySize -= int64(len(insertPrefix)) + values := &strings.Builder{} + + execQuery := func(vals *strings.Builder) (*sqltypes.Result, error) { + if tp.BulkInsertOnDup != nil { + vals.WriteString(tp.BulkInsertOnDup.Query) + } + tp.TablePlanBuilder.stats.BulkQueryCount.Add("insert", 1) + return executor(insertPrefix + vals.String()) + } + + newStmt := true + for _, rowInsert := range rowInserts { + rowValues := &strings.Builder{} + bindvars := make(map[string]*querypb.BindVariable, len(tp.Fields)) + vals := sqltypes.MakeRowTrusted(tp.Fields, rowInsert.After) + for n, field := range tp.Fields { + bindVar, err := tp.bindFieldVal(field, &vals[n]) + if err != nil { + return nil, err + } + bindvars["a_"+field.Name] = bindVar + } + if err := tp.BulkInsertValues.Append(rowValues, bindvars, nil); err != nil { + return nil, err + } + if int64(values.Len()+2+rowValues.Len()) > maxQuerySize { // Plus 2 for the comma and space + if _, err := execQuery(values); err != nil { + return nil, err + } + values.Reset() + newStmt = true + } + if !newStmt { + values.WriteString(", ") + } + values.WriteString(rowValues.String()) + newStmt = false + } + + return execQuery(values) +} + func getQuery(pq *sqlparser.ParsedQuery, bindvars map[string]*querypb.BindVariable) (string, error) { sql, err := pq.GenerateQuery(bindvars, nil) if err != nil { @@ -481,3 +607,74 @@ func valsEqual(v1, v2 sqltypes.Value) bool { // Compare content only if none are null. return v1.ToString() == v2.ToString() } + +// AppendFromRow behaves like Append but takes a querypb.Row directly, assuming that +// the fields in the row are in the same order as the placeholders in this query. The fields might include generated +// columns which are dropped, by checking against skipFields, before binding the variables +// note: there can be more fields than bind locations since extra columns might be requested from the source if not all +// primary keys columns are present in the target table, for example. Also some values in the row may not correspond for +// values from the database on the source: sum/count for aggregation queries, for example +func appendFromRow(pq *sqlparser.ParsedQuery, buf *bytes2.Buffer, fields []*querypb.Field, row *querypb.Row, skipFields map[string]bool) error { + bindLocations := pq.BindLocations() + if len(fields) < len(bindLocations) { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of fields: got %d fields for %d bind locations ", + len(fields), len(bindLocations)) + } + + type colInfo struct { + typ querypb.Type + length int64 + offset int64 + } + rowInfo := make([]*colInfo, 0) + + offset := int64(0) + for i, field := range fields { // collect info required for fields to be bound + length := row.Lengths[i] + if !skipFields[strings.ToLower(field.Name)] { + rowInfo = append(rowInfo, &colInfo{ + typ: field.Type, + length: length, + offset: offset, + }) + } + if length > 0 { + offset += row.Lengths[i] + } + } + + // bind field values to locations + var offsetQuery int + for i, loc := range bindLocations { + col := rowInfo[i] + buf.WriteString(pq.Query[offsetQuery:loc.Offset]) + typ := col.typ + + switch typ { + case querypb.Type_TUPLE: + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected Type_TUPLE for value %d", i) + case querypb.Type_JSON: + if col.length < 0 { // An SQL NULL and not an actual JSON value + buf.WriteString(sqltypes.NullStr) + } else { // A JSON value (which may be a JSON null literal value) + buf2 := row.Values[col.offset : col.offset+col.length] + vv, err := vjson.MarshalSQLValue(buf2) + if err != nil { + return err + } + buf.WriteString(vv.RawStr()) + } + default: + if col.length < 0 { + // -1 means a null variable; serialize it directly + buf.WriteString(sqltypes.NullStr) + } else { + vv := sqltypes.MakeTrusted(typ, row.Values[col.offset:col.offset+col.length]) + vv.EncodeSQLBytes2(buf) + } + } + offsetQuery = loc.Offset + loc.Length + } + buf.WriteString(pq.Query[offsetQuery:]) + return nil +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go index 780b1c0d064..6c9f92128ac 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go @@ -21,11 +21,14 @@ import ( "strings" "testing" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/binlog/binlogplayer" + "vitess.io/vitess/go/vt/sqlparser" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) @@ -239,7 +242,7 @@ func TestBuildPlayerPlan(t *testing.T) { PKReferences: []string{"c1"}, InsertFront: "insert into t1(c1,c2,c3)", InsertValues: "(:a_c1,:a_c2,:a_c3)", - InsertOnDup: "on duplicate key update c2=values(c2)", + InsertOnDup: " on duplicate key update c2=values(c2)", Insert: "insert into t1(c1,c2,c3) values (:a_c1,:a_c2,:a_c3) on duplicate key update c2=values(c2)", Update: "update t1 set c2=:a_c2 where c1=:b_c1", Delete: "update t1 set c2=null where c1=:b_c1", @@ -261,7 +264,7 @@ func TestBuildPlayerPlan(t *testing.T) { PKReferences: []string{"c1", "pk1", "pk2"}, InsertFront: "insert into t1(c1,c2,c3)", InsertValues: "(:a_c1,:a_c2,:a_c3)", - InsertOnDup: "on duplicate key update c2=values(c2)", + InsertOnDup: " on duplicate key update c2=values(c2)", Insert: "insert into t1(c1,c2,c3) select :a_c1, :a_c2, :a_c3 from dual where (:a_pk1,:a_pk2) <= (1,'aaa') on duplicate key update c2=values(c2)", Update: "update t1 set c2=:a_c2 where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", Delete: "update t1 set c2=null where c1=:b_c1 and (:b_pk1,:b_pk2) <= (1,'aaa')", @@ -571,16 +574,16 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "bad query", }}, }, - err: "syntax error at position 4 near 'bad'", + err: "syntax error at position 4 near 'bad' in query: bad query", }, { // not a select input: &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ Match: "t1", - Filter: "update t1 set val=1", + Filter: "update t1 set val = 1", }}, }, - err: "unexpected: update t1 set val = 1", + err: "unsupported non-select statement in query: update t1 set val = 1", }, { // no distinct input: &binlogdatapb.Filter{ @@ -589,7 +592,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select distinct c1 from t1", }}, }, - err: "unexpected: select distinct c1 from t1", + err: "unsupported distinct clause in query: select distinct c1 from t1", }, { // no ',' join input: &binlogdatapb.Filter{ @@ -598,7 +601,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select * from t1, t2", }}, }, - err: "unexpected: select * from t1, t2", + err: "unsupported multi-table usage in query: select * from t1, t2", }, { // no join input: &binlogdatapb.Filter{ @@ -607,7 +610,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select * from t1 join t2", }}, }, - err: "unexpected: select * from t1 join t2", + err: "unsupported from expression (*sqlparser.JoinTableExpr) in query: select * from t1 join t2", }, { // no subqueries input: &binlogdatapb.Filter{ @@ -616,7 +619,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select * from (select * from t2) as a", }}, }, - err: "unexpected: select * from (select * from t2) as a", + err: "unsupported from source (*sqlparser.DerivedTable) in query: select * from (select * from t2) as a", }, { // cannot combine '*' with other input: &binlogdatapb.Filter{ @@ -625,7 +628,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select *, c1 from t1", }}, }, - err: "unexpected: select *, c1 from t1", + err: "unsupported mix of '*' and columns in query: select *, c1 from t1", }, { // cannot combine '*' with other (different code path) input: &binlogdatapb.Filter{ @@ -634,7 +637,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select c1, * from t1", }}, }, - err: "unexpected: *", + err: "invalid expression: * in query: select c1, * from t1", }, { // no distinct in func input: &binlogdatapb.Filter{ @@ -643,7 +646,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select hour(distinct c1) as a from t1", }}, }, - err: "syntax error at position 21 near 'distinct'", + err: "syntax error at position 21 near 'distinct' in query: select hour(distinct c1) as a from t1", }, { // funcs need alias input: &binlogdatapb.Filter{ @@ -652,7 +655,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select hour(c1) from t1", }}, }, - err: "expression needs an alias: hour(c1)", + err: "expression needs an alias: hour(c1) in query: select hour(c1) from t1", }, { // only count(*) input: &binlogdatapb.Filter{ @@ -661,7 +664,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select count(c1) as c from t1", }}, }, - err: "only count(*) is supported: count(c1)", + err: "only count(*) is supported: count(c1) in query: select count(c1) as c from t1", }, { // no sum(*) input: &binlogdatapb.Filter{ @@ -670,7 +673,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select sum(*) as c from t1", }}, }, - err: "syntax error at position 13", + err: "syntax error at position 13 in query: select sum(*) as c from t1", }, { // sum should have only one argument input: &binlogdatapb.Filter{ @@ -679,7 +682,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select sum(a, b) as c from t1", }}, }, - err: "syntax error at position 14", + err: "syntax error at position 14 in query: select sum(a, b) as c from t1", }, { // no complex expr in sum input: &binlogdatapb.Filter{ @@ -688,7 +691,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select sum(a + b) as c from t1", }}, }, - err: "unexpected: sum(a + b)", + err: "unsupported non-column name in sum clause: sum(a + b) in query: select sum(a + b) as c from t1", }, { // no complex expr in group by input: &binlogdatapb.Filter{ @@ -697,7 +700,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select a from t1 group by a + 1", }}, }, - err: "unexpected: a + 1", + err: "unsupported non-column name or alias in group by clause: a + 1 in query: select a from t1 group by a + 1", }, { // group by does not reference alias input: &binlogdatapb.Filter{ @@ -706,7 +709,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select a as b from t1 group by a", }}, }, - err: "group by expression does not reference an alias in the select list: a", + err: "group by expression does not reference an alias in the select list: a in query: select a as b from t1 group by a", }, { // cannot group by aggr input: &binlogdatapb.Filter{ @@ -715,7 +718,7 @@ func TestBuildPlayerPlan(t *testing.T) { Filter: "select count(*) as a from t1 group by a", }}, }, - err: "group by expression is not allowed to reference an aggregate expression: a", + err: "group by expression is not allowed to reference an aggregate expression: a in query: select count(*) as a from t1 group by a", }} PrimaryKeyInfos := map[string][]*ColumnInfo{ @@ -733,29 +736,23 @@ func TestBuildPlayerPlan(t *testing.T) { } for _, tcase := range testcases { - plan, err := buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) - gotPlan, _ := json.Marshal(plan) - wantPlan, _ := json.Marshal(tcase.plan) - if string(gotPlan) != string(wantPlan) { - t.Errorf("Filter(%v):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) - } + plan, err := buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, nil, binlogplayer.NewStats(), collations.MySQL8(), sqlparser.NewTestParser()) gotErr := "" if err != nil { gotErr = err.Error() } - if gotErr != tcase.err { - t.Errorf("Filter err(%v): %s, want %v", tcase.input, gotErr, tcase.err) - } + require.Equal(t, tcase.err, gotErr, "Filter err(%v): %s, want %v", tcase.input, gotErr, tcase.err) + gotPlan, _ := json.Marshal(plan) + wantPlan, _ := json.Marshal(tcase.plan) + require.Equal(t, string(wantPlan), string(gotPlan), "Filter(%v):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) - plan, err = buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, copyState, binlogplayer.NewStats()) + plan, err = buildReplicatorPlan(getSource(tcase.input), PrimaryKeyInfos, copyState, binlogplayer.NewStats(), collations.MySQL8(), sqlparser.NewTestParser()) if err != nil { continue } gotPlan, _ = json.Marshal(plan) wantPlan, _ = json.Marshal(tcase.planpk) - if string(gotPlan) != string(wantPlan) { - t.Errorf("Filter(%v,copyState):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) - } + require.Equal(t, string(wantPlan), string(gotPlan), "Filter(%v,copyState):\n%s, want\n%s", tcase.input, gotPlan, wantPlan) } } @@ -777,7 +774,7 @@ func TestBuildPlayerPlanNoDup(t *testing.T) { Filter: "select * from t", }}, } - _, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) + _, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats(), collations.MySQL8(), sqlparser.NewTestParser()) want := "more than one target for source table t" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("buildReplicatorPlan err: %v, must contain: %v", err, want) @@ -798,7 +795,7 @@ func TestBuildPlayerPlanExclude(t *testing.T) { Filter: "", }}, } - plan, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats()) + plan, err := buildReplicatorPlan(getSource(input), PrimaryKeyInfos, nil, binlogplayer.NewStats(), collations.MySQL8(), sqlparser.NewTestParser()) assert.NoError(t, err) want := &TestReplicatorPlan{ diff --git a/go/vt/vttablet/tabletmanager/vreplication/stats.go b/go/vt/vttablet/tabletmanager/vreplication/stats.go index 6379a9ba04f..5b5b6ede24c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/stats.go +++ b/go/vt/vttablet/tabletmanager/vreplication/stats.go @@ -59,10 +59,12 @@ type vrStats struct { mu sync.Mutex isOpen bool controllers map[int32]*controller + + ThrottledCount *stats.Counter } func (st *vrStats) register() { - + st.ThrottledCount = stats.NewCounter("", "") stats.NewGaugeFunc("VReplicationStreamCount", "Number of vreplication streams", st.numControllers) stats.NewGaugeFunc("VReplicationLagSecondsMax", "Max vreplication seconds behind primary", st.maxReplicationLagSeconds) stats.NewStringMapFuncWithMultiLabels( @@ -254,6 +256,39 @@ func (st *vrStats) register() { return result }) + stats.NewGaugesFuncWithMultiLabels( + "VReplicationBulkQueryCount", + "vreplication vplayer queries with consolidated row events counts per DML type per stream", + []string{"source_keyspace", "source_shard", "workflow", "counts", "dml_type"}, + func() map[string]int64 { + st.mu.Lock() + defer st.mu.Unlock() + result := make(map[string]int64, len(st.controllers)) + for _, ct := range st.controllers { + for label, count := range ct.blpStats.BulkQueryCount.Counts() { + if label == "" { + continue + } + result[ct.source.Keyspace+"."+ct.source.Shard+"."+ct.workflow+"."+fmt.Sprintf("%v", ct.id)+"."+label] = count + } + } + return result + }) + stats.NewCounterFunc( + "VReplicationBulkQueryCountTotal", + "vreplication vplayer queries with consolidated row events counts aggregated across all streams", + func() int64 { + st.mu.Lock() + defer st.mu.Unlock() + result := int64(0) + for _, ct := range st.controllers { + for _, count := range ct.blpStats.BulkQueryCount.Counts() { + result += count + } + } + return result + }) + stats.NewGaugesFuncWithMultiLabels( "VReplicationNoopQueryCount", "vreplication noop query counts per stream", @@ -287,6 +322,41 @@ func (st *vrStats) register() { } return result }) + + stats.NewGaugesFuncWithMultiLabels( + "VReplicationTrxQueryBatchCount", + "vreplication vplayer transaction query batch counts per type per stream", + []string{"source_keyspace", "source_shard", "workflow", "counts", "commit_or_not"}, + func() map[string]int64 { + st.mu.Lock() + defer st.mu.Unlock() + result := make(map[string]int64, len(st.controllers)) + for _, ct := range st.controllers { + for label, count := range ct.blpStats.TrxQueryBatchCount.Counts() { + if label == "" { + continue + } + result[ct.source.Keyspace+"."+ct.source.Shard+"."+ct.workflow+"."+fmt.Sprintf("%v", ct.id)+"."+label] = count + } + } + return result + }) + + stats.NewCounterFunc( + "VReplicationTrxQueryBatchCountTotal", + "vreplication vplayer transaction query batch counts aggregated across all streams", + func() int64 { + st.mu.Lock() + defer st.mu.Unlock() + result := int64(0) + for _, ct := range st.controllers { + for _, count := range ct.blpStats.TrxQueryBatchCount.Counts() { + result += count + } + } + return result + }) + stats.NewGaugesFuncWithMultiLabels( "VReplicationCopyRowCount", "vreplication rows copied in copy phase per stream", @@ -434,6 +504,29 @@ func (st *vrStats) register() { return result }) + stats.NewCounterFunc( + "VReplicationThrottledCountTotal", + "The total number of times that vreplication has been throttled", + func() int64 { + st.mu.Lock() + defer st.mu.Unlock() + return st.ThrottledCount.Get() + }) + stats.NewCountersFuncWithMultiLabels( + "VReplicationThrottledCounts", + "The number of times vreplication was throttled by workflow, id, throttler (trx or tablet), and the sub-component that was throttled", + []string{"workflow", "id", "throttler", "component"}, + func() map[string]int64 { + st.mu.Lock() + defer st.mu.Unlock() + result := make(map[string]int64) + for _, ct := range st.controllers { + for key, val := range ct.blpStats.ThrottledCounts.Counts() { + result[fmt.Sprintf("%s.%d.%s", ct.workflow, ct.id, key)] = val + } + } + return result + }) } func (st *vrStats) numControllers() int64 { @@ -476,6 +569,8 @@ func (st *vrStats) status() *EngineStatus { SourceTablet: ct.sourceTablet.Load().(*topodatapb.TabletAlias), Messages: ct.blpStats.MessageHistory(), QueryCounts: ct.blpStats.QueryCount.Counts(), + BulkQueryCounts: ct.blpStats.BulkQueryCount.Counts(), + TrxQueryBatchCounts: ct.blpStats.TrxQueryBatchCount.Counts(), PhaseTimings: ct.blpStats.PhaseTimings.Counts(), CopyRowCount: ct.blpStats.CopyRowCount.Get(), CopyLoopCount: ct.blpStats.CopyLoopCount.Get(), @@ -514,6 +609,8 @@ type ControllerStatus struct { SourceTablet *topodatapb.TabletAlias Messages []string QueryCounts map[string]int64 + BulkQueryCounts map[string]int64 + TrxQueryBatchCounts map[string]int64 PhaseTimings map[string]int64 CopyRowCount int64 CopyLoopCount int64 diff --git a/go/vt/vttablet/tabletmanager/vreplication/stats_test.go b/go/vt/vttablet/tabletmanager/vreplication/stats_test.go index d5b5eacbdf2..d94802adb7b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/stats_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/stats_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/proto/binlogdata" @@ -132,7 +133,9 @@ func TestStatusHtml(t *testing.T) { func TestVReplicationStats(t *testing.T) { blpStats := binlogplayer.NewStats() defer blpStats.Stop() - testStats := &vrStats{} + testStats := &vrStats{ + ThrottledCount: stats.NewCounter("", ""), + } testStats.isOpen = true testStats.controllers = map[int32]*controller{ 1: { @@ -169,11 +172,29 @@ func TestVReplicationStats(t *testing.T) { require.Equal(t, int64(11), testStats.status().Controllers[0].QueryCounts["replicate"]) require.Equal(t, int64(23), testStats.status().Controllers[0].QueryCounts["fastforward"]) + blpStats.BulkQueryCount.Add("insert", 101) + blpStats.BulkQueryCount.Add("delete", 203) + require.Equal(t, int64(101), testStats.status().Controllers[0].BulkQueryCounts["insert"]) + require.Equal(t, int64(203), testStats.status().Controllers[0].BulkQueryCounts["delete"]) + + blpStats.TrxQueryBatchCount.Add("without_commit", 10) + blpStats.TrxQueryBatchCount.Add("with_commit", 2193) + require.Equal(t, int64(10), testStats.status().Controllers[0].TrxQueryBatchCounts["without_commit"]) + require.Equal(t, int64(2193), testStats.status().Controllers[0].TrxQueryBatchCounts["with_commit"]) + blpStats.CopyLoopCount.Add(100) blpStats.CopyRowCount.Add(200) require.Equal(t, int64(100), testStats.status().Controllers[0].CopyLoopCount) require.Equal(t, int64(200), testStats.status().Controllers[0].CopyRowCount) + testStats.ThrottledCount.Add(99) + require.Equal(t, int64(99), testStats.ThrottledCount.Get()) + + blpStats.ThrottledCounts.Add([]string{"tablet", "vcopier"}, 10) + blpStats.ThrottledCounts.Add([]string{"tablet", "vplayer"}, 80) + require.Equal(t, int64(10), testStats.controllers[1].blpStats.ThrottledCounts.Counts()["tablet.vcopier"]) + require.Equal(t, int64(80), testStats.controllers[1].blpStats.ThrottledCounts.Counts()["tablet.vplayer"]) + var tm int64 = 1234567890 blpStats.RecordHeartbeat(tm) require.Equal(t, tm, blpStats.Heartbeat()) diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index d94d0640529..17afe030d11 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -22,6 +22,7 @@ import ( "sort" "strings" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -29,6 +30,7 @@ import ( "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" @@ -59,6 +61,8 @@ type tablePlanBuilder struct { stats *binlogplayer.Stats source *binlogdatapb.BinlogSource pkIndices []bool + + collationEnv *collations.Environment } // colExpr describes the processing to be performed to @@ -128,7 +132,7 @@ const ( // The TablePlan built is a partial plan. The full plan for a table is built // when we receive field information from events or rows sent by the source. // buildExecutionPlan is the function that builds the full plan. -func buildReplicatorPlan(source *binlogdatapb.BinlogSource, colInfoMap map[string][]*ColumnInfo, copyState map[string]*sqltypes.Result, stats *binlogplayer.Stats) (*ReplicatorPlan, error) { +func buildReplicatorPlan(source *binlogdatapb.BinlogSource, colInfoMap map[string][]*ColumnInfo, copyState map[string]*sqltypes.Result, stats *binlogplayer.Stats, collationEnv *collations.Environment, parser *sqlparser.Parser) (*ReplicatorPlan, error) { filter := source.Filter plan := &ReplicatorPlan{ VStreamFilter: &binlogdatapb.Filter{FieldEventMode: filter.FieldEventMode}, @@ -137,6 +141,7 @@ func buildReplicatorPlan(source *binlogdatapb.BinlogSource, colInfoMap map[strin ColInfoMap: colInfoMap, stats: stats, Source: source, + collationEnv: collationEnv, } for tableName := range colInfoMap { lastpk, ok := copyState[tableName] @@ -155,7 +160,7 @@ func buildReplicatorPlan(source *binlogdatapb.BinlogSource, colInfoMap map[strin if !ok { return nil, fmt.Errorf("table %s not found in schema", tableName) } - tablePlan, err := buildTablePlan(tableName, rule, colInfos, lastpk, stats, source) + tablePlan, err := buildTablePlan(tableName, rule, colInfos, lastpk, stats, source, collationEnv, parser) if err != nil { return nil, err } @@ -195,7 +200,13 @@ func MatchTable(tableName string, filter *binlogdatapb.Filter) (*binlogdatapb.Ru } func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*ColumnInfo, lastpk *sqltypes.Result, - stats *binlogplayer.Stats, source *binlogdatapb.BinlogSource) (*TablePlan, error) { + stats *binlogplayer.Stats, source *binlogdatapb.BinlogSource, collationEnv *collations.Environment, parser *sqlparser.Parser) (*TablePlan, error) { + + planError := func(err error, query string) error { + // Use the error string here to ensure things are uniform across + // vterrors (from parse) and errors (all others). + return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s in query: %s", err.Error(), query) + } filter := rule.Filter query := filter @@ -212,9 +223,9 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum case filter == ExcludeStr: return nil, nil } - sel, fromTable, err := analyzeSelectFrom(query) + sel, fromTable, err := analyzeSelectFrom(query, parser) if err != nil { - return nil, err + return nil, planError(err, query) } sendRule := &binlogdatapb.Rule{ Match: fromTable, @@ -230,10 +241,10 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum // If it's a "select *", we return a partial plan, and complete // it when we get back field info from the stream. if len(sel.SelectExprs) != 1 { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(sel)) + return nil, planError(fmt.Errorf("unsupported mix of '*' and columns"), sqlparser.String(sel)) } if !expr.TableName.IsEmpty() { - return nil, fmt.Errorf("unsupported qualifier for '*' expression: %v", sqlparser.String(expr)) + return nil, planError(fmt.Errorf("unsupported qualifier for '*' expression"), sqlparser.String(expr)) } sendRule.Filter = query tablePlan := &TablePlan{ @@ -244,6 +255,7 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum EnumValuesMap: enumValuesMap, ConvertCharset: rule.ConvertCharset, ConvertIntToEnum: rule.ConvertIntToEnum, + CollationEnv: collationEnv, } return tablePlan, nil @@ -255,14 +267,15 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum From: sel.From, Where: sel.Where, }, - lastpk: lastpk, - colInfos: colInfos, - stats: stats, - source: source, + lastpk: lastpk, + colInfos: colInfos, + stats: stats, + source: source, + collationEnv: collationEnv, } if err := tpb.analyzeExprs(sel.SelectExprs); err != nil { - return nil, err + return nil, planError(err, sqlparser.String(sel)) } // It's possible that the target table does not materialize all // the primary keys of the source table. In such situations, @@ -277,7 +290,7 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum } } if err := tpb.analyzeGroupBy(sel.GroupBy); err != nil { - return nil, err + return nil, planError(err, sqlparser.String(sel)) } targetKeyColumnNames, err := textutil.SplitUnescape(rule.TargetUniqueKeyColumns, ",") if err != nil { @@ -309,6 +322,9 @@ func buildTablePlan(tableName string, rule *binlogdatapb.Rule, colInfos []*Colum if rule.SourceUniqueKeyColumns != "" { commentsList = append(commentsList, fmt.Sprintf(`ukColumns="%s"`, rule.SourceUniqueKeyColumns)) } + if rule.ForceUniqueKey != "" { + commentsList = append(commentsList, fmt.Sprintf(`ukForce="%s"`, rule.ForceUniqueKey)) + } if len(commentsList) > 0 { comments := sqlparser.Comments{ fmt.Sprintf(`/*vt+ %s */`, strings.Join(commentsList, " ")), @@ -361,6 +377,7 @@ func (tpb *tablePlanBuilder) generate() *TablePlan { Insert: tpb.generateInsertStatement(), Update: tpb.generateUpdateStatement(), Delete: tpb.generateDeleteStatement(), + MultiDelete: tpb.generateMultiDeleteStatement(), PKReferences: pkrefs, PKIndices: tpb.pkIndices, Stats: tpb.stats, @@ -369,31 +386,32 @@ func (tpb *tablePlanBuilder) generate() *TablePlan { TablePlanBuilder: tpb, PartialInserts: make(map[string]*sqlparser.ParsedQuery, 0), PartialUpdates: make(map[string]*sqlparser.ParsedQuery, 0), + CollationEnv: tpb.collationEnv, } } -func analyzeSelectFrom(query string) (sel *sqlparser.Select, from string, err error) { - statement, err := sqlparser.Parse(query) +func analyzeSelectFrom(query string, parser *sqlparser.Parser) (sel *sqlparser.Select, from string, err error) { + statement, err := parser.Parse(query) if err != nil { return nil, "", err } sel, ok := statement.(*sqlparser.Select) if !ok { - return nil, "", fmt.Errorf("unexpected: %v", sqlparser.String(statement)) + return nil, "", fmt.Errorf("unsupported non-select statement") } if sel.Distinct { - return nil, "", fmt.Errorf("unexpected: %v", sqlparser.String(sel)) + return nil, "", fmt.Errorf("unsupported distinct clause") } if len(sel.From) > 1 { - return nil, "", fmt.Errorf("unexpected: %v", sqlparser.String(sel)) + return nil, "", fmt.Errorf("unsupported multi-table usage") } node, ok := sel.From[0].(*sqlparser.AliasedTableExpr) if !ok { - return nil, "", fmt.Errorf("unexpected: %v", sqlparser.String(sel)) + return nil, "", fmt.Errorf("unsupported from expression (%T)", sel.From[0]) } fromTable := sqlparser.GetTableName(node.Expr) if fromTable.IsEmpty() { - return nil, "", fmt.Errorf("unexpected: %v", sqlparser.String(sel)) + return nil, "", fmt.Errorf("unsupported from source (%T)", node.Expr) } return sel, fromTable.String(), nil } @@ -412,7 +430,7 @@ func (tpb *tablePlanBuilder) analyzeExprs(selExprs sqlparser.SelectExprs) error func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr, error) { aliased, ok := selExpr.(*sqlparser.AliasedExpr) if !ok { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(selExpr)) + return nil, fmt.Errorf("invalid expression: %v", sqlparser.String(selExpr)) } as := aliased.As if as.IsEmpty() { @@ -461,7 +479,7 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr switch fname := expr.Name.Lowered(); fname { case "keyspace_id": if len(expr.Exprs) != 0 { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + return nil, fmt.Errorf("unsupported multiple keyspace_id expressions: %v", sqlparser.String(expr)) } tpb.sendSelect.SelectExprs = append(tpb.sendSelect.SelectExprs, &sqlparser.AliasedExpr{Expr: aliased.Expr}) // The vstreamer responds with "keyspace_id" as the field name for this request. @@ -471,7 +489,7 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr } if expr, ok := aliased.Expr.(sqlparser.AggrFunc); ok { if sqlparser.IsDistinct(expr) { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + return nil, fmt.Errorf("unsupported distinct expression usage: %v", sqlparser.String(expr)) } switch fname := expr.AggrName(); fname { case "count": @@ -482,11 +500,11 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr return cexpr, nil case "sum": if len(expr.GetArgs()) != 1 { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + return nil, fmt.Errorf("unsupported multiple columns in sum clause: %v", sqlparser.String(expr)) } innerCol, ok := expr.GetArg().(*sqlparser.ColName) if !ok { - return nil, fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + return nil, fmt.Errorf("unsupported non-column name in sum clause: %v", sqlparser.String(expr)) } if !innerCol.Qualifier.IsEmpty() { return nil, fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(innerCol)) @@ -509,7 +527,7 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr case *sqlparser.Subquery: return false, fmt.Errorf("unsupported subquery: %v", sqlparser.String(node)) case sqlparser.AggrFunc: - return false, fmt.Errorf("unexpected: %v", sqlparser.String(node)) + return false, fmt.Errorf("unsupported aggregation function: %v", sqlparser.String(node)) } return true, nil }, aliased.Expr) @@ -536,7 +554,7 @@ func (tpb *tablePlanBuilder) analyzeGroupBy(groupBy sqlparser.GroupBy) error { for _, expr := range groupBy { colname, ok := expr.(*sqlparser.ColName) if !ok { - return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + return fmt.Errorf("unsupported non-column name or alias in group by clause: %v", sqlparser.String(expr)) } cexpr := tpb.findCol(colname.Name) if cexpr == nil { @@ -870,6 +888,18 @@ func (tpb *tablePlanBuilder) generateDeleteStatement() *sqlparser.ParsedQuery { return buf.ParsedQuery() } +func (tpb *tablePlanBuilder) generateMultiDeleteStatement() *sqlparser.ParsedQuery { + if vttablet.VReplicationExperimentalFlags&vttablet.VReplicationExperimentalFlagVPlayerBatching == 0 || + (len(tpb.pkCols)+len(tpb.extraSourcePkCols)) != 1 { + return nil + } + return sqlparser.BuildParsedQuery("delete from %s where %s in %a", + sqlparser.String(tpb.name), + sqlparser.String(tpb.pkCols[0].colName), + "::bulk_pks", + ) +} + func (tpb *tablePlanBuilder) generateWhere(buf *sqlparser.TrackedBuffer, bvf *bindvarFormatter) { buf.WriteString(" where ") bvf.mode = bvBefore diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go index cbf524c54c3..dfe51f71dbd 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go @@ -21,26 +21,28 @@ import ( "fmt" "io" "math" + "slices" "strconv" "strings" "time" + "golang.org/x/exp/maps" "google.golang.org/protobuf/encoding/prototext" - "vitess.io/vitess/go/vt/vttablet" - "vitess.io/vitess/go/bytes2" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/pools" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/log" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) type vcopier struct { @@ -219,7 +221,7 @@ func newVCopierCopyWorker( func (vc *vcopier) initTablesForCopy(ctx context.Context) error { defer vc.vr.dbClient.Rollback() - plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats) + plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats, vc.vr.vre.env.CollationEnv(), vc.vr.vre.env.Parser()) if err != nil { return err } @@ -230,9 +232,12 @@ func (vc *vcopier) initTablesForCopy(ctx context.Context) error { if len(plan.TargetTables) != 0 { var buf strings.Builder buf.WriteString("insert into _vt.copy_state(vrepl_id, table_name) values ") + // Sort the tables by name to ensure a consistent order. + tableNames := maps.Keys(plan.TargetTables) + slices.Sort(tableNames) prefix := "" - for name := range plan.TargetTables { - fmt.Fprintf(&buf, "%s(%d, %s)", prefix, vc.vr.id, encodeString(name)) + for _, tableName := range tableNames { + fmt.Fprintf(&buf, "%s(%d, %s)", prefix, vc.vr.id, encodeString(tableName)) prefix = ", " } if _, err := vc.vr.dbClient.Execute(buf.String()); err != nil { @@ -256,8 +261,8 @@ func (vc *vcopier) initTablesForCopy(ctx context.Context) error { len(plan.TargetTables))); err != nil { return err } - for name := range plan.TargetTables { - if err := vc.vr.stashSecondaryKeys(ctx, name); err != nil { + for _, tableName := range tableNames { + if err := vc.vr.stashSecondaryKeys(ctx, tableName); err != nil { return err } } @@ -294,7 +299,7 @@ func (vc *vcopier) initTablesForCopy(ctx context.Context) error { // primary key that was copied. A nil Result means that nothing has been copied. // A table that was fully copied is removed from copyState. func (vc *vcopier) copyNext(ctx context.Context, settings binlogplayer.VRSettings) error { - qr, err := vc.vr.dbClient.Execute(fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state group by vrepl_id, table_name)", vc.vr.id)) + qr, err := vc.vr.dbClient.Execute(fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state group by vrepl_id, table_name) order by table_name", vc.vr.id)) if err != nil { return err } @@ -385,7 +390,7 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma log.Infof("Copying table %s, lastpk: %v", tableName, copyState[tableName]) - plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats) + plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats, vc.vr.vre.env.CollationEnv(), vc.vr.vre.env.Parser()) if err != nil { return err } @@ -612,7 +617,7 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma case result := <-resultCh: switch result.state { case vcopierCopyTaskCancel: - // A task cancelation probably indicates an expired context due + // A task cancellation probably indicates an expired context due // to a PlannedReparentShard or elapsed copy phase duration, // neither of which are error conditions. case vcopierCopyTaskComplete: @@ -1087,7 +1092,7 @@ func (vbc *vcopierCopyWorker) execute(ctx context.Context, task *vcopierCopyTask advanceFn = func(context.Context, *vcopierCopyTaskArgs) error { // Commit. if err := vbc.vdbClient.Commit(); err != nil { - return vterrors.Wrapf(err, "error commiting transaction") + return vterrors.Wrapf(err, "error committing transaction") } return nil } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier_atomic.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier_atomic.go index 4da072e3955..02e1188cdb7 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier_atomic.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier_atomic.go @@ -54,7 +54,7 @@ func newCopyAllState(vc *vcopier) (*copyAllState, error) { state := ©AllState{ vc: vc, } - plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats) + plan, err := buildReplicatorPlan(vc.vr.source, vc.vr.colInfoMap, nil, vc.vr.stats, vc.vr.vre.env.CollationEnv(), vc.vr.vre.env.Parser()) if err != nil { return nil, err } @@ -303,7 +303,6 @@ func (vc *vcopier) copyAll(ctx context.Context, settings binlogplayer.VRSettings // deleteCopyState deletes the copy state entry for a table, signifying that the copy phase is complete for that table. func (vc *vcopier) deleteCopyState(tableName string) error { log.Infof("Deleting copy state for table %s", tableName) - //FIXME get sidecar db name delQuery := fmt.Sprintf("delete from _vt.copy_state where table_name=%s and vrepl_id = %d", encodeString(tableName), vc.vr.id) if _, err := vc.vr.dbClient.Execute(delQuery); err != nil { return err diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go index 82a6d211b4f..c32482641b2 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier_test.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "regexp" "strings" "testing" "time" @@ -102,7 +103,7 @@ func testPlayerCopyCharPK(t *testing.T) { defer func() { vttablet.CopyPhaseDuration = savedCopyPhaseDuration }() savedWaitRetryTime := waitRetryTime - // waitRetry time should be very low to cause the wait loop to execute multipel times. + // waitRetry time should be very low to cause the wait loop to execute multiple times. waitRetryTime = 10 * time.Millisecond defer func() { waitRetryTime = savedWaitRetryTime }() @@ -332,7 +333,7 @@ func testPlayerCopyVarcharCompositePKCaseSensitiveCollation(t *testing.T) { defer func() { vttablet.CopyPhaseDuration = savedCopyPhaseDuration }() savedWaitRetryTime := waitRetryTime - // waitRetry time should be very low to cause the wait loop to execute multipel times. + // waitRetry time should be very low to cause the wait loop to execute multiple times. waitRetryTime = 10 * time.Millisecond defer func() { waitRetryTime = savedWaitRetryTime }() @@ -562,15 +563,19 @@ func testPlayerCopyTables(t *testing.T) { defer deleteTablet(addTablet(100)) execStatements(t, []string{ + "create table ast1(id int, primary key(id))", "create table src1(id int, val varbinary(128), d decimal(8,0), j json, primary key(id))", "insert into src1 values(2, 'bbb', 1, '{\"foo\": \"bar\"}'), (1, 'aaa', 0, JSON_ARRAY(123456789012345678901234567890, \"abcd\")), (3, 'ccc', 2, 'null'), (4, 'ddd', 3, '{\"name\": \"matt\", \"size\": null}'), (5, 'eee', 4, null)", + fmt.Sprintf("create table %s.ast1(id int, primary key(id))", vrepldb), fmt.Sprintf("create table %s.dst1(id int, val varbinary(128), val2 varbinary(128), d decimal(8,0), j json, primary key(id))", vrepldb), "create table yes(id int, val varbinary(128), primary key(id))", fmt.Sprintf("create table %s.yes(id int, val varbinary(128), primary key(id))", vrepldb), "create table no(id int, val varbinary(128), primary key(id))", }) defer execStatements(t, []string{ + "drop table ast1", "drop table src1", + fmt.Sprintf("drop table %s.ast1", vrepldb), fmt.Sprintf("drop table %s.dst1", vrepldb), "drop table yes", fmt.Sprintf("drop table %s.yes", vrepldb), @@ -582,6 +587,9 @@ func testPlayerCopyTables(t *testing.T) { Rules: []*binlogdatapb.Rule{{ Match: "dst1", Filter: "select id, val, val as val2, d, j from src1", + }, { + Match: "ast1", + Filter: "select * from ast1", }, { Match: "/yes", }}, @@ -595,9 +603,7 @@ func testPlayerCopyTables(t *testing.T) { } query := binlogplayer.CreateVReplicationState("test", bls, "", binlogdatapb.VReplicationWorkflowState_Init, playerEngine.dbName, 0, 0) qr, err := playerEngine.Exec(query) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer func() { query := fmt.Sprintf("delete from _vt.vreplication where id = %d", qr.InsertID) if _, err := playerEngine.Exec(query); err != nil { @@ -607,15 +613,24 @@ func testPlayerCopyTables(t *testing.T) { }() expectDBClientQueries(t, qh.Expect( - "/insert into _vt.vreplication", + // Filters should be lexicographically ordered by name. + regexp.QuoteMeta("/insert into _vt.vreplication (workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state, db_name, workflow_type, workflow_sub_type) values ('test', 'keyspace:\\\"vttest\\\" shard:\\\"0\\\" filter:{rules:{match:\\\"ast1\\\" filter:\\\"select * from ast1\\\"} rules:{match:\\\"dst1\\\" filter:\\\"select id, val, val as val2, d, j from src1\\\"} rules:{match:\\\"/yes\\\"}}'"), "/update _vt.vreplication set message='Picked source tablet.*", // Create the list of tables to copy and transition to Copying state. "begin", - "/insert into _vt.copy_state", + // The table names should be lexicographically ordered by name. + fmt.Sprintf("insert into _vt.copy_state(vrepl_id, table_name) values (%d, 'ast1'), (%d, 'dst1'), (%d, 'yes')", qr.InsertID, qr.InsertID, qr.InsertID), "/update _vt.vreplication set state='Copying'", "commit", // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", + // Now the tables should be copied in lexicographical order: ast1, dst1, yes. + // Nothing to copy from ast1. Delete from copy_state. + "/delete cs, pca from _vt.copy_state as cs left join _vt.post_copy_action as pca on cs.vrepl_id=pca.vrepl_id and cs.table_name=pca.table_name.*ast1", + // The next FF executes and updates the position before copying. + "begin", + "/update _vt.vreplication set pos=", + "commit", "begin", "insert into dst1(id,val,val2,d,j) values (1,'aaa','aaa',0,JSON_ARRAY(123456789012345678901234567890, _utf8mb4'abcd')), (2,'bbb','bbb',1,JSON_OBJECT(_utf8mb4'foo', _utf8mb4'bar')), (3,'ccc','ccc',2,CAST(_utf8mb4'null' as JSON)), (4,'ddd','ddd',3,JSON_OBJECT(_utf8mb4'name', _utf8mb4'matt', _utf8mb4'size', null)), (5,'eee','eee',4,null)", `/insert into _vt.copy_state \(lastpk, vrepl_id, table_name\) values \('fields:{name:\\"id\\" type:INT32 charset:63 flags:53251} rows:{lengths:1 values:\\"5\\"}'.*`, @@ -811,7 +826,7 @@ func testPlayerCopyWildcardRule(t *testing.T) { defer func() { vttablet.CopyPhaseDuration = savedCopyPhaseDuration }() savedWaitRetryTime := waitRetryTime - // waitRetry time should be very low to cause the wait loop to execute multipel times. + // waitRetry time should be very low to cause the wait loop to execute multiple times. waitRetryTime = 10 * time.Millisecond defer func() { waitRetryTime = savedWaitRetryTime }() @@ -979,7 +994,7 @@ func testPlayerCopyTableContinuation(t *testing.T) { "update src1 set id2=10 where id1=5", // move row from within to outside range. "update src1 set id1=12 where id1=6", - // move row from outside to witihn range. + // move row from outside to within range. "update src1 set id1=4 where id1=11", // modify the copied table. "update copied set val='bbb' where id=1", @@ -1676,22 +1691,26 @@ func TestCopyTablesWithInvalidDates(t *testing.T) { func testCopyTablesWithInvalidDates(t *testing.T) { defer deleteTablet(addTablet(100)) - execStatements(t, []string{ - "create table src1(id int, dt date, primary key(id))", - fmt.Sprintf("create table %s.dst1(id int, dt date, primary key(id))", vrepldb), - "insert into src1 values(1, '2020-01-12'), (2, '0000-00-00');", - }) + conn, err := env.Mysqld.GetDbaConnection(context.Background()) + require.NoError(t, err) // default mysql flavor allows invalid dates: so disallow explicitly for this test - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')"); err != nil { + if _, err := conn.ExecuteFetch("SET @@session.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')", 0, false); err != nil { fmt.Fprintf(os.Stderr, "%v", err) } defer func() { - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(@@global.sql_mode, ',NO_ZERO_DATE,NO_ZERO_IN_DATE','')"); err != nil { + if _, err := conn.ExecuteFetch("SET @@session.sql_mode=REPLACE(@@session.sql_mode, ',NO_ZERO_DATE,NO_ZERO_IN_DATE','')", 0, false); err != nil { fmt.Fprintf(os.Stderr, "%v", err) } }() - defer execStatements(t, []string{ + + execConnStatements(t, conn, []string{ + "create table src1(id int, dt date, primary key(id))", + fmt.Sprintf("create table %s.dst1(id int, dt date, primary key(id))", vrepldb), + "insert into src1 values(1, '2020-01-12'), (2, '0000-00-00');", + }) + + defer execConnStatements(t, conn, []string{ "drop table src1", fmt.Sprintf("drop table %s.dst1", vrepldb), }) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go index c3941b0f1bb..39a8229efc6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go @@ -19,12 +19,15 @@ package vreplication import ( "context" "io" + "strings" "time" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" ) // vdbClient is a wrapper on binlogplayer.DBClient. @@ -35,6 +38,9 @@ type vdbClient struct { InTransaction bool startTime time.Time queries []string + queriesPos int64 + batchSize int64 + maxBatchSize int64 } func newVDBClient(dbclient binlogplayer.DBClient, stats *binlogplayer.Stats) *vdbClient { @@ -51,6 +57,13 @@ func (vc *vdbClient) Begin() error { if err := vc.DBClient.Begin(); err != nil { return err } + + // If we're batching, we only batch the contents of the + // transaction, which starts with the begin and ends with + // the commit. + vc.queriesPos = int64(len(vc.queries)) + vc.batchSize = 6 // begin and semicolon + vc.queries = append(vc.queries, "begin") vc.InTransaction = true vc.startTime = time.Now() @@ -63,10 +76,30 @@ func (vc *vdbClient) Commit() error { } vc.InTransaction = false vc.queries = nil + vc.batchSize = 0 vc.stats.Timings.Record(binlogplayer.BlplTransaction, vc.startTime) return nil } +// CommitTrxQueryBatch sends the current transaction's query batch -- which +// is often the full contents of the transaction, unless we've crossed +// the maxBatchSize one or more times -- down the wire to the database, +// including the final commit. +func (vc *vdbClient) CommitTrxQueryBatch() error { + vc.queries = append(vc.queries, "commit") + queries := strings.Join(vc.queries[vc.queriesPos:], ";") + for _, err := vc.DBClient.ExecuteFetchMulti(queries, -1); err != nil; { + return err + } + vc.InTransaction = false + vc.queries = nil + vc.queriesPos = 0 + vc.batchSize = 0 + vc.stats.TrxQueryBatchCount.Add("with_commit", 1) + vc.stats.Timings.Record(binlogplayer.BlplBatchTransaction, vc.startTime) + return nil +} + func (vc *vdbClient) Rollback() error { if !vc.InTransaction { return nil @@ -90,6 +123,43 @@ func (vc *vdbClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Result, return vc.DBClient.ExecuteFetch(query, maxrows) } +// AddQueryToTrxBatch adds the query to the current transaction's query +// batch. If this new query would cause the current batch to exceed +// the maxBatchSize, then the current unsent batch is sent down the +// wire and this query will be included in the next batch. +func (vc *vdbClient) AddQueryToTrxBatch(query string) error { + if !vc.InTransaction { + return vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "cannot batch query outside of a transaction: %s", query) + } + + addedSize := int64(len(query)) + 1 // Plus 1 for the semicolon + if vc.batchSize+addedSize > vc.maxBatchSize { + if _, err := vc.ExecuteTrxQueryBatch(); err != nil { + return err + } + } + vc.queries = append(vc.queries, query) + vc.batchSize += addedSize + + return nil +} + +// ExecuteQueryBatch sends the transaction's current batch of queries +// down the wire to the database. +func (vc *vdbClient) ExecuteTrxQueryBatch() ([]*sqltypes.Result, error) { + defer vc.stats.Timings.Record(binlogplayer.BlplMultiQuery, time.Now()) + + qrs, err := vc.DBClient.ExecuteFetchMulti(strings.Join(vc.queries[vc.queriesPos:], ";"), -1) + if err != nil { + return nil, err + } + vc.stats.TrxQueryBatchCount.Add("without_commit", 1) + vc.queriesPos += int64(len(vc.queries[vc.queriesPos:])) + vc.batchSize = 0 + + return qrs, nil +} + // Execute is ExecuteFetch without the maxrows. func (vc *vdbClient) Execute(query string) (*sqltypes.Result, error) { // Number of rows should never exceed relayLogMaxItems. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 8eee211ff9e..f2cb0a96e71 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -28,9 +28,9 @@ import ( "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vttablet" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -47,6 +47,14 @@ type vplayer struct { replicatorPlan *ReplicatorPlan tablePlans map[string]*TablePlan + // These are set when creating the VPlayer based on whether the VPlayer + // is in batch (stmt and trx) execution mode or not. + query func(ctx context.Context, sql string) (*sqltypes.Result, error) + commit func() error + // If the VPlayer is in batch mode, we accumulate each transaction's statements + // that are then sent as a single multi-statement protocol request to the database. + batchMode bool + pos replication.Position // unsavedEvent is set any time we skip an event without // saving, which is on an empty commit. @@ -104,6 +112,47 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map settings.StopPos = pausePos saveStop = false } + + queryFunc := func(ctx context.Context, sql string) (*sqltypes.Result, error) { + return vr.dbClient.ExecuteWithRetry(ctx, sql) + } + commitFunc := func() error { + return vr.dbClient.Commit() + } + batchMode := false + if vttablet.VReplicationExperimentalFlags&vttablet.VReplicationExperimentalFlagVPlayerBatching != 0 { + batchMode = true + } + if batchMode { + // relayLogMaxSize is effectively the limit used when not batching. + maxAllowedPacket := int64(relayLogMaxSize) + // We explicitly do NOT want to batch this, we want to send it down the wire + // immediately so we use ExecuteFetch directly. + res, err := vr.dbClient.ExecuteFetch("select @@session.max_allowed_packet as max_allowed_packet", 1) + if err != nil { + log.Errorf("Error getting max_allowed_packet, will use the relay_log_max_size value of %d bytes: %v", relayLogMaxSize, err) + } else { + if maxAllowedPacket, err = res.Rows[0][0].ToInt64(); err != nil { + log.Errorf("Error getting max_allowed_packet, will use the relay_log_max_size value of %d bytes: %v", relayLogMaxSize, err) + } + } + // Leave 64 bytes of room for the commit to be sure that we have a more than + // ample buffer left. The default value of max_allowed_packet is 4MiB in 5.7 + // and 64MiB in 8.0 -- and the default for max_relay_log_size is 250000 + // bytes -- so we have plenty of room. + maxAllowedPacket -= 64 + queryFunc = func(ctx context.Context, sql string) (*sqltypes.Result, error) { + if !vr.dbClient.InTransaction { // Should be sent down the wire immediately + return vr.dbClient.Execute(sql) + } + return nil, vr.dbClient.AddQueryToTrxBatch(sql) // Should become part of the trx batch + } + commitFunc = func() error { + return vr.dbClient.CommitTrxQueryBatch() // Commit the current trx batch + } + vr.dbClient.maxBatchSize = maxAllowedPacket + } + return &vplayer{ vr: vr, startPos: settings.StartPos, @@ -115,6 +164,9 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map tablePlans: make(map[string]*TablePlan), phase: phase, throttlerAppName: throttlerapp.VCopierName.ConcatenateString(vr.throttlerAppName()), + query: queryFunc, + commit: commitFunc, + batchMode: batchMode, } } @@ -128,7 +180,7 @@ func (vp *vplayer) play(ctx context.Context) error { return nil } - plan, err := buildReplicatorPlan(vp.vr.source, vp.vr.colInfoMap, vp.copyState, vp.vr.stats) + plan, err := buildReplicatorPlan(vp.vr.source, vp.vr.colInfoMap, vp.copyState, vp.vr.stats, vp.vr.vre.env.CollationEnv(), vp.vr.vre.env.Parser()) if err != nil { vp.vr.stats.ErrorCounts.Add([]string{"Plan"}, 1) return err @@ -152,8 +204,21 @@ func (vp *vplayer) play(ctx context.Context) error { // The foreign_key_checks value for a transaction is determined by the 2nd bit (least significant) of the flags: // - If set (1), foreign key checks are disabled. // - If unset (0), foreign key checks are enabled. -// updateFKCheck also updates the state for the first row event that this vplayer and hence the connection sees. +// updateFKCheck also updates the state for the first row event that this vplayer, and hence the db connection, sees. func (vp *vplayer) updateFKCheck(ctx context.Context, flags2 uint32) error { + mustUpdate := false + if vp.vr.WorkflowSubType == int32(binlogdatapb.VReplicationWorkflowSubType_AtomicCopy) { + // If this is an atomic copy, we must update the foreign_key_checks state even when the vplayer runs during + // the copy phase, i.e., for catchup and fastforward. + mustUpdate = true + } else if vp.vr.state == binlogdatapb.VReplicationWorkflowState_Running { + // If the vreplication workflow is in Running state, we must update the foreign_key_checks + // state for all workflow types. + mustUpdate = true + } + if !mustUpdate { + return nil + } dbForeignKeyChecksEnabled := true if flags2&NoForeignKeyCheckFlagBitmask == NoForeignKeyCheckFlagBitmask { dbForeignKeyChecksEnabled = false @@ -164,7 +229,7 @@ func (vp *vplayer) updateFKCheck(ctx context.Context, flags2 uint32) error { return nil } log.Infof("Setting this session's foreign_key_checks to %s", strconv.FormatBool(dbForeignKeyChecksEnabled)) - if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, "set @@session.foreign_key_checks="+strconv.FormatBool(dbForeignKeyChecksEnabled)); err != nil { + if _, err := vp.query(ctx, "set @@session.foreign_key_checks="+strconv.FormatBool(dbForeignKeyChecksEnabled)); err != nil { return fmt.Errorf("failed to set session foreign_key_checks: %w", err) } vp.foreignKeyChecksEnabled = dbForeignKeyChecksEnabled @@ -250,7 +315,7 @@ func (vp *vplayer) applyStmtEvent(ctx context.Context, event *binlogdatapb.VEven } if event.Type == binlogdatapb.VEventType_SAVEPOINT || vp.canAcceptStmtEvents { start := time.Now() - _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) + _, err := vp.query(ctx, sql) vp.vr.stats.QueryTimings.Record(vp.phase, start) vp.vr.stats.QueryCount.Add(vp.phase, 1) return err @@ -266,27 +331,46 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row if tplan == nil { return fmt.Errorf("unexpected event on table %s", rowEvent.TableName) } + applyFunc := func(sql string) (*sqltypes.Result, error) { + stats := NewVrLogStats("ROWCHANGE") + start := time.Now() + qr, err := vp.query(ctx, sql) + vp.vr.stats.QueryCount.Add(vp.phase, 1) + vp.vr.stats.QueryTimings.Record(vp.phase, start) + stats.Send(sql) + return qr, err + } + + if vp.batchMode && len(rowEvent.RowChanges) > 1 { + // If we have multiple delete row events for a table with a single PK column + // then we can perform a simple bulk DELETE using an IN clause. + if (rowEvent.RowChanges[0].Before != nil && rowEvent.RowChanges[0].After == nil) && + tplan.MultiDelete != nil { + _, err := tplan.applyBulkDeleteChanges(rowEvent.RowChanges, applyFunc, vp.vr.dbClient.maxBatchSize) + return err + } + // If we're done with the copy phase then we will be replicating all INSERTS + // regardless of the PK value and can use a single INSERT statment with + // multiple VALUES clauses. + if len(vp.copyState) == 0 && (rowEvent.RowChanges[0].Before == nil && rowEvent.RowChanges[0].After != nil) { + _, err := tplan.applyBulkInsertChanges(rowEvent.RowChanges, applyFunc, vp.vr.dbClient.maxBatchSize) + return err + } + } + for _, change := range rowEvent.RowChanges { - _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { - stats := NewVrLogStats("ROWCHANGE") - start := time.Now() - qr, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) - vp.vr.stats.QueryCount.Add(vp.phase, 1) - vp.vr.stats.QueryTimings.Record(vp.phase, start) - stats.Send(sql) - return qr, err - }) - if err != nil { + if _, err := tplan.applyChange(change, applyFunc); err != nil { return err } } + return nil } -func (vp *vplayer) updatePos(ts int64) (posReached bool, err error) { +func (vp *vplayer) updatePos(ctx context.Context, ts int64) (posReached bool, err error) { vp.numAccumulatedHeartbeats = 0 update := binlogplayer.GenerateUpdatePos(vp.vr.id, vp.pos, time.Now().Unix(), ts, vp.vr.stats.CopyRowCount.Get(), vreplicationStoreCompressedGTID) - if _, err := vp.vr.dbClient.Execute(update); err != nil { + if _, err := vp.query(ctx, update); err != nil { return false, fmt.Errorf("error %v updating position", err) } vp.unsavedEvent = nil @@ -346,8 +430,8 @@ func (vp *vplayer) recordHeartbeat() error { // of transactions come in, with the last one being partial. In this case, all transactions // up to the last one have to be committed, and the final one must be partially applied. // -// Of the above events, the saveable ones are COMMIT, DDL, and OTHER. Eventhough -// A GTID comes as a separate event, it's not saveable until a subsequent saveable +// Of the above events, the saveable ones are COMMIT, DDL, and OTHER. Even though +// a GTID comes as a separate event, it's not saveable until a subsequent saveable // event occurs. VStreamer currently sequences the GTID to be sent just before // a saveable event, but we do not rely on this. To handle this, we only remember // the position when a GTID is encountered. The next saveable event causes the @@ -380,7 +464,7 @@ func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { if ctx.Err() != nil { return ctx.Err() } - // check throttler. + // Check throttler. if !vp.vr.vre.throttlerClient.ThrottleCheckOKOrWaitAppName(ctx, throttlerapp.Name(vp.throttlerAppName)) { _ = vp.vr.updateTimeThrottled(throttlerapp.VPlayerName) continue @@ -404,7 +488,7 @@ func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { // In both cases, now > timeLastSaved. If so, the GTID of the last unsavedEvent // must be saved. if time.Since(vp.timeLastSaved) >= idleTimeout && vp.unsavedEvent != nil { - posReached, err := vp.updatePos(vp.unsavedEvent.Timestamp) + posReached, err := vp.updatePos(ctx, vp.unsavedEvent.Timestamp) if err != nil { return err } @@ -503,11 +587,11 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m vp.unsavedEvent = event return nil } - posReached, err := vp.updatePos(event.Timestamp) + posReached, err := vp.updatePos(ctx, event.Timestamp) if err != nil { return err } - if err := vp.vr.dbClient.Commit(); err != nil { + if err := vp.commit(); err != nil { return err } if posReached { @@ -560,7 +644,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return fmt.Errorf("internal error: vplayer is in a transaction on event: %v", event) } // Just update the position. - posReached, err := vp.updatePos(event.Timestamp) + posReached, err := vp.updatePos(ctx, event.Timestamp) if err != nil { return err } @@ -576,7 +660,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m switch vp.vr.source.OnDdl { case binlogdatapb.OnDDLAction_IGNORE: // We still have to update the position. - posReached, err := vp.updatePos(event.Timestamp) + posReached, err := vp.updatePos(ctx, event.Timestamp) if err != nil { return err } @@ -587,13 +671,13 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.vr.dbClient.Begin(); err != nil { return err } - if _, err := vp.updatePos(event.Timestamp); err != nil { + if _, err := vp.updatePos(ctx, event.Timestamp); err != nil { return err } if err := vp.vr.setState(binlogdatapb.VReplicationWorkflowState_Stopped, fmt.Sprintf("Stopped at DDL %s", event.Statement)); err != nil { return err } - if err := vp.vr.dbClient.Commit(); err != nil { + if err := vp.commit(); err != nil { return err } return io.EOF @@ -602,11 +686,11 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m // So, we apply the DDL first, and then save the position. // Manual intervention may be needed if there is a partial // failure here. - if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Statement); err != nil { + if _, err := vp.query(ctx, event.Statement); err != nil { return err } stats.Send(fmt.Sprintf("%v", event.Statement)) - posReached, err := vp.updatePos(event.Timestamp) + posReached, err := vp.updatePos(ctx, event.Timestamp) if err != nil { return err } @@ -614,11 +698,11 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return io.EOF } case binlogdatapb.OnDDLAction_EXEC_IGNORE: - if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Statement); err != nil { + if _, err := vp.query(ctx, event.Statement); err != nil { log.Infof("Ignoring error: %v for DDL: %s", err, event.Statement) } stats.Send(fmt.Sprintf("%v", event.Statement)) - posReached, err := vp.updatePos(event.Timestamp) + posReached, err := vp.updatePos(ctx, event.Timestamp) if err != nil { return err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go index 3b215d03791..d9b68d052c3 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "os" + "regexp" "strconv" "strings" "sync" @@ -185,7 +186,6 @@ func TestPlayerInvisibleColumns(t *testing.T) { output := qh.Expect(tcases.output) expectNontxQueries(t, output) time.Sleep(1 * time.Second) - log.Flush() if tcases.table != "" { expectData(t, tcases.table, tcases.data) } @@ -691,8 +691,8 @@ func TestPlayerFilters(t *testing.T) { fmt.Sprintf("create table %s.dst4(id1 int, val varbinary(128), primary key(id1))", vrepldb), "create table src5(id1 int, id2 int, val varbinary(128), primary key(id1))", fmt.Sprintf("create table %s.dst5(id1 int, val varbinary(128), primary key(id1))", vrepldb), - "create table srcCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", - fmt.Sprintf("create table %s.dstCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, val2 varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", vrepldb), + "create table src_charset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", + fmt.Sprintf("create table %s.dst_charset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, val2 varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", vrepldb), }) defer execStatements(t, []string{ "drop table src1", @@ -710,8 +710,8 @@ func TestPlayerFilters(t *testing.T) { fmt.Sprintf("drop table %s.dst4", vrepldb), "drop table src5", fmt.Sprintf("drop table %s.dst5", vrepldb), - "drop table srcCharset", - fmt.Sprintf("drop table %s.dstCharset", vrepldb), + "drop table src_charset", + fmt.Sprintf("drop table %s.dst_charset", vrepldb), }) env.SchemaEngine.Reload(context.Background()) @@ -736,8 +736,8 @@ func TestPlayerFilters(t *testing.T) { Match: "dst5", Filter: "select id1, val from src5 where val = 'abc'", }, { - Match: "dstCharset", - Filter: "select id1, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val2 from srcCharset", + Match: "dst_charset", + Filter: "select id1, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val2 from src_charset", }}, } bls := &binlogdatapb.BinlogSource{ @@ -985,14 +985,14 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{{"1", "abc"}, {"4", "abc"}}, }, { // test collation + filter - input: "insert into srcCharset values (1,'木元')", + input: "insert into src_charset values (1,'木元')", output: qh.Expect( "begin", - "insert into dstCharset(id1,val,val2) values (1,concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'),concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'))", + "insert into dst_charset(id1,val,val2) values (1,concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'),concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'))", "/update _vt.vreplication set pos=", "commit", ), - table: "dstCharset", + table: "dst_charset", data: [][]string{{"1", "木abcxyz", "木abcxyz"}}, }} @@ -2335,7 +2335,7 @@ func TestPlayerCancelOnLock(t *testing.T) { } } -func TestPlayerBatching(t *testing.T) { +func TestPlayerTransactions(t *testing.T) { defer deleteTablet(addTablet(100)) execStatements(t, []string{ @@ -2900,7 +2900,7 @@ func TestPlayerInvalidDates(t *testing.T) { fmt.Sprintf("drop table %s.dst1", vrepldb), }) pos := primaryPosition(t) - execStatements(t, []string{"set sql_mode='';insert into src1 values(1, '0000-00-00');set sql_mode='STRICT_TRANS_TABLES';"}) + execStatements(t, []string{"set sql_mode=''", "insert into src1 values(1, '0000-00-00')", "set sql_mode='STRICT_TRANS_TABLES'"}) env.SchemaEngine.Reload(context.Background()) // default mysql flavor allows invalid dates: so disallow explicitly for this test @@ -3093,7 +3093,6 @@ func TestPlayerNoBlob(t *testing.T) { output := qh.Expect(tcases.output) expectNontxQueries(t, output) time.Sleep(1 * time.Second) - log.Flush() if tcases.table != "" { expectData(t, tcases.table, tcases.data) } @@ -3107,6 +3106,255 @@ func TestPlayerNoBlob(t *testing.T) { require.Equal(t, int64(4), stats.PartialQueryCount.Counts()["update"]) } +func TestPlayerBatchMode(t *testing.T) { + // To test trx batch splitting at 1024-64 bytes. + maxAllowedPacket := 1024 + oldVreplicationExperimentalFlags := vttablet.VReplicationExperimentalFlags + vttablet.VReplicationExperimentalFlags = vttablet.VReplicationExperimentalFlagVPlayerBatching + defer func() { + vttablet.VReplicationExperimentalFlags = oldVreplicationExperimentalFlags + }() + + defer deleteTablet(addTablet(100)) + execStatements(t, []string{ + fmt.Sprintf("set @@global.max_allowed_packet=%d", maxAllowedPacket), + "create table t1(id bigint, val1 varchar(1000), primary key(id))", + fmt.Sprintf("create table %s.t1(id bigint, val1 varchar(1000), primary key(id))", vrepldb), + }) + defer execStatements(t, []string{ + "drop table t1", + fmt.Sprintf("drop table %s.t1", vrepldb), + }) + env.SchemaEngine.Reload(context.Background()) + + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select * from t1", + }}, + } + bls := &binlogdatapb.BinlogSource{ + Keyspace: env.KeyspaceName, + Shard: env.ShardName, + Filter: filter, + OnDdl: binlogdatapb.OnDDLAction_IGNORE, + } + cancel, vrID := startVReplication(t, bls, "") + defer cancel() + + maxBatchSize := maxAllowedPacket - 64 // VPlayer leaves 64 bytes of room + // When the trx will be in a single batch. + trxFullBatchExpectRE := `^begin;(set @@session\.foreign_key_checks=.*;)?%s;update _vt\.vreplication set pos=.*;commit$` + // If the trx batch is split, then we only expect the end part. + trxLastBatchExpectRE := `%s;update _vt\.vreplication set pos=.*;commit$` + // The vreplication position update statement will look like this: + // update _vt.vreplication set pos='MySQL56/b213e4de-937a-11ee-b184-668979c675f4:1-38', time_updated=1701786574, transaction_timestamp=1701786574, rows_copied=0, message='' where id=1; + // So it will use 182 bytes in the batch. + // This long value can be used to test the handling of bulk statements + // which bump up against the max batch size, as well as testing the trx + // batch splitting into multiple wire messages when hitting the max size. + longStr := strings.Repeat("a", maxBatchSize-70) + + testcases := []struct { + input string + output []string + expectedNonCommitBatches int64 + expectedInLastBatch string // Should only be set if we expect 1+ non-commit batches + expectedBulkInserts int64 + expectedBulkDeletes int64 + table string + data [][]string + }{ + { + input: "insert into t1(id, val1) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc'), (4, 'ddd'), (5, 'eee')", + output: []string{"insert into t1(id,val1) values (1,'aaa'), (2,'bbb'), (3,'ccc'), (4,'ddd'), (5,'eee')"}, + expectedBulkInserts: 1, + table: "t1", + data: [][]string{ + {"1", "aaa"}, + {"2", "bbb"}, + {"3", "ccc"}, + {"4", "ddd"}, + {"5", "eee"}, + }, + }, + { + input: "delete from t1 where id = 1", + output: []string{"delete from t1 where id=1"}, + table: "t1", + data: [][]string{ + {"2", "bbb"}, + {"3", "ccc"}, + {"4", "ddd"}, + {"5", "eee"}, + }, + }, + { + input: "delete from t1 where id > 3", + output: []string{"delete from t1 where id in (4, 5)"}, + expectedBulkDeletes: 1, + table: "t1", + data: [][]string{ + {"2", "bbb"}, + {"3", "ccc"}, + }, + }, + { + input: fmt.Sprintf("insert into t1(id, val1) values (1, '%s'), (2, 'bbb'), (3, 'ccc') on duplicate key update id = id+100", longStr), + output: []string{ + fmt.Sprintf("insert into t1(id,val1) values (1,'%s')", longStr), + "delete from t1 where id=2", + "insert into t1(id,val1) values (102,'bbb')", + "delete from t1 where id=3", + // This will be in the second/last batch, along with the vrepl pos update. + "insert into t1(id,val1) values (103,'ccc')", + }, + expectedInLastBatch: "insert into t1(id,val1) values (103,'ccc')", + expectedNonCommitBatches: 1, + table: "t1", + data: [][]string{ + {"1", longStr}, + {"102", "bbb"}, + {"103", "ccc"}, + }, + }, + { + input: "insert into t1(id, val1) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc') on duplicate key update id = id+500, val1 = values(val1)", + output: []string{ + "delete from t1 where id=1", + "insert into t1(id,val1) values (501,'aaa')", + "insert into t1(id,val1) values (2,'bbb'), (3,'ccc')", + }, + expectedBulkInserts: 1, + table: "t1", + data: [][]string{ + {"2", "bbb"}, + {"3", "ccc"}, + {"102", "bbb"}, + {"103", "ccc"}, + {"501", "aaa"}, + }, + }, + { + input: "delete from t1", + output: []string{"delete from t1 where id in (2, 3, 102, 103, 501)"}, + expectedBulkDeletes: 1, + table: "t1", + }, + { + input: fmt.Sprintf("insert into t1(id, val1) values (1, '%s'), (2, 'bbb'), (3, 'ccc'), (4, 'ddd'), (5, 'eee')", longStr), + output: []string{ + // This bulk insert is long enough that the BEGIN gets sent down by itself. + // The bulk query then gets split into two queries. It also causes the trx + // to get split into three batches (BEGIN, INSERT, INSERT). + fmt.Sprintf("insert into t1(id,val1) values (1,'%s'), (2,'bbb'), (3,'ccc'), (4,'ddd')", longStr), + // This will be in the second/last batch, along with the vrepl pos update. + "insert into t1(id,val1) values (5,'eee')", + }, + expectedBulkInserts: 2, + // The BEGIN, then the INSERT. + expectedNonCommitBatches: 2, // The last one includes the commit + expectedInLastBatch: "insert into t1(id,val1) values (5,'eee')", + table: "t1", + data: [][]string{ + {"1", longStr}, + {"2", "bbb"}, + {"3", "ccc"}, + {"4", "ddd"}, + {"5", "eee"}, + }, + }, + { + input: "insert into t1(id, val1) values (1000000000000, 'x'), (1000000000001, 'x'), (1000000000002, 'x'), (1000000000003, 'x'), (1000000000004, 'x'), (1000000000005, 'x'), (1000000000006, 'x'), (1000000000007, 'x'), (1000000000008, 'x'), (1000000000009, 'x'), (1000000000010, 'x'), (1000000000011, 'x'), (1000000000012, 'x'), (1000000000013, 'x'), (1000000000014, 'x'), (1000000000015, 'x'), (1000000000016, 'x'), (1000000000017, 'x'), (1000000000018, 'x'), (1000000000019, 'x'), (1000000000020, 'x'), (1000000000021, 'x'), (1000000000022, 'x'), (1000000000023, 'x'), (1000000000024, 'x'), (1000000000025, 'x'), (1000000000026, 'x'), (1000000000027, 'x'), (1000000000028, 'x'), (1000000000029, 'x'), (1000000000030, 'x'), (1000000000031, 'x'), (1000000000032, 'x'), (1000000000033, 'x'), (1000000000034, 'x'), (1000000000035, 'x'), (1000000000036, 'x'), (1000000000037, 'x'), (1000000000038, 'x'), (1000000000039, 'x'), (1000000000040, 'x'), (1000000000041, 'x'), (1000000000042, 'x'), (1000000000043, 'x'), (1000000000044, 'x'), (1000000000045, 'x'), (1000000000046, 'x'), (1000000000047, 'x'), (1000000000048, 'x'), (1000000000049, 'x'), (1000000000050, 'x'), (1000000000051, 'x'), (1000000000052, 'x'), (1000000000053, 'x'), (1000000000054, 'x'), (1000000000055, 'x'), (1000000000056, 'x'), (1000000000057, 'x'), (1000000000058, 'x'), (1000000000059, 'x'), (1000000000060, 'x'), (1000000000061, 'x'), (1000000000062, 'x'), (1000000000063, 'x'), (1000000000064, 'x'), (1000000000065, 'x'), (1000000000066, 'x'), (1000000000067, 'x'), (1000000000068, 'x'), (1000000000069, 'x'), (1000000000070, 'x'), (1000000000071, 'x'), (1000000000072, 'x'), (1000000000073, 'x'), (1000000000074, 'x'), (1000000000075, 'x'), (1000000000076, 'x'), (1000000000077, 'x'), (1000000000078, 'x'), (1000000000079, 'x'), (1000000000080, 'x'), (1000000000081, 'x'), (1000000000082, 'x'), (1000000000083, 'x'), (1000000000084, 'x'), (1000000000085, 'x'), (1000000000086, 'x'), (1000000000087, 'x'), (1000000000088, 'x'), (1000000000089, 'x'), (1000000000090, 'x'), (1000000000091, 'x'), (1000000000092, 'x'), (1000000000093, 'x'), (1000000000094, 'x'), (1000000000095, 'x'), (1000000000096, 'x'), (1000000000097, 'x'), (1000000000098, 'x'), (1000000000099, 'x'), (1000000000100, 'x'), (1000000000101, 'x'), (1000000000102, 'x'), (1000000000103, 'x'), (1000000000104, 'x'), (1000000000105, 'x'), (1000000000106, 'x'), (1000000000107, 'x'), (1000000000108, 'x'), (1000000000109, 'x'), (1000000000110, 'x'), (1000000000111, 'x'), (1000000000112, 'x'), (1000000000113, 'x'), (1000000000114, 'x'), (1000000000115, 'x'), (1000000000116, 'x'), (1000000000117, 'x'), (1000000000118, 'x'), (1000000000119, 'x'), (1000000000120, 'x'), (1000000000121, 'x'), (1000000000122, 'x'), (1000000000123, 'x'), (1000000000124, 'x'), (1000000000125, 'x'), (1000000000126, 'x'), (1000000000127, 'x'), (1000000000128, 'x'), (1000000000129, 'x'), (1000000000130, 'x'), (1000000000131, 'x'), (1000000000132, 'x'), (1000000000133, 'x'), (1000000000134, 'x'), (1000000000135, 'x'), (1000000000136, 'x'), (1000000000137, 'x'), (1000000000138, 'x'), (1000000000139, 'x'), (1000000000140, 'x'), (1000000000141, 'x'), (1000000000142, 'x'), (1000000000143, 'x'), (1000000000144, 'x'), (1000000000145, 'x'), (1000000000146, 'x'), (1000000000147, 'x'), (1000000000148, 'x'), (1000000000149, 'x'), (1000000000150, 'x')", + output: []string{ + "insert into t1(id,val1) values (1000000000000,'x'), (1000000000001,'x'), (1000000000002,'x'), (1000000000003,'x'), (1000000000004,'x'), (1000000000005,'x'), (1000000000006,'x'), (1000000000007,'x'), (1000000000008,'x'), (1000000000009,'x'), (1000000000010,'x'), (1000000000011,'x'), (1000000000012,'x'), (1000000000013,'x'), (1000000000014,'x'), (1000000000015,'x'), (1000000000016,'x'), (1000000000017,'x'), (1000000000018,'x'), (1000000000019,'x'), (1000000000020,'x'), (1000000000021,'x'), (1000000000022,'x'), (1000000000023,'x'), (1000000000024,'x'), (1000000000025,'x'), (1000000000026,'x'), (1000000000027,'x'), (1000000000028,'x'), (1000000000029,'x'), (1000000000030,'x'), (1000000000031,'x'), (1000000000032,'x'), (1000000000033,'x'), (1000000000034,'x'), (1000000000035,'x'), (1000000000036,'x'), (1000000000037,'x'), (1000000000038,'x'), (1000000000039,'x'), (1000000000040,'x'), (1000000000041,'x'), (1000000000042,'x'), (1000000000043,'x')", + "insert into t1(id,val1) values (1000000000044,'x'), (1000000000045,'x'), (1000000000046,'x'), (1000000000047,'x'), (1000000000048,'x'), (1000000000049,'x'), (1000000000050,'x'), (1000000000051,'x'), (1000000000052,'x'), (1000000000053,'x'), (1000000000054,'x'), (1000000000055,'x'), (1000000000056,'x'), (1000000000057,'x'), (1000000000058,'x'), (1000000000059,'x'), (1000000000060,'x'), (1000000000061,'x'), (1000000000062,'x'), (1000000000063,'x'), (1000000000064,'x'), (1000000000065,'x'), (1000000000066,'x'), (1000000000067,'x'), (1000000000068,'x'), (1000000000069,'x'), (1000000000070,'x'), (1000000000071,'x'), (1000000000072,'x'), (1000000000073,'x'), (1000000000074,'x'), (1000000000075,'x'), (1000000000076,'x'), (1000000000077,'x'), (1000000000078,'x'), (1000000000079,'x'), (1000000000080,'x'), (1000000000081,'x'), (1000000000082,'x'), (1000000000083,'x'), (1000000000084,'x'), (1000000000085,'x'), (1000000000086,'x'), (1000000000087,'x')", + "insert into t1(id,val1) values (1000000000088,'x'), (1000000000089,'x'), (1000000000090,'x'), (1000000000091,'x'), (1000000000092,'x'), (1000000000093,'x'), (1000000000094,'x'), (1000000000095,'x'), (1000000000096,'x'), (1000000000097,'x'), (1000000000098,'x'), (1000000000099,'x'), (1000000000100,'x'), (1000000000101,'x'), (1000000000102,'x'), (1000000000103,'x'), (1000000000104,'x'), (1000000000105,'x'), (1000000000106,'x'), (1000000000107,'x'), (1000000000108,'x'), (1000000000109,'x'), (1000000000110,'x'), (1000000000111,'x'), (1000000000112,'x'), (1000000000113,'x'), (1000000000114,'x'), (1000000000115,'x'), (1000000000116,'x'), (1000000000117,'x'), (1000000000118,'x'), (1000000000119,'x'), (1000000000120,'x'), (1000000000121,'x'), (1000000000122,'x'), (1000000000123,'x'), (1000000000124,'x'), (1000000000125,'x'), (1000000000126,'x'), (1000000000127,'x'), (1000000000128,'x'), (1000000000129,'x'), (1000000000130,'x'), (1000000000131,'x')", + // This will be in the last batch, along with the vrepl pos update. + "insert into t1(id,val1) values (1000000000132,'x'), (1000000000133,'x'), (1000000000134,'x'), (1000000000135,'x'), (1000000000136,'x'), (1000000000137,'x'), (1000000000138,'x'), (1000000000139,'x'), (1000000000140,'x'), (1000000000141,'x'), (1000000000142,'x'), (1000000000143,'x'), (1000000000144,'x'), (1000000000145,'x'), (1000000000146,'x'), (1000000000147,'x'), (1000000000148,'x'), (1000000000149,'x'), (1000000000150,'x')", + }, + expectedBulkInserts: 4, + expectedNonCommitBatches: 3, // The last one includes the commit + expectedInLastBatch: "insert into t1(id,val1) values (1000000000132,'x'), (1000000000133,'x'), (1000000000134,'x'), (1000000000135,'x'), (1000000000136,'x'), (1000000000137,'x'), (1000000000138,'x'), (1000000000139,'x'), (1000000000140,'x'), (1000000000141,'x'), (1000000000142,'x'), (1000000000143,'x'), (1000000000144,'x'), (1000000000145,'x'), (1000000000146,'x'), (1000000000147,'x'), (1000000000148,'x'), (1000000000149,'x'), (1000000000150,'x')", + table: "t1", + data: [][]string{ + {"1", longStr}, + {"2", "bbb"}, + {"3", "ccc"}, + {"4", "ddd"}, + {"5", "eee"}, + {"1000000000000", "x"}, {"1000000000001", "x"}, {"1000000000002", "x"}, {"1000000000003", "x"}, {"1000000000004", "x"}, {"1000000000005", "x"}, {"1000000000006", "x"}, {"1000000000007", "x"}, {"1000000000008", "x"}, {"1000000000009", "x"}, {"1000000000010", "x"}, {"1000000000011", "x"}, {"1000000000012", "x"}, {"1000000000013", "x"}, {"1000000000014", "x"}, {"1000000000015", "x"}, {"1000000000016", "x"}, {"1000000000017", "x"}, {"1000000000018", "x"}, {"1000000000019", "x"}, {"1000000000020", "x"}, {"1000000000021", "x"}, {"1000000000022", "x"}, {"1000000000023", "x"}, {"1000000000024", "x"}, {"1000000000025", "x"}, {"1000000000026", "x"}, {"1000000000027", "x"}, {"1000000000028", "x"}, {"1000000000029", "x"}, {"1000000000030", "x"}, {"1000000000031", "x"}, {"1000000000032", "x"}, {"1000000000033", "x"}, {"1000000000034", "x"}, {"1000000000035", "x"}, {"1000000000036", "x"}, {"1000000000037", "x"}, {"1000000000038", "x"}, {"1000000000039", "x"}, {"1000000000040", "x"}, {"1000000000041", "x"}, {"1000000000042", "x"}, {"1000000000043", "x"}, {"1000000000044", "x"}, {"1000000000045", "x"}, {"1000000000046", "x"}, {"1000000000047", "x"}, {"1000000000048", "x"}, {"1000000000049", "x"}, {"1000000000050", "x"}, {"1000000000051", "x"}, {"1000000000052", "x"}, {"1000000000053", "x"}, {"1000000000054", "x"}, {"1000000000055", "x"}, {"1000000000056", "x"}, {"1000000000057", "x"}, {"1000000000058", "x"}, {"1000000000059", "x"}, {"1000000000060", "x"}, {"1000000000061", "x"}, {"1000000000062", "x"}, {"1000000000063", "x"}, {"1000000000064", "x"}, {"1000000000065", "x"}, {"1000000000066", "x"}, {"1000000000067", "x"}, {"1000000000068", "x"}, {"1000000000069", "x"}, {"1000000000070", "x"}, {"1000000000071", "x"}, {"1000000000072", "x"}, {"1000000000073", "x"}, {"1000000000074", "x"}, {"1000000000075", "x"}, {"1000000000076", "x"}, {"1000000000077", "x"}, {"1000000000078", "x"}, {"1000000000079", "x"}, {"1000000000080", "x"}, {"1000000000081", "x"}, {"1000000000082", "x"}, {"1000000000083", "x"}, {"1000000000084", "x"}, {"1000000000085", "x"}, {"1000000000086", "x"}, {"1000000000087", "x"}, {"1000000000088", "x"}, {"1000000000089", "x"}, {"1000000000090", "x"}, {"1000000000091", "x"}, {"1000000000092", "x"}, {"1000000000093", "x"}, {"1000000000094", "x"}, {"1000000000095", "x"}, {"1000000000096", "x"}, {"1000000000097", "x"}, {"1000000000098", "x"}, {"1000000000099", "x"}, {"1000000000100", "x"}, {"1000000000101", "x"}, {"1000000000102", "x"}, {"1000000000103", "x"}, {"1000000000104", "x"}, {"1000000000105", "x"}, {"1000000000106", "x"}, {"1000000000107", "x"}, {"1000000000108", "x"}, {"1000000000109", "x"}, {"1000000000110", "x"}, {"1000000000111", "x"}, {"1000000000112", "x"}, {"1000000000113", "x"}, {"1000000000114", "x"}, {"1000000000115", "x"}, {"1000000000116", "x"}, {"1000000000117", "x"}, {"1000000000118", "x"}, {"1000000000119", "x"}, {"1000000000120", "x"}, {"1000000000121", "x"}, {"1000000000122", "x"}, {"1000000000123", "x"}, {"1000000000124", "x"}, {"1000000000125", "x"}, {"1000000000126", "x"}, {"1000000000127", "x"}, {"1000000000128", "x"}, {"1000000000129", "x"}, {"1000000000130", "x"}, {"1000000000131", "x"}, {"1000000000132", "x"}, {"1000000000133", "x"}, {"1000000000134", "x"}, {"1000000000135", "x"}, {"1000000000136", "x"}, {"1000000000137", "x"}, {"1000000000138", "x"}, {"1000000000139", "x"}, {"1000000000140", "x"}, {"1000000000141", "x"}, {"1000000000142", "x"}, {"1000000000143", "x"}, {"1000000000144", "x"}, {"1000000000145", "x"}, {"1000000000146", "x"}, {"1000000000147", "x"}, {"1000000000148", "x"}, {"1000000000149", "x"}, {"1000000000150", "x"}, + }, + }, + { // Now we have enough long IDs to cause the bulk delete to also be split along with the trx batch. + input: "delete from t1 where id > 1 and id <= 1000000000149", + output: []string{ + "delete from t1 where id in (2, 3, 4, 5, 1000000000000, 1000000000001, 1000000000002, 1000000000003, 1000000000004, 1000000000005, 1000000000006, 1000000000007, 1000000000008, 1000000000009, 1000000000010, 1000000000011, 1000000000012, 1000000000013, 1000000000014, 1000000000015, 1000000000016, 1000000000017, 1000000000018, 1000000000019, 1000000000020, 1000000000021, 1000000000022, 1000000000023, 1000000000024, 1000000000025, 1000000000026, 1000000000027, 1000000000028, 1000000000029, 1000000000030, 1000000000031, 1000000000032, 1000000000033, 1000000000034, 1000000000035, 1000000000036, 1000000000037, 1000000000038, 1000000000039, 1000000000040, 1000000000041, 1000000000042, 1000000000043, 1000000000044, 1000000000045, 1000000000046, 1000000000047, 1000000000048, 1000000000049, 1000000000050, 1000000000051, 1000000000052, 1000000000053, 1000000000054, 1000000000055, 1000000000056, 1000000000057, 1000000000058, 1000000000059)", + "delete from t1 where id in (1000000000060, 1000000000061, 1000000000062, 1000000000063, 1000000000064, 1000000000065, 1000000000066, 1000000000067, 1000000000068, 1000000000069, 1000000000070, 1000000000071, 1000000000072, 1000000000073, 1000000000074, 1000000000075, 1000000000076, 1000000000077, 1000000000078, 1000000000079, 1000000000080, 1000000000081, 1000000000082, 1000000000083, 1000000000084, 1000000000085, 1000000000086, 1000000000087, 1000000000088, 1000000000089, 1000000000090, 1000000000091, 1000000000092, 1000000000093, 1000000000094, 1000000000095, 1000000000096, 1000000000097, 1000000000098, 1000000000099, 1000000000100, 1000000000101, 1000000000102, 1000000000103, 1000000000104, 1000000000105, 1000000000106, 1000000000107, 1000000000108, 1000000000109, 1000000000110, 1000000000111, 1000000000112, 1000000000113, 1000000000114, 1000000000115, 1000000000116, 1000000000117, 1000000000118, 1000000000119, 1000000000120)", + // This will be in the last batch, along with the vrepl pos update. + "delete from t1 where id in (1000000000121, 1000000000122, 1000000000123, 1000000000124, 1000000000125, 1000000000126, 1000000000127, 1000000000128, 1000000000129, 1000000000130, 1000000000131, 1000000000132, 1000000000133, 1000000000134, 1000000000135, 1000000000136, 1000000000137, 1000000000138, 1000000000139, 1000000000140, 1000000000141, 1000000000142, 1000000000143, 1000000000144, 1000000000145, 1000000000146, 1000000000147, 1000000000148, 1000000000149)", + }, + expectedBulkDeletes: 3, + expectedNonCommitBatches: 2, // The last one includes the commit + expectedInLastBatch: "delete from t1 where id in (1000000000121, 1000000000122, 1000000000123, 1000000000124, 1000000000125, 1000000000126, 1000000000127, 1000000000128, 1000000000129, 1000000000130, 1000000000131, 1000000000132, 1000000000133, 1000000000134, 1000000000135, 1000000000136, 1000000000137, 1000000000138, 1000000000139, 1000000000140, 1000000000141, 1000000000142, 1000000000143, 1000000000144, 1000000000145, 1000000000146, 1000000000147, 1000000000148, 1000000000149)", + table: "t1", + data: [][]string{ + {"1", longStr}, + {"1000000000150", "x"}, + }, + }, + { + input: "delete from t1 where id = 1 or id > 1000000000149", + output: []string{"delete from t1 where id in (1, 1000000000150)"}, + expectedBulkDeletes: 1, + table: "t1", + }, + } + + expectedBulkInserts, expectedBulkDeletes, expectedTrxBatchExecs, expectedTrxBatchCommits := int64(0), int64(0), int64(0), int64(0) + stats := globalStats.controllers[int32(vrID)].blpStats + + for _, tcase := range testcases { + t.Run(fmt.Sprintf("%.50s", tcase.input), func(t *testing.T) { + execStatements(t, []string{tcase.input}) + var output qh.ExpectationSequencer + switch len(tcase.output) { + case 0: + require.FailNow(t, "no expected output provided for test case") + case 1: + output = qh.Expect(tcase.output[0]) + default: + output = qh.Expect(tcase.output[0], tcase.output[1:]...) + } + for _, stmt := range tcase.output { + require.LessOrEqual(t, len(stmt), maxBatchSize, "expected output statement is longer than the max batch size (%d): %s", maxBatchSize, stmt) + } + expectNontxQueries(t, output) + time.Sleep(1 * time.Second) + if tcase.table != "" { + expectData(t, tcase.table, tcase.data) + } + + // Confirm that the row events generated the expected multi-row + // statements and the statements were sent in multi-statement + // protocol message(s) as expected. + expectedBulkDeletes += tcase.expectedBulkDeletes + expectedBulkInserts += tcase.expectedBulkInserts + expectedTrxBatchCommits++ // Should only ever be 1 per test case + expectedTrxBatchExecs += tcase.expectedNonCommitBatches + if tcase.expectedInLastBatch != "" { // We expect the trx to be split + require.Regexpf(t, regexp.MustCompile(fmt.Sprintf(trxLastBatchExpectRE, regexp.QuoteMeta(tcase.expectedInLastBatch))), lastMultiExecQuery, "Unexpected batch statement: %s", lastMultiExecQuery) + } else { + require.Regexpf(t, regexp.MustCompile(fmt.Sprintf(trxFullBatchExpectRE, regexp.QuoteMeta(strings.Join(tcase.output, ";")))), lastMultiExecQuery, "Unexpected batch statement: %s", lastMultiExecQuery) + } + require.Equal(t, expectedBulkInserts, stats.BulkQueryCount.Counts()["insert"], "expected %d bulk inserts but got %d", expectedBulkInserts, stats.BulkQueryCount.Counts()["insert"]) + require.Equal(t, expectedBulkDeletes, stats.BulkQueryCount.Counts()["delete"], "expected %d bulk deletes but got %d", expectedBulkDeletes, stats.BulkQueryCount.Counts()["delete"]) + require.Equal(t, expectedTrxBatchExecs, stats.TrxQueryBatchCount.Counts()["without_commit"], "expected %d trx batch execs but got %d", expectedTrxBatchExecs, stats.TrxQueryBatchCount.Counts()["without_commit"]) + require.Equal(t, expectedTrxBatchCommits, stats.TrxQueryBatchCount.Counts()["with_commit"], "expected %d trx batch commits but got %d", expectedTrxBatchCommits, stats.TrxQueryBatchCount.Counts()["with_commit"]) + }) + } +} + func expectJSON(t *testing.T, table string, values [][]string, id int, exec func(ctx context.Context, query string) (*sqltypes.Result, error)) { t.Helper() diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index e148151934e..daa642c53af 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -28,22 +28,20 @@ import ( "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/timer" + "vitess.io/vitess/go/vt/binlog/binlogplayer" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/binlog/binlogplayer" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/mysqlctl" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) var ( @@ -329,7 +327,13 @@ type ColumnInfo struct { } func (vr *vreplicator) buildColInfoMap(ctx context.Context) (map[string][]*ColumnInfo, error) { - req := &tabletmanagerdatapb.GetSchemaRequest{Tables: []string{"/.*/"}, ExcludeTables: []string{"/" + schema.GCTableNameExpression + "/"}} + req := &tabletmanagerdatapb.GetSchemaRequest{ + Tables: []string{"/.*/"}, + ExcludeTables: []string{ + "/" + schema.OldGCTableNameExpression + "/", + "/" + schema.GCTableNameExpression + "/", + }, + } schema, err := vr.mysqld.GetSchema(ctx, vr.dbClient.DBName(), req) if err != nil { return nil, err @@ -352,7 +356,11 @@ func (vr *vreplicator) buildColInfoMap(ctx context.Context) (map[string][]*Colum pks = td.PrimaryKeyColumns } else { // Use a PK equivalent if one exists. - if pks, _, err = vr.mysqld.GetPrimaryKeyEquivalentColumns(ctx, vr.dbClient.DBName(), td.Name); err != nil { + executeFetch := func(query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { + // This sets wantfields to true. + return vr.dbClient.ExecuteFetch(query, maxrows) + } + if pks, _, err = mysqlctl.GetPrimaryKeyEquivalentColumns(ctx, executeFetch, vr.dbClient.DBName(), td.Name); err != nil { return nil, err } // Fall back to using every column in the table if there's no PK or PKE. @@ -561,7 +569,7 @@ func (vr *vreplicator) setSQLMode(ctx context.Context, dbClient *vdbClient) (fun // - "vreplication" for most flows // - "vreplication:online-ddl" for online ddl flows. // Note that with such name, it's possible to throttle -// the worflow by either /throttler/throttle-app?app=vreplication and/or /throttler/throttle-app?app=online-ddl +// the workflow by either /throttler/throttle-app?app=vreplication and/or /throttler/throttle-app?app=online-ddl // This is useful when we want to throttle all migrations. We throttle "online-ddl" and that applies to both vreplication // migrations as well as gh-ost migrations. func (vr *vreplicator) throttlerAppName() string { @@ -572,10 +580,21 @@ func (vr *vreplicator) throttlerAppName() string { return throttlerapp.Concatenate(names...) } +// updateTimeThrottled updates the time_throttled field in the _vt.vreplication record +// with a rate limit so that it's only saved in the database at most once per +// throttleUpdatesRateLimiter.tickerTime. +// It also increments the throttled count in the stats to keep track of how many +// times a VReplication workflow, and the specific sub-component, is throttled by the +// tablet throttler over time. It also increments the global throttled count to keep +// track of how many times in total vreplication has been throttled across all workflows +// (both ones that currently exist and ones that no longer do). func (vr *vreplicator) updateTimeThrottled(appThrottled throttlerapp.Name) error { + appName := appThrottled.String() + vr.stats.ThrottledCounts.Add([]string{"tablet", appName}, 1) + globalStats.ThrottledCount.Add(1) err := vr.throttleUpdatesRateLimiter.Do(func() error { tm := time.Now().Unix() - update, err := binlogplayer.GenerateUpdateTimeThrottled(vr.id, tm, appThrottled.String()) + update, err := binlogplayer.GenerateUpdateTimeThrottled(vr.id, tm, appName) if err != nil { return err } @@ -728,7 +747,7 @@ func (vr *vreplicator) getTableSecondaryKeys(ctx context.Context, tableName stri } tableSchema := schema.TableDefinitions[0].Schema var secondaryKeys []*sqlparser.IndexDefinition - parsedDDL, err := sqlparser.ParseStrictDDL(tableSchema) + parsedDDL, err := vr.vre.env.Parser().ParseStrictDDL(tableSchema) if err != nil { return secondaryKeys, err } @@ -739,8 +758,27 @@ func (vr *vreplicator) getTableSecondaryKeys(ctx context.Context, tableName stri return nil, fmt.Errorf("could not determine CREATE TABLE statement from table schema %q", tableSchema) } - for _, index := range createTable.GetTableSpec().Indexes { + tableSpec := createTable.GetTableSpec() + fkIndexCols := make(map[string]bool) + for _, constraint := range tableSpec.Constraints { + if fkDef, ok := constraint.Details.(*sqlparser.ForeignKeyDefinition); ok { + fkCols := make([]string, len(fkDef.Source)) + for i, fkCol := range fkDef.Source { + fkCols[i] = fkCol.Lowered() + } + fkIndexCols[strings.Join(fkCols, ",")] = true + } + } + for _, index := range tableSpec.Indexes { if index.Info.Type != sqlparser.IndexTypePrimary { + cols := make([]string, len(index.Columns)) + for i, col := range index.Columns { + cols[i] = col.Column.Lowered() + } + if fkIndexCols[strings.Join(cols, ",")] { + // This index is needed for a FK constraint so we cannot drop it. + continue + } secondaryKeys = append(secondaryKeys, index) } } @@ -956,7 +994,7 @@ func (vr *vreplicator) execPostCopyActions(ctx context.Context, tableName string // the table schema and if so move forward and delete the // post_copy_action record. if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERDupKeyName { - stmt, err := sqlparser.ParseStrictDDL(action.Task) + stmt, err := vr.vre.env.Parser().ParseStrictDDL(action.Task) if err != nil { return failedAlterErr } @@ -1044,7 +1082,7 @@ func (vr *vreplicator) setExistingRowsCopied() { if vr.stats.CopyRowCount.Get() == 0 { rowsCopiedExisting, err := vr.readExistingRowsCopied(vr.id) if err != nil { - log.Warningf("Failed to read existing rows copied value for %s worfklow: %v", vr.WorkflowName, err) + log.Warningf("Failed to read existing rows copied value for %s workflow: %v", vr.WorkflowName, err) } else if rowsCopiedExisting != 0 { log.Infof("Resuming the %s vreplication workflow started on another tablet, setting rows copied counter to %v", vr.WorkflowName, rowsCopiedExisting) vr.stats.CopyRowCount.Set(rowsCopiedExisting) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go index 128d41d4bc2..3be0525dc88 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go @@ -34,9 +34,10 @@ import ( "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/schemadiff" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - "vitess.io/vitess/go/vt/schemadiff" ) func TestRecalculatePKColsInfoByColumnNames(t *testing.T) { @@ -182,7 +183,10 @@ func TestPrimaryKeyEquivalentColumns(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require.NoError(t, env.Mysqld.ExecuteSuperQuery(ctx, tt.ddl)) - cols, indexName, err := env.Mysqld.GetPrimaryKeyEquivalentColumns(ctx, env.Dbcfgs.DBName, tt.table) + conn, err := env.Mysqld.GetDbaConnection(ctx) + require.NoError(t, err, "could not connect to mysqld: %v", err) + defer conn.Close() + cols, indexName, err := mysqlctl.GetPrimaryKeyEquivalentColumns(ctx, conn.ExecuteFetch, env.Dbcfgs.DBName, tt.table) if (err != nil) != tt.wantErr { t.Errorf("Mysqld.GetPrimaryKeyEquivalentColumns() error = %v, wantErr %v", err, tt.wantErr) return @@ -256,6 +260,7 @@ func TestDeferSecondaryKeys(t *testing.T) { wantStashErr string wantExecErr string expectFinalSchemaDiff bool + preStashHook func() error postStashHook func() error }{ { @@ -297,6 +302,54 @@ func TestDeferSecondaryKeys(t *testing.T) { actionDDL: "alter table %s.t1 add key c1 (c1), add key c2 (c2)", WorkflowType: int32(binlogdatapb.VReplicationWorkflowType_MoveTables), }, + { + name: "2SK:1FK", + tableName: "t1", + initialDDL: "create table t1 (id int not null, c1 int default null, c2 int default null, t2_id int not null, primary key (id), key c1 (c1), key c2 (c2), foreign key (t2_id) references t2 (id))", + // Secondary key t2_id is needed to enforce the FK constraint so we do not drop it. + strippedDDL: "create table t1 (id int not null, c1 int default null, c2 int default null, t2_id int not null, primary key (id), key t2_id (t2_id), constraint t1_ibfk_1 foreign key (t2_id) references t2 (id))", + actionDDL: "alter table %s.t1 add key c1 (c1), add key c2 (c2)", + WorkflowType: int32(binlogdatapb.VReplicationWorkflowType_MoveTables), + preStashHook: func() error { + if _, err := dbClient.ExecuteFetch("drop table if exists t2", 1); err != nil { + return err + } + _, err = dbClient.ExecuteFetch("create table t2 (id int not null, c1 int not null, primary key (id))", 1) + return err + }, + }, + { + name: "3SK:2FK", + tableName: "t1", + initialDDL: "create table t1 (id int not null, id2 int default null, c1 int default null, c2 int default null, c3 int default null, t2_id int not null, t2_id2 int not null, primary key (id), key c1 (c1), key c2 (c2), foreign key (t2_id) references t2 (id), key c3 (c3), foreign key (t2_id2) references t2 (id2))", + // Secondary keys t2_id and t2_id2 are needed to enforce the FK constraint so we do not drop them. + strippedDDL: "create table t1 (id int not null, id2 int default null, c1 int default null, c2 int default null, c3 int default null, t2_id int not null, t2_id2 int not null, primary key (id), key t2_id (t2_id), key t2_id2 (t2_id2), constraint t1_ibfk_1 foreign key (t2_id) references t2 (id), constraint t1_ibfk_2 foreign key (t2_id2) references t2 (id2))", + actionDDL: "alter table %s.t1 add key c1 (c1), add key c2 (c2), add key c3 (c3)", + WorkflowType: int32(binlogdatapb.VReplicationWorkflowType_MoveTables), + preStashHook: func() error { + if _, err := dbClient.ExecuteFetch("drop table if exists t2", 1); err != nil { + return err + } + _, err = dbClient.ExecuteFetch("create table t2 (id int not null, id2 int default null, c1 int not null, primary key (id), key (id2))", 1) + return err + }, + }, + { + name: "5SK:2FK_multi-column", + tableName: "t1", + initialDDL: "create table t1 (id int not null, id2 int default null, c1 int default null, c2 int default null, c3 int default null, t2_id int not null, t2_id2 int not null, primary key (id), key c1 (c1), key c2 (c2), key t2_cs (c1,c2), key t2_ids (t2_id,t2_id2), foreign key (t2_id,t2_id2) references t2 (id, id2), key c3 (c3), foreign key (c1, c2) references t2 (c1, c2))", + // Secondary keys t2_ids and t2_cs are needed to enforce the FK constraint so we do not drop them. + strippedDDL: "create table t1 (id int not null, id2 int default null, c1 int default null, c2 int default null, c3 int default null, t2_id int not null, t2_id2 int not null, primary key (id), key t2_cs (c1,c2), key t2_ids (t2_id,t2_id2), constraint t1_ibfk_1 foreign key (t2_id, t2_id2) references t2 (id, id2), constraint t1_ibfk_2 foreign key (c1, c2) references t2 (c1, c2))", + actionDDL: "alter table %s.t1 add key c1 (c1), add key c2 (c2), add key c3 (c3)", + WorkflowType: int32(binlogdatapb.VReplicationWorkflowType_MoveTables), + preStashHook: func() error { + if _, err := dbClient.ExecuteFetch("drop table if exists t2", 1); err != nil { + return err + } + _, err = dbClient.ExecuteFetch("create table t2 (id int not null, id2 int not null, c1 int not null, c2 int not null, primary key (id,id2), key (c1,c2))", 1) + return err + }, + }, { name: "2tSK", tableName: "t1", @@ -339,7 +392,7 @@ func TestDeferSecondaryKeys(t *testing.T) { myvr.WorkflowType = int32(binlogdatapb.VReplicationWorkflowType_Reshard) // Insert second post copy action record to simulate a shard merge where you // have N controllers/replicators running for the same table on the tablet. - // This forces a second row, which would otherwise not get created beacause + // This forces a second row, which would otherwise not get created because // when this is called there's no secondary keys to stash anymore. addlAction, err := json.Marshal(PostCopyAction{ Type: PostCopyActionSQL, @@ -425,6 +478,11 @@ func TestDeferSecondaryKeys(t *testing.T) { // MoveTables and Reshard workflows. vr.WorkflowType = tcase.WorkflowType + if tcase.preStashHook != nil { + err = tcase.preStashHook() + require.NoError(t, err, "error executing pre stash hook: %v", err) + } + // Create the table. _, err := dbClient.ExecuteFetch(tcase.initialDDL, 1) require.NoError(t, err) @@ -456,7 +514,7 @@ func TestDeferSecondaryKeys(t *testing.T) { if tcase.postStashHook != nil { err = tcase.postStashHook() - require.NoError(t, err) + require.NoError(t, err, "error executing post stash hook: %v", err) // We should still NOT have any secondary keys because there's still // a running controller/vreplicator in the copy phase. @@ -494,7 +552,7 @@ func TestDeferSecondaryKeys(t *testing.T) { // order in the table schema. if !tcase.expectFinalSchemaDiff { currentDDL := getCurrentDDL(tcase.tableName) - sdiff, err := schemadiff.DiffCreateTablesQueries(currentDDL, tcase.initialDDL, diffHints) + sdiff, err := schemadiff.DiffCreateTablesQueries(schemadiff.NewTestEnv(), currentDDL, tcase.initialDDL, diffHints) require.NoError(t, err) require.Nil(t, sdiff, "Expected no schema difference but got: %s", sdiff.CanonicalStatementString()) } diff --git a/go/vt/vttablet/tabletserver/connpool/dbconn.go b/go/vt/vttablet/tabletserver/connpool/dbconn.go index b39299e49e6..a41fcc7b7ec 100644 --- a/go/vt/vttablet/tabletserver/connpool/dbconn.go +++ b/go/vt/vttablet/tabletserver/connpool/dbconn.go @@ -85,7 +85,7 @@ func newPooledConn(ctx context.Context, pool *Pool, appParams dbconfigs.Connecto } // NewConn creates a new Conn without a pool. -func NewConn(ctx context.Context, params dbconfigs.Connector, dbaPool *dbconnpool.ConnectionPool, setting *smartconnpool.Setting) (*Conn, error) { +func NewConn(ctx context.Context, params dbconfigs.Connector, dbaPool *dbconnpool.ConnectionPool, setting *smartconnpool.Setting, env tabletenv.Env) (*Conn, error) { c, err := dbconnpool.NewDBConnection(ctx, params) if err != nil { return nil, err @@ -94,6 +94,7 @@ func NewConn(ctx context.Context, params dbconfigs.Connector, dbaPool *dbconnpoo conn: c, dbaPool: dbaPool, stats: tabletenv.NewStats(servenv.NewExporter("Temp", "Tablet")), + env: env, killTimeout: defaultKillTimeout, } if setting == nil { @@ -497,9 +498,9 @@ func (dbc *Conn) CurrentForLogging() string { if dbc.env != nil && dbc.env.Config() != nil && !dbc.env.Config().SanitizeLogMessages { queryToLog = dbc.Current() } else { - queryToLog, _ = sqlparser.RedactSQLQuery(dbc.Current()) + queryToLog, _ = dbc.env.Environment().Parser().RedactSQLQuery(dbc.Current()) } - return sqlparser.TruncateForLog(queryToLog) + return dbc.env.Environment().Parser().TruncateForLog(queryToLog) } func (dbc *Conn) applySameSetting(ctx context.Context) (err error) { diff --git a/go/vt/vttablet/tabletserver/connpool/dbconn_test.go b/go/vt/vttablet/tabletserver/connpool/dbconn_test.go index b0e10c4c0d8..09264c51203 100644 --- a/go/vt/vttablet/tabletserver/connpool/dbconn_test.go +++ b/go/vt/vttablet/tabletserver/connpool/dbconn_test.go @@ -27,14 +27,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/pools/smartconnpool" - - "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) func compareTimingCounts(t *testing.T, op string, delta int64, before, after map[string]int64) { @@ -64,11 +66,12 @@ func TestDBConnExec(t *testing.T) { connPool := newPool() mysqlTimings := connPool.env.Stats().MySQLTimings startCounts := mysqlTimings.Counts() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) defer cancel() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) if dbConn != nil { defer dbConn.Close() } @@ -137,11 +140,12 @@ func TestDBConnExecLost(t *testing.T) { connPool := newPool() mysqlTimings := connPool.env.Stats().MySQLTimings startCounts := mysqlTimings.Counts() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) defer cancel() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) if dbConn != nil { defer dbConn.Close() } @@ -195,14 +199,15 @@ func TestDBConnDeadline(t *testing.T) { connPool := newPool() mysqlTimings := connPool.env.Stats().MySQLTimings startCounts := mysqlTimings.Counts() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() db.SetConnDelay(100 * time.Millisecond) ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(50*time.Millisecond)) defer cancel() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) if dbConn != nil { defer dbConn.Close() } @@ -253,9 +258,10 @@ func TestDBConnKill(t *testing.T) { db := fakesqldb.New(t) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) if dbConn != nil { defer dbConn.Close() } @@ -350,9 +356,10 @@ func TestDBConnClose(t *testing.T) { db := fakesqldb.New(t) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) require.NoError(t, err) defer dbConn.Close() @@ -375,9 +382,10 @@ func TestDBConnClose(t *testing.T) { func TestDBNoPoolConnKill(t *testing.T) { db := fakesqldb.New(t) connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() - dbConn, err := NewConn(context.Background(), db.ConnParams(), connPool.dbaPool, nil) + dbConn, err := NewConn(context.Background(), params, connPool.dbaPool, nil, tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestDBNoPoolConnKill")) if dbConn != nil { defer dbConn.Close() } @@ -429,11 +437,12 @@ func TestDBConnStream(t *testing.T) { } db.AddQuery(sql, expectedResult) connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) defer cancel() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) if dbConn != nil { defer dbConn.Close() } @@ -489,9 +498,10 @@ func TestDBConnStreamKill(t *testing.T) { } db.AddQuery(sql, expectedResult) connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) require.NoError(t, err) defer dbConn.Close() @@ -518,10 +528,11 @@ func TestDBConnReconnect(t *testing.T) { defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() - dbConn, err := newPooledConn(context.Background(), connPool, db.ConnParams()) + dbConn, err := newPooledConn(context.Background(), connPool, params) require.NoError(t, err) defer dbConn.Close() @@ -543,11 +554,12 @@ func TestDBConnReApplySetting(t *testing.T) { db.OrderMatters() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() ctx := context.Background() - dbConn, err := newPooledConn(ctx, connPool, db.ConnParams()) + dbConn, err := newPooledConn(ctx, connPool, params) require.NoError(t, err) defer dbConn.Close() diff --git a/go/vt/vttablet/tabletserver/connpool/pool.go b/go/vt/vttablet/tabletserver/connpool/pool.go index 6f8b72870e0..567745e37b5 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool.go +++ b/go/vt/vttablet/tabletserver/connpool/pool.go @@ -68,14 +68,14 @@ type Pool struct { // to publish stats only. func NewPool(env tabletenv.Env, name string, cfg tabletenv.ConnPoolConfig) *Pool { cp := &Pool{ - timeout: cfg.TimeoutSeconds.Get(), + timeout: cfg.Timeout, env: env, } config := smartconnpool.Config[*Conn]{ Capacity: int64(cfg.Size), - IdleTimeout: cfg.IdleTimeoutSeconds.Get(), - MaxLifetime: cfg.MaxLifetimeSeconds.Get(), + IdleTimeout: cfg.IdleTimeout, + MaxLifetime: cfg.MaxLifetime, RefreshInterval: mysqlctl.PoolDynamicHostnameResolution, } @@ -126,7 +126,7 @@ func (cp *Pool) Get(ctx context.Context, setting *smartconnpool.Setting) (*Poole defer span.Finish() if cp.isCallerIDAppDebug(ctx) { - conn, err := NewConn(ctx, cp.appDebugParams, cp.dbaPool, setting) + conn, err := NewConn(ctx, cp.appDebugParams, cp.dbaPool, setting, cp.env) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletserver/connpool/pool_test.go b/go/vt/vttablet/tabletserver/connpool/pool_test.go index ecdd2df4465..28f3e27803a 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool_test.go +++ b/go/vt/vttablet/tabletserver/connpool/pool_test.go @@ -28,6 +28,8 @@ import ( "vitess.io/vitess/go/pools/smartconnpool" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -35,7 +37,8 @@ func TestConnPoolGet(t *testing.T) { db := fakesqldb.New(t) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() dbConn, err := connPool.Get(context.Background(), nil) if err != nil { @@ -54,10 +57,11 @@ func TestConnPoolTimeout(t *testing.T) { cfg := tabletenv.ConnPoolConfig{ Size: 1, } - _ = cfg.TimeoutSeconds.Set("1s") - _ = cfg.IdleTimeoutSeconds.Set("10s") - connPool := NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", cfg) - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + cfg.Timeout = time.Second + cfg.IdleTimeout = 10 * time.Second + connPool := NewPool(tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "PoolTest"), "TestPool", cfg) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() dbConn, err := connPool.Get(context.Background(), nil) require.NoError(t, err) @@ -68,10 +72,11 @@ func TestConnPoolTimeout(t *testing.T) { func TestConnPoolGetEmptyDebugConfig(t *testing.T) { db := fakesqldb.New(t) - debugConn := db.ConnParamsWithUname("") + debugConn := dbconfigs.New(db.ConnParamsWithUname("")) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), debugConn) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, debugConn) im := callerid.NewImmediateCallerID("") ecid := callerid.NewEffectiveCallerID("p", "c", "sc") ctx := context.Background() @@ -89,14 +94,15 @@ func TestConnPoolGetEmptyDebugConfig(t *testing.T) { func TestConnPoolGetAppDebug(t *testing.T) { db := fakesqldb.New(t) - debugConn := db.ConnParamsWithUname("debugUsername") + debugConn := dbconfigs.New(db.ConnParamsWithUname("debugUsername")) ctx := context.Background() im := callerid.NewImmediateCallerID("debugUsername") ecid := callerid.NewEffectiveCallerID("p", "c", "sc") ctx = callerid.NewContext(ctx, ecid, im) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), debugConn) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, debugConn) defer connPool.Close() dbConn, err := connPool.Get(ctx, nil) if err != nil { @@ -115,7 +121,8 @@ func TestConnPoolSetCapacity(t *testing.T) { db := fakesqldb.New(t) defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() assert.Panics(t, func() { @@ -134,7 +141,8 @@ func TestConnPoolStatJSON(t *testing.T) { if connPool.StatsJSON() != "{}" { t.Fatalf("pool is closed, stats json should be empty; was: %q", connPool.StatsJSON()) } - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() statsJSON := connPool.StatsJSON() if statsJSON == "" || statsJSON == "{}" { @@ -153,7 +161,8 @@ func TestConnPoolStateWhilePoolIsOpen(t *testing.T) { defer db.Close() idleTimeout := 10 * time.Second connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() assert.EqualValues(t, 100, connPool.Capacity(), "pool capacity should be 100") assert.EqualValues(t, 0, connPool.Metrics.WaitTime(), "pool wait time should be 0") @@ -179,7 +188,8 @@ func TestConnPoolStateWithSettings(t *testing.T) { defer db.Close() capacity := 5 connPool := newPoolWithCapacity(capacity) - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() assert.EqualValues(t, 5, connPool.Available(), "pool available connections should be 5") assert.EqualValues(t, 0, connPool.Active(), "pool active connections should be 0") @@ -294,7 +304,8 @@ func TestPoolGetConnTime(t *testing.T) { defer db.Close() connPool := newPool() - connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + connPool.Open(params, params, params) defer connPool.Close() connPool.getConnTime.Reset() @@ -325,9 +336,8 @@ func newPool() *Pool { } func newPoolWithCapacity(capacity int) *Pool { - cfg := tabletenv.ConnPoolConfig{ - Size: capacity, - } - _ = cfg.IdleTimeoutSeconds.Set("10s") - return NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", cfg) + return NewPool(tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + Size: capacity, + IdleTimeout: 10 * time.Second, + }) } diff --git a/go/vt/vttablet/tabletserver/controller.go b/go/vt/vttablet/tabletserver/controller.go index ca4eeb8747b..4d7e35862de 100644 --- a/go/vt/vttablet/tabletserver/controller.go +++ b/go/vt/vttablet/tabletserver/controller.go @@ -66,7 +66,7 @@ type Controller interface { // ClearQueryPlanCache clears internal query plan cache ClearQueryPlanCache() - // ReloadSchema makes the quey service reload its schema cache + // ReloadSchema makes the query service reload its schema cache ReloadSchema(ctx context.Context) error // RegisterQueryRuleSource adds a query rule source diff --git a/go/vt/vttablet/tabletserver/debugenv.go b/go/vt/vttablet/tabletserver/debugenv.go index e229c46cadd..c780a28ed90 100644 --- a/go/vt/vttablet/tabletserver/debugenv.go +++ b/go/vt/vttablet/tabletserver/debugenv.go @@ -125,7 +125,7 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) case "RowStreamerMaxMySQLReplLagSecs": setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val }) case "UnhealthyThreshold": - setDurationVal(func(d time.Duration) { _ = tsv.Config().Healthcheck.UnhealthyThresholdSeconds.Set(d.String()) }) + setDurationVal(func(d time.Duration) { tsv.Config().Healthcheck.UnhealthyThreshold = d }) setDurationVal(tsv.hs.SetUnhealthyThreshold) setDurationVal(tsv.sm.SetUnhealthyThreshold) case "ThrottleMetricThreshold": @@ -145,7 +145,7 @@ func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) vars = addVar(vars, "WarnResultSize", tsv.WarnResultSize) vars = addVar(vars, "RowStreamerMaxInnoDBTrxHistLen", func() int64 { return tsv.Config().RowStreamer.MaxInnoDBTrxHistLen }) vars = addVar(vars, "RowStreamerMaxMySQLReplLagSecs", func() int64 { return tsv.Config().RowStreamer.MaxMySQLReplLagSecs }) - vars = addVar(vars, "UnhealthyThreshold", tsv.Config().Healthcheck.UnhealthyThresholdSeconds.Get) + vars = addVar(vars, "UnhealthyThreshold", func() time.Duration { return tsv.Config().Healthcheck.UnhealthyThreshold }) vars = addVar(vars, "ThrottleMetricThreshold", tsv.ThrottleMetricThreshold) vars = append(vars, envValue{ Name: "Consolidator", diff --git a/go/vt/vttablet/tabletserver/exclude_race_test.go b/go/vt/vttablet/tabletserver/exclude_race_test.go deleted file mode 100644 index 6e55671ac96..00000000000 --- a/go/vt/vttablet/tabletserver/exclude_race_test.go +++ /dev/null @@ -1,62 +0,0 @@ -//go:build !race - -package tabletserver - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" -) - -// TestHandlePanicAndSendLogStatsMessageTruncation tests that when an error truncation -// length is set and a panic occurs, the code in handlePanicAndSendLogStats will -// truncate the error text in logs, but will not truncate the error text in the -// error value. -func TestHandlePanicAndSendLogStatsMessageTruncation(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - tl := newTestLogger() - defer tl.Close() - logStats := tabletenv.NewLogStats(ctx, "TestHandlePanicAndSendLogStatsMessageTruncation") - db, tsv := setupTabletServerTest(t, ctx, "") - defer tsv.StopService() - defer db.Close() - - longSql := "select * from test_table_loooooooooooooooooooooooooooooooooooong" - longBv := map[string]*querypb.BindVariable{ - "bv1": sqltypes.Int64BindVariable(1111111111), - "bv2": sqltypes.Int64BindVariable(2222222222), - "bv3": sqltypes.Int64BindVariable(3333333333), - "bv4": sqltypes.Int64BindVariable(4444444444), - } - origTruncateErrLen := sqlparser.GetTruncateErrLen() - sqlparser.SetTruncateErrLen(32) - defer sqlparser.SetTruncateErrLen(origTruncateErrLen) - - defer func() { - err := logStats.Error - want := "Uncaught panic for Sql: \"select * from test_table_loooooooooooooooooooooooooooooooooooong\", BindVars: {bv1: \"type:INT64 value:\\\"1111111111\\\"\"bv2: \"type:INT64 value:\\\"2222222222\\\"\"bv3: \"type:INT64 value:\\\"3333333333\\\"\"bv4: \"type:INT64 value:\\\"4444444444\\\"\"}" - require.Error(t, err) - assert.Contains(t, err.Error(), want) - want = "Uncaught panic for Sql: \"select * from test_t [TRUNCATED]\", BindVars: {bv1: \"typ [TRUNCATED]" - gotWhatWeWant := false - for _, log := range tl.getLogs() { - if strings.HasPrefix(log, want) { - gotWhatWeWant = true - break - } - } - assert.True(t, gotWhatWeWant) - }() - - defer tsv.handlePanicAndSendLogStats(longSql, longBv, logStats) - panic("panic from TestHandlePanicAndSendLogStatsMessageTruncation") -} diff --git a/go/vt/vttablet/tabletserver/fuzz.go b/go/vt/vttablet/tabletserver/fuzz.go index fb14455d3f4..c7f3dabde97 100644 --- a/go/vt/vttablet/tabletserver/fuzz.go +++ b/go/vt/vttablet/tabletserver/fuzz.go @@ -23,8 +23,10 @@ import ( fuzz "github.com/AdaLogics/go-fuzz-headers" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -57,7 +59,7 @@ func FuzzGetPlan(data []byte) int { // Set up the environment config := tabletenv.NewDefaultConfig() config.DB = newDBConfigs(db) - env := tabletenv.NewEnv(config, "TabletServerTest") + env := tabletenv.NewEnv(config, "TabletServerTest", collations.MySQL8(), sqlparser.NewTestParser()) se := schema.NewEngine(env) qe := NewQueryEngine(env, se) defer qe.Close() diff --git a/go/vt/vttablet/tabletserver/gc/tablegc.go b/go/vt/vttablet/tabletserver/gc/tablegc.go index 4947fd9c97a..fced176b027 100644 --- a/go/vt/vttablet/tabletserver/gc/tablegc.go +++ b/go/vt/vttablet/tabletserver/gc/tablegc.go @@ -19,7 +19,6 @@ package gc import ( "context" "fmt" - "math" "sort" "sync" "sync/atomic" @@ -27,9 +26,9 @@ import ( "github.com/spf13/pflag" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/dbconnpool" "vitess.io/vitess/go/vt/log" @@ -49,9 +48,12 @@ const ( ) var ( - checkInterval = 1 * time.Hour - purgeReentranceInterval = 1 * time.Minute - gcLifecycle = "hold,purge,evac,drop" + checkInterval = 1 * time.Hour + purgeReentranceInterval = 1 * time.Minute + nextPurgeReentry = 1 * time.Second + checkTablesReentryMinInterval = 10 * time.Second + NextChecksIntervals = []time.Duration{time.Second, checkTablesReentryMinInterval + 5*time.Second} + gcLifecycle = "hold,purge,evac,drop" ) func init() { @@ -65,15 +67,14 @@ func registerGCFlags(fs *pflag.FlagSet) { // purgeReentranceInterval marks the interval between searching tables to purge fs.DurationVar(&purgeReentranceInterval, "gc_purge_check_interval", purgeReentranceInterval, "Interval between purge discovery checks") // gcLifecycle is the sequence of steps the table goes through in the process of getting dropped - fs.StringVar(&gcLifecycle, "table_gc_lifecycle", gcLifecycle, "States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implcitly always included)") + fs.StringVar(&gcLifecycle, "table_gc_lifecycle", gcLifecycle, "States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implicitly always included)") } var ( - sqlPurgeTable = `delete from %a limit 50` - sqlShowVtTables = `show full tables like '\_vt\_%'` - sqlDropTable = "drop table if exists `%a`" - sqlDropView = "drop view if exists `%a`" - purgeReentranceFlag int64 + sqlPurgeTable = `delete from %a limit 50` + sqlShowVtTables = `show full tables like '\_vt\_%'` + sqlDropTable = "drop table if exists `%a`" + sqlDropView = "drop view if exists `%a`" ) type gcTable struct { @@ -105,6 +106,10 @@ type TableGC struct { isOpen int64 cancelOperation context.CancelFunc + purgeReentranceFlag atomic.Int64 + readReentranceFlag atomic.Int64 + checkRequestChan chan bool + throttlerClient *throttle.Client env tabletenv.Env @@ -120,7 +125,7 @@ type TableGC struct { lifecycleStates map[schema.TableGCState]bool } -// Status published some status valus from the collector +// Status published some status values from the collector type Status struct { Keyspace string Shard string @@ -139,11 +144,12 @@ func NewTableGC(env tabletenv.Env, ts *topo.Server, lagThrottler *throttle.Throt env: env, ts: ts, pool: connpool.NewPool(env, "TableGCPool", tabletenv.ConnPoolConfig{ - Size: 2, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: 2, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }), - purgingTables: map[string]bool{}, + purgingTables: map[string]bool{}, + checkRequestChan: make(chan bool), } return collector @@ -183,7 +189,7 @@ func (collector *TableGC) Open() (err error) { return err } defer conn.Close() - serverSupportsFastDrops, err := conn.SupportsCapability(mysql.FastDropTableFlavorCapability) + serverSupportsFastDrops, err := conn.SupportsCapability(capabilities.FastDropTableFlavorCapability) if err != nil { return err } @@ -226,6 +232,16 @@ func (collector *TableGC) Close() { log.Infof("TableGC - finished execution of Close") } +// RequestChecks requests that the GC will do a table check right away, as well as in a few seconds. +// Calling this function is useful to modules that are performing operations that affect GC tables. Those modules +// _know_ that changes have been made, and now have a way to tell TableGC: "please take a look asap rather +// than in the next hour". +func (collector *TableGC) RequestChecks() { + for _, d := range NextChecksIntervals { + time.AfterFunc(d, func() { collector.checkRequestChan <- true }) + } +} + // operate is the main entry point for the table garbage collector operation and logic. func (collector *TableGC) operate(ctx context.Context) { @@ -254,55 +270,46 @@ func (collector *TableGC) operate(ctx context.Context) { case <-ctx.Done(): log.Info("TableGC: done operating") return + case <-collector.checkRequestChan: + // Got a request to check tables. Probably some event took place and we will + // find something new to do. + go tableCheckTicker.TickNow() case <-tableCheckTicker.C: - { - log.Info("TableGC: tableCheckTicker") - if gcTables, err := collector.readTables(ctx); err != nil { - log.Errorf("TableGC: error while reading tables: %+v", err) - } else { - _ = collector.checkTables(ctx, gcTables, dropTablesChan, transitionRequestsChan) - } + if err := collector.readAndCheckTables(ctx, dropTablesChan, transitionRequestsChan); err != nil { + log.Error(err) } case <-purgeReentranceTicker.C: - { - // relay the request - go func() { purgeRequestsChan <- true }() - } + // relay the request + go func() { purgeRequestsChan <- true }() case <-purgeRequestsChan: - { - go func() { - tableName, err := collector.purge(ctx) - if err != nil { - log.Errorf("TableGC: error purging table %s: %+v", tableName, err) - return - } - if tableName == "" { - // No table purged (or at least not to completion) - // Either because there _is_ nothing to purge, or because PURGE isn't a handled state - return - } - // The table has been purged! Let's move the table into the next phase: - _, _, uuid, _, _ := schema.AnalyzeGCTableName(tableName) - collector.submitTransitionRequest(ctx, transitionRequestsChan, schema.PurgeTableGCState, tableName, true, uuid) - collector.removePurgingTable(tableName) - // Chances are, there's more tables waiting to be purged. Let's speed things by - // requesting another purge, instead of waiting a full purgeReentranceInterval cycle - time.AfterFunc(time.Second, func() { purgeRequestsChan <- true }) - }() - } - case dropTable := <-dropTablesChan: - { - log.Infof("TableGC: found %v in dropTablesChan", dropTable.tableName) - if err := collector.dropTable(ctx, dropTable.tableName, dropTable.isBaseTable); err != nil { - log.Errorf("TableGC: error dropping table %s: %+v", dropTable.tableName, err) + go func() { + tableName, err := collector.purge(ctx) + if err != nil { + log.Errorf("TableGC: error purging table %s: %+v", tableName, err) + return } + if tableName == "" { + // No table purged (or at least not to completion) + // Either because there _is_ nothing to purge, or because PURGE isn't a handled state + return + } + // The table has been purged! Let's move the table into the next phase: + _, _, uuid, _, _ := schema.AnalyzeGCTableName(tableName) + collector.submitTransitionRequest(ctx, transitionRequestsChan, schema.PurgeTableGCState, tableName, true, uuid) + collector.removePurgingTable(tableName) + // Chances are, there's more tables waiting to be purged. Let's speed things by + // requesting another purge, instead of waiting a full purgeReentranceInterval cycle + purgeReentranceTicker.TickAfter(nextPurgeReentry) + }() + case dropTable := <-dropTablesChan: + log.Infof("TableGC: found %v in dropTablesChan", dropTable.tableName) + if err := collector.dropTable(ctx, dropTable.tableName, dropTable.isBaseTable); err != nil { + log.Errorf("TableGC: error dropping table %s: %+v", dropTable.tableName, err) } case transition := <-transitionRequestsChan: - { - log.Info("TableGC: transitionRequestsChan, transition=%v", transition) - if err := collector.transitionTable(ctx, transition); err != nil { - log.Errorf("TableGC: error transitioning table %s to %+v: %+v", transition.fromTableName, transition.toGCState, err) - } + log.Info("TableGC: transitionRequestsChan, transition=%v", transition) + if err := collector.transitionTable(ctx, transition); err != nil { + log.Errorf("TableGC: error transitioning table %s to %+v: %+v", transition.fromTableName, transition.toGCState, err) } } } @@ -378,17 +385,41 @@ func (collector *TableGC) shouldTransitionTable(tableName string) (shouldTransit return true, state, uuid, nil } +// readAndCheckTables is the routine check for which GC tables exist, and which of those need to transition +// into the next state. The function is non-reentrant, and poses a minimal duration between any two executions. +func (collector *TableGC) readAndCheckTables( + ctx context.Context, + dropTablesChan chan<- *gcTable, + transitionRequestsChan chan<- *transitionRequest, +) (err error) { + if !collector.readReentranceFlag.CompareAndSwap(0, 1) { + // An instance of this function is already running + return nil + } + defer time.AfterFunc(checkTablesReentryMinInterval, func() { + collector.readReentranceFlag.Store(0) + }) + + log.Info("TableGC: readAndCheckTables") + gcTables, err := collector.readTables(ctx) + if err != nil { + return fmt.Errorf("TableGC: error while reading tables: %+v", err) + } + if err := collector.checkTables(ctx, gcTables, dropTablesChan, transitionRequestsChan); err != nil { + return err + } + return nil +} + // readTables reads the list of _vt_% tables from the database func (collector *TableGC) readTables(ctx context.Context) (gcTables []*gcTable, err error) { - log.Infof("TableGC: read tables") - conn, err := collector.pool.Get(ctx, nil) if err != nil { return nil, err } defer conn.Recycle() - res, err := conn.Conn.Exec(ctx, sqlShowVtTables, math.MaxInt32, true) + res, err := conn.Conn.Exec(ctx, sqlShowVtTables, -1, true) if err != nil { return nil, err } @@ -406,8 +437,6 @@ func (collector *TableGC) readTables(ctx context.Context) (gcTables []*gcTable, // It lists _vt_% tables, then filters through those which are due-date. // It then applies the necessary operation per table. func (collector *TableGC) checkTables(ctx context.Context, gcTables []*gcTable, dropTablesChan chan<- *gcTable, transitionRequestsChan chan<- *transitionRequest) error { - log.Infof("TableGC: check tables") - for i := range gcTables { table := gcTables[i] // we capture as local variable as we will later use this in a goroutine shouldTransition, state, uuid, err := collector.shouldTransitionTable(table.tableName) @@ -457,12 +486,11 @@ func (collector *TableGC) checkTables(ctx context.Context, gcTables []*gcTable, // This function is non-reentrant: there's only one instance of this function running at any given time. // A timer keeps calling this function, so if it bails out (e.g. on error) it will later resume work func (collector *TableGC) purge(ctx context.Context) (tableName string, err error) { - if atomic.CompareAndSwapInt64(&purgeReentranceFlag, 0, 1) { - defer atomic.StoreInt64(&purgeReentranceFlag, 0) - } else { + if !collector.purgeReentranceFlag.CompareAndSwap(0, 1) { // An instance of this function is already running return "", nil } + defer collector.purgeReentranceFlag.Store(0) tableName, found := collector.nextTableToPurge() if !found { @@ -575,7 +603,7 @@ func (collector *TableGC) transitionTable(ctx context.Context, transition *trans // when we transition into PURGE, that means we want to begin purging immediately // when we transition into DROP, that means we want to drop immediately - // Thereforce the default timestamp is Now + // Therefore the default timestamp is Now t := time.Now().UTC() switch transition.toGCState { case schema.EvacTableGCState: @@ -598,16 +626,29 @@ func (collector *TableGC) transitionTable(ctx context.Context, transition *trans return err } log.Infof("TableGC: renamed table: %s", transition.fromTableName) + // Since the table has transitioned, there is a potential for more work on this table or on other tables, + // let's kick a check request. + collector.RequestChecks() return nil } -// addPurgingTable adds a table to the list of droppingpurging (or pending purging) tables +// addPurgingTable adds a table to the list of dropping purging (or pending purging) tables func (collector *TableGC) addPurgingTable(tableName string) (added bool) { if _, ok := collector.lifecycleStates[schema.PurgeTableGCState]; !ok { // PURGE is not a handled state. We don't want to purge this table or any other table, // so we don't populate the purgingTables map. return false } + isGCTable, state, _, _, err := schema.AnalyzeGCTableName(tableName) + if err != nil { + return false + } + if !isGCTable { + return false + } + if state != schema.PurgeTableGCState { + return false + } collector.purgeMutex.Lock() defer collector.purgeMutex.Unlock() diff --git a/go/vt/vttablet/tabletserver/gc/tablegc_test.go b/go/vt/vttablet/tabletserver/gc/tablegc_test.go index 446f6e6ff85..6e26a77f291 100644 --- a/go/vt/vttablet/tabletserver/gc/tablegc_test.go +++ b/go/vt/vttablet/tabletserver/gc/tablegc_test.go @@ -29,15 +29,18 @@ import ( func TestNextTableToPurge(t *testing.T) { tt := []struct { + name string tables []string next string ok bool }{ { + name: "empty", tables: []string{}, ok: false, }, { + name: "first", tables: []string{ "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", "_vt_PURGE_2ace8bcef73211ea87e9f875a4d24e90_20200915120411", @@ -48,6 +51,7 @@ func TestNextTableToPurge(t *testing.T) { ok: true, }, { + name: "mid", tables: []string{ "_vt_PURGE_2ace8bcef73211ea87e9f875a4d24e90_20200915120411", "_vt_PURGE_3ace8bcef73211ea87e9f875a4d24e90_20200915120412", @@ -57,19 +61,71 @@ func TestNextTableToPurge(t *testing.T) { next: "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", ok: true, }, + { + name: "none", + tables: []string{ + "_vt_HOLD_2ace8bcef73211ea87e9f875a4d24e90_20200915120411", + "_vt_EVAC_3ace8bcef73211ea87e9f875a4d24e90_20200915120412", + "_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_20200915120410", + "_vt_DROP_4ace8bcef73211ea87e9f875a4d24e90_20200915120413", + }, + next: "", + ok: false, + }, + { + name: "first, new format", + tables: []string{ + "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_prg_2ace8bcef73211ea87e9f875a4d24e90_20200915120411_", + "_vt_prg_3ace8bcef73211ea87e9f875a4d24e90_20200915120412_", + "_vt_prg_4ace8bcef73211ea87e9f875a4d24e90_20200915120413_", + }, + next: "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + ok: true, + }, + { + name: "mid, new format", + tables: []string{ + "_vt_prg_2ace8bcef73211ea87e9f875a4d24e90_20200915120411_", + "_vt_prg_3ace8bcef73211ea87e9f875a4d24e90_20200915120412_", + "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_prg_4ace8bcef73211ea87e9f875a4d24e90_20200915120413_", + }, + next: "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + ok: true, + }, + { + name: "none, new format", + tables: []string{ + "_vt_hld_2ace8bcef73211ea87e9f875a4d24e90_20200915120411_", + "_vt_evc_3ace8bcef73211ea87e9f875a4d24e90_20200915120412_", + "_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + "_vt_drp_4ace8bcef73211ea87e9f875a4d24e90_20200915120413_", + "_vt_prg_4ace8bcef73211ea87e9f875a4d24e90_20200915999999_", + }, + next: "", + ok: false, + }, } for _, ts := range tt { - collector := &TableGC{ - purgingTables: make(map[string]bool), - } - for _, table := range ts.tables { - collector.purgingTables[table] = true - } - next, ok := collector.nextTableToPurge() - assert.Equal(t, ts.ok, ok) - if ok { - assert.Equal(t, ts.next, next) - } + t.Run(ts.name, func(t *testing.T) { + collector := &TableGC{ + purgingTables: make(map[string]bool), + checkRequestChan: make(chan bool), + } + var err error + collector.lifecycleStates, err = schema.ParseGCLifecycle("hold,purge,evac,drop") + assert.NoError(t, err) + for _, table := range ts.tables { + collector.addPurgingTable(table) + } + + next, ok := collector.nextTableToPurge() + assert.Equal(t, ts.ok, ok) + if ok { + assert.Equal(t, ts.next, next) + } + }) } } @@ -171,6 +227,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: true, }, + { + name: "purge, old timestamp, new format", + table: "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_", + state: schema.PurgeTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: true, + }, { name: "no purge, future timestamp", table: "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -178,6 +241,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: false, }, + { + name: "no purge, future timestamp, new format", + table: "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.PurgeTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: false, + }, { name: "no purge, PURGE not handled state", table: "_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -186,6 +256,14 @@ func TestShouldTransitionTable(t *testing.T) { handledStates: "hold,evac", // no PURGE shouldTransition: true, }, + { + name: "no purge, PURGE not handled state, new format", + table: "_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.PurgeTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + handledStates: "hold,evac", // no PURGE + shouldTransition: true, + }, { name: "no drop, future timestamp", table: "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -193,6 +271,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: false, }, + { + name: "no drop, future timestamp, new format", + table: "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.DropTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: false, + }, { name: "drop, old timestamp", table: "_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20090915120410", @@ -200,6 +285,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: true, }, + { + name: "drop, old timestamp, new format", + table: "_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20090915120410_", + state: schema.DropTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: true, + }, { name: "no evac, future timestamp", table: "_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -207,6 +299,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: false, }, + { + name: "no evac, future timestamp, new format", + table: "_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.EvacTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: false, + }, { name: "no hold, HOLD not handled state", table: "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -214,6 +313,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "6ace8bcef73211ea87e9f875a4d24e90", shouldTransition: true, }, + { + name: "no hold, HOLD not handled state, new format", + table: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.HoldTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + shouldTransition: true, + }, { name: "hold, future timestamp", table: "_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -222,6 +328,14 @@ func TestShouldTransitionTable(t *testing.T) { handledStates: "hold,purge,evac,drop", shouldTransition: false, }, + { + name: "hold, future timestamp, new format", + table: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_29990915120410_", + state: schema.HoldTableGCState, + uuid: "6ace8bcef73211ea87e9f875a4d24e90", + handledStates: "hold,purge,evac,drop", + shouldTransition: false, + }, { name: "not a GC table", table: "_vt_SOMETHING_6ace8bcef73211ea87e9f875a4d24e90_29990915120410", @@ -229,6 +343,13 @@ func TestShouldTransitionTable(t *testing.T) { uuid: "", shouldTransition: false, }, + { + name: "invalid new format", + table: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_29990915999999_", + state: "", + uuid: "", + shouldTransition: false, + }, } for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { @@ -256,8 +377,9 @@ func TestShouldTransitionTable(t *testing.T) { func TestCheckTables(t *testing.T) { collector := &TableGC{ - isOpen: 0, - purgingTables: map[string]bool{}, + isOpen: 0, + purgingTables: map[string]bool{}, + checkRequestChan: make(chan bool), } var err error collector.lifecycleStates, err = schema.ParseGCLifecycle("hold,purge,evac,drop") @@ -268,35 +390,70 @@ func TestCheckTables(t *testing.T) { tableName: "_vt_something_that_isnt_a_gc_table", isBaseTable: true, }, + { + tableName: "_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_29990915999999_", + isBaseTable: true, + }, { tableName: "_vt_HOLD_11111111111111111111111111111111_20990920093324", // 2099 is in the far future isBaseTable: true, }, + { + tableName: "_vt_hld_11111111111111111111111111111111_20990920093324_", // 2099 is in the far future + isBaseTable: true, + }, { tableName: "_vt_HOLD_22222222222222222222222222222222_20200920093324", isBaseTable: true, }, + { + tableName: "_vt_hld_22222222222222222222222222222222_20200920093324_", + isBaseTable: true, + }, { tableName: "_vt_DROP_33333333333333333333333333333333_20200919083451", isBaseTable: true, }, + { + tableName: "_vt_drp_33333333333333333333333333333333_20200919083451_", + isBaseTable: true, + }, { tableName: "_vt_DROP_44444444444444444444444444444444_20200919083451", isBaseTable: false, }, + { + tableName: "_vt_drp_44444444444444444444444444444444_20200919083451_", + isBaseTable: false, + }, } - // one gcTable above is irrelevant, does not have a GC table name + expectResponses := len(gcTables) + // one gcTable above is irrelevant: it does not have a GC table name + expectResponses = expectResponses - 1 // one will not transition: its date is 2099 - expectResponses := len(gcTables) - 2 + expectResponses = expectResponses - 1 + // one gcTable above is irrelevant: it has an invalid new format timestamp + expectResponses = expectResponses - 1 + // one will not transition: its date is 2099 in new format + expectResponses = expectResponses - 1 + expectDropTables := []*gcTable{ { tableName: "_vt_DROP_33333333333333333333333333333333_20200919083451", isBaseTable: true, }, + { + tableName: "_vt_drp_33333333333333333333333333333333_20200919083451_", + isBaseTable: true, + }, { tableName: "_vt_DROP_44444444444444444444444444444444_20200919083451", isBaseTable: false, }, + { + tableName: "_vt_drp_44444444444444444444444444444444_20200919083451_", + isBaseTable: false, + }, } expectTransitionRequests := []*transitionRequest{ { @@ -305,6 +462,12 @@ func TestCheckTables(t *testing.T) { toGCState: schema.PurgeTableGCState, uuid: "22222222222222222222222222222222", }, + { + fromTableName: "_vt_hld_22222222222222222222222222222222_20200920093324_", + isBaseTable: true, + toGCState: schema.PurgeTableGCState, + uuid: "22222222222222222222222222222222", + }, } ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) diff --git a/go/vt/vttablet/tabletserver/health_streamer.go b/go/vt/vttablet/tabletserver/health_streamer.go index 87c70a7133d..8ff6834aeaf 100644 --- a/go/vt/vttablet/tabletserver/health_streamer.go +++ b/go/vt/vttablet/tabletserver/health_streamer.go @@ -20,25 +20,19 @@ import ( "context" "fmt" "io" - "strings" "sync" "sync/atomic" "time" "github.com/spf13/pflag" - "vitess.io/vitess/go/constants/sidecar" - vtschema "vitess.io/vitess/go/vt/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/vt/servenv" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/history" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -50,8 +44,8 @@ import ( ) var ( - // blpFunc is a legaacy feature. - // TODO(sougou): remove after legacy resharding worflows are removed. + // blpFunc is a legacy feature. + // TODO(sougou): remove after legacy resharding workflows are removed. blpFunc = vreplication.StatusSummary errUnintialized = "tabletserver uninitialized" @@ -98,13 +92,13 @@ func newHealthStreamer(env tabletenv.Env, alias *topodatapb.TabletAlias, engine if env.Config().SignalWhenSchemaChange { // We need one connection for the reloader. pool = connpool.NewPool(env, "", tabletenv.ConnPoolConfig{ - Size: 1, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: 1, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }) } hs := &healthStreamer{ stats: env.Stats(), - degradedThreshold: env.Config().Healthcheck.DegradedThresholdSeconds.Get(), + degradedThreshold: env.Config().Healthcheck.DegradedThreshold, clients: make(map[chan *querypb.StreamHealthResponse]struct{}), state: &querypb.StreamHealthResponse{ @@ -122,7 +116,7 @@ func newHealthStreamer(env tabletenv.Env, alias *topodatapb.TabletAlias, engine viewsEnabled: env.Config().EnableViews, se: engine, } - hs.unhealthyThreshold.Store(env.Config().Healthcheck.UnhealthyThresholdSeconds.Get().Nanoseconds()) + hs.unhealthyThreshold.Store(env.Config().Healthcheck.UnhealthyThreshold.Nanoseconds()) return hs } @@ -371,14 +365,6 @@ func (hs *healthStreamer) reload(full map[string]*schema.Table, created, altered } } - // Reload the tables and views. - // This stores the data that is used by VTGates upto v17. So, we can remove this reload of - // tables and views in v19. - err = hs.reloadTables(ctx, conn.Conn, tables) - if err != nil { - return err - } - // no change detected if len(tables) == 0 && len(views) == 0 { return nil @@ -393,41 +379,3 @@ func (hs *healthStreamer) reload(full map[string]*schema.Table, created, altered return nil } - -func (hs *healthStreamer) reloadTables(ctx context.Context, conn *connpool.Conn, tableNames []string) error { - if len(tableNames) == 0 { - return nil - } - var escapedTableNames []string - for _, tableName := range tableNames { - escapedTblName := sqlparser.String(sqlparser.NewStrLiteral(tableName)) - escapedTableNames = append(escapedTableNames, escapedTblName) - } - - tableNamePredicate := fmt.Sprintf("table_name IN (%s)", strings.Join(escapedTableNames, ", ")) - del := fmt.Sprintf("%s AND %s", sqlparser.BuildParsedQuery(mysql.ClearSchemaCopy, sidecar.GetIdentifier()).Query, tableNamePredicate) - upd := fmt.Sprintf("%s AND %s", sqlparser.BuildParsedQuery(mysql.InsertIntoSchemaCopy, sidecar.GetIdentifier()).Query, tableNamePredicate) - - // Reload the schema in a transaction. - _, err := conn.Exec(ctx, "begin", 1, false) - if err != nil { - return err - } - defer conn.Exec(ctx, "rollback", 1, false) - - _, err = conn.Exec(ctx, del, 1, false) - if err != nil { - return err - } - - _, err = conn.Exec(ctx, upd, 1, false) - if err != nil { - return err - } - - _, err = conn.Exec(ctx, "commit", 1, false) - if err != nil { - return err - } - return nil -} diff --git a/go/vt/vttablet/tabletserver/health_streamer_test.go b/go/vt/vttablet/tabletserver/health_streamer_test.go index b2fbb2db1ea..14a1899d07b 100644 --- a/go/vt/vttablet/tabletserver/health_streamer_test.go +++ b/go/vt/vttablet/tabletserver/health_streamer_test.go @@ -29,14 +29,13 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" - "vitess.io/vitess/go/constants/sidecar" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -44,8 +43,8 @@ import ( func TestHealthStreamerClosed(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - config := newConfig(db) - env := tabletenv.NewEnv(config, "ReplTrackerTest") + cfg := newConfig(db) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "ReplTrackerTest") alias := &topodatapb.TabletAlias{ Cell: "cell", Uid: 1, @@ -69,10 +68,10 @@ func newConfig(db *fakesqldb.DB) *tabletenv.TabletConfig { func TestNotServingPrimaryNoWrite(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - config := newConfig(db) - config.SignalWhenSchemaChange = true + cfg := newConfig(db) + cfg.SignalWhenSchemaChange = true - env := tabletenv.NewEnv(config, "TestNotServingPrimary") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TestNotServingPrimary") alias := &topodatapb.TabletAlias{ Cell: "cell", Uid: 1, @@ -80,11 +79,11 @@ func TestNotServingPrimaryNoWrite(t *testing.T) { // Create a new health streamer and set it to a serving primary state hs := newHealthStreamer(env, alias, &schema.Engine{}) hs.isServingPrimary = true - hs.InitDBConfig(&querypb.Target{TabletType: topodatapb.TabletType_PRIMARY}, config.DB.DbaWithDB()) + hs.InitDBConfig(&querypb.Target{TabletType: topodatapb.TabletType_PRIMARY}, cfg.DB.DbaWithDB()) hs.Open() defer hs.Close() target := &querypb.Target{} - hs.InitDBConfig(target, db.ConnParams()) + hs.InitDBConfig(target, dbconfigs.New(db.ConnParams())) // Let's say the tablet goes to a non-serving primary state. hs.MakePrimary(false) @@ -100,21 +99,21 @@ func TestNotServingPrimaryNoWrite(t *testing.T) { func TestHealthStreamerBroadcast(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - config := newConfig(db) - config.SignalWhenSchemaChange = false + cfg := newConfig(db) + cfg.SignalWhenSchemaChange = false - env := tabletenv.NewEnv(config, "ReplTrackerTest") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "ReplTrackerTest") alias := &topodatapb.TabletAlias{ Cell: "cell", Uid: 1, } blpFunc = testBlpFunc hs := newHealthStreamer(env, alias, &schema.Engine{}) - hs.InitDBConfig(&querypb.Target{TabletType: topodatapb.TabletType_PRIMARY}, config.DB.DbaWithDB()) + hs.InitDBConfig(&querypb.Target{TabletType: topodatapb.TabletType_PRIMARY}, cfg.DB.DbaWithDB()) hs.Open() defer hs.Close() target := &querypb.Target{} - hs.InitDBConfig(target, db.ConnParams()) + hs.InitDBConfig(target, dbconfigs.New(db.ConnParams())) ch, cancel := testStream(hs) defer cancel() @@ -214,11 +213,11 @@ func TestReloadSchema(t *testing.T) { defer cancel() db := fakesqldb.New(t) defer db.Close() - config := newConfig(db) - config.SignalWhenSchemaChange = testcase.enableSchemaChange - _ = config.SchemaReloadIntervalSeconds.Set("100ms") + cfg := newConfig(db) + cfg.SignalWhenSchemaChange = testcase.enableSchemaChange + cfg.SchemaReloadInterval = 100 * time.Millisecond - env := tabletenv.NewEnv(config, "ReplTrackerTest") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "ReplTrackerTest") alias := &topodatapb.TabletAlias{ Cell: "cell", Uid: 1, @@ -228,10 +227,8 @@ func TestReloadSchema(t *testing.T) { hs := newHealthStreamer(env, alias, se) target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} - configs := config.DB + configs := cfg.DB - db.AddQueryPattern(sqlparser.BuildParsedQuery(mysql.ClearSchemaCopy, sidecar.GetIdentifier()).Query+".*", &sqltypes.Result{}) - db.AddQueryPattern(sqlparser.BuildParsedQuery(mysql.InsertIntoSchemaCopy, sidecar.GetIdentifier()).Query+".*", &sqltypes.Result{}) db.AddQueryPattern("SELECT UNIX_TIMESTAMP()"+".*", sqltypes.MakeTestResult( sqltypes.MakeTestFields( "UNIX_TIMESTAMP(now())", @@ -331,21 +328,19 @@ func TestReloadView(t *testing.T) { defer cancel() db := fakesqldb.New(t) defer db.Close() - config := newConfig(db) - config.SignalWhenSchemaChange = true - _ = config.SchemaReloadIntervalSeconds.Set("100ms") - config.EnableViews = true + cfg := newConfig(db) + cfg.SignalWhenSchemaChange = true + cfg.SchemaReloadInterval = 100 * time.Millisecond + cfg.EnableViews = true - env := tabletenv.NewEnv(config, "TestReloadView") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TestReloadView") alias := &topodatapb.TabletAlias{Cell: "cell", Uid: 1} se := schema.NewEngine(env) hs := newHealthStreamer(env, alias, se) target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} - configs := config.DB + configs := cfg.DB - db.AddQueryPattern(sqlparser.BuildParsedQuery(mysql.ClearSchemaCopy, sidecar.GetIdentifier()).Query+".*", &sqltypes.Result{}) - db.AddQueryPattern(sqlparser.BuildParsedQuery(mysql.InsertIntoSchemaCopy, sidecar.GetIdentifier()).Query+".*", &sqltypes.Result{}) db.AddQueryPattern("SELECT UNIX_TIMESTAMP()"+".*", sqltypes.MakeTestResult( sqltypes.MakeTestFields( "UNIX_TIMESTAMP(now())", diff --git a/go/vt/vttablet/tabletserver/livequeryz_test.go b/go/vt/vttablet/tabletserver/livequeryz_test.go index 18e62047226..e507f365afb 100644 --- a/go/vt/vttablet/tabletserver/livequeryz_test.go +++ b/go/vt/vttablet/tabletserver/livequeryz_test.go @@ -17,18 +17,19 @@ limitations under the License. package tabletserver import ( + "context" "net/http" "net/http/httptest" "testing" - "context" + "vitess.io/vitess/go/vt/sqlparser" ) func TestLiveQueryzHandlerJSON(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/livequeryz/?format=json", nil) - queryList := NewQueryList("test") + queryList := NewQueryList("test", sqlparser.NewTestParser()) queryList.Add(NewQueryDetail(context.Background(), &testConn{id: 1})) queryList.Add(NewQueryDetail(context.Background(), &testConn{id: 2})) @@ -39,7 +40,7 @@ func TestLiveQueryzHandlerHTTP(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/livequeryz/", nil) - queryList := NewQueryList("test") + queryList := NewQueryList("test", sqlparser.NewTestParser()) queryList.Add(NewQueryDetail(context.Background(), &testConn{id: 1})) queryList.Add(NewQueryDetail(context.Background(), &testConn{id: 2})) @@ -50,7 +51,7 @@ func TestLiveQueryzHandlerHTTPFailedInvalidForm(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/livequeryz/", nil) - livequeryzHandler([]*QueryList{NewQueryList("test")}, resp, req) + livequeryzHandler([]*QueryList{NewQueryList("test", sqlparser.NewTestParser())}, resp, req) if resp.Code != http.StatusInternalServerError { t.Fatalf("http call should fail and return code: %d, but got: %d", http.StatusInternalServerError, resp.Code) @@ -61,7 +62,7 @@ func TestLiveQueryzHandlerTerminateConn(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/livequeryz//terminate?connID=1", nil) - queryList := NewQueryList("test") + queryList := NewQueryList("test", sqlparser.NewTestParser()) testConn := &testConn{id: 1} queryList.Add(NewQueryDetail(context.Background(), testConn)) if testConn.IsKilled() { @@ -77,7 +78,7 @@ func TestLiveQueryzHandlerTerminateFailedInvalidConnID(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/livequeryz//terminate?connID=invalid", nil) - livequeryzTerminateHandler([]*QueryList{NewQueryList("test")}, resp, req) + livequeryzTerminateHandler([]*QueryList{NewQueryList("test", sqlparser.NewTestParser())}, resp, req) if resp.Code != http.StatusInternalServerError { t.Fatalf("http call should fail and return code: %d, but got: %d", http.StatusInternalServerError, resp.Code) @@ -88,7 +89,7 @@ func TestLiveQueryzHandlerTerminateFailedInvalidForm(t *testing.T) { resp := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/livequeryz//terminate?inva+lid=2", nil) - livequeryzTerminateHandler([]*QueryList{NewQueryList("test")}, resp, req) + livequeryzTerminateHandler([]*QueryList{NewQueryList("test", sqlparser.NewTestParser())}, resp, req) if resp.Code != http.StatusInternalServerError { t.Fatalf("http call should fail and return code: %d, but got: %d", http.StatusInternalServerError, resp.Code) diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index e134a6fbe21..eda585694f1 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -21,10 +21,10 @@ import ( "reflect" "testing" - "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -67,9 +67,7 @@ var ( ) func TestEngineSchemaChanged(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - engine := newTestEngine(db) + engine := newTestEngine() defer engine.Close() engine.schemaChanged(nil, []*schema.Table{meTableT1, tableT2}, nil, nil) @@ -110,9 +108,7 @@ func extractManagerNames(in map[string]*messageManager) map[string]bool { } func TestSubscribe(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - engine := newTestEngine(db) + engine := newTestEngine() engine.schemaChanged(nil, []*schema.Table{meTableT1, meTableT2}, nil, nil) f1, ch1 := newEngineReceiver() f2, ch2 := newEngineReceiver() @@ -142,9 +138,7 @@ func TestSubscribe(t *testing.T) { } func TestEngineGenerate(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - engine := newTestEngine(db) + engine := newTestEngine() defer engine.Close() engine.schemaChanged(nil, []*schema.Table{meTableT1}, nil, nil) @@ -157,10 +151,10 @@ func TestEngineGenerate(t *testing.T) { } } -func newTestEngine(db *fakesqldb.DB) *Engine { - config := tabletenv.NewDefaultConfig() +func newTestEngine() *Engine { + cfg := tabletenv.NewDefaultConfig() tsv := &fakeTabletServer{ - Env: tabletenv.NewEnv(config, "MessagerTest"), + Env: tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "MessagerTest"), } se := schema.NewEngine(tsv) te := NewEngine(tsv, se, newFakeVStreamer()) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 0629b31629f..845eaf8df40 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -28,17 +28,17 @@ import ( "golang.org/x/sync/semaphore" "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/log" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" ) var ( @@ -227,7 +227,7 @@ type messageManager struct { // wg is for ensuring all running goroutines have returned // before we can close the manager. You need to Add before - // launching any gorooutine while holding a lock on mu. + // launching any goroutine while holding a lock on mu. // The goroutine must in turn defer on Done. wg sync.WaitGroup @@ -272,7 +272,7 @@ func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, pos } mm.readByPriorityAndTimeNext = sqlparser.BuildParsedQuery( // There should be a poller_idx defined on (time_acked, priority, time_next desc) - // for this to be as effecient as possible + // for this to be as efficient as possible "select priority, time_next, epoch, time_acked, %s from %v where time_acked is null and time_next < %a order by priority, time_next desc limit %a", columnList, mm.name, ":time_next", ":max") mm.ackQuery = sqlparser.BuildParsedQuery( diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index b8ca47ae46d..fdf39556e5c 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -34,6 +34,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" @@ -317,7 +318,7 @@ func TestMessageManagerPostponeThrottle(t *testing.T) { // Postpone will wait on the unbuffered ch. <-r1.ch - // Set up a second subsriber, add a message. + // Set up a second subscriber, add a message. r2 := newTestReceiver(1) mm.Subscribe(context.Background(), r2.rcv) <-r2.ch @@ -831,9 +832,9 @@ type fakeTabletServer struct { } func newFakeTabletServer() *fakeTabletServer { - config := tabletenv.NewDefaultConfig() + cfg := tabletenv.NewDefaultConfig() return &fakeTabletServer{ - Env: tabletenv.NewEnv(config, "MessagerTest"), + Env: tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "MessagerTest"), } } diff --git a/go/vt/vttablet/tabletserver/planbuilder/builder.go b/go/vt/vttablet/tabletserver/planbuilder/builder.go index 3cae292b593..94f5fc1caa2 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/builder.go +++ b/go/vt/vttablet/tabletserver/planbuilder/builder.go @@ -20,6 +20,7 @@ import ( "strings" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -27,7 +28,7 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { +func analyzeSelect(env *vtenv.Environment, sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanSelect, FullQuery: GenerateLimitQuery(sel), @@ -48,7 +49,10 @@ func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", sqlparser.ToString(sel.From)) } plan.PlanID = PlanNextval - v, err := evalengine.Translate(nextVal.Expr, nil) + v, err := evalengine.Translate(nextVal.Expr, &evalengine.Config{ + Environment: env, + Collation: env.CollationEnv().DefaultConnectionCharset(), + }) if err != nil { return nil, err } @@ -219,3 +223,21 @@ func analyzeDDL(stmt sqlparser.DDLStatement) (*Plan, error) { } return &Plan{PlanID: PlanDDL, FullQuery: fullQuery, FullStmt: stmt, NeedsReservedConn: stmt.IsTemporary()}, nil } + +func analyzeFlush(stmt *sqlparser.Flush, tables map[string]*schema.Table) (*Plan, error) { + plan := &Plan{PlanID: PlanFlush, FullQuery: GenerateFullQuery(stmt)} + + for _, tbl := range stmt.TableNames { + if schemaTbl, ok := tables[tbl.Name.String()]; ok { + plan.AllTables = append(plan.AllTables, schemaTbl) + } + } + if len(plan.AllTables) == 1 { + plan.Table = plan.AllTables[0] + } + + if stmt.WithLock { + plan.NeedsReservedConn = true + } + return plan, nil +} diff --git a/go/vt/vttablet/tabletserver/planbuilder/permission.go b/go/vt/vttablet/tabletserver/planbuilder/permission.go index a9d772f2931..79b2f9eb430 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/permission.go +++ b/go/vt/vttablet/tabletserver/planbuilder/permission.go @@ -65,7 +65,8 @@ func BuildPermissions(stmt sqlparser.Statement) []Permission { case *sqlparser.Analyze: permissions = buildTableNamePermissions(node.Table, tableacl.WRITER, permissions) case *sqlparser.OtherAdmin, *sqlparser.CallProc, *sqlparser.Begin, *sqlparser.Commit, *sqlparser.Rollback, - *sqlparser.Load, *sqlparser.Savepoint, *sqlparser.Release, *sqlparser.SRollback, *sqlparser.Set, *sqlparser.Show, sqlparser.Explain: + *sqlparser.Load, *sqlparser.Savepoint, *sqlparser.Release, *sqlparser.SRollback, *sqlparser.Set, *sqlparser.Show, sqlparser.Explain, + *sqlparser.UnlockTables: // no op default: panic(fmt.Errorf("BUG: unexpected statement type: %T", node)) @@ -75,18 +76,15 @@ func BuildPermissions(stmt sqlparser.Statement) []Permission { func buildSubqueryPermissions(stmt sqlparser.Statement, role tableacl.Role, permissions []Permission) []Permission { _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { - switch node := node.(type) { - case *sqlparser.Select: - permissions = buildTableExprsPermissions(node.From, role, permissions) - case sqlparser.TableExprs: - return false, nil + if sel, ok := node.(*sqlparser.Select); ok { + permissions = buildTableExprsPermissions(sel.From, role, permissions) } return true, nil }, stmt) return permissions } -func buildTableExprsPermissions(node sqlparser.TableExprs, role tableacl.Role, permissions []Permission) []Permission { +func buildTableExprsPermissions(node []sqlparser.TableExpr, role tableacl.Role, permissions []Permission) []Permission { for _, node := range node { permissions = buildTableExprPermissions(node, role, permissions) } @@ -96,14 +94,11 @@ func buildTableExprsPermissions(node sqlparser.TableExprs, role tableacl.Role, p func buildTableExprPermissions(node sqlparser.TableExpr, role tableacl.Role, permissions []Permission) []Permission { switch node := node.(type) { case *sqlparser.AliasedTableExpr: - // An AliasedTableExpr can also be a subquery, but we should skip them here + // An AliasedTableExpr can also be a derived table, but we should skip them here // because the buildSubQueryPermissions walker will catch them and extract // the corresponding table names. - switch node := node.Expr.(type) { - case sqlparser.TableName: - permissions = buildTableNamePermissions(node, role, permissions) - case *sqlparser.DerivedTable: - permissions = buildSubqueryPermissions(node.Select, role, permissions) + if tblName, ok := node.Expr.(sqlparser.TableName); ok { + permissions = buildTableNamePermissions(tblName, role, permissions) } case *sqlparser.ParenTableExpr: permissions = buildTableExprsPermissions(node.Exprs, role, permissions) diff --git a/go/vt/vttablet/tabletserver/planbuilder/permission_test.go b/go/vt/vttablet/tabletserver/planbuilder/permission_test.go index 17baa72595e..6d42118cb0b 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/permission_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/permission_test.go @@ -17,9 +17,9 @@ limitations under the License. package planbuilder import ( - "reflect" "testing" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" ) @@ -169,22 +169,21 @@ func TestBuildPermissions(t *testing.T) { }, { input: "update (select * from t1) as a join t2 on a=b set c=d", output: []Permission{{ - TableName: "t1", - Role: tableacl.WRITER, - }, { TableName: "t2", Role: tableacl.WRITER, + }, { + TableName: "t1", // derived table in update or delete needs reader permission as they cannot be modified. }}, }} for _, tcase := range tcases { - stmt, err := sqlparser.Parse(tcase.input) - if err != nil { - t.Fatal(err) - } - got := BuildPermissions(stmt) - if !reflect.DeepEqual(got, tcase.output) { - t.Errorf("BuildPermissions(%s): %v, want %v", tcase.input, got, tcase.output) - } + t.Run(tcase.input, func(t *testing.T) { + stmt, err := sqlparser.NewTestParser().Parse(tcase.input) + if err != nil { + t.Fatal(err) + } + got := BuildPermissions(stmt) + utils.MustMatch(t, tcase.output, got) + }) } } diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index c4a8f905607..7b1e57c2f90 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -20,11 +20,11 @@ import ( "encoding/json" "strings" - "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -202,7 +202,7 @@ func (plan *Plan) TableNames() (names []string) { } // Build builds a plan based on the schema. -func Build(statement sqlparser.Statement, tables map[string]*schema.Table, dbName string, viewsEnabled bool) (plan *Plan, err error) { +func Build(env *vtenv.Environment, statement sqlparser.Statement, tables map[string]*schema.Table, dbName string, viewsEnabled bool) (plan *Plan, err error) { switch stmt := statement.(type) { case *sqlparser.Union: plan, err = &Plan{ @@ -210,7 +210,7 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table, dbNam FullQuery: GenerateLimitQuery(stmt), }, nil case *sqlparser.Select: - plan, err = analyzeSelect(stmt, tables) + plan, err = analyzeSelect(env, stmt, tables) case *sqlparser.Insert: plan, err = analyzeInsert(stmt, tables) case *sqlparser.Update: @@ -246,7 +246,9 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table, dbNam case *sqlparser.Load: plan, err = &Plan{PlanID: PlanLoad}, nil case *sqlparser.Flush: - plan, err = &Plan{PlanID: PlanFlush, FullQuery: GenerateFullQuery(stmt)}, nil + plan, err = analyzeFlush(stmt, tables) + case *sqlparser.UnlockTables: + plan, err = &Plan{PlanID: PlanUnlockTables}, nil case *sqlparser.CallProc: plan, err = &Plan{PlanID: PlanCallProc, FullQuery: GenerateFullQuery(stmt)}, nil default: @@ -321,7 +323,7 @@ func hasLockFunc(sel *sqlparser.Select) bool { } // BuildSettingQuery builds a query for system settings. -func BuildSettingQuery(settings []string) (query string, resetQuery string, err error) { +func BuildSettingQuery(settings []string, parser *sqlparser.Parser) (query string, resetQuery string, err error) { if len(settings) == 0 { return "", "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG]: plan called for empty system settings") } @@ -329,7 +331,7 @@ func BuildSettingQuery(settings []string) (query string, resetQuery string, err var resetSetExprs sqlparser.SetExprs lDefault := sqlparser.NewStrLiteral("default") for _, setting := range settings { - stmt, err := sqlparser.Parse(setting) + stmt, err := parser.Parse(setting) if err != nil { return "", "", vterrors.Wrapf(err, "[BUG]: failed to parse system setting: %s", setting) } diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go index 7c1f364cac8..9569121cb8f 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" ) @@ -73,6 +74,7 @@ func TestDDLPlan(t *testing.T) { func testPlan(t *testing.T, fileName string) { t.Helper() + parser := sqlparser.NewTestParser() testSchema := loadSchema("schema_test.json") for tcase := range iterateExecFile(fileName) { t.Run(tcase.input, func(t *testing.T) { @@ -81,9 +83,9 @@ func testPlan(t *testing.T, fileName string) { } var plan *Plan var err error - statement, err := sqlparser.Parse(tcase.input) + statement, err := parser.Parse(tcase.input) if err == nil { - plan, err = Build(statement, testSchema, "dbName", false) + plan, err = Build(vtenv.NewTestEnv(), statement, testSchema, "dbName", false) } PassthroughDMLs = false @@ -111,6 +113,7 @@ func testPlan(t *testing.T, fileName string) { func TestPlanInReservedConn(t *testing.T) { testSchema := loadSchema("schema_test.json") + parser := sqlparser.NewTestParser() for tcase := range iterateExecFile("exec_cases.txt") { t.Run(tcase.input, func(t *testing.T) { if strings.Contains(tcase.options, "PassthroughDMLs") { @@ -118,9 +121,9 @@ func TestPlanInReservedConn(t *testing.T) { } var plan *Plan var err error - statement, err := sqlparser.Parse(tcase.input) + statement, err := parser.Parse(tcase.input) if err == nil { - plan, err = Build(statement, testSchema, "dbName", false) + plan, err = Build(vtenv.NewTestEnv(), statement, testSchema, "dbName", false) } PassthroughDMLs = false @@ -154,6 +157,7 @@ func TestCustom(t *testing.T) { t.Log("No schemas to test") return } + parser := sqlparser.NewTestParser() for _, schemFile := range testSchemas { schem := loadSchema(schemFile) t.Logf("Testing schema %s", schemFile) @@ -167,11 +171,11 @@ func TestCustom(t *testing.T) { for _, file := range files { t.Logf("Testing file %s", file) for tcase := range iterateExecFile(file) { - statement, err := sqlparser.Parse(tcase.input) + statement, err := parser.Parse(tcase.input) if err != nil { t.Fatalf("Got error: %v, parsing sql: %v", err.Error(), tcase.input) } - plan, err := Build(statement, schem, "dbName", false) + plan, err := Build(vtenv.NewTestEnv(), statement, schem, "dbName", false) var out string if err != nil { out = err.Error() @@ -192,10 +196,11 @@ func TestCustom(t *testing.T) { func TestStreamPlan(t *testing.T) { testSchema := loadSchema("schema_test.json") + parser := sqlparser.NewTestParser() for tcase := range iterateExecFile("stream_cases.txt") { var plan *Plan var err error - statement, err := sqlparser.Parse(tcase.input) + statement, err := parser.Parse(tcase.input) if err == nil { plan, err = BuildStreaming(statement, testSchema) } @@ -252,13 +257,14 @@ func TestMessageStreamingPlan(t *testing.T) { func TestLockPlan(t *testing.T) { testSchema := loadSchema("schema_test.json") + parser := sqlparser.NewTestParser() for tcase := range iterateExecFile("lock_cases.txt") { t.Run(tcase.input, func(t *testing.T) { var plan *Plan var err error - statement, err := sqlparser.Parse(tcase.input) + statement, err := parser.Parse(tcase.input) if err == nil { - plan, err = Build(statement, testSchema, "dbName", false) + plan, err = Build(vtenv.NewTestEnv(), statement, testSchema, "dbName", false) } var out string diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index 5565f405bc7..977b3822050 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -175,7 +175,7 @@ "NextCount": ":a" } -# squence with bad value +# sequence with bad value "select next 12345667852342342342323423423 values from seq" { "PlanID": "Nextval", @@ -339,7 +339,7 @@ } ], "FullQuery": "update d set foo = 'foo' where `name` in ('a', 'b') limit :#maxLimit", - "WhereClause": "where `name` in ('a', 'b')" + "WhereClause": " where `name` in ('a', 'b')" } # normal update @@ -355,7 +355,7 @@ options:PassthroughDMLs } ], "FullQuery": "update d set foo = 'foo' where `name` in ('a', 'b')", - "WhereClause": "where `name` in ('a', 'b')" + "WhereClause": " where `name` in ('a', 'b')" } # cross-db update @@ -370,7 +370,7 @@ options:PassthroughDMLs } ], "FullQuery": "update a.b set foo = 'foo' where `name` in ('a', 'b')", - "WhereClause": "where `name` in ('a', 'b')" + "WhereClause": " where `name` in ('a', 'b')" } # update unknown table @@ -385,7 +385,7 @@ options:PassthroughDMLs } ], "FullQuery": "update bogus set `name` = 'foo' where id = 1", - "WhereClause": "where id = 1" + "WhereClause": " where id = 1" } # update unknown table @@ -401,7 +401,7 @@ options:PassthroughDMLs } ], "FullQuery": "update bogus set `name` = 'foo' where id = 1", - "WhereClause": "where id = 1" + "WhereClause": " where id = 1" } # multi-table update @@ -420,7 +420,7 @@ options:PassthroughDMLs } ], "FullQuery": "update a, b set a.`name` = 'foo' where a.id = b.id and b.var = 'test'", - "WhereClause": "where a.id = b.id and b.var = 'test'" + "WhereClause": " where a.id = b.id and b.var = 'test'" } # multi-table update @@ -440,7 +440,7 @@ options:PassthroughDMLs } ], "FullQuery": "update a join b on a.id = b.id set a.`name` = 'foo' where b.var = 'test'", - "WhereClause": "where b.var = 'test'" + "WhereClause": " where b.var = 'test'" } @@ -499,7 +499,7 @@ options:PassthroughDMLs } ], "FullQuery": "delete from d where `name` in ('a', 'b') limit :#maxLimit", - "WhereClause": "where `name` in ('a', 'b')" + "WhereClause": " where `name` in ('a', 'b')" } # normal delete @@ -515,7 +515,7 @@ options:PassthroughDMLs } ], "FullQuery": "delete from d where `name` in ('a', 'b')", - "WhereClause": "where `name` in ('a', 'b')" + "WhereClause": " where `name` in ('a', 'b')" } # delete unknown table @@ -563,7 +563,7 @@ options:PassthroughDMLs } ], "FullQuery": "delete a, b from a, b where id = 1", - "WhereClause": "where id = 1" + "WhereClause": " where id = 1" } @@ -939,6 +939,25 @@ options:PassthroughDMLs "FullQuery": "flush tables a, b" } +# flush statement with read lock +"flush tables a,b with read lock" +{ + "PlanID": "Flush", + "TableName": "", + "Permissions": [ + { + "TableName": "a", + "Role": 2 + }, + { + "TableName": "b", + "Role": 2 + } + ], + "FullQuery": "flush tables a, b with read lock", + "NeedsReservedConn": true +} + # call proc "call getAllTheThings()" { diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index 7f83a29fc51..f009a72ceee 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -109,7 +109,7 @@ func (ep *TabletPlan) IsValid(hasReservedCon, hasSysSettings bool) error { func isValid(planType planbuilder.PlanType, hasReservedCon bool, hasSysSettings bool) error { switch planType { - case planbuilder.PlanSelectLockFunc, planbuilder.PlanDDL: + case planbuilder.PlanSelectLockFunc, planbuilder.PlanDDL, planbuilder.PlanFlush: if hasReservedCon { return nil } @@ -188,6 +188,7 @@ type QueryEngine struct { // stats // Note: queryErrorCountsWithCode is similar to queryErrorCounts except it contains error code as an additional dimension queryCounts, queryCountsWithTabletType, queryTimes, queryErrorCounts, queryErrorCountsWithCode, queryRowsAffected, queryRowsReturned *stats.CountersWithMultiLabels + queryCacheHits, queryCacheMisses *stats.CounterFunc // stats flags enablePerWorkloadTableMetrics bool @@ -280,6 +281,12 @@ func NewQueryEngine(env tabletenv.Env, se *schema.Engine) *QueryEngine { env.Exporter().NewCounterFunc("QueryCacheEvictions", "Query engine query cache evictions", func() int64 { return qe.plans.Metrics.Evicted() }) + qe.queryCacheHits = env.Exporter().NewCounterFunc("QueryCacheHits", "Query engine query cache hits", func() int64 { + return qe.plans.Metrics.Hits() + }) + qe.queryCacheMisses = env.Exporter().NewCounterFunc("QueryCacheMisses", "Query engine query cache misses", func() int64 { + return qe.plans.Metrics.Misses() + }) labels := []string{"Table", "Plan"} if config.EnablePerWorkloadTableMetrics { @@ -359,11 +366,11 @@ func (qe *QueryEngine) Close() { var errNoCache = errors.New("plan should not be cached") func (qe *QueryEngine) getPlan(curSchema *currentSchema, sql string) (*TabletPlan, error) { - statement, err := sqlparser.Parse(sql) + statement, err := qe.env.Environment().Parser().Parse(sql) if err != nil { return nil, err } - splan, err := planbuilder.Build(statement, curSchema.tables, qe.env.Config().DB.DBName, qe.env.Config().EnableViews) + splan, err := planbuilder.Build(qe.env.Environment(), statement, curSchema.tables, qe.env.Config().DB.DBName, qe.env.Config().EnableViews) if err != nil { return nil, err } @@ -377,7 +384,7 @@ func (qe *QueryEngine) getPlan(curSchema *currentSchema, sql string) (*TabletPla return plan, errNoCache } -// GetPlan returns the TabletPlan that for the query. Plans are cached in a theine LRU cache. +// GetPlan returns the TabletPlan that for the query. Plans are cached in an LRU cache. func (qe *QueryEngine) GetPlan(ctx context.Context, logStats *tabletenv.LogStats, sql string, skipQueryPlanCache bool) (*TabletPlan, error) { span, _ := trace.NewSpan(ctx, "QueryEngine.GetPlan") defer span.Finish() @@ -402,7 +409,7 @@ func (qe *QueryEngine) GetPlan(ctx context.Context, logStats *tabletenv.LogStats } func (qe *QueryEngine) getStreamPlan(curSchema *currentSchema, sql string) (*TabletPlan, error) { - statement, err := sqlparser.Parse(sql) + statement, err := qe.env.Environment().Parser().Parse(sql) if err != nil { return nil, err } @@ -424,7 +431,7 @@ func (qe *QueryEngine) getStreamPlan(curSchema *currentSchema, sql string) (*Tab return plan, errNoCache } -// GetStreamPlan returns the TabletPlan that for the query. Plans are cached in a theine LRU cache. +// GetStreamPlan returns the TabletPlan that for the query. Plans are cached in an LRU cache. func (qe *QueryEngine) GetStreamPlan(ctx context.Context, logStats *tabletenv.LogStats, sql string, skipQueryPlanCache bool) (*TabletPlan, error) { span, _ := trace.NewSpan(ctx, "QueryEngine.GetStreamPlan") defer span.Finish() @@ -479,7 +486,7 @@ func (qe *QueryEngine) GetConnSetting(ctx context.Context, settings []string) (* cacheKey := SettingsCacheKey(buf.String()) connSetting, _, err := qe.settings.GetOrLoad(cacheKey, 0, func() (*smartconnpool.Setting, error) { // build the setting queries - query, resetQuery, err := planbuilder.BuildSettingQuery(settings) + query, resetQuery, err := planbuilder.BuildSettingQuery(settings, qe.env.Environment().Parser()) if err != nil { return nil, err } @@ -609,7 +616,7 @@ func (qe *QueryEngine) handleHTTPQueryPlans(response http.ResponseWriter, reques response.Header().Set("Content-Type", "text/plain") qe.ForEachPlan(func(plan *TabletPlan) bool { - response.Write([]byte(fmt.Sprintf("%#v\n", sqlparser.TruncateForUI(plan.Original)))) + response.Write([]byte(fmt.Sprintf("%#v\n", qe.env.Environment().Parser().TruncateForUI(plan.Original)))) if b, err := json.MarshalIndent(plan.Plan, "", " "); err != nil { response.Write([]byte(err.Error())) } else { @@ -629,7 +636,7 @@ func (qe *QueryEngine) handleHTTPQueryStats(response http.ResponseWriter, reques var qstats []perQueryStats qe.ForEachPlan(func(plan *TabletPlan) bool { var pqstats perQueryStats - pqstats.Query = unicoded(sqlparser.TruncateForUI(plan.Original)) + pqstats.Query = unicoded(qe.env.Environment().Parser().TruncateForUI(plan.Original)) pqstats.Table = plan.TableName().String() pqstats.Plan = plan.PlanID pqstats.QueryCount, pqstats.Time, pqstats.MysqlTime, pqstats.RowsAffected, pqstats.RowsReturned, pqstats.ErrorCount = plan.Stats() @@ -697,7 +704,7 @@ func (qe *QueryEngine) handleHTTPConsolidations(response http.ResponseWriter, re for _, v := range items { var query string if streamlog.GetRedactDebugUIQueries() { - query, _ = sqlparser.RedactSQLQuery(v.Query) + query, _ = qe.env.Environment().Parser().RedactSQLQuery(v.Query) } else { query = v.Query } diff --git a/go/vt/vttablet/tabletserver/query_engine_test.go b/go/vt/vttablet/tabletserver/query_engine_test.go index 7bfac4988f2..604f65f3311 100644 --- a/go/vt/vttablet/tabletserver/query_engine_test.go +++ b/go/vt/vttablet/tabletserver/query_engine_test.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/cache/theine" "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/sqlparser" @@ -60,9 +61,9 @@ func TestStrictMode(t *testing.T) { schematest.AddDefaultQueries(db) // Test default behavior. - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - env := tabletenv.NewEnv(config, "TabletServerTest") + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) qe.se.InitDBConfig(newDBConfigs(db).DbaWithDB()) @@ -89,7 +90,7 @@ func TestStrictMode(t *testing.T) { qe.Close() // Test that we succeed if the enforcement flag is off. - config.EnforceStrictTransTables = false + cfg.EnforceStrictTransTables = false qe = NewQueryEngine(env, se) if err := qe.Open(); err != nil { t.Fatal(err) @@ -185,11 +186,27 @@ func TestQueryPlanCache(t *testing.T) { ctx := context.Background() logStats := tabletenv.NewLogStats(ctx, "GetPlanStats") + initialHits := qe.queryCacheHits.Get() + initialMisses := qe.queryCacheMisses.Get() + firstPlan, err := qe.GetPlan(ctx, logStats, firstQuery, false) require.NoError(t, err) require.NotNil(t, firstPlan, "plan should not be nil") assertPlanCacheSize(t, qe, 1) + + require.Equal(t, int64(0), qe.queryCacheHits.Get()-initialHits) + require.Equal(t, int64(1), qe.queryCacheMisses.Get()-initialMisses) + + secondPlan, err := qe.GetPlan(ctx, logStats, firstQuery, false) + require.NoError(t, err) + require.NotNil(t, secondPlan, "plan should not be nil") + + assertPlanCacheSize(t, qe, 1) + + require.Equal(t, int64(1), qe.queryCacheHits.Get()-initialHits) + require.Equal(t, int64(1), qe.queryCacheMisses.Get()-initialMisses) + qe.ClearQueryPlanCache() } @@ -350,12 +367,12 @@ func TestStatsURL(t *testing.T) { } func newTestQueryEngine(idleTimeout time.Duration, strict bool, dbcfgs *dbconfigs.DBConfigs) *QueryEngine { - config := tabletenv.NewDefaultConfig() - config.DB = dbcfgs - _ = config.OltpReadPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - _ = config.OlapReadPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - _ = config.TxPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - env := tabletenv.NewEnv(config, "TabletServerTest") + cfg := tabletenv.NewDefaultConfig() + cfg.DB = dbcfgs + cfg.OltpReadPool.IdleTimeout = idleTimeout + cfg.OlapReadPool.IdleTimeout = idleTimeout + cfg.TxPool.IdleTimeout = idleTimeout + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) // the integration tests that check cache behavior do not expect a doorkeeper; disable it @@ -452,10 +469,10 @@ func benchmarkPlanCache(b *testing.B, db *fakesqldb.DB, par int) { b.Helper() dbcfgs := newDBConfigs(db) - config := tabletenv.NewDefaultConfig() - config.DB = dbcfgs + cfg := tabletenv.NewDefaultConfig() + cfg.DB = dbcfgs - env := tabletenv.NewEnv(config, "TabletServerTest") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) @@ -509,11 +526,11 @@ func TestPlanCachePollution(t *testing.T) { db.AddQueryPattern(".*", &sqltypes.Result{}) dbcfgs := newDBConfigs(db) - config := tabletenv.NewDefaultConfig() - config.DB = dbcfgs + cfg := tabletenv.NewDefaultConfig() + cfg.DB = dbcfgs // config.LFUQueryCacheSizeBytes = 3 * 1024 * 1024 - env := tabletenv.NewEnv(config, "TabletServerTest") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) @@ -535,7 +552,7 @@ func TestPlanCachePollution(t *testing.T) { go func() { cacheMode := "lfu" - out, err := os.Create(path.Join(plotPath, fmt.Sprintf("cache_plot_%d_%s.dat", config.QueryCacheMemory, cacheMode))) + out, err := os.Create(path.Join(plotPath, fmt.Sprintf("cache_plot_%d_%s.dat", cfg.QueryCacheMemory, cacheMode))) require.NoError(t, err) defer out.Close() @@ -826,10 +843,10 @@ func TestAddQueryStats(t *testing.T) { t.Parallel() for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(fakesqldb.New(t)) - config.EnablePerWorkloadTableMetrics = testcase.enablePerWorkloadTableMetrics - env := tabletenv.NewEnv(config, "TestAddQueryStats_"+testcase.name) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(fakesqldb.New(t)) + cfg.EnablePerWorkloadTableMetrics = testcase.enablePerWorkloadTableMetrics + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TestAddQueryStats_"+testcase.name) se := schema.NewEngine(env) qe := NewQueryEngine(env, se) qe.AddStats(testcase.planType, testcase.tableName, testcase.workload, testcase.tabletType, testcase.queryCount, testcase.duration, testcase.mysqlTime, testcase.rowsAffected, testcase.rowsReturned, testcase.errorCount, testcase.errorCode) @@ -868,9 +885,9 @@ func TestPlanPoolUnsafe(t *testing.T) { } for _, tcase := range tcases { t.Run(tcase.name, func(t *testing.T) { - statement, err := sqlparser.Parse(tcase.query) + statement, err := sqlparser.NewTestParser().Parse(tcase.query) require.NoError(t, err) - plan, err := planbuilder.Build(statement, map[string]*schema.Table{}, "dbName", false) + plan, err := planbuilder.Build(vtenv.NewTestEnv(), statement, map[string]*schema.Table{}, "dbName", false) // Plan building will not fail, but it will mark that reserved connection is needed. // checking plan is valid will fail. require.NoError(t, err) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 63dcd42d0a8..5690c209ebb 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -18,18 +18,18 @@ package tabletserver import ( "context" + "errors" "fmt" "io" "strings" "sync" "time" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/pools/smartconnpool" - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/callerid" @@ -62,8 +62,11 @@ type QueryExecutor struct { ctx context.Context logStats *tabletenv.LogStats tsv *TabletServer - tabletType topodatapb.TabletType - setting *smartconnpool.Setting + // targetTabletType stores the target tablet type that we got as part of the request. + // We have the tablet server object too, which stores the current tablet type, but this is different. + // The target type we requested might be different from tsv's tablet type, if we had a change to the tablet type recently. + targetTabletType topodatapb.TabletType + setting *smartconnpool.Setting } const ( @@ -108,10 +111,10 @@ func (qre *QueryExecutor) shouldConsolidate() bool { case querypb.ExecuteOptions_CONSOLIDATOR_ENABLED: return true case querypb.ExecuteOptions_CONSOLIDATOR_ENABLED_REPLICAS: - return qre.tabletType != topodatapb.TabletType_PRIMARY + return qre.targetTabletType != topodatapb.TabletType_PRIMARY default: cm := qre.tsv.qe.consolidatorMode.Load().(string) - return cm == tabletenv.Enable || (cm == tabletenv.NotOnPrimary && qre.tabletType != topodatapb.TabletType_PRIMARY) + return cm == tabletenv.Enable || (cm == tabletenv.NotOnPrimary && qre.targetTabletType != topodatapb.TabletType_PRIMARY) } } @@ -122,7 +125,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { defer func(start time.Time) { duration := time.Since(start) qre.tsv.stats.QueryTimings.Add(planName, duration) - qre.tsv.stats.QueryTimingsByTabletType.Add(qre.tabletType.String(), duration) + qre.tsv.stats.QueryTimingsByTabletType.Add(qre.targetTabletType.String(), duration) qre.recordUserQuery("Execute", int64(duration)) mysqlTime := qre.logStats.MysqlResponseTime @@ -136,12 +139,12 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { errCode = vtErrorCode.String() if reply == nil { - qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, qre.options.GetWorkloadName(), qre.tabletType, 1, duration, mysqlTime, 0, 0, 1, errCode) + qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, qre.options.GetWorkloadName(), qre.targetTabletType, 1, duration, mysqlTime, 0, 0, 1, errCode) qre.plan.AddStats(1, duration, mysqlTime, 0, 0, 1) return } - qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, qre.options.GetWorkloadName(), qre.tabletType, 1, duration, mysqlTime, int64(reply.RowsAffected), int64(len(reply.Rows)), 0, errCode) + qre.tsv.qe.AddStats(qre.plan.PlanID, tableName, qre.options.GetWorkloadName(), qre.targetTabletType, 1, duration, mysqlTime, int64(reply.RowsAffected), int64(len(reply.Rows)), 0, errCode) qre.plan.AddStats(1, duration, mysqlTime, reply.RowsAffected, uint64(len(reply.Rows)), 0) qre.logStats.RowsAffected = int(reply.RowsAffected) qre.logStats.Rows = reply.Rows @@ -207,6 +210,8 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return qre.execShowThrottledApps() case p.PlanShowThrottlerStatus: return qre.execShowThrottlerStatus() + case p.PlanUnlockTables: + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "unlock tables should be executed with an existing connection") case p.PlanSet: if qre.setting == nil { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "[BUG] %s not allowed without setting connection", qre.query) @@ -279,7 +284,7 @@ func (qre *QueryExecutor) txConnExec(conn *StatefulConnection) (*sqltypes.Result return qre.txFetch(conn, true) case p.PlanUpdateLimit, p.PlanDeleteLimit: return qre.execDMLLimit(conn) - case p.PlanOtherRead, p.PlanOtherAdmin, p.PlanFlush: + case p.PlanOtherRead, p.PlanOtherAdmin, p.PlanFlush, p.PlanUnlockTables: return qre.execStatefulConn(conn, qre.query, true) case p.PlanSavepoint, p.PlanRelease, p.PlanSRollback: return qre.execStatefulConn(conn, qre.query, true) @@ -313,7 +318,7 @@ func (qre *QueryExecutor) Stream(callback StreamCallback) error { defer func(start time.Time) { qre.tsv.stats.QueryTimings.Record(qre.plan.PlanID.String(), start) - qre.tsv.stats.QueryTimingsByTabletType.Record(qre.tabletType.String(), start) + qre.tsv.stats.QueryTimingsByTabletType.Record(qre.targetTabletType.String(), start) qre.recordUserQuery("Stream", int64(time.Since(start))) }(time.Now()) @@ -340,7 +345,7 @@ func (qre *QueryExecutor) Stream(callback StreamCallback) error { if consolidator := qre.tsv.qe.streamConsolidator; consolidator != nil { if qre.connID == 0 && qre.plan.PlanID == p.PlanSelectStream && qre.shouldConsolidate() { - return consolidator.Consolidate(qre.logStats, sqlWithoutComments, callback, + return consolidator.Consolidate(qre.tsv.stats.WaitTimings, qre.logStats, sqlWithoutComments, callback, func(callback StreamCallback) error { dbConn, err := qre.getStreamConn() if err != nil { @@ -403,7 +408,7 @@ func (qre *QueryExecutor) MessageStream(callback StreamCallback) error { defer func(start time.Time) { qre.tsv.stats.QueryTimings.Record(qre.plan.PlanID.String(), start) - qre.tsv.stats.QueryTimingsByTabletType.Record(qre.tabletType.String(), start) + qre.tsv.stats.QueryTimingsByTabletType.Record(qre.targetTabletType.String(), start) qre.recordUserQuery("MessageStream", int64(time.Since(start))) }(time.Now()) @@ -611,13 +616,13 @@ func (*QueryExecutor) BeginAgain(ctx context.Context, dc *StatefulConnection) er } func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { - env := evalengine.NewExpressionEnv(qre.ctx, qre.bindVars, nil) + env := evalengine.NewExpressionEnv(qre.ctx, qre.bindVars, evalengine.NewEmptyVCursor(qre.tsv.Environment(), time.Local)) result, err := env.Evaluate(qre.plan.NextCount) if err != nil { return nil, err } tableName := qre.plan.TableName() - v := result.Value(collations.Default()) + v := result.Value(qre.tsv.env.CollationEnv().DefaultConnectionCharset()) inc, err := v.ToInt64() if err != nil || inc < 1 { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid increment for sequence %s: %s", tableName, v.String()) @@ -754,7 +759,7 @@ func (qre *QueryExecutor) verifyRowCount(count, maxrows int64) error { if warnThreshold > 0 && count > warnThreshold { callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) qre.tsv.Stats().Warnings.Add("ResultsExceeded", 1) - log.Warningf("caller id: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars, qre.tsv.Config().SanitizeLogMessages, true)) + log.Warningf("caller id: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars, qre.tsv.Config().SanitizeLogMessages, true, qre.tsv.env.Parser())) } return nil } @@ -772,12 +777,13 @@ func (qre *QueryExecutor) getConn() (*connpool.PooledConn, error) { span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.getConn") defer span.Finish() - start := time.Now() + defer func(start time.Time) { + qre.logStats.WaitingForConnection += time.Since(start) + }(time.Now()) conn, err := qre.tsv.qe.conns.Get(ctx, qre.setting) switch err { case nil: - qre.logStats.WaitingForConnection += time.Since(start) return conn, nil case connpool.ErrConnPoolClosed: return nil, err @@ -789,11 +795,13 @@ func (qre *QueryExecutor) getStreamConn() (*connpool.PooledConn, error) { span, ctx := trace.NewSpan(qre.ctx, "QueryExecutor.getStreamConn") defer span.Finish() - start := time.Now() + defer func(start time.Time) { + qre.logStats.WaitingForConnection += time.Since(start) + }(time.Now()) conn, err := qre.tsv.qe.streamConns.Get(ctx, qre.setting) + switch err { case nil: - qre.logStats.WaitingForConnection += time.Since(start) return conn, nil case connpool.ErrConnPoolClosed: return nil, err @@ -875,6 +883,9 @@ func (qre *QueryExecutor) execCallProc() (*sqltypes.Result, error) { } qr, err := qre.execDBConn(conn.Conn, sql, true) + if errors.Is(err, mysql.ErrExecuteFetchMultipleResults) { + return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "Multi-Resultset not supported in stored procedure") + } if err != nil { return nil, rewriteOUTParamError(err) } @@ -948,6 +959,10 @@ func (qre *QueryExecutor) execAlterMigration() (*sqltypes.Result, error) { return qre.tsv.onlineDDLExecutor.UnthrottleMigration(qre.ctx, alterMigration.UUID) case sqlparser.UnthrottleAllMigrationType: return qre.tsv.onlineDDLExecutor.UnthrottleAllMigrations(qre.ctx) + case sqlparser.ForceCutOverMigrationType: + return qre.tsv.onlineDDLExecutor.ForceCutOverMigration(qre.ctx, alterMigration.UUID) + case sqlparser.ForceCutOverAllMigrationType: + return qre.tsv.onlineDDLExecutor.ForceCutOverPendingMigrations(qre.ctx) } return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "ALTER VITESS_MIGRATION not implemented") } @@ -1139,7 +1154,7 @@ func (qre *QueryExecutor) GetSchemaDefinitions(tableType querypb.SchemaTableType } func (qre *QueryExecutor) getViewDefinitions(viewNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { - query, err := eschema.GetFetchViewQuery(viewNames) + query, err := eschema.GetFetchViewQuery(viewNames, qre.tsv.env.Parser()) if err != nil { return err } @@ -1147,7 +1162,7 @@ func (qre *QueryExecutor) getViewDefinitions(viewNames []string, callback func(s } func (qre *QueryExecutor) getTableDefinitions(tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { - query, err := eschema.GetFetchTableQuery(tableNames) + query, err := eschema.GetFetchTableQuery(tableNames, qre.tsv.env.Parser()) if err != nil { return err } @@ -1155,7 +1170,7 @@ func (qre *QueryExecutor) getTableDefinitions(tableNames []string, callback func } func (qre *QueryExecutor) getAllDefinitions(tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) error { - query, err := eschema.GetFetchTableAndViewsQuery(tableNames) + query, err := eschema.GetFetchTableAndViewsQuery(tableNames, qre.tsv.env.Parser()) if err != nil { return err } @@ -1172,7 +1187,12 @@ func (qre *QueryExecutor) executeGetSchemaQuery(query string, callback func(sche return qre.execStreamSQL(conn, false /* isTransaction */, query, func(result *sqltypes.Result) error { schemaDef := make(map[string]string) for _, row := range result.Rows { - schemaDef[row[0].ToString()] = row[1].ToString() + tableName := row[0].ToString() + // Schema RPC should ignore the internal table in the response. + if schema.IsInternalOperationTableName(tableName) { + continue + } + schemaDef[tableName] = row[1].ToString() } return callback(&querypb.GetSchemaResponse{TableDefinition: schemaDef}) }) diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index d4058df8ad2..3466a55133d 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -28,6 +28,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" @@ -71,7 +74,7 @@ func TestQueryExecutorPlans(t *testing.T) { input string // passThrough specifies if planbuilder.PassthroughDML must be set. passThrough bool - // dbResponses specifes the list of queries and responses to add to the fake db. + // dbResponses specifies the list of queries and responses to add to the fake db. dbResponses []dbResponse // resultWant is the result we want. resultWant *sqltypes.Result @@ -1434,6 +1437,44 @@ func TestQueryExecutorShouldConsolidate(t *testing.T) { } } +func TestGetConnectionLogStats(t *testing.T) { + db := setUpQueryExecutorTest(t) + defer db.Close() + + ctx := context.Background() + tsv := newTestTabletServer(ctx, noFlags, db) + input := "select * from test_table limit 1" + + // getConn() happy path + qre := newTestQueryExecutor(ctx, tsv, input, 0) + conn, err := qre.getConn() + assert.NoError(t, err) + assert.NotNil(t, conn) + assert.True(t, qre.logStats.WaitingForConnection > 0) + + // getStreamConn() happy path + qre = newTestQueryExecutor(ctx, tsv, input, 0) + conn, err = qre.getStreamConn() + assert.NoError(t, err) + assert.NotNil(t, conn) + assert.True(t, qre.logStats.WaitingForConnection > 0) + + // Close the db connection to induce connection errors + db.Close() + + // getConn() error path + qre = newTestQueryExecutor(ctx, tsv, input, 0) + _, err = qre.getConn() + assert.Error(t, err) + assert.True(t, qre.logStats.WaitingForConnection > 0) + + // getStreamConn() error path + qre = newTestQueryExecutor(ctx, tsv, input, 0) + _, err = qre.getStreamConn() + assert.Error(t, err) + assert.True(t, qre.logStats.WaitingForConnection > 0) +} + type executorFlags int64 const ( @@ -1449,48 +1490,48 @@ const ( // newTestQueryExecutor uses a package level variable testTabletServer defined in tabletserver_test.go func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb.DB) *TabletServer { - config := tabletenv.NewDefaultConfig() - config.OltpReadPool.Size = 100 + cfg := tabletenv.NewDefaultConfig() + cfg.OltpReadPool.Size = 100 if flags&smallTxPool > 0 { - config.TxPool.Size = 3 + cfg.TxPool.Size = 3 } else { - config.TxPool.Size = 100 + cfg.TxPool.Size = 100 } if flags&enableStrictTableACL > 0 { - config.StrictTableACL = true + cfg.StrictTableACL = true } else { - config.StrictTableACL = false + cfg.StrictTableACL = false } if flags&noTwopc > 0 { - config.TwoPCEnable = false + cfg.TwoPCEnable = false } else { - config.TwoPCEnable = true + cfg.TwoPCEnable = true } if flags&disableOnlineDDL > 0 { - config.EnableOnlineDDL = false + cfg.EnableOnlineDDL = false } else { - config.EnableOnlineDDL = true + cfg.EnableOnlineDDL = true } - config.TwoPCCoordinatorAddress = "fake" + cfg.TwoPCCoordinatorAddress = "fake" if flags&shortTwopcAge > 0 { - config.TwoPCAbandonAge = 0.5 + cfg.TwoPCAbandonAge = 0.5 } else { - config.TwoPCAbandonAge = 10 + cfg.TwoPCAbandonAge = 10 } if flags&smallResultSize > 0 { - config.Oltp.MaxRows = 2 + cfg.Oltp.MaxRows = 2 } if flags&enableConsolidator > 0 { - config.Consolidator = tabletenv.Enable + cfg.Consolidator = tabletenv.Enable } else { - config.Consolidator = tabletenv.Disable + cfg.Consolidator = tabletenv.Disable } dbconfigs := newDBConfigs(db) - config.DB = dbconfigs - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg.DB = dbconfigs + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} err := tsv.StartService(target, dbconfigs, nil /* mysqld */) - if config.TwoPCEnable { + if cfg.TwoPCEnable { tsv.TwoPCEngineWait() } if err != nil { @@ -1565,7 +1606,7 @@ func initQueryExecutorTestDB(db *fakesqldb.DB) { "varchar|int64"), "Innodb_rows_read|0", )) - sidecardb.AddSchemaInitQueries(db, true) + sidecardb.AddSchemaInitQueries(db, true, sqlparser.NewTestParser()) } func getTestTableFields() []*querypb.Field { @@ -1658,7 +1699,7 @@ func addQueryExecutorSupportedQueries(db *fakesqldb.DB) { fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, } - sidecardb.AddSchemaInitQueries(db, true) + sidecardb.AddSchemaInitQueries(db, true, sqlparser.NewTestParser()) for query, result := range queryResultMap { db.AddQuery(query, result) } @@ -1729,7 +1770,7 @@ func TestQueryExecSchemaReloadCount(t *testing.T) { testcases := []struct { // input is the input query. input string - // dbResponses specifes the list of queries and responses to add to the fake db. + // dbResponses specifies the list of queries and responses to add to the fake db. dbResponses []dbResponse schemaReloadCount int }{{ diff --git a/go/vt/vttablet/tabletserver/query_list.go b/go/vt/vttablet/tabletserver/query_list.go index efe63ab0a8e..a41f23b6aa0 100644 --- a/go/vt/vttablet/tabletserver/query_list.go +++ b/go/vt/vttablet/tabletserver/query_list.go @@ -57,13 +57,16 @@ type QueryList struct { // so have to maintain a list to compare with the actual connection. // and remove appropriately. queryDetails map[int64][]*QueryDetail + + parser *sqlparser.Parser } // NewQueryList creates a new QueryList -func NewQueryList(name string) *QueryList { +func NewQueryList(name string, parser *sqlparser.Parser) *QueryList { return &QueryList{ name: name, queryDetails: make(map[int64][]*QueryDetail), + parser: parser, } } @@ -150,7 +153,7 @@ func (ql *QueryList) AppendQueryzRows(rows []QueryDetailzRow) []QueryDetailzRow for _, qd := range qds { query := qd.conn.Current() if streamlog.GetRedactDebugUIQueries() { - query, _ = sqlparser.RedactSQLQuery(query) + query, _ = ql.parser.RedactSQLQuery(query) } row := QueryDetailzRow{ Type: ql.name, diff --git a/go/vt/vttablet/tabletserver/query_list_test.go b/go/vt/vttablet/tabletserver/query_list_test.go index 02b24d86cda..57b672a16e0 100644 --- a/go/vt/vttablet/tabletserver/query_list_test.go +++ b/go/vt/vttablet/tabletserver/query_list_test.go @@ -22,6 +22,8 @@ import ( "time" "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/sqlparser" ) type testConn struct { @@ -44,7 +46,7 @@ func (tc *testConn) IsKilled() bool { } func TestQueryList(t *testing.T) { - ql := NewQueryList("test") + ql := NewQueryList("test", sqlparser.NewTestParser()) connID := int64(1) qd := NewQueryDetail(context.Background(), &testConn{id: connID}) ql.Add(qd) @@ -69,7 +71,7 @@ func TestQueryList(t *testing.T) { } func TestQueryListChangeConnIDInMiddle(t *testing.T) { - ql := NewQueryList("test") + ql := NewQueryList("test", sqlparser.NewTestParser()) connID := int64(1) qd1 := NewQueryDetail(context.Background(), &testConn{id: connID}) ql.Add(qd1) diff --git a/go/vt/vttablet/tabletserver/querylogz.go b/go/vt/vttablet/tabletserver/querylogz.go index 41a40a0720c..33341d1641b 100644 --- a/go/vt/vttablet/tabletserver/querylogz.go +++ b/go/vt/vttablet/tabletserver/querylogz.go @@ -26,7 +26,6 @@ import ( "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -57,10 +56,9 @@ var ( `) querylogzFuncMap = template.FuncMap{ - "stampMicro": func(t time.Time) string { return t.Format(time.StampMicro) }, - "cssWrappable": logz.Wrappable, - "truncateQuery": sqlparser.TruncateForUI, - "unquote": func(s string) string { return strings.Trim(s, "\"") }, + "stampMicro": func(t time.Time) string { return t.Format(time.StampMicro) }, + "cssWrappable": logz.Wrappable, + "unquote": func(s string) string { return strings.Trim(s, "\"") }, } querylogzTmpl = template.Must(template.New("example").Funcs(querylogzFuncMap).Parse(` @@ -74,7 +72,7 @@ var ( - + @@ -86,17 +84,9 @@ var ( `)) ) -func init() { - servenv.HTTPHandleFunc("/querylogz", func(w http.ResponseWriter, r *http.Request) { - ch := tabletenv.StatsLogger.Subscribe("querylogz") - defer tabletenv.StatsLogger.Unsubscribe(ch) - querylogzHandler(ch, w, r) - }) -} - // querylogzHandler serves a human readable snapshot of the // current query log. -func querylogzHandler(ch chan *tabletenv.LogStats, w http.ResponseWriter, r *http.Request) { +func querylogzHandler(ch chan *tabletenv.LogStats, w http.ResponseWriter, r *http.Request, parser *sqlparser.Parser) { if err := acl.CheckAccessHTTP(r, acl.DEBUGGING); err != nil { acl.SendError(w, err) return @@ -127,7 +117,8 @@ func querylogzHandler(ch chan *tabletenv.LogStats, w http.ResponseWriter, r *htt tmplData := struct { *tabletenv.LogStats ColorLevel string - }{stats, level} + Parser *sqlparser.Parser + }{stats, level, parser} if err := querylogzTmpl.Execute(w, tmplData); err != nil { log.Errorf("querylogz: couldn't execute template: %v", err) } diff --git a/go/vt/vttablet/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go index 2e5caa3891b..25f03c762c7 100644 --- a/go/vt/vttablet/tabletserver/querylogz_test.go +++ b/go/vt/vttablet/tabletserver/querylogz_test.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -76,7 +77,7 @@ func TestQuerylogzHandler(t *testing.T) { response := httptest.NewRecorder() ch := make(chan *tabletenv.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ := io.ReadAll(response.Body) checkQuerylogzHasStats(t, fastQueryPattern, logStats, body) @@ -107,7 +108,7 @@ func TestQuerylogzHandler(t *testing.T) { response = httptest.NewRecorder() ch = make(chan *tabletenv.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, mediumQueryPattern, logStats, body) @@ -137,7 +138,7 @@ func TestQuerylogzHandler(t *testing.T) { logStats.EndTime = logStats.StartTime.Add(500 * time.Millisecond) ch = make(chan *tabletenv.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, slowQueryPattern, logStats, body) @@ -147,7 +148,7 @@ func TestQuerylogzHandler(t *testing.T) { defer func() { streamlog.SetQueryLogFilterTag("") }() ch = make(chan *tabletenv.LogStats, 1) ch <- logStats - querylogzHandler(ch, response, req) + querylogzHandler(ch, response, req, sqlparser.NewTestParser()) close(ch) body, _ = io.ReadAll(response.Body) checkQuerylogzHasStats(t, slowQueryPattern, logStats, body) diff --git a/go/vt/vttablet/tabletserver/queryz.go b/go/vt/vttablet/tabletserver/queryz.go index 151f028ca09..5d674b260cf 100644 --- a/go/vt/vttablet/tabletserver/queryz.go +++ b/go/vt/vttablet/tabletserver/queryz.go @@ -27,7 +27,6 @@ import ( "vitess.io/vitess/go/acl" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logz" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" ) @@ -157,7 +156,7 @@ func queryzHandler(qe *QueryEngine, w http.ResponseWriter, r *http.Request) { return true } Value := &queryzRow{ - Query: logz.Wrappable(sqlparser.TruncateForUI(plan.Original)), + Query: logz.Wrappable(qe.env.Environment().Parser().TruncateForUI(plan.Original)), Table: plan.TableName().String(), Plan: plan.PlanID, } diff --git a/go/vt/vttablet/tabletserver/repltracker/reader.go b/go/vt/vttablet/tabletserver/repltracker/reader.go index fe469bb2e31..694778d1119 100644 --- a/go/vt/vttablet/tabletserver/repltracker/reader.go +++ b/go/vt/vttablet/tabletserver/repltracker/reader.go @@ -71,7 +71,7 @@ func newHeartbeatReader(env tabletenv.Env) *heartbeatReader { return &heartbeatReader{} } - heartbeatInterval := config.ReplicationTracker.HeartbeatIntervalSeconds.Get() + heartbeatInterval := config.ReplicationTracker.HeartbeatInterval return &heartbeatReader{ env: env, enabled: true, @@ -80,8 +80,8 @@ func newHeartbeatReader(env tabletenv.Env) *heartbeatReader { ticks: timer.NewTimer(heartbeatInterval), errorLog: logutil.NewThrottledLogger("HeartbeatReporter", 60*time.Second), pool: connpool.NewPool(env, "HeartbeatReadPool", tabletenv.ConnPoolConfig{ - Size: 1, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: 1, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }), } } diff --git a/go/vt/vttablet/tabletserver/repltracker/reader_test.go b/go/vt/vttablet/tabletserver/repltracker/reader_test.go index 54ece70fc1a..e065b05da7a 100644 --- a/go/vt/vttablet/tabletserver/repltracker/reader_test.go +++ b/go/vt/vttablet/tabletserver/repltracker/reader_test.go @@ -21,14 +21,14 @@ import ( "testing" "time" - "vitess.io/vitess/go/test/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" @@ -137,15 +137,15 @@ func TestReaderReadHeartbeatError(t *testing.T) { } func newReader(db *fakesqldb.DB, frozenTime *time.Time) *heartbeatReader { - config := tabletenv.NewDefaultConfig() - config.ReplicationTracker.Mode = tabletenv.Heartbeat - _ = config.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") - params, _ := db.ConnParams().MysqlParams() + cfg := tabletenv.NewDefaultConfig() + cfg.ReplicationTracker.Mode = tabletenv.Heartbeat + cfg.ReplicationTracker.HeartbeatInterval = time.Second + params := db.ConnParams() cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - config.DB = dbc + cfg.DB = dbc - tr := newHeartbeatReader(tabletenv.NewEnv(config, "ReaderTest")) + tr := newHeartbeatReader(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "ReaderTest")) tr.keyspaceShard = "test:0" if frozenTime != nil { diff --git a/go/vt/vttablet/tabletserver/repltracker/repltracker.go b/go/vt/vttablet/tabletserver/repltracker/repltracker.go index 5ab44eb774e..6f504b2a445 100644 --- a/go/vt/vttablet/tabletserver/repltracker/repltracker.go +++ b/go/vt/vttablet/tabletserver/repltracker/repltracker.go @@ -66,7 +66,7 @@ type ReplTracker struct { func NewReplTracker(env tabletenv.Env, alias *topodatapb.TabletAlias) *ReplTracker { return &ReplTracker{ mode: env.Config().ReplicationTracker.Mode, - forceHeartbeat: env.Config().ReplicationTracker.HeartbeatOnDemandSeconds.Get() > 0, + forceHeartbeat: env.Config().ReplicationTracker.HeartbeatOnDemand > 0, hw: newHeartbeatWriter(env, alias), hr: newHeartbeatReader(env), poller: &poller{}, diff --git a/go/vt/vttablet/tabletserver/repltracker/repltracker_test.go b/go/vt/vttablet/tabletserver/repltracker/repltracker_test.go index 01912c3f689..5e6150ddeb3 100644 --- a/go/vt/vttablet/tabletserver/repltracker/repltracker_test.go +++ b/go/vt/vttablet/tabletserver/repltracker/repltracker_test.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/vt/mysqlctl" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -35,13 +36,13 @@ func TestReplTracker(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - config := tabletenv.NewDefaultConfig() - config.ReplicationTracker.Mode = tabletenv.Heartbeat - _ = config.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") - params, _ := db.ConnParams().MysqlParams() + cfg := tabletenv.NewDefaultConfig() + cfg.ReplicationTracker.Mode = tabletenv.Heartbeat + cfg.ReplicationTracker.HeartbeatInterval = time.Second + params := db.ConnParams() cp := *params - config.DB = dbconfigs.NewTestDBConfigs(cp, cp, "") - env := tabletenv.NewEnv(config, "ReplTrackerTest") + cfg.DB = dbconfigs.NewTestDBConfigs(cp, cp, "") + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "ReplTrackerTest") alias := &topodatapb.TabletAlias{ Cell: "cell", Uid: 1, @@ -78,7 +79,7 @@ func TestReplTracker(t *testing.T) { assert.False(t, rt.hw.isOpen) assert.False(t, rt.hr.isOpen) - config.ReplicationTracker.Mode = tabletenv.Polling + cfg.ReplicationTracker.Mode = tabletenv.Polling rt = NewReplTracker(env, alias) rt.InitDBConfig(target, mysqld) assert.Equal(t, tabletenv.Polling, rt.mode) diff --git a/go/vt/vttablet/tabletserver/repltracker/writer.go b/go/vt/vttablet/tabletserver/repltracker/writer.go index b13b78b59b7..a72b44d1845 100644 --- a/go/vt/vttablet/tabletserver/repltracker/writer.go +++ b/go/vt/vttablet/tabletserver/repltracker/writer.go @@ -59,6 +59,7 @@ type heartbeatWriter struct { appPool *dbconnpool.ConnectionPool allPrivsPool *dbconnpool.ConnectionPool ticks *timer.Timer + writeConnID atomic.Int64 onDemandDuration time.Duration onDemandMu sync.Mutex @@ -72,17 +73,17 @@ func newHeartbeatWriter(env tabletenv.Env, alias *topodatapb.TabletAlias) *heart config := env.Config() // config.EnableLagThrottler is a feature flag for the throttler; if throttler runs, then heartbeat must also run - if config.ReplicationTracker.Mode != tabletenv.Heartbeat && config.ReplicationTracker.HeartbeatOnDemandSeconds.Get() == 0 { + if config.ReplicationTracker.Mode != tabletenv.Heartbeat && config.ReplicationTracker.HeartbeatOnDemand == 0 { return &heartbeatWriter{} } - heartbeatInterval := config.ReplicationTracker.HeartbeatIntervalSeconds.Get() + heartbeatInterval := config.ReplicationTracker.HeartbeatInterval w := &heartbeatWriter{ env: env, enabled: true, tabletAlias: alias.CloneVT(), now: time.Now, interval: heartbeatInterval, - onDemandDuration: config.ReplicationTracker.HeartbeatOnDemandSeconds.Get(), + onDemandDuration: config.ReplicationTracker.HeartbeatOnDemand, ticks: timer.NewTimer(heartbeatInterval), errorLog: logutil.NewThrottledLogger("HeartbeatWriter", 60*time.Second), // We make this pool size 2; to prevent pool exhausted @@ -90,9 +91,10 @@ func newHeartbeatWriter(env tabletenv.Env, alias *topodatapb.TabletAlias) *heart appPool: dbconnpool.NewConnectionPool("HeartbeatWriteAppPool", env.Exporter(), 2, mysqlctl.DbaIdleTimeout, 0, mysqlctl.PoolDynamicHostnameResolution), allPrivsPool: dbconnpool.NewConnectionPool("HeartbeatWriteAllPrivsPool", env.Exporter(), 2, mysqlctl.DbaIdleTimeout, 0, mysqlctl.PoolDynamicHostnameResolution), } + w.writeConnID.Store(-1) if w.onDemandDuration > 0 { // see RequestHeartbeats() for use of onDemandRequestTicks - // it's basically a mechnism to rate limit operation RequestHeartbeats(). + // it's basically a mechanism to rate limit operation RequestHeartbeats(). // and selectively drop excessive requests. w.allowNextHeartbeatRequest() go func() { @@ -123,7 +125,7 @@ func (w *heartbeatWriter) Open() { if w.isOpen { return } - log.Info("Hearbeat Writer: opening") + log.Info("Heartbeat Writer: opening") // We cannot create the database and tables in this Open function // since, this is run when a tablet changes to Primary type. The other replicas @@ -159,7 +161,7 @@ func (w *heartbeatWriter) Close() { w.appPool.Close() w.allPrivsPool.Close() w.isOpen = false - log.Info("Hearbeat Writer: closed") + log.Info("Heartbeat Writer: closed") } // bindHeartbeatVars takes a heartbeat write (insert or update) and @@ -192,11 +194,6 @@ func (w *heartbeatWriter) write() error { defer w.env.LogError() ctx, cancel := context.WithDeadline(context.Background(), w.now().Add(w.interval)) defer cancel() - allPrivsConn, err := w.allPrivsPool.Get(ctx) - if err != nil { - return err - } - defer allPrivsConn.Recycle() upsert, err := w.bindHeartbeatVars(sqlUpsertHeartbeat) if err != nil { @@ -207,6 +204,8 @@ func (w *heartbeatWriter) write() error { return err } defer appConn.Recycle() + w.writeConnID.Store(appConn.Conn.ID()) + defer w.writeConnID.Store(-1) _, err = appConn.Conn.ExecuteFetch(upsert, 1, false) if err != nil { return err @@ -215,11 +214,14 @@ func (w *heartbeatWriter) write() error { } func (w *heartbeatWriter) recordError(err error) { + if err == nil { + return + } w.errorLog.Errorf("%v", err) writeErrors.Add(1) } -// enableWrites actives or deactives heartbeat writes +// enableWrites activates or deactivates heartbeat writes func (w *heartbeatWriter) enableWrites(enable bool) { if w.ticks == nil { return @@ -238,7 +240,17 @@ func (w *heartbeatWriter) enableWrites(enable bool) { w.ticks.Start(w.writeHeartbeat) }() case false: - w.ticks.Stop() + // We stop the ticks in a separate go routine because it can block if the write is stuck on semi-sync ACKs. + // At the same time we try and kill the write that is in progress. We use the context and its cancellation + // for coordination between the two go-routines. In the end we will have guaranteed that the ticks have stopped + // and no write is in progress. + ctx, cancel := context.WithCancel(context.Background()) + go func() { + w.ticks.Stop() + cancel() + }() + w.killWritesUntilStopped(ctx) + if w.onDemandDuration > 0 { // Let the next RequestHeartbeats() go through w.allowNextHeartbeatRequest() @@ -246,6 +258,45 @@ func (w *heartbeatWriter) enableWrites(enable bool) { } } +// killWritesUntilStopped tries to kill the write in progress until the ticks have stopped. +func (w *heartbeatWriter) killWritesUntilStopped(ctx context.Context) { + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + for { + // Actually try to kill the query. + err := w.killWrite() + w.recordError(err) + select { + case <-ctx.Done(): + // If the context has been cancelled, then we know that the ticks have stopped. + // This guarantees that there are no writes in progress, so there is nothing to kill. + return + case <-ticker.C: + } + } +} + +// killWrite kills the write in progress (if any). +func (w *heartbeatWriter) killWrite() error { + defer w.env.LogError() + writeId := w.writeConnID.Load() + if writeId == -1 { + return nil + } + + ctx, cancel := context.WithDeadline(context.Background(), w.now().Add(w.interval)) + defer cancel() + killConn, err := w.allPrivsPool.Get(ctx) + if err != nil { + log.Errorf("Kill conn didn't get connection :(") + return err + } + defer killConn.Recycle() + + _, err = killConn.Conn.ExecuteFetch(fmt.Sprintf("kill %d", writeId), 1, false) + return err +} + // allowNextHeartbeatRequest ensures that the next call to RequestHeartbeats() passes through and // is not dropped. func (w *heartbeatWriter) allowNextHeartbeatRequest() { diff --git a/go/vt/vttablet/tabletserver/repltracker/writer_test.go b/go/vt/vttablet/tabletserver/repltracker/writer_test.go index 5044586c0d2..0add32a1de0 100644 --- a/go/vt/vttablet/tabletserver/repltracker/writer_test.go +++ b/go/vt/vttablet/tabletserver/repltracker/writer_test.go @@ -17,7 +17,9 @@ limitations under the License. package repltracker import ( + "context" "fmt" + "sync" "testing" "time" @@ -27,6 +29,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -63,16 +66,58 @@ func TestWriteHeartbeatError(t *testing.T) { assert.Equal(t, int64(1), writeErrors.Get()) } +// TestCloseWhileStuckWriting tests that Close shouldn't get stuck even if the heartbeat writer is stuck waiting for a semi-sync ACK. +func TestCloseWhileStuckWriting(t *testing.T) { + db := fakesqldb.New(t) + tw := newTestWriter(db, nil) + tw.isOpen = true + + killWg := sync.WaitGroup{} + killWg.Add(1) + startedWaitWg := sync.WaitGroup{} + startedWaitWg.Add(1) + + // Insert a query pattern that causes the upsert to block indefinitely until it has been killed. + // This simulates a stuck primary write due to a semi-sync ACK requirement. + db.AddQueryPatternWithCallback(`INSERT INTO .*heartbeat \(ts, tabletUid, keyspaceShard\).*`, &sqltypes.Result{}, func(s string) { + startedWaitWg.Done() + killWg.Wait() + }) + + // When we receive a kill query, we want to finish running the wait group to unblock the upsert query. + db.AddQueryPatternWithCallback("kill.*", &sqltypes.Result{}, func(s string) { + killWg.Done() + }) + + // Now we enable writes, but the first write will get blocked. + tw.enableWrites(true) + // We wait until the write has blocked to ensure we only call Close after we are stuck writing. + startedWaitWg.Wait() + // Even if the write is blocked, we should be able to disable writes without waiting indefinitely. + // This is what we call, when we try to Close the heartbeat writer. + ctx, cancel := context.WithCancel(context.Background()) + go func() { + tw.enableWrites(false) + cancel() + }() + select { + case <-ctx.Done(): + db.Close() + case <-time.After(1000 * time.Second): + t.Fatalf("Timed out waiting for heartbeat writer to close") + } +} + func newTestWriter(db *fakesqldb.DB, frozenTime *time.Time) *heartbeatWriter { - config := tabletenv.NewDefaultConfig() - config.ReplicationTracker.Mode = tabletenv.Heartbeat - _ = config.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") + cfg := tabletenv.NewDefaultConfig() + cfg.ReplicationTracker.Mode = tabletenv.Heartbeat + cfg.ReplicationTracker.HeartbeatInterval = time.Second - params, _ := db.ConnParams().MysqlParams() + params := db.ConnParams() cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - tw := newHeartbeatWriter(tabletenv.NewEnv(config, "WriterTest"), &topodatapb.TabletAlias{Cell: "test", Uid: 1111}) + tw := newHeartbeatWriter(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "WriterTest"), &topodatapb.TabletAlias{Cell: "test", Uid: 1111}) tw.keyspaceShard = "test:0" if frozenTime != nil { diff --git a/go/vt/vttablet/tabletserver/report.xml b/go/vt/vttablet/tabletserver/report.xml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/go/vt/vttablet/tabletserver/rules/cached_size.go b/go/vt/vttablet/tabletserver/rules/cached_size.go index acfd199f1f2..1375ef2cb7b 100644 --- a/go/vt/vttablet/tabletserver/rules/cached_size.go +++ b/go/vt/vttablet/tabletserver/rules/cached_size.go @@ -108,7 +108,7 @@ func (cached *bvcre) CachedSize(alloc bool) int64 { } // field re *regexp.Regexp if cached.re != nil { - size += hack.RuntimeAllocSize(int64(153)) + size += hack.RuntimeAllocSize(int64(160)) } return size } @@ -124,7 +124,7 @@ func (cached *namedRegexp) CachedSize(alloc bool) int64 { size += hack.RuntimeAllocSize(int64(len(cached.name))) // field Regexp *regexp.Regexp if cached.Regexp != nil { - size += hack.RuntimeAllocSize(int64(153)) + size += hack.RuntimeAllocSize(int64(160)) } return size } diff --git a/go/vt/vttablet/tabletserver/rules/map_test.go b/go/vt/vttablet/tabletserver/rules/map_test.go index 1e86b938a48..bd1030f119c 100644 --- a/go/vt/vttablet/tabletserver/rules/map_test.go +++ b/go/vt/vttablet/tabletserver/rules/map_test.go @@ -136,7 +136,7 @@ func TestMapGetSetQueryRules(t *testing.T) { t.Errorf("Failed to set custom Rules: %s", err) } - // Test if we can successfully retrieve rules that've been set + // Test if we can successfully retrieve rules which been set qrs, err = qri.Get(denyListQueryRules) if err != nil { t.Errorf("GetRules failed to retrieve denyListQueryRules that has been set: %s", err) diff --git a/go/vt/vttablet/tabletserver/rules/rules.go b/go/vt/vttablet/tabletserver/rules/rules.go index efbfcdf87e4..4a7d128b950 100644 --- a/go/vt/vttablet/tabletserver/rules/rules.go +++ b/go/vt/vttablet/tabletserver/rules/rules.go @@ -27,15 +27,14 @@ import ( "time" "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -//----------------------------------------------- +// ----------------------------------------------- const ( bufferedTableRuleName = "buffered_table" @@ -189,7 +188,7 @@ func (qrs *Rules) GetAction( return QRContinue, nil, 0, "" } -//----------------------------------------------- +// ----------------------------------------------- // Rule represents one rule (conditions-action). // Name is meant to uniquely identify a rule. @@ -561,10 +560,10 @@ func bvMatch(bvcond BindVarCond, bindVars map[string]*querypb.BindVariable) bool return bvcond.value.eval(bv, bvcond.op, bvcond.onMismatch) } -//----------------------------------------------- +// ----------------------------------------------- // Support types for Rule -// Action speficies the list of actions to perform +// Action specifies the list of actions to perform // when a Rule is triggered. type Action int @@ -656,7 +655,7 @@ func init() { } } -// These are return statii. +// These are return states. const ( QROK = iota QRMismatch @@ -852,13 +851,13 @@ func getint64(val *querypb.BindVariable) (iv int64, status int) { // TODO(sougou): this is inefficient. Optimize to use []byte. func getstring(val *querypb.BindVariable) (s string, status int) { - if sqltypes.IsIntegral(val.Type) || sqltypes.IsFloat(val.Type) || sqltypes.IsText(val.Type) || sqltypes.IsBinary(val.Type) { + if sqltypes.IsIntegral(val.Type) || sqltypes.IsFloat(val.Type) || sqltypes.IsTextOrBinary(val.Type) { return string(val.Value), QROK } return "", QRMismatch } -//----------------------------------------------- +// ----------------------------------------------- // Support functions for JSON // MapStrOperator maps a string representation to an Operator. diff --git a/go/vt/vttablet/tabletserver/schema/db.go b/go/vt/vttablet/tabletserver/schema/db.go index 5699ffc1bde..4bea80c4010 100644 --- a/go/vt/vttablet/tabletserver/schema/db.go +++ b/go/vt/vttablet/tabletserver/schema/db.go @@ -89,7 +89,7 @@ where table_schema = database() and table_name in ::viewNames` ) // reloadTablesDataInDB reloads teh tables information we have stored in our database we use for schema-tracking. -func reloadTablesDataInDB(ctx context.Context, conn *connpool.Conn, tables []*Table, droppedTables []string) error { +func reloadTablesDataInDB(ctx context.Context, conn *connpool.Conn, tables []*Table, droppedTables []string, parser *sqlparser.Parser) error { // No need to do anything if we have no tables to refresh or drop. if len(tables) == 0 && len(droppedTables) == 0 { return nil @@ -117,7 +117,7 @@ func reloadTablesDataInDB(ctx context.Context, conn *connpool.Conn, tables []*Ta } // Generate the queries to delete and insert table data. - clearTableParsedQuery, err := generateFullQuery(deleteFromSchemaEngineTablesTable) + clearTableParsedQuery, err := generateFullQuery(deleteFromSchemaEngineTablesTable, parser) if err != nil { return err } @@ -126,7 +126,7 @@ func reloadTablesDataInDB(ctx context.Context, conn *connpool.Conn, tables []*Ta return err } - insertTablesParsedQuery, err := generateFullQuery(insertTableIntoSchemaEngineTables) + insertTablesParsedQuery, err := generateFullQuery(insertTableIntoSchemaEngineTables, parser) if err != nil { return err } @@ -162,8 +162,8 @@ func reloadTablesDataInDB(ctx context.Context, conn *connpool.Conn, tables []*Ta } // generateFullQuery generates the full query from the query as a string. -func generateFullQuery(query string) (*sqlparser.ParsedQuery, error) { - stmt, err := sqlparser.Parse( +func generateFullQuery(query string, parser *sqlparser.Parser) (*sqlparser.ParsedQuery, error) { + stmt, err := parser.Parse( sqlparser.BuildParsedQuery(query, sidecar.GetIdentifier(), sidecar.GetIdentifier()).Query) if err != nil { return nil, err @@ -174,7 +174,7 @@ func generateFullQuery(query string) (*sqlparser.ParsedQuery, error) { } // reloadViewsDataInDB reloads teh views information we have stored in our database we use for schema-tracking. -func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Table, droppedViews []string) error { +func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Table, droppedViews []string, parser *sqlparser.Parser) error { // No need to do anything if we have no views to refresh or drop. if len(views) == 0 && len(droppedViews) == 0 { return nil @@ -213,7 +213,7 @@ func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Tabl return nil }, func() *sqltypes.Result { return &sqltypes.Result{} }, - 1000, + 1000, parser, ) if err != nil { return err @@ -221,7 +221,7 @@ func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Tabl } // Generate the queries to delete and insert view data. - clearViewParsedQuery, err := generateFullQuery(deleteFromSchemaEngineViewsTable) + clearViewParsedQuery, err := generateFullQuery(deleteFromSchemaEngineViewsTable, parser) if err != nil { return err } @@ -230,7 +230,7 @@ func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Tabl return err } - insertViewsParsedQuery, err := generateFullQuery(insertViewIntoSchemaEngineViews) + insertViewsParsedQuery, err := generateFullQuery(insertViewIntoSchemaEngineViews, parser) if err != nil { return err } @@ -266,8 +266,8 @@ func reloadViewsDataInDB(ctx context.Context, conn *connpool.Conn, views []*Tabl } // getViewDefinition gets the viewDefinition for the given views. -func getViewDefinition(ctx context.Context, conn *connpool.Conn, bv map[string]*querypb.BindVariable, callback func(qr *sqltypes.Result) error, alloc func() *sqltypes.Result, bufferSize int) error { - viewsDefParsedQuery, err := generateFullQuery(fetchViewDefinitions) +func getViewDefinition(ctx context.Context, conn *connpool.Conn, bv map[string]*querypb.BindVariable, callback func(qr *sqltypes.Result) error, alloc func() *sqltypes.Result, bufferSize int, parser *sqlparser.Parser) error { + viewsDefParsedQuery, err := generateFullQuery(fetchViewDefinitions, parser) if err != nil { return err } @@ -358,7 +358,7 @@ func (se *Engine) getMismatchedTableNames(ctx context.Context, conn *connpool.Co } // reloadDataInDB reloads the schema tracking data in the database -func reloadDataInDB(ctx context.Context, conn *connpool.Conn, altered []*Table, created []*Table, dropped []*Table) error { +func reloadDataInDB(ctx context.Context, conn *connpool.Conn, altered []*Table, created []*Table, dropped []*Table, parser *sqlparser.Parser) error { // tablesToReload and viewsToReload stores the tables and views that need reloading and storing in our MySQL database. var tablesToReload, viewsToReload []*Table // droppedTables, droppedViews stores the list of tables and views we need to delete, respectively. @@ -382,19 +382,19 @@ func reloadDataInDB(ctx context.Context, conn *connpool.Conn, altered []*Table, } } - if err := reloadTablesDataInDB(ctx, conn, tablesToReload, droppedTables); err != nil { + if err := reloadTablesDataInDB(ctx, conn, tablesToReload, droppedTables, parser); err != nil { return err } - if err := reloadViewsDataInDB(ctx, conn, viewsToReload, droppedViews); err != nil { + if err := reloadViewsDataInDB(ctx, conn, viewsToReload, droppedViews, parser); err != nil { return err } return nil } // GetFetchViewQuery gets the fetch query to run for getting the listed views. If no views are provided, then all the views are fetched. -func GetFetchViewQuery(viewNames []string) (string, error) { +func GetFetchViewQuery(viewNames []string, parser *sqlparser.Parser) (string, error) { if len(viewNames) == 0 { - parsedQuery, err := generateFullQuery(fetchViews) + parsedQuery, err := generateFullQuery(fetchViews, parser) if err != nil { return "", err } @@ -407,7 +407,7 @@ func GetFetchViewQuery(viewNames []string) (string, error) { } bv := map[string]*querypb.BindVariable{"viewNames": viewsBV} - parsedQuery, err := generateFullQuery(fetchUpdatedViews) + parsedQuery, err := generateFullQuery(fetchUpdatedViews, parser) if err != nil { return "", err } @@ -415,9 +415,9 @@ func GetFetchViewQuery(viewNames []string) (string, error) { } // GetFetchTableQuery gets the fetch query to run for getting the listed tables. If no tables are provided, then all the tables are fetched. -func GetFetchTableQuery(tableNames []string) (string, error) { +func GetFetchTableQuery(tableNames []string, parser *sqlparser.Parser) (string, error) { if len(tableNames) == 0 { - parsedQuery, err := generateFullQuery(fetchTables) + parsedQuery, err := generateFullQuery(fetchTables, parser) if err != nil { return "", err } @@ -430,7 +430,7 @@ func GetFetchTableQuery(tableNames []string) (string, error) { } bv := map[string]*querypb.BindVariable{"tableNames": tablesBV} - parsedQuery, err := generateFullQuery(fetchUpdatedTables) + parsedQuery, err := generateFullQuery(fetchUpdatedTables, parser) if err != nil { return "", err } @@ -438,9 +438,9 @@ func GetFetchTableQuery(tableNames []string) (string, error) { } // GetFetchTableAndViewsQuery gets the fetch query to run for getting the listed tables and views. If no table names are provided, then all the tables and views are fetched. -func GetFetchTableAndViewsQuery(tableNames []string) (string, error) { +func GetFetchTableAndViewsQuery(tableNames []string, parser *sqlparser.Parser) (string, error) { if len(tableNames) == 0 { - parsedQuery, err := generateFullQuery(fetchTablesAndViews) + parsedQuery, err := generateFullQuery(fetchTablesAndViews, parser) if err != nil { return "", err } @@ -453,7 +453,7 @@ func GetFetchTableAndViewsQuery(tableNames []string) (string, error) { } bv := map[string]*querypb.BindVariable{"tableNames": tablesBV} - parsedQuery, err := generateFullQuery(fetchUpdatedTablesAndViews) + parsedQuery, err := generateFullQuery(fetchUpdatedTablesAndViews, parser) if err != nil { return "", err } diff --git a/go/vt/vttablet/tabletserver/schema/db_test.go b/go/vt/vttablet/tabletserver/schema/db_test.go index ac6999d309a..fec6469d4cf 100644 --- a/go/vt/vttablet/tabletserver/schema/db_test.go +++ b/go/vt/vttablet/tabletserver/schema/db_test.go @@ -23,15 +23,17 @@ import ( "testing" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "vitess.io/vitess/go/constants/sidecar" - "vitess.io/vitess/go/maps2" - "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) var ( @@ -81,7 +83,7 @@ func TestGenerateFullQuery(t *testing.T) { tt.wantQuery = tt.query } - got, err := generateFullQuery(tt.query) + got, err := generateFullQuery(tt.query, sqlparser.NewTestParser()) if tt.wantErr != "" { require.EqualError(t, err, tt.wantErr) return @@ -96,7 +98,8 @@ func TestGenerateFullQuery(t *testing.T) { func TestGetCreateStatement(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestGetCreateStatement") + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) // Success view @@ -131,7 +134,8 @@ func TestGetCreateStatement(t *testing.T) { func TestGetChangedViewNames(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestGetChangedViewNames") + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) // Success @@ -145,7 +149,7 @@ func TestGetChangedViewNames(t *testing.T) { got, err := getChangedViewNames(context.Background(), conn, true) require.NoError(t, err) require.Len(t, got, 3) - require.ElementsMatch(t, maps2.Keys(got), []string{"v1", "v2", "lead"}) + require.ElementsMatch(t, maps.Keys(got), []string{"v1", "v2", "lead"}) require.NoError(t, db.LastError()) // Not serving primary @@ -164,7 +168,8 @@ func TestGetChangedViewNames(t *testing.T) { func TestGetViewDefinition(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestGetViewDefinition") + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) viewsBV, err := sqltypes.BuildBindVariable([]string{"v1", "lead"}) @@ -181,7 +186,7 @@ func TestGetViewDefinition(t *testing.T) { got, err := collectGetViewDefinitions(conn, bv) require.NoError(t, err) require.Len(t, got, 2) - require.ElementsMatch(t, maps2.Keys(got), []string{"v1", "lead"}) + require.ElementsMatch(t, maps.Keys(got), []string{"v1", "lead"}) require.Equal(t, "create_view_v1", got["v1"]) require.Equal(t, "create_view_lead", got["lead"]) require.NoError(t, db.LastError()) @@ -209,7 +214,7 @@ func collectGetViewDefinitions(conn *connpool.Conn, bv map[string]*querypb.BindV return nil }, func() *sqltypes.Result { return &sqltypes.Result{} - }, 1000) + }, 1000, sqlparser.NewTestParser()) return viewDefinitions, err } @@ -226,7 +231,7 @@ func TestGetMismatchedTableNames(t *testing.T) { expectedError string }{ { - name: "Table create time differs", + name: "TableCreateTimeDiffers", tables: map[string]*Table{ "t1": { Name: sqlparser.NewIdentifierCS("t1"), @@ -239,7 +244,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: true, expectedTableNames: []string{"t1"}, }, { - name: "Table got deleted", + name: "TableGotDeleted", tables: map[string]*Table{ "t1": { Name: sqlparser.NewIdentifierCS("t1"), @@ -253,7 +258,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: true, expectedTableNames: []string{"t2"}, }, { - name: "Table got created", + name: "TableGotCreated", tables: map[string]*Table{ "t1": { Name: sqlparser.NewIdentifierCS("t1"), @@ -270,7 +275,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: true, expectedTableNames: []string{"t2"}, }, { - name: "Dual gets ignored", + name: "DualGetsIgnored", tables: map[string]*Table{ "dual": NewTable("dual", NoType), "t2": { @@ -284,7 +289,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: true, expectedTableNames: []string{}, }, { - name: "All problems", + name: "AllProblems", tables: map[string]*Table{ "dual": NewTable("dual", NoType), "t2": { @@ -304,7 +309,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: true, expectedTableNames: []string{"t1", "t2", "t3"}, }, { - name: "Not serving primary", + name: "NotServingPrimary", tables: map[string]*Table{ "t1": { Name: sqlparser.NewIdentifierCS("t1"), @@ -317,7 +322,7 @@ func TestGetMismatchedTableNames(t *testing.T) { isServingPrimary: false, expectedTableNames: []string{}, }, { - name: "Error in query", + name: "ErrorInQuery", tables: map[string]*Table{ "t1": { Name: sqlparser.NewIdentifierCS("t1"), @@ -336,7 +341,8 @@ func TestGetMismatchedTableNames(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tc.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) if tc.dbError != "" { @@ -351,7 +357,7 @@ func TestGetMismatchedTableNames(t *testing.T) { if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) } else { - require.ElementsMatch(t, maps2.Keys(mismatchedTableNames), tc.expectedTableNames) + require.ElementsMatch(t, maps.Keys(mismatchedTableNames), tc.expectedTableNames) require.NoError(t, db.LastError()) } }) @@ -370,7 +376,7 @@ func TestReloadTablesInDB(t *testing.T) { expectedError string }{ { - name: "Only tables to delete", + name: "OnlyTablesToDelete", tablesToDelete: []string{"t1", "lead"}, expectedQueries: map[string]*sqltypes.Result{ "begin": {}, @@ -379,7 +385,7 @@ func TestReloadTablesInDB(t *testing.T) { "delete from _vt.`tables` where table_schema = database() and table_name in ('t1', 'lead')": {}, }, }, { - name: "Only tables to reload", + name: "OnlyTablesToReload", tablesToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -404,7 +410,7 @@ func TestReloadTablesInDB(t *testing.T) { "insert into _vt.`tables`(table_schema, table_name, create_statement, create_time) values (database(), 'lead', 'create_table_lead', 1234)": {}, }, }, { - name: "Reload and Delete", + name: "ReloadAndDelete", tablesToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -430,7 +436,7 @@ func TestReloadTablesInDB(t *testing.T) { "insert into _vt.`tables`(table_schema, table_name, create_statement, create_time) values (database(), 'lead', 'create_table_lead', 1234)": {}, }, }, { - name: "Error In Insert", + name: "ErrorInInsert", tablesToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -456,7 +462,8 @@ func TestReloadTablesInDB(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tc.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) // Add queries with the expected results and errors. @@ -467,7 +474,7 @@ func TestReloadTablesInDB(t *testing.T) { db.AddRejectedQuery(query, errorToThrow) } - err = reloadTablesDataInDB(context.Background(), conn, tc.tablesToReload, tc.tablesToDelete) + err = reloadTablesDataInDB(context.Background(), conn, tc.tablesToReload, tc.tablesToDelete, sqlparser.NewTestParser()) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) return @@ -491,7 +498,7 @@ func TestReloadViewsInDB(t *testing.T) { expectedError string }{ { - name: "Only views to delete", + name: "OnlyViewsToDelete", viewsToDelete: []string{"v1", "lead"}, expectedQueries: map[string]*sqltypes.Result{ "begin": {}, @@ -500,7 +507,7 @@ func TestReloadViewsInDB(t *testing.T) { "delete from _vt.views where table_schema = database() and table_name in ('v1', 'lead')": {}, }, }, { - name: "Only views to reload", + name: "OnlyViewsToReload", viewsToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -529,7 +536,7 @@ func TestReloadViewsInDB(t *testing.T) { "insert into _vt.views(table_schema, table_name, create_statement, view_definition) values (database(), 'lead', 'create_view_lead', 'select_lead')": {}, }, }, { - name: "Reload and delete", + name: "ReloadAndDelete", viewsToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -559,7 +566,7 @@ func TestReloadViewsInDB(t *testing.T) { "insert into _vt.views(table_schema, table_name, create_statement, view_definition) values (database(), 'lead', 'create_view_lead', 'select_lead')": {}, }, }, { - name: "Error In Insert", + name: "ErrorInInsert", viewsToReload: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -588,7 +595,8 @@ func TestReloadViewsInDB(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tc.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) // Add queries with the expected results and errors. @@ -599,7 +607,7 @@ func TestReloadViewsInDB(t *testing.T) { db.AddRejectedQuery(query, errorToThrow) } - err = reloadViewsDataInDB(context.Background(), conn, tc.viewsToReload, tc.viewsToDelete) + err = reloadViewsDataInDB(context.Background(), conn, tc.viewsToReload, tc.viewsToDelete, sqlparser.NewTestParser()) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) return @@ -625,7 +633,7 @@ func TestReloadDataInDB(t *testing.T) { expectedError string }{ { - name: "Only views to delete", + name: "OnlyViewsToDelete", dropped: []*Table{ NewTable("v1", View), NewTable("lead", View), @@ -637,7 +645,7 @@ func TestReloadDataInDB(t *testing.T) { "delete from _vt.views where table_schema = database() and table_name in ('v1', 'lead')": {}, }, }, { - name: "Only views to reload", + name: "OnlyViewsToReload", created: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -669,7 +677,7 @@ func TestReloadDataInDB(t *testing.T) { "insert into _vt.views(table_schema, table_name, create_statement, view_definition) values (database(), 'lead', 'create_view_lead', 'select_lead')": {}, }, }, { - name: "Reload and delete views", + name: "ReloadAndDeleteViews", created: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -705,7 +713,7 @@ func TestReloadDataInDB(t *testing.T) { "insert into _vt.views(table_schema, table_name, create_statement, view_definition) values (database(), 'lead', 'create_view_lead', 'select_lead')": {}, }, }, { - name: "Error In Inserting View Data", + name: "ErrorInInsertingViewData", created: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -729,7 +737,7 @@ func TestReloadDataInDB(t *testing.T) { }, expectedError: errMessage, }, { - name: "Only tables to delete", + name: "OnlyTablesToDelete", dropped: []*Table{ NewTable("t1", NoType), NewTable("lead", NoType), @@ -741,7 +749,7 @@ func TestReloadDataInDB(t *testing.T) { "delete from _vt.`tables` where table_schema = database() and table_name in ('t1', 'lead')": {}, }, }, { - name: "Only tables to reload", + name: "OnlyTablesToReload", created: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -769,7 +777,7 @@ func TestReloadDataInDB(t *testing.T) { "insert into _vt.`tables`(table_schema, table_name, create_statement, create_time) values (database(), 'lead', 'create_table_lead', 1234)": {}, }, }, { - name: "Reload and delete tables", + name: "ReloadAndDeleteTables", created: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -801,7 +809,7 @@ func TestReloadDataInDB(t *testing.T) { "insert into _vt.`tables`(table_schema, table_name, create_statement, create_time) values (database(), 'lead', 'create_table_lead', 1234)": {}, }, }, { - name: "Error In Inserting Table Data", + name: "ErrorInInsertingTableData", altered: []*Table{ { Name: sqlparser.NewIdentifierCS("t1"), @@ -822,7 +830,7 @@ func TestReloadDataInDB(t *testing.T) { }, expectedError: errMessage, }, { - name: "Reload and delete all", + name: "ReloadAndDeleteAll", created: []*Table{ { Name: sqlparser.NewIdentifierCS("v1"), @@ -878,7 +886,8 @@ func TestReloadDataInDB(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tc.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) // Add queries with the expected results and errors. @@ -889,7 +898,7 @@ func TestReloadDataInDB(t *testing.T) { db.AddRejectedQuery(query, errorToThrow) } - err = reloadDataInDB(context.Background(), conn, tc.altered, tc.created, tc.dropped) + err = reloadDataInDB(context.Background(), conn, tc.altered, tc.created, tc.dropped, sqlparser.NewTestParser()) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) return @@ -920,7 +929,7 @@ func TestGetFetchViewQuery(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - query, err := GetFetchViewQuery(testcase.viewNames) + query, err := GetFetchViewQuery(testcase.viewNames, sqlparser.NewTestParser()) require.NoError(t, err) require.Equal(t, testcase.expectedQuery, query) }) @@ -947,7 +956,7 @@ func TestGetFetchTableQuery(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - query, err := GetFetchTableQuery(testcase.tableNames) + query, err := GetFetchTableQuery(testcase.tableNames, sqlparser.NewTestParser()) require.NoError(t, err) require.Equal(t, testcase.expectedQuery, query) }) @@ -974,7 +983,7 @@ func TestGetFetchTableAndViewsQuery(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - query, err := GetFetchTableAndViewsQuery(testcase.tableNames) + query, err := GetFetchTableAndViewsQuery(testcase.tableNames, sqlparser.NewTestParser()) require.NoError(t, err) require.Equal(t, testcase.expectedQuery, query) }) diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index ae50b460a96..1995bd5472d 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -26,10 +26,12 @@ import ( "sync" "time" + "golang.org/x/exp/maps" + "vitess.io/vitess/go/constants/sidecar" - "vitess.io/vitess/go/maps2" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/acl" "vitess.io/vitess/go/mysql" @@ -99,14 +101,14 @@ type Engine struct { // NewEngine creates a new Engine. func NewEngine(env tabletenv.Env) *Engine { - reloadTime := env.Config().SchemaReloadIntervalSeconds.Get() + reloadTime := env.Config().SchemaReloadInterval se := &Engine{ env: env, // We need three connections: one for the reloader, one for // the historian, and one for the tracker. conns: connpool.NewPool(env, "", tabletenv.ConnPoolConfig{ - Size: 3, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: 3, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }), ticks: timer.NewTimer(reloadTime), } @@ -160,7 +162,7 @@ func (se *Engine) syncSidecarDB(ctx context.Context, conn *dbconnpool.DBConnecti } return conn.ExecuteFetch(query, maxRows, true) } - if err := sidecardb.Init(ctx, exec); err != nil { + if err := sidecardb.Init(ctx, se.env.Environment(), exec); err != nil { log.Errorf("Error in sidecardb.Init: %+v", err) if se.env.Config().DB.HasGlobalSettings() { log.Warning("Ignoring sidecardb.Init error for unmanaged tablets") @@ -497,7 +499,7 @@ func (se *Engine) reload(ctx context.Context, includeStats bool) error { log.V(2).Infof("Reading schema for table: %s", tableName) tableType := row[1].String() - table, err := LoadTable(conn, se.cp.DBName(), tableName, tableType, row[3].ToString()) + table, err := LoadTable(conn, se.cp.DBName(), tableName, tableType, row[3].ToString(), se.env.Environment().CollationEnv()) if err != nil { if isView := strings.Contains(tableType, tmutils.TableView); isView { log.Warningf("Failed reading schema for the view: %s, error: %v", tableName, err) @@ -534,7 +536,7 @@ func (se *Engine) reload(ctx context.Context, includeStats bool) error { if shouldUseDatabase { // If reloadDataInDB succeeds, then we don't want to prevent sending the broadcast notification. // So, we do this step in the end when we can receive no more errors that fail the reload operation. - err = reloadDataInDB(ctx, conn.Conn, altered, created, dropped) + err = reloadDataInDB(ctx, conn.Conn, altered, created, dropped, se.env.Environment().Parser()) if err != nil { log.Errorf("error in updating schema information in Engine.reload() - %v", err) } @@ -586,7 +588,7 @@ func (se *Engine) getDroppedTables(curTables map[string]bool, changedViews map[s } } - return maps2.Values(dropped) + return maps.Values(dropped) } func getTableData(ctx context.Context, conn *connpool.Conn, includeStats bool) (*sqltypes.Result, error) { @@ -706,7 +708,8 @@ func (se *Engine) RegisterNotifier(name string, f notifier, runNotifier bool) { created = append(created, table) } if runNotifier { - f(se.tables, created, nil, nil) + s := maps.Clone(se.tables) + f(s, created, nil, nil) } } @@ -734,10 +737,7 @@ func (se *Engine) broadcast(created, altered, dropped []*Table) { se.notifierMu.Lock() defer se.notifierMu.Unlock() - s := make(map[string]*Table, len(se.tables)) - for k, v := range se.tables { - s[k] = v - } + s := maps.Clone(se.tables) for _, f := range se.notifiers { f(s, created, altered, dropped) } @@ -755,10 +755,7 @@ func (se *Engine) GetTable(tableName sqlparser.IdentifierCS) *Table { func (se *Engine) GetSchema() map[string]*Table { se.mu.Lock() defer se.mu.Unlock() - tables := make(map[string]*Table, len(se.tables)) - for k, v := range se.tables { - tables[k] = v - } + tables := maps.Clone(se.tables) return tables } @@ -831,6 +828,7 @@ func NewEngineForTests() *Engine { isOpen: true, tables: make(map[string]*Table), historian: newHistorian(false, 0, nil), + env: tabletenv.NewEnv(vtenv.NewTestEnv(), tabletenv.NewDefaultConfig(), "SchemaEngineForTests"), } return se } @@ -846,6 +844,10 @@ func (se *Engine) GetDBConnector() dbconfigs.Connector { return se.cp } +func (se *Engine) Environment() *vtenv.Environment { + return se.env.Environment() +} + func extractNamesFromTablesList(tables []*Table) []string { var tableNames []string for _, table := range tables { diff --git a/go/vt/vttablet/tabletserver/schema/engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go index 0a98a6ee676..b9492cbd185 100644 --- a/go/vt/vttablet/tabletserver/schema/engine_test.go +++ b/go/vt/vttablet/tabletserver/schema/engine_test.go @@ -32,19 +32,18 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/constants/sidecar" - - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/event/syslogger" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema/schematest" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -565,7 +564,7 @@ func TestSchemaEngineCloseTickRace(t *testing.T) { } finished <- true }() - // Wait until the ticks are stopped or 2 seonds have expired. + // Wait until the ticks are stopped or 2 seconds have expired. select { case <-finished: return @@ -575,19 +574,19 @@ func TestSchemaEngineCloseTickRace(t *testing.T) { } func newEngine(reloadTime time.Duration, idleTimeout time.Duration, schemaMaxAgeSeconds int64, db *fakesqldb.DB) *Engine { - config := tabletenv.NewDefaultConfig() - _ = config.SchemaReloadIntervalSeconds.Set(reloadTime.String()) - _ = config.OltpReadPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - _ = config.OlapReadPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - _ = config.TxPool.IdleTimeoutSeconds.Set(idleTimeout.String()) - config.SchemaVersionMaxAgeSeconds = schemaMaxAgeSeconds - se := NewEngine(tabletenv.NewEnv(config, "SchemaTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.SchemaReloadInterval = reloadTime + cfg.OltpReadPool.IdleTimeout = idleTimeout + cfg.OlapReadPool.IdleTimeout = idleTimeout + cfg.TxPool.IdleTimeout = idleTimeout + cfg.SchemaVersionMaxAgeSeconds = schemaMaxAgeSeconds + se := NewEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "SchemaTest")) se.InitDBConfig(newDBConfigs(db).DbaWithDB()) return se } func newDBConfigs(db *fakesqldb.DB) *dbconfigs.DBConfigs { - params, _ := db.ConnParams().MysqlParams() + params := db.ConnParams() cp := *params return dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb") } @@ -705,6 +704,29 @@ func AddFakeInnoDBReadRowsResult(db *fakesqldb.DB, value int) *fakesqldb.Expecte )) } +// TestRegisterNotifier tests the functionality of RegisterNotifier +// It also makes sure that writing to the tables map in the schema engine doesn't change the tables received by the notifiers. +func TestRegisterNotifier(t *testing.T) { + // Create a new engine for testing + se := NewEngineForTests() + se.notifiers = map[string]notifier{} + se.tables = map[string]*Table{ + "t1": nil, + "t2": nil, + "t3": nil, + } + + var tablesReceived map[string]*Table + // Register a notifier and make it run immediately. + se.RegisterNotifier("TestRegisterNotifier", func(full map[string]*Table, created, altered, dropped []*Table) { + tablesReceived = full + }, true) + + // Change the se.tables and make sure it doesn't affect the tables received by the notifier. + se.tables["t4"] = nil + require.Len(t, tablesReceived, 3) +} + // TestEngineMysqlTime tests the functionality of Engine.mysqlTime function func TestEngineMysqlTime(t *testing.T) { tests := []struct { @@ -742,7 +764,8 @@ func TestEngineMysqlTime(t *testing.T) { t.Run(tt.name, func(t *testing.T) { se := &Engine{} db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tt.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) if tt.timeStampErr != nil { @@ -848,7 +871,8 @@ func TestEnginePopulatePrimaryKeys(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tt.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) se := &Engine{} @@ -909,7 +933,8 @@ func TestEngineUpdateInnoDBRowsRead(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, tt.name) + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) se := &Engine{} se.innoDbReadRowsCounter = stats.NewCounter("TestEngineUpdateInnoDBRowsRead-"+tt.name, "") @@ -936,7 +961,8 @@ func TestEngineUpdateInnoDBRowsRead(t *testing.T) { // TestEngineGetTableData tests the functionality of getTableData function func TestEngineGetTableData(t *testing.T) { db := fakesqldb.New(t) - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestEngineGetTableData") + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) tests := []struct { @@ -1110,7 +1136,8 @@ func TestEngineReload(t *testing.T) { cfg := tabletenv.NewDefaultConfig() cfg.DB = newDBConfigs(db) cfg.SignalWhenSchemaChange = true - conn, err := connpool.NewConn(context.Background(), db.ConnParams(), nil, nil) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TestEngineReload") + conn, err := connpool.NewConn(context.Background(), dbconfigs.New(db.ConnParams()), nil, nil, env) require.NoError(t, err) se := newEngine(10*time.Second, 10*time.Second, 0, db) @@ -1162,23 +1189,23 @@ func TestEngineReload(t *testing.T) { } // MySQL unix timestamp query. db.AddQuery("SELECT UNIX_TIMESTAMP()", sqltypes.MakeTestResult(sqltypes.MakeTestFields("UNIX_TIMESTAMP", "int64"), "987654326")) - // Table t2 is updated, t3 is created and t4 is deleted. - // View v2 is updated, v3 is created and v4 is deleted. + // Table t2 is updated, T2 is created and t4 is deleted. + // View v2 is updated, V2 is created and v4 is deleted. db.AddQuery(conn.BaseShowTables(), sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name|table_type|unix_timestamp(create_time)|table_comment", "varchar|varchar|int64|varchar"), "t1|BASE_TABLE|123456789|", "t2|BASE_TABLE|123456790|", - "t3|BASE_TABLE|123456789|", + "T2|BASE_TABLE|123456789|", "v1|VIEW|123456789|", "v2|VIEW|123456789|", - "v3|VIEW|123456789|", + "V2|VIEW|123456789|", )) // Detecting view changes. - // According to the database, v2, v3, v4, and v5 require updating. + // According to the database, v2, V2, v4, and v5 require updating. db.AddQuery(fmt.Sprintf(detectViewChange, sidecar.GetIdentifier()), sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name", "varchar"), "v2", - "v3", + "V2", "v4", "v5", )) @@ -1197,7 +1224,7 @@ func TestEngineReload(t *testing.T) { "Innodb_rows_read|35")) // Queries to load the tables' information. - for _, tableName := range []string{"t2", "t3", "v2", "v3"} { + for _, tableName := range []string{"t2", "T2", "v2", "V2"} { db.AddQuery(fmt.Sprintf(`SELECT COLUMN_NAME as column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'fakesqldb' AND TABLE_NAME = '%s' @@ -1211,12 +1238,12 @@ func TestEngineReload(t *testing.T) { db.AddQuery(mysql.BaseShowPrimary, sqltypes.MakeTestResult(mysql.ShowPrimaryFields, "t1|col1", "t2|col1", - "t3|col1", + "T2|col1", )) // Queries for reloading the tables' information. { - for _, tableName := range []string{"t2", "t3"} { + for _, tableName := range []string{"t2", "T2"} { db.AddQuery(fmt.Sprintf(`show create table %s`, tableName), sqltypes.MakeTestResult(sqltypes.MakeTestFields("Table | Create Table", "varchar|varchar"), fmt.Sprintf("%v|create_table_%v", tableName, tableName))) @@ -1225,41 +1252,41 @@ func TestEngineReload(t *testing.T) { db.AddQuery("commit", &sqltypes.Result{}) db.AddQuery("rollback", &sqltypes.Result{}) // We are adding both the variants of the delete statements that we can see in the test, since the deleted tables are initially stored as a map, the order is not defined. - db.AddQuery("delete from _vt.`tables` where TABLE_SCHEMA = database() and TABLE_NAME in ('t5', 't4', 't3', 't2')", &sqltypes.Result{}) - db.AddQuery("delete from _vt.`tables` where TABLE_SCHEMA = database() and TABLE_NAME in ('t4', 't5', 't3', 't2')", &sqltypes.Result{}) + db.AddQuery("delete from _vt.`tables` where TABLE_SCHEMA = database() and TABLE_NAME in ('t5', 't4', 'T2', 't2')", &sqltypes.Result{}) + db.AddQuery("delete from _vt.`tables` where TABLE_SCHEMA = database() and TABLE_NAME in ('t4', 't5', 'T2', 't2')", &sqltypes.Result{}) db.AddQuery("insert into _vt.`tables`(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, CREATE_TIME) values (database(), 't2', 'create_table_t2', 123456790)", &sqltypes.Result{}) - db.AddQuery("insert into _vt.`tables`(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, CREATE_TIME) values (database(), 't3', 'create_table_t3', 123456789)", &sqltypes.Result{}) + db.AddQuery("insert into _vt.`tables`(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, CREATE_TIME) values (database(), 'T2', 'create_table_T2', 123456789)", &sqltypes.Result{}) } // Queries for reloading the views' information. { - for _, tableName := range []string{"v2", "v3"} { + for _, tableName := range []string{"v2", "V2"} { db.AddQuery(fmt.Sprintf(`show create table %s`, tableName), sqltypes.MakeTestResult(sqltypes.MakeTestFields(" View | Create View | character_set_client | collation_connection", "varchar|varchar|varchar|varchar"), fmt.Sprintf("%v|create_table_%v|utf8mb4|utf8mb4_0900_ai_ci", tableName, tableName))) } // We are adding both the variants of the select statements that we can see in the test, since the deleted views are initially stored as a map, the order is not defined. - db.AddQuery("select table_name, view_definition from information_schema.views where table_schema = database() and table_name in ('v4', 'v5', 'v3', 'v2')", + db.AddQuery("select table_name, view_definition from information_schema.views where table_schema = database() and table_name in ('v4', 'v5', 'V2', 'v2')", sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name|view_definition", "varchar|varchar"), "v2|select_v2", - "v3|select_v3", + "V2|select_V2", )) - db.AddQuery("select table_name, view_definition from information_schema.views where table_schema = database() and table_name in ('v5', 'v4', 'v3', 'v2')", + db.AddQuery("select table_name, view_definition from information_schema.views where table_schema = database() and table_name in ('v5', 'v4', 'V2', 'v2')", sqltypes.MakeTestResult(sqltypes.MakeTestFields("table_name|view_definition", "varchar|varchar"), "v2|select_v2", - "v3|select_v3", + "V2|select_V2", )) // We are adding both the variants of the delete statements that we can see in the test, since the deleted views are initially stored as a map, the order is not defined. - db.AddQuery("delete from _vt.views where TABLE_SCHEMA = database() and TABLE_NAME in ('v4', 'v5', 'v3', 'v2')", &sqltypes.Result{}) - db.AddQuery("delete from _vt.views where TABLE_SCHEMA = database() and TABLE_NAME in ('v5', 'v4', 'v3', 'v2')", &sqltypes.Result{}) + db.AddQuery("delete from _vt.views where TABLE_SCHEMA = database() and TABLE_NAME in ('v4', 'v5', 'V2', 'v2')", &sqltypes.Result{}) + db.AddQuery("delete from _vt.views where TABLE_SCHEMA = database() and TABLE_NAME in ('v5', 'v4', 'V2', 'v2')", &sqltypes.Result{}) db.AddQuery("insert into _vt.views(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, VIEW_DEFINITION) values (database(), 'v2', 'create_table_v2', 'select_v2')", &sqltypes.Result{}) - db.AddQuery("insert into _vt.views(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, VIEW_DEFINITION) values (database(), 'v3', 'create_table_v3', 'select_v3')", &sqltypes.Result{}) + db.AddQuery("insert into _vt.views(TABLE_SCHEMA, TABLE_NAME, CREATE_STATEMENT, VIEW_DEFINITION) values (database(), 'V2', 'create_table_V2', 'select_V2')", &sqltypes.Result{}) } // Verify the list of created, altered and dropped tables seen. se.RegisterNotifier("test", func(full map[string]*Table, created, altered, dropped []*Table) { - require.ElementsMatch(t, extractNamesFromTablesList(created), []string{"t3", "v3"}) + require.ElementsMatch(t, extractNamesFromTablesList(created), []string{"T2", "V2"}) require.ElementsMatch(t, extractNamesFromTablesList(altered), []string{"t2", "v2"}) require.ElementsMatch(t, extractNamesFromTablesList(dropped), []string{"t4", "v4", "t5", "v5"}) }, false) diff --git a/go/vt/vttablet/tabletserver/schema/historian_test.go b/go/vt/vttablet/tabletserver/schema/historian_test.go index f66306966de..1d66ecefd97 100644 --- a/go/vt/vttablet/tabletserver/schema/historian_test.go +++ b/go/vt/vttablet/tabletserver/schema/historian_test.go @@ -39,7 +39,7 @@ func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks [ fields := []*querypb.Field{} for i := range fieldNames { typ := fieldTypes[i] - cs := collations.DefaultCollationForType(typ) + cs := collations.CollationForType(typ, collations.MySQL8().DefaultConnectionCharset()) fields = append(fields, &querypb.Field{ Name: fieldNames[i], Type: typ, diff --git a/go/vt/vttablet/tabletserver/schema/load_table.go b/go/vt/vttablet/tabletserver/schema/load_table.go index 687672a4a02..e4e464f3fce 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table.go +++ b/go/vt/vttablet/tabletserver/schema/load_table.go @@ -34,7 +34,7 @@ import ( ) // LoadTable creates a Table from the schema info in the database. -func LoadTable(conn *connpool.PooledConn, databaseName, tableName, tableType string, comment string) (*Table, error) { +func LoadTable(conn *connpool.PooledConn, databaseName, tableName, tableType string, comment string, collationEnv *collations.Environment) (*Table, error) { ta := NewTable(tableName, NoType) sqlTableName := sqlparser.String(ta.Name) if err := fetchColumns(ta, conn, databaseName, sqlTableName); err != nil { @@ -45,7 +45,7 @@ func LoadTable(conn *connpool.PooledConn, databaseName, tableName, tableType str ta.Type = Sequence ta.SequenceInfo = &SequenceInfo{} case strings.Contains(comment, "vitess_message"): - if err := loadMessageInfo(ta, comment); err != nil { + if err := loadMessageInfo(ta, comment, collationEnv); err != nil { return nil, err } ta.Type = Message @@ -68,7 +68,7 @@ func fetchColumns(ta *Table, conn *connpool.PooledConn, databaseName, sqlTableNa return nil } -func loadMessageInfo(ta *Table, comment string) error { +func loadMessageInfo(ta *Table, comment string, collationEnv *collations.Environment) error { ta.MessageInfo = &MessageInfo{} // Extract keyvalues. keyvals := make(map[string]string) @@ -152,7 +152,7 @@ func loadMessageInfo(ta *Table, comment string) error { if specifiedCols[0] != "id" { return fmt.Errorf("vt_message_cols must begin with id: %s", ta.Name.String()) } - ta.MessageInfo.Fields = getSpecifiedMessageFields(ta.Fields, specifiedCols) + ta.MessageInfo.Fields = getSpecifiedMessageFields(ta.Fields, specifiedCols, collationEnv) } else { ta.MessageInfo.Fields = getDefaultMessageFields(ta.Fields, hiddenCols) } @@ -211,11 +211,11 @@ func getDefaultMessageFields(tableFields []*querypb.Field, hiddenCols map[string // we have already validated that all the specified columns exist in the table schema, so we don't need to // check again and possibly return an error here. -func getSpecifiedMessageFields(tableFields []*querypb.Field, specifiedCols []string) []*querypb.Field { +func getSpecifiedMessageFields(tableFields []*querypb.Field, specifiedCols []string, collationEnv *collations.Environment) []*querypb.Field { fields := make([]*querypb.Field, 0, len(specifiedCols)) for _, col := range specifiedCols { for _, field := range tableFields { - if res, _ := evalengine.NullsafeCompare(sqltypes.NewVarChar(field.Name), sqltypes.NewVarChar(strings.TrimSpace(col)), collations.Default()); res == 0 { + if res, _ := evalengine.NullsafeCompare(sqltypes.NewVarChar(field.Name), sqltypes.NewVarChar(strings.TrimSpace(col)), collationEnv, collationEnv.DefaultConnectionCharset()); res == 0 { fields = append(fields, field) break } diff --git a/go/vt/vttablet/tabletserver/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go index eeefb688e61..6416e2e306e 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table_test.go +++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go @@ -23,7 +23,10 @@ import ( "testing" "time" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -227,13 +230,13 @@ func TestLoadTableMessage(t *testing.T) { func newTestLoadTable(tableType string, comment string, db *fakesqldb.DB) (*Table, error) { ctx := context.Background() - appParams := db.ConnParams() - dbaParams := db.ConnParams() + appParams := dbconfigs.New(db.ConnParams()) + dbaParams := dbconfigs.New(db.ConnParams()) cfg := tabletenv.ConnPoolConfig{ - Size: 2, + Size: 2, + IdleTimeout: 10 * time.Second, } - _ = cfg.IdleTimeoutSeconds.Set("10s") - connPool := connpool.NewPool(tabletenv.NewEnv(nil, "SchemaTest"), "", cfg) + connPool := connpool.NewPool(tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "SchemaTest"), "", cfg) connPool.Open(appParams, dbaParams, appParams) conn, err := connPool.Get(ctx, nil) if err != nil { @@ -241,7 +244,7 @@ func newTestLoadTable(tableType string, comment string, db *fakesqldb.DB) (*Tabl } defer conn.Recycle() - return LoadTable(conn, "fakesqldb", "test_table", tableType, comment) + return LoadTable(conn, "fakesqldb", "test_table", tableType, comment, collations.MySQL8()) } func mockLoadTableQueries(db *fakesqldb.DB) { diff --git a/go/vt/vttablet/tabletserver/schema/schema.go b/go/vt/vttablet/tabletserver/schema/schema.go index 95c191392cd..4b3d9c88fb5 100644 --- a/go/vt/vttablet/tabletserver/schema/schema.go +++ b/go/vt/vttablet/tabletserver/schema/schema.go @@ -62,7 +62,7 @@ type Table struct { AllocatedSize uint64 } -// SequenceInfo contains info specific to sequence tabels. +// SequenceInfo contains info specific to sequence tables. // It must be locked before accessing the values inside. // If CurVal==LastVal, we have to cache new values. // When the schema is first loaded, the values are all 0, diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 9b4deaff6c4..bce5e4b33d6 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -134,12 +134,12 @@ func (tr *Tracker) process(ctx context.Context) { gtid = event.Gtid } if event.Type == binlogdatapb.VEventType_DDL && - MustReloadSchemaOnDDL(event.Statement, tr.engine.cp.DBName()) { + MustReloadSchemaOnDDL(event.Statement, tr.engine.cp.DBName(), tr.env.Environment().Parser()) { if err := tr.schemaUpdated(gtid, event.Statement, event.Timestamp); err != nil { tr.env.Stats().ErrorCounters.Add(vtrpcpb.Code_INTERNAL.String(), 1) log.Errorf("Error updating schema: %s for ddl %s, gtid %s", - sqlparser.TruncateForLog(err.Error()), event.Statement, gtid) + tr.env.Environment().Parser().TruncateForLog(err.Error()), event.Statement, gtid) } } } @@ -248,8 +248,8 @@ func encodeString(in string) string { } // MustReloadSchemaOnDDL returns true if the ddl is for the db which is part of the workflow and is not an online ddl artifact -func MustReloadSchemaOnDDL(sql string, dbname string) bool { - ast, err := sqlparser.Parse(sql) +func MustReloadSchemaOnDDL(sql string, dbname string, parser *sqlparser.Parser) bool { + ast, err := parser.Parse(sql) if err != nil { return false } @@ -263,7 +263,7 @@ func MustReloadSchemaOnDDL(sql string, dbname string) bool { if table.IsEmpty() { continue } - if !table.Qualifier.IsEmpty() && table.Qualifier.String() != dbname { + if table.Qualifier.NotEmpty() && table.Qualifier.String() != dbname { continue } tableName := table.Name.String() diff --git a/go/vt/vttablet/tabletserver/schema/tracker_test.go b/go/vt/vttablet/tabletserver/schema/tracker_test.go index 2029235b2e3..32f68597779 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker_test.go +++ b/go/vt/vttablet/tabletserver/schema/tracker_test.go @@ -17,14 +17,15 @@ limitations under the License. package schema import ( + "context" "testing" "github.com/stretchr/testify/require" - "context" - "vitess.io/vitess/go/sqltypes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" ) @@ -76,9 +77,9 @@ func TestTracker(t *testing.T) { }, }}, } - config := se.env.Config() - config.TrackSchemaVersions = true - env := tabletenv.NewEnv(config, "TrackerTest") + cfg := se.env.Config() + cfg.TrackSchemaVersions = true + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TrackerTest") initial := env.Stats().ErrorCounters.Counts()["INTERNAL"] tracker := NewTracker(env, vs, se) tracker.Open() @@ -120,9 +121,9 @@ func TestTrackerShouldNotInsertInitialSchema(t *testing.T) { }, }}, } - config := se.env.Config() - config.TrackSchemaVersions = true - env := tabletenv.NewEnv(config, "TrackerTest") + cfg := se.env.Config() + cfg.TrackSchemaVersions = true + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TrackerTest") tracker := NewTracker(env, vs, se) tracker.Open() <-vs.done @@ -170,7 +171,7 @@ func TestMustReloadSchemaOnDDL(t *testing.T) { } for _, tc := range testcases { t.Run("", func(t *testing.T) { - require.Equal(t, tc.want, MustReloadSchemaOnDDL(tc.query, tc.dbname)) + require.Equal(t, tc.want, MustReloadSchemaOnDDL(tc.query, tc.dbname, sqlparser.NewTestParser())) }) } } diff --git a/go/vt/vttablet/tabletserver/state_manager.go b/go/vt/vttablet/tabletserver/state_manager.go index 2115871c6bb..9c01610f770 100644 --- a/go/vt/vttablet/tabletserver/state_manager.go +++ b/go/vt/vttablet/tabletserver/state_manager.go @@ -64,6 +64,9 @@ func (state servingState) String() string { // transitionRetryInterval is for tests. var transitionRetryInterval = 1 * time.Second +var logInitTime sync.Once + +var ErrNoTarget = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "No target") // stateManager manages state transition for all the TabletServer // subcomponents. @@ -96,6 +99,10 @@ type stateManager struct { alsoAllow []topodatapb.TabletType reason string transitionErr error + // requestsWaitCounter is the number of goroutines that are waiting for requests to be empty. + // If this value is greater than zero, then we have to ensure that we don't Add to the requests + // to avoid any panics in the wait. + requestsWaitCounter int requests sync.WaitGroup @@ -121,7 +128,7 @@ type stateManager struct { throttler lagThrottler tableGC tableGarbageCollector - // hcticks starts on initialiazation and runs forever. + // hcticks starts on initialization and runs forever. hcticks *timer.Timer // checkMySQLThrottler ensures that CheckMysql @@ -194,11 +201,11 @@ func (sm *stateManager) Init(env tabletenv.Env, target *querypb.Target) { sm.target = target.CloneVT() sm.transitioning = semaphore.NewWeighted(1) sm.checkMySQLThrottler = semaphore.NewWeighted(1) - sm.timebombDuration = env.Config().OltpReadPool.TimeoutSeconds.Get() * 10 - sm.hcticks = timer.NewTimer(env.Config().Healthcheck.IntervalSeconds.Get()) - sm.unhealthyThreshold.Store(env.Config().Healthcheck.UnhealthyThresholdSeconds.Get().Nanoseconds()) - sm.shutdownGracePeriod = env.Config().GracePeriods.ShutdownSeconds.Get() - sm.transitionGracePeriod = env.Config().GracePeriods.TransitionSeconds.Get() + sm.timebombDuration = env.Config().OltpReadPool.Timeout * 10 + sm.hcticks = timer.NewTimer(env.Config().Healthcheck.Interval) + sm.unhealthyThreshold.Store(env.Config().Healthcheck.UnhealthyThreshold.Nanoseconds()) + sm.shutdownGracePeriod = env.Config().GracePeriods.Shutdown + sm.transitionGracePeriod = env.Config().GracePeriods.Transition } // SetServingType changes the state to the specified settings. @@ -351,6 +358,20 @@ func (sm *stateManager) checkMySQL() { }() } +// addRequestsWaitCounter adds to the requestsWaitCounter while being protected by a mutex. +func (sm *stateManager) addRequestsWaitCounter(val int) { + sm.mu.Lock() + defer sm.mu.Unlock() + sm.requestsWaitCounter += val +} + +// waitForRequestsToBeEmpty waits for requests to be empty. It also increments and decrements the requestsWaitCounter as required. +func (sm *stateManager) waitForRequestsToBeEmpty() { + sm.addRequestsWaitCounter(1) + sm.requests.Wait() + sm.addRequestsWaitCounter(-1) +} + func (sm *stateManager) setWantState(stateWanted servingState) { sm.mu.Lock() defer sm.mu.Unlock() @@ -389,7 +410,9 @@ func (sm *stateManager) StartRequest(ctx context.Context, target *querypb.Target } shuttingDown := sm.wantState != StateServing - if shuttingDown && !allowOnShutdown { + // If requestsWaitCounter is not zero, then there are go-routines blocked on waiting for requests to be empty. + // We cannot allow adding to the requests to prevent any panics from happening. + if (shuttingDown && !allowOnShutdown) || sm.requestsWaitCounter > 0 { // This specific error string needs to be returned for vtgate buffering to work. return vterrors.New(vtrpcpb.Code_CLUSTER_EVENT, vterrors.ShuttingDown) } @@ -432,7 +455,7 @@ func (sm *stateManager) verifyTargetLocked(ctx context.Context, target *querypb. } } else { if !tabletenv.IsLocalContext(ctx) { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "No target") + return ErrNoTarget } } return nil @@ -557,7 +580,7 @@ func (sm *stateManager) unserveCommon() { log.Info("Finished Killing all OLAP queries. Started tracker close") sm.tracker.Close() log.Infof("Finished tracker close. Started wait for requests") - sm.requests.Wait() + sm.waitForRequestsToBeEmpty() log.Infof("Finished wait for requests. Finished execution of unserveCommon") } @@ -611,9 +634,9 @@ func (sm *stateManager) setTimeBomb() chan struct{} { // setState changes the state and logs the event. func (sm *stateManager) setState(tabletType topodatapb.TabletType, state servingState) { - defer func() { + defer logInitTime.Do(func() { log.Infof("Tablet Init took %d ms", time.Since(servenv.GetInitStartTime()).Milliseconds()) - }() + }) sm.mu.Lock() defer sm.mu.Unlock() if tabletType == topodatapb.TabletType_UNKNOWN { diff --git a/go/vt/vttablet/tabletserver/state_manager_test.go b/go/vt/vttablet/tabletserver/state_manager_test.go index 23e70a66760..59909888935 100644 --- a/go/vt/vttablet/tabletserver/state_manager_test.go +++ b/go/vt/vttablet/tabletserver/state_manager_test.go @@ -24,17 +24,19 @@ import ( "testing" "time" - "google.golang.org/protobuf/proto" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" - "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -397,6 +399,10 @@ func (k *killableConn) Kill(message string, elapsed time.Duration) error { return nil } +func (k *killableConn) SQLParser() *sqlparser.Parser { + return sqlparser.NewTestParser() +} + func TestStateManagerShutdownGracePeriod(t *testing.T) { sm := newTestStateManager(t) defer sm.StopService() @@ -695,6 +701,29 @@ func TestRefreshReplHealthLocked(t *testing.T) { assert.False(t, sm.replHealthy) } +// TestPanicInWait tests that we don't panic when we wait for requests if more StartRequest calls come up after we start waiting. +func TestPanicInWait(t *testing.T) { + sm := newTestStateManager(t) + sm.wantState = StateServing + sm.state = StateServing + sm.replHealthy = true + ctx := context.Background() + // Simulate an Execute RPC running + err := sm.StartRequest(ctx, sm.target, false) + require.NoError(t, err) + go func() { + time.Sleep(100 * time.Millisecond) + // Simulate the previous RPC finishing after some delay + sm.EndRequest() + // Simulate a COMMIT call arriving right afterwards + _ = sm.StartRequest(ctx, sm.target, true) + }() + + // Simulate going to a not serving state and calling unserveCommon that waits on requests. + sm.wantState = StateNotServing + sm.waitForRequestsToBeEmpty() +} + func verifySubcomponent(t *testing.T, order int64, component any, state testState) { tos := component.(orderState) assert.Equal(t, order, tos.Order()) @@ -703,12 +732,13 @@ func verifySubcomponent(t *testing.T, order int64, component any, state testStat func newTestStateManager(t *testing.T) *stateManager { order.Store(0) - config := tabletenv.NewDefaultConfig() - env := tabletenv.NewEnv(config, "StateManagerTest") + cfg := tabletenv.NewDefaultConfig() + parser := sqlparser.NewTestParser() + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "StateManagerTest") sm := &stateManager{ - statelessql: NewQueryList("stateless"), - statefulql: NewQueryList("stateful"), - olapql: NewQueryList("olap"), + statelessql: NewQueryList("stateless", parser), + statefulql: NewQueryList("stateful", parser), + olapql: NewQueryList("olap", parser), hs: newHealthStreamer(env, &topodatapb.TabletAlias{}, schema.NewEngine(env)), se: &testSchemaEngine{}, rt: &testReplTracker{lag: 1 * time.Second}, @@ -724,7 +754,7 @@ func newTestStateManager(t *testing.T) *stateManager { tableGC: &testTableGC{}, } sm.Init(env, &querypb.Target{}) - sm.hs.InitDBConfig(&querypb.Target{}, fakesqldb.New(t).ConnParams()) + sm.hs.InitDBConfig(&querypb.Target{}, dbconfigs.New(fakesqldb.New(t).ConnParams())) log.Infof("returning sm: %p", sm) return sm } diff --git a/go/vt/vttablet/tabletserver/stateful_connection.go b/go/vt/vttablet/tabletserver/stateful_connection.go index 739ed5c4295..067f2194655 100644 --- a/go/vt/vttablet/tabletserver/stateful_connection.go +++ b/go/vt/vttablet/tabletserver/stateful_connection.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -184,11 +185,11 @@ func (sc *StatefulConnection) Renew() error { } // String returns a printable version of the connection info. -func (sc *StatefulConnection) String(sanitize bool) string { +func (sc *StatefulConnection) String(sanitize bool, parser *sqlparser.Parser) string { return fmt.Sprintf( "%v\t%s", sc.ConnID, - sc.txProps.String(sanitize), + sc.txProps.String(sanitize, parser), ) } diff --git a/go/vt/vttablet/tabletserver/stateful_connection_pool.go b/go/vt/vttablet/tabletserver/stateful_connection_pool.go index ce6f917610e..64268825b70 100644 --- a/go/vt/vttablet/tabletserver/stateful_connection_pool.go +++ b/go/vt/vttablet/tabletserver/stateful_connection_pool.go @@ -93,7 +93,7 @@ func (sf *StatefulConnectionPool) Close() { if conn.IsInTransaction() { thing = "transaction" } - log.Warningf("killing %s for shutdown: %s", thing, conn.String(sf.env.Config().SanitizeLogMessages)) + log.Warningf("killing %s for shutdown: %s", thing, conn.String(sf.env.Config().SanitizeLogMessages, sf.env.Environment().Parser())) sf.env.Stats().InternalErrors.Add("StrayTransactions", 1) conn.Close() conn.Releasef("pool closed") diff --git a/go/vt/vttablet/tabletserver/stateful_connection_pool_test.go b/go/vt/vttablet/tabletserver/stateful_connection_pool_test.go index b9ea4dfc185..a84052f1d0f 100644 --- a/go/vt/vttablet/tabletserver/stateful_connection_pool_test.go +++ b/go/vt/vttablet/tabletserver/stateful_connection_pool_test.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" ) @@ -37,7 +38,8 @@ func TestActivePoolClientRowsFound(t *testing.T) { db.AddQuery("begin", &sqltypes.Result{}) pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) startNormalSize := pool.conns.Available() startFoundRowsSize := pool.foundRowsPool.Available() @@ -63,7 +65,8 @@ func TestActivePoolForAllTxProps(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) conn1, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) require.NoError(t, err) conn1.txProps = &tx.Properties{} @@ -91,7 +94,8 @@ func TestStatefulPoolShutdownNonTx(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) // conn1 non-tx, not in use. conn1, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) @@ -131,7 +135,8 @@ func TestStatefulPoolShutdownAll(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) // conn1 not in use conn1, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) @@ -157,7 +162,8 @@ func TestActivePoolGetConnNonExistentTransaction(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) _, err := pool.GetAndLock(12345, "for query") require.EqualError(t, err, "not found") } @@ -167,7 +173,8 @@ func TestExecWithAbortedCtx(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) conn, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) require.NoError(t, err) cancel() @@ -181,7 +188,8 @@ func TestExecWithDbconnClosed(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) conn, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) require.NoError(t, err) conn.Close() @@ -196,7 +204,8 @@ func TestExecWithDbconnClosedHavingTx(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) conn, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) require.NoError(t, err) conn.txProps = &tx.Properties{Conclusion: "foobar"} @@ -212,7 +221,8 @@ func TestFailOnConnectionRegistering(t *testing.T) { db := fakesqldb.New(t) defer db.Close() pool := newActivePool() - pool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + pool.Open(params, params, params) conn, err := pool.NewConn(ctx, &querypb.ExecuteOptions{}, nil) require.NoError(t, err) defer conn.Close() diff --git a/go/vt/vttablet/tabletserver/status.go b/go/vt/vttablet/tabletserver/status.go index f91cc4ad566..b1ebb24bc57 100644 --- a/go/vt/vttablet/tabletserver/status.go +++ b/go/vt/vttablet/tabletserver/status.go @@ -229,8 +229,8 @@ func (tsv *TabletServer) AddStatusHeader() { // AddStatusPart registers the status part for the status page. func (tsv *TabletServer) AddStatusPart() { // Save the threshold values for reporting. - degradedThreshold.Store(tsv.config.Healthcheck.DegradedThresholdSeconds.Get().Nanoseconds()) - unhealthyThreshold.Store(tsv.config.Healthcheck.UnhealthyThresholdSeconds.Get().Nanoseconds()) + degradedThreshold.Store(tsv.config.Healthcheck.DegradedThreshold.Nanoseconds()) + unhealthyThreshold.Store(tsv.config.Healthcheck.UnhealthyThreshold.Nanoseconds()) tsv.exporter.AddStatusPart("Health", queryserviceStatusTemplate, func() any { status := queryserviceStatus{ diff --git a/go/vt/vttablet/tabletserver/stream_consolidator.go b/go/vt/vttablet/tabletserver/stream_consolidator.go index 497c9011040..cbf99eaffd4 100644 --- a/go/vt/vttablet/tabletserver/stream_consolidator.go +++ b/go/vt/vttablet/tabletserver/stream_consolidator.go @@ -19,9 +19,11 @@ package tabletserver import ( "sync" "sync/atomic" + "time" "vitess.io/vitess/go/sqltypes" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -70,7 +72,7 @@ func (sc *StreamConsolidator) SetBlocking(block bool) { // `callback`. A `leaderCallback` must also be supplied: this function must perform the actual // query in the upstream MySQL server, yielding results into the modified callback that it receives // as an argument. -func (sc *StreamConsolidator) Consolidate(logStats *tabletenv.LogStats, sql string, callback StreamCallback, leaderCallback func(StreamCallback) error) error { +func (sc *StreamConsolidator) Consolidate(waitTimings *servenv.TimingsWrapper, logStats *tabletenv.LogStats, sql string, callback StreamCallback, leaderCallback func(StreamCallback) error) error { var ( inflight *streamInFlight catchup []*sqltypes.Result @@ -100,9 +102,11 @@ func (sc *StreamConsolidator) Consolidate(logStats *tabletenv.LogStats, sql stri // if we have a followChan, we're following up on a query that is already being served if followChan != nil { + startTime := time.Now() defer func() { memchange := inflight.unfollow(followChan, sc.cleanup) atomic.AddInt64(&sc.memory, memchange) + waitTimings.Record("StreamConsolidations", startTime) }() logStats.QuerySources |= tabletenv.QuerySourceConsolidator @@ -252,7 +256,7 @@ func (s *streamInFlight) update(result *sqltypes.Result, block bool, maxMemoryQu s.mu.Lock() defer s.mu.Unlock() - // if this stream can still be catched up with, we need to store the result in + // if this stream can still be caught up with, we need to store the result in // a catch up buffer; otherwise, we can skip this altogether and just fan out the result // to all the followers that are already caught up if s.catchupAllowed { diff --git a/go/vt/vttablet/tabletserver/stream_consolidator_flaky_test.go b/go/vt/vttablet/tabletserver/stream_consolidator_flaky_test.go index 0c903933412..caa519cc477 100644 --- a/go/vt/vttablet/tabletserver/stream_consolidator_flaky_test.go +++ b/go/vt/vttablet/tabletserver/stream_consolidator_flaky_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/sqltypes" @@ -123,10 +124,12 @@ func (ct *consolidationTest) run(workers int, generateCallback func(int) (string go func(worker int) { defer wg.Done() + exporter := servenv.NewExporter("ConsolidatorTest", "") + timings := exporter.NewTimings("ConsolidatorWaits", "", "StreamConsolidations") logStats := tabletenv.NewLogStats(context.Background(), "StreamConsolidation") query, callback := generateCallback(worker) start := time.Now() - err := ct.cc.Consolidate(logStats, query, func(result *sqltypes.Result) error { + err := ct.cc.Consolidate(timings, logStats, query, func(result *sqltypes.Result) error { cr := ct.results[worker] cr.items = append(cr.items, result) atomic.AddInt64(&cr.count, 1) diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index d490c97326a..25352aba91b 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -122,42 +122,26 @@ func registerTabletEnvFlags(fs *pflag.FlagSet) { fs.IntVar(¤tConfig.OlapReadPool.Size, "queryserver-config-stream-pool-size", defaultConfig.OlapReadPool.Size, "query server stream connection pool size, stream pool is used by stream queries: queries that return results to client in a streaming fashion") fs.IntVar(¤tConfig.TxPool.Size, "queryserver-config-transaction-cap", defaultConfig.TxPool.Size, "query server transaction cap is the maximum number of transactions allowed to happen at any given point of a time for a single vttablet. E.g. by setting transaction cap to 100, there are at most 100 transactions will be processed by a vttablet and the 101th transaction will be blocked (and fail if it cannot get connection within specified timeout)") fs.IntVar(¤tConfig.MessagePostponeParallelism, "queryserver-config-message-postpone-cap", defaultConfig.MessagePostponeParallelism, "query server message postpone cap is the maximum number of messages that can be postponed at any given time. Set this number to substantially lower than transaction cap, so that the transaction pool isn't exhausted by the message subsystem.") - currentConfig.Oltp.TxTimeoutSeconds = defaultConfig.Oltp.TxTimeoutSeconds.Clone() - fs.Var(¤tConfig.Oltp.TxTimeoutSeconds, currentConfig.Oltp.TxTimeoutSeconds.Name(), "query server transaction timeout (in seconds), a transaction will be killed if it takes longer than this value") - currentConfig.GracePeriods.ShutdownSeconds = flagutil.NewDeprecatedFloat64Seconds(defaultConfig.GracePeriods.ShutdownSeconds.Name(), defaultConfig.GracePeriods.TransitionSeconds.Get()) - fs.Var(¤tConfig.GracePeriods.ShutdownSeconds, currentConfig.GracePeriods.ShutdownSeconds.Name(), "how long to wait (in seconds) for queries and transactions to complete during graceful shutdown.") + fs.DurationVar(¤tConfig.Oltp.TxTimeout, "queryserver-config-transaction-timeout", defaultConfig.Oltp.TxTimeout, "query server transaction timeout, a transaction will be killed if it takes longer than this value") + fs.DurationVar(¤tConfig.GracePeriods.Shutdown, "shutdown_grace_period", defaultConfig.GracePeriods.Shutdown, "how long to wait for queries and transactions to complete during graceful shutdown.") fs.IntVar(¤tConfig.Oltp.MaxRows, "queryserver-config-max-result-size", defaultConfig.Oltp.MaxRows, "query server max result size, maximum number of rows allowed to return from vttablet for non-streaming queries.") fs.IntVar(¤tConfig.Oltp.WarnRows, "queryserver-config-warn-result-size", defaultConfig.Oltp.WarnRows, "query server result size warning threshold, warn if number of rows returned from vttablet for non-streaming queries exceeds this") fs.BoolVar(¤tConfig.PassthroughDML, "queryserver-config-passthrough-dmls", defaultConfig.PassthroughDML, "query server pass through all dml statements without rewriting") fs.IntVar(¤tConfig.StreamBufferSize, "queryserver-config-stream-buffer-size", defaultConfig.StreamBufferSize, "query server stream buffer size, the maximum number of bytes sent from vttablet for each stream call. It's recommended to keep this value in sync with vtgate's stream_buffer_size.") - fs.Int("queryserver-config-query-cache-size", 0, "query server query cache size, maximum number of queries to be cached. vttablet analyzes every incoming query and generate a query plan, these plans are being cached in a lru cache. This config controls the capacity of the lru cache.") - _ = fs.MarkDeprecated("queryserver-config-query-cache-size", "`--queryserver-config-query-cache-size` is deprecated and will be removed in `v19.0`. This option only applied to LRU caches, which are now unsupported.") - fs.Int64Var(¤tConfig.QueryCacheMemory, "queryserver-config-query-cache-memory", defaultConfig.QueryCacheMemory, "query server query cache size in bytes, maximum amount of memory to be used for caching. vttablet analyzes every incoming query and generate a query plan, these plans are being cached in a lru cache. This config controls the capacity of the lru cache.") - fs.Bool("queryserver-config-query-cache-lfu", false, "query server cache algorithm. when set to true, a new cache algorithm based on a TinyLFU admission policy will be used to improve cache behavior and prevent pollution from sparse queries") - _ = fs.MarkDeprecated("queryserver-config-query-cache-lfu", "`--queryserver-config-query-cache-lfu` is deprecated and will be removed in `v19.0`. The query cache always uses a LFU implementation now.") - - currentConfig.SchemaReloadIntervalSeconds = defaultConfig.SchemaReloadIntervalSeconds.Clone() - fs.Var(¤tConfig.SchemaReloadIntervalSeconds, currentConfig.SchemaReloadIntervalSeconds.Name(), "query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance in seconds. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time.") + fs.DurationVar(¤tConfig.SchemaReloadInterval, "queryserver-config-schema-reload-time", defaultConfig.SchemaReloadInterval, "query server schema reload time, how often vttablet reloads schemas from underlying MySQL instance. vttablet keeps table schemas in its own memory and periodically refreshes it from MySQL. This config controls the reload time.") fs.DurationVar(¤tConfig.SchemaChangeReloadTimeout, "schema-change-reload-timeout", defaultConfig.SchemaChangeReloadTimeout, "query server schema change reload timeout, this is how long to wait for the signaled schema reload operation to complete before giving up") fs.BoolVar(¤tConfig.SignalWhenSchemaChange, "queryserver-config-schema-change-signal", defaultConfig.SignalWhenSchemaChange, "query server schema signal, will signal connected vtgates that schema has changed whenever this is detected. VTGates will need to have -schema_change_signal enabled for this to work") - currentConfig.Olap.TxTimeoutSeconds = defaultConfig.Olap.TxTimeoutSeconds.Clone() - fs.Var(¤tConfig.Olap.TxTimeoutSeconds, defaultConfig.Olap.TxTimeoutSeconds.Name(), "query server transaction timeout (in seconds), after which a transaction in an OLAP session will be killed") - currentConfig.Oltp.QueryTimeoutSeconds = defaultConfig.Oltp.QueryTimeoutSeconds.Clone() - fs.Var(¤tConfig.Oltp.QueryTimeoutSeconds, currentConfig.Oltp.QueryTimeoutSeconds.Name(), "query server query timeout (in seconds), this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed.") - currentConfig.OltpReadPool.TimeoutSeconds = defaultConfig.OltpReadPool.TimeoutSeconds.Clone() - fs.Var(¤tConfig.OltpReadPool.TimeoutSeconds, currentConfig.OltpReadPool.TimeoutSeconds.Name(), "query server query pool timeout (in seconds), it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead.") - currentConfig.OlapReadPool.TimeoutSeconds = defaultConfig.OlapReadPool.TimeoutSeconds.Clone() - fs.Var(¤tConfig.OlapReadPool.TimeoutSeconds, currentConfig.OlapReadPool.TimeoutSeconds.Name(), "query server stream pool timeout (in seconds), it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout.") - currentConfig.TxPool.TimeoutSeconds = defaultConfig.TxPool.TimeoutSeconds.Clone() - fs.Var(¤tConfig.TxPool.TimeoutSeconds, currentConfig.TxPool.TimeoutSeconds.Name(), "query server transaction pool timeout, it is how long vttablet waits if tx pool is full") - currentConfig.OltpReadPool.IdleTimeoutSeconds = defaultConfig.OltpReadPool.IdleTimeoutSeconds.Clone() - fs.Var(¤tConfig.OltpReadPool.IdleTimeoutSeconds, currentConfig.OltpReadPool.IdleTimeoutSeconds.Name(), "query server idle timeout (in seconds), vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance.") - currentConfig.OltpReadPool.MaxLifetimeSeconds = defaultConfig.OltpReadPool.MaxLifetimeSeconds.Clone() - fs.Var(¤tConfig.OltpReadPool.MaxLifetimeSeconds, currentConfig.OltpReadPool.MaxLifetimeSeconds.Name(), "query server connection max lifetime (in seconds), vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool.") + fs.DurationVar(¤tConfig.Olap.TxTimeout, "queryserver-config-olap-transaction-timeout", defaultConfig.Olap.TxTimeout, "query server transaction timeout (in seconds), after which a transaction in an OLAP session will be killed") + fs.DurationVar(¤tConfig.Oltp.QueryTimeout, "queryserver-config-query-timeout", defaultConfig.Oltp.QueryTimeout, "query server query timeout, this is the query timeout in vttablet side. If a query takes more than this timeout, it will be killed.") + fs.DurationVar(¤tConfig.OltpReadPool.Timeout, "queryserver-config-query-pool-timeout", defaultConfig.OltpReadPool.Timeout, "query server query pool timeout, it is how long vttablet waits for a connection from the query pool. If set to 0 (default) then the overall query timeout is used instead.") + fs.DurationVar(¤tConfig.OlapReadPool.Timeout, "queryserver-config-stream-pool-timeout", defaultConfig.OlapReadPool.Timeout, "query server stream pool timeout, it is how long vttablet waits for a connection from the stream pool. If set to 0 (default) then there is no timeout.") + fs.DurationVar(¤tConfig.TxPool.Timeout, "queryserver-config-txpool-timeout", defaultConfig.TxPool.Timeout, "query server transaction pool timeout, it is how long vttablet waits if tx pool is full") + fs.DurationVar(¤tConfig.OltpReadPool.IdleTimeout, "queryserver-config-idle-timeout", defaultConfig.OltpReadPool.IdleTimeout, "query server idle timeout, vttablet manages various mysql connection pools. This config means if a connection has not been used in given idle timeout, this connection will be removed from pool. This effectively manages number of connection objects and optimize the pool performance.") + fs.DurationVar(¤tConfig.OltpReadPool.MaxLifetime, "queryserver-config-pool-conn-max-lifetime", defaultConfig.OltpReadPool.MaxLifetime, "query server connection max lifetime, vttablet manages various mysql connection pools. This config means if a connection has lived at least this long, it connection will be removed from pool upon the next time it is returned to the pool.") fs.IntVar(¤tConfig.OltpReadPool.MaxWaiters, "queryserver-config-query-pool-waiter-cap", defaultConfig.OltpReadPool.MaxWaiters, "query server query pool waiter limit, this is the maximum number of queries that can be queued waiting to get a connection") fs.IntVar(¤tConfig.OlapReadPool.MaxWaiters, "queryserver-config-stream-pool-waiter-cap", defaultConfig.OlapReadPool.MaxWaiters, "query server stream pool waiter limit, this is the maximum number of streaming queries that can be queued waiting to get a connection") fs.IntVar(¤tConfig.TxPool.MaxWaiters, "queryserver-config-txpool-waiter-cap", defaultConfig.TxPool.MaxWaiters, "query server transaction pool waiter limit, this is the maximum number of transactions that can be queued waiting to get a connection") @@ -207,13 +191,9 @@ func registerTabletEnvFlags(fs *pflag.FlagSet) { fs.Int64Var(¤tConfig.ConsolidatorStreamQuerySize, "consolidator-stream-query-size", defaultConfig.ConsolidatorStreamQuerySize, "Configure the stream consolidator query size in bytes. Setting to 0 disables the stream consolidator.") fs.Int64Var(¤tConfig.ConsolidatorStreamTotalSize, "consolidator-stream-total-size", defaultConfig.ConsolidatorStreamTotalSize, "Configure the stream consolidator total size in bytes. Setting to 0 disables the stream consolidator.") - currentConfig.Healthcheck.IntervalSeconds = flagutil.NewDeprecatedFloat64Seconds(defaultConfig.Healthcheck.IntervalSeconds.Name(), defaultConfig.Healthcheck.IntervalSeconds.Get()) - currentConfig.Healthcheck.DegradedThresholdSeconds = flagutil.NewDeprecatedFloat64Seconds(defaultConfig.Healthcheck.DegradedThresholdSeconds.Name(), defaultConfig.Healthcheck.DegradedThresholdSeconds.Get()) - currentConfig.Healthcheck.UnhealthyThresholdSeconds = flagutil.NewDeprecatedFloat64Seconds(defaultConfig.Healthcheck.UnhealthyThresholdSeconds.Name(), defaultConfig.Healthcheck.UnhealthyThresholdSeconds.Get()) - - fs.DurationVar(&healthCheckInterval, currentConfig.Healthcheck.IntervalSeconds.Name(), currentConfig.Healthcheck.IntervalSeconds.Get(), "Interval between health checks") - fs.DurationVar(°radedThreshold, currentConfig.Healthcheck.DegradedThresholdSeconds.Name(), currentConfig.Healthcheck.DegradedThresholdSeconds.Get(), "replication lag after which a replica is considered degraded") - fs.DurationVar(&unhealthyThreshold, currentConfig.Healthcheck.UnhealthyThresholdSeconds.Name(), currentConfig.Healthcheck.UnhealthyThresholdSeconds.Get(), "replication lag after which a replica is considered unhealthy") + fs.DurationVar(&healthCheckInterval, "health_check_interval", defaultConfig.Healthcheck.Interval, "Interval between health checks") + fs.DurationVar(°radedThreshold, "degraded_threshold", defaultConfig.Healthcheck.DegradedThreshold, "replication lag after which a replica is considered degraded") + fs.DurationVar(&unhealthyThreshold, "unhealthy_threshold", defaultConfig.Healthcheck.UnhealthyThreshold, "replication lag after which a replica is considered unhealthy") fs.DurationVar(&transitionGracePeriod, "serving_state_grace_period", 0, "how long to pause after broadcasting health to vtgate, before enforcing a new serving state") fs.BoolVar(&enableReplicationReporter, "enable_replication_reporter", false, "Use polling to track replication lag.") @@ -238,10 +218,10 @@ var ( func Init() { // IdleTimeout is only initialized for OltpReadPool , but the other pools need to inherit the value. // TODO(sougou): Make a decision on whether this should be global or per-pool. - _ = currentConfig.OlapReadPool.IdleTimeoutSeconds.Set(currentConfig.OltpReadPool.IdleTimeoutSeconds.Get().String()) - _ = currentConfig.TxPool.IdleTimeoutSeconds.Set(currentConfig.OltpReadPool.IdleTimeoutSeconds.Get().String()) - _ = currentConfig.OlapReadPool.MaxLifetimeSeconds.Set(currentConfig.OltpReadPool.MaxLifetimeSeconds.Get().String()) - _ = currentConfig.TxPool.MaxLifetimeSeconds.Set(currentConfig.OltpReadPool.MaxLifetimeSeconds.Get().String()) + currentConfig.OlapReadPool.IdleTimeout = currentConfig.OltpReadPool.IdleTimeout + currentConfig.TxPool.IdleTimeout = currentConfig.OltpReadPool.IdleTimeout + currentConfig.OlapReadPool.MaxLifetime = currentConfig.OltpReadPool.MaxLifetime + currentConfig.TxPool.MaxLifetime = currentConfig.OltpReadPool.MaxLifetime if enableHotRowProtection { if enableHotRowProtectionDryRun { @@ -263,7 +243,7 @@ func Init() { } if heartbeatInterval == 0 { - heartbeatInterval = defaultConfig.ReplicationTracker.HeartbeatIntervalSeconds.Get() + heartbeatInterval = defaultConfig.ReplicationTracker.HeartbeatInterval } if heartbeatInterval > time.Second { heartbeatInterval = time.Second @@ -271,8 +251,8 @@ func Init() { if heartbeatOnDemandDuration < 0 { heartbeatOnDemandDuration = 0 } - _ = currentConfig.ReplicationTracker.HeartbeatIntervalSeconds.Set(heartbeatInterval.String()) - _ = currentConfig.ReplicationTracker.HeartbeatOnDemandSeconds.Set(heartbeatOnDemandDuration.String()) + currentConfig.ReplicationTracker.HeartbeatInterval = heartbeatInterval + currentConfig.ReplicationTracker.HeartbeatOnDemand = heartbeatOnDemandDuration switch { case enableHeartbeat: @@ -283,10 +263,10 @@ func Init() { currentConfig.ReplicationTracker.Mode = Disable } - _ = currentConfig.Healthcheck.IntervalSeconds.Set(healthCheckInterval.String()) - _ = currentConfig.Healthcheck.DegradedThresholdSeconds.Set(degradedThreshold.String()) - _ = currentConfig.Healthcheck.UnhealthyThresholdSeconds.Set(unhealthyThreshold.String()) - _ = currentConfig.GracePeriods.TransitionSeconds.Set(transitionGracePeriod.String()) + currentConfig.Healthcheck.Interval = healthCheckInterval + currentConfig.Healthcheck.DegradedThreshold = degradedThreshold + currentConfig.Healthcheck.UnhealthyThreshold = unhealthyThreshold + currentConfig.GracePeriods.Transition = transitionGracePeriod switch streamlog.GetQueryLogFormat() { case streamlog.QueryLogFormatText: @@ -326,24 +306,24 @@ type TabletConfig struct { ReplicationTracker ReplicationTrackerConfig `json:"replicationTracker,omitempty"` // Consolidator can be enable, disable, or notOnPrimary. Default is enable. - Consolidator string `json:"consolidator,omitempty"` - PassthroughDML bool `json:"passthroughDML,omitempty"` - StreamBufferSize int `json:"streamBufferSize,omitempty"` - ConsolidatorStreamTotalSize int64 `json:"consolidatorStreamTotalSize,omitempty"` - ConsolidatorStreamQuerySize int64 `json:"consolidatorStreamQuerySize,omitempty"` - QueryCacheMemory int64 `json:"queryCacheMemory,omitempty"` - QueryCacheDoorkeeper bool `json:"queryCacheDoorkeeper,omitempty"` - SchemaReloadIntervalSeconds flagutil.DeprecatedFloat64Seconds `json:"schemaReloadIntervalSeconds,omitempty"` - SignalSchemaChangeReloadIntervalSeconds flagutil.DeprecatedFloat64Seconds `json:"signalSchemaChangeReloadIntervalSeconds,omitempty"` - SchemaChangeReloadTimeout time.Duration `json:"schemaChangeReloadTimeout,omitempty"` - WatchReplication bool `json:"watchReplication,omitempty"` - TrackSchemaVersions bool `json:"trackSchemaVersions,omitempty"` - SchemaVersionMaxAgeSeconds int64 `json:"schemaVersionMaxAgeSeconds,omitempty"` - TerseErrors bool `json:"terseErrors,omitempty"` - TruncateErrorLen int `json:"truncateErrorLen,omitempty"` - AnnotateQueries bool `json:"annotateQueries,omitempty"` - MessagePostponeParallelism int `json:"messagePostponeParallelism,omitempty"` - SignalWhenSchemaChange bool `json:"signalWhenSchemaChange,omitempty"` + Consolidator string `json:"consolidator,omitempty"` + PassthroughDML bool `json:"passthroughDML,omitempty"` + StreamBufferSize int `json:"streamBufferSize,omitempty"` + ConsolidatorStreamTotalSize int64 `json:"consolidatorStreamTotalSize,omitempty"` + ConsolidatorStreamQuerySize int64 `json:"consolidatorStreamQuerySize,omitempty"` + QueryCacheMemory int64 `json:"queryCacheMemory,omitempty"` + QueryCacheDoorkeeper bool `json:"queryCacheDoorkeeper,omitempty"` + SchemaReloadInterval time.Duration `json:"schemaReloadIntervalSeconds,omitempty"` + SignalSchemaChangeReloadInterval time.Duration `json:"signalSchemaChangeReloadIntervalSeconds,omitempty"` + SchemaChangeReloadTimeout time.Duration `json:"schemaChangeReloadTimeout,omitempty"` + WatchReplication bool `json:"watchReplication,omitempty"` + TrackSchemaVersions bool `json:"trackSchemaVersions,omitempty"` + SchemaVersionMaxAgeSeconds int64 `json:"schemaVersionMaxAgeSeconds,omitempty"` + TerseErrors bool `json:"terseErrors,omitempty"` + TruncateErrorLen int `json:"truncateErrorLen,omitempty"` + AnnotateQueries bool `json:"annotateQueries,omitempty"` + MessagePostponeParallelism int `json:"messagePostponeParallelism,omitempty"` + SignalWhenSchemaChange bool `json:"signalWhenSchemaChange,omitempty"` ExternalConnections map[string]*dbconfigs.DBConfigs `json:"externalConnections,omitempty"` @@ -383,15 +363,19 @@ func (cfg *TabletConfig) MarshalJSON() ([]byte, error) { tmp := struct { TCProxy - SchemaReloadIntervalSeconds string `json:"schemaReloadIntervalSeconds,omitempty"` - SignalSchemaChangeReloadIntervalSeconds string `json:"signalSchemaChangeReloadIntervalSeconds,omitempty"` - SchemaChangeReloadTimeout string `json:"schemaChangeReloadTimeout,omitempty"` + SchemaReloadInterval string `json:"schemaReloadIntervalSeconds,omitempty"` + SignalSchemaChangeReloadInterval string `json:"signalSchemaChangeReloadIntervalSeconds,omitempty"` + SchemaChangeReloadTimeout string `json:"schemaChangeReloadTimeout,omitempty"` }{ TCProxy: TCProxy(*cfg), } - if d := cfg.SchemaReloadIntervalSeconds.Get(); d != 0 { - tmp.SchemaReloadIntervalSeconds = d.String() + if d := cfg.SchemaReloadInterval; d != 0 { + tmp.SchemaReloadInterval = d.String() + } + + if d := cfg.SignalSchemaChangeReloadInterval; d != 0 { + tmp.SignalSchemaChangeReloadInterval = d.String() } if d := cfg.SchemaChangeReloadTimeout; d != 0 { @@ -401,14 +385,62 @@ func (cfg *TabletConfig) MarshalJSON() ([]byte, error) { return json.Marshal(&tmp) } +func (cfg *TabletConfig) UnmarshalJSON(data []byte) (err error) { + type TCProxy TabletConfig + + var tmp struct { + TCProxy + SchemaReloadInterval string `json:"schemaReloadIntervalSeconds,omitempty"` + SignalSchemaChangeReloadInterval string `json:"signalSchemaChangeReloadIntervalSeconds,omitempty"` + SchemaChangeReloadTimeout string `json:"schemaChangeReloadTimeout,omitempty"` + } + + tmp.TCProxy = TCProxy(*cfg) + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + *cfg = TabletConfig(tmp.TCProxy) + + if tmp.SchemaReloadInterval != "" { + cfg.SchemaReloadInterval, err = time.ParseDuration(tmp.SchemaReloadInterval) + if err != nil { + return err + } + } else { + cfg.SchemaReloadInterval = 0 + } + + if tmp.SignalSchemaChangeReloadInterval != "" { + cfg.SignalSchemaChangeReloadInterval, err = time.ParseDuration(tmp.SignalSchemaChangeReloadInterval) + if err != nil { + return err + } + } else { + cfg.SignalSchemaChangeReloadInterval = 0 + } + + if tmp.SchemaChangeReloadTimeout != "" { + cfg.SchemaChangeReloadTimeout, err = time.ParseDuration(tmp.SchemaChangeReloadTimeout) + if err != nil { + return err + } + } else { + cfg.SchemaChangeReloadTimeout = 0 + } + + return nil +} + // ConnPoolConfig contains the config for a conn pool. type ConnPoolConfig struct { - Size int `json:"size,omitempty"` - TimeoutSeconds flagutil.DeprecatedFloat64Seconds `json:"timeoutSeconds,omitempty"` - IdleTimeoutSeconds flagutil.DeprecatedFloat64Seconds `json:"idleTimeoutSeconds,omitempty"` - MaxLifetimeSeconds flagutil.DeprecatedFloat64Seconds `json:"maxLifetimeSeconds,omitempty"` - PrefillParallelism int `json:"prefillParallelism,omitempty"` - MaxWaiters int `json:"maxWaiters,omitempty"` + Size int `json:"size,omitempty"` + Timeout time.Duration `json:"timeoutSeconds,omitempty"` + IdleTimeout time.Duration `json:"idleTimeoutSeconds,omitempty"` + MaxLifetime time.Duration `json:"maxLifetimeSeconds,omitempty"` + PrefillParallelism int `json:"prefillParallelism,omitempty"` + MaxWaiters int `json:"maxWaiters,omitempty"` } func (cfg *ConnPoolConfig) MarshalJSON() ([]byte, error) { @@ -416,31 +448,73 @@ func (cfg *ConnPoolConfig) MarshalJSON() ([]byte, error) { tmp := struct { Proxy - TimeoutSeconds string `json:"timeoutSeconds,omitempty"` - IdleTimeoutSeconds string `json:"idleTimeoutSeconds,omitempty"` - MaxLifetimeSeconds string `json:"maxLifetimeSeconds,omitempty"` + Timeout string `json:"timeoutSeconds,omitempty"` + IdleTimeout string `json:"idleTimeoutSeconds,omitempty"` + MaxLifetime string `json:"maxLifetimeSeconds,omitempty"` }{ Proxy: Proxy(*cfg), } - if d := cfg.TimeoutSeconds.Get(); d != 0 { - tmp.TimeoutSeconds = d.String() + if d := cfg.Timeout; d != 0 { + tmp.Timeout = d.String() } - if d := cfg.IdleTimeoutSeconds.Get(); d != 0 { - tmp.IdleTimeoutSeconds = d.String() + if d := cfg.IdleTimeout; d != 0 { + tmp.IdleTimeout = d.String() } - if d := cfg.MaxLifetimeSeconds.Get(); d != 0 { - tmp.MaxLifetimeSeconds = d.String() + if d := cfg.MaxLifetime; d != 0 { + tmp.MaxLifetime = d.String() } return json.Marshal(&tmp) } +func (cfg *ConnPoolConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + Size int `json:"size,omitempty"` + Timeout string `json:"timeoutSeconds,omitempty"` + IdleTimeout string `json:"idleTimeoutSeconds,omitempty"` + MaxLifetime string `json:"maxLifetimeSeconds,omitempty"` + PrefillParallelism int `json:"prefillParallelism,omitempty"` + MaxWaiters int `json:"maxWaiters,omitempty"` + } + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.Timeout != "" { + cfg.Timeout, err = time.ParseDuration(tmp.Timeout) + if err != nil { + return err + } + } + + if tmp.IdleTimeout != "" { + cfg.IdleTimeout, err = time.ParseDuration(tmp.IdleTimeout) + if err != nil { + return err + } + } + + if tmp.MaxLifetime != "" { + cfg.MaxLifetime, err = time.ParseDuration(tmp.MaxLifetime) + if err != nil { + return err + } + } + + cfg.Size = tmp.Size + cfg.PrefillParallelism = tmp.PrefillParallelism + cfg.MaxWaiters = tmp.MaxWaiters + + return nil +} + // OlapConfig contains the config for olap settings. type OlapConfig struct { - TxTimeoutSeconds flagutil.DeprecatedFloat64Seconds `json:"txTimeoutSeconds,omitempty"` + TxTimeout time.Duration `json:"txTimeoutSeconds,omitempty"` } func (cfg *OlapConfig) MarshalJSON() ([]byte, error) { @@ -453,19 +527,38 @@ func (cfg *OlapConfig) MarshalJSON() ([]byte, error) { Proxy: Proxy(*cfg), } - if d := cfg.TxTimeoutSeconds.Get(); d != 0 { + if d := cfg.TxTimeout; d != 0 { tmp.TxTimeoutSeconds = d.String() } return json.Marshal(&tmp) } +func (cfg *OlapConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + TxTimeout string `json:"txTimeoutSeconds,omitempty"` + } + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.TxTimeout != "" { + cfg.TxTimeout, err = time.ParseDuration(tmp.TxTimeout) + if err != nil { + return err + } + } + + return nil +} + // OltpConfig contains the config for oltp settings. type OltpConfig struct { - QueryTimeoutSeconds flagutil.DeprecatedFloat64Seconds `json:"queryTimeoutSeconds,omitempty"` - TxTimeoutSeconds flagutil.DeprecatedFloat64Seconds `json:"txTimeoutSeconds,omitempty"` - MaxRows int `json:"maxRows,omitempty"` - WarnRows int `json:"warnRows,omitempty"` + QueryTimeout time.Duration `json:"queryTimeoutSeconds,omitempty"` + TxTimeout time.Duration `json:"txTimeoutSeconds,omitempty"` + MaxRows int `json:"maxRows,omitempty"` + WarnRows int `json:"warnRows,omitempty"` } func (cfg *OltpConfig) MarshalJSON() ([]byte, error) { @@ -473,23 +566,51 @@ func (cfg *OltpConfig) MarshalJSON() ([]byte, error) { tmp := struct { Proxy - QueryTimeoutSeconds string `json:"queryTimeoutSeconds,omitempty"` - TxTimeoutSeconds string `json:"txTimeoutSeconds,omitempty"` + QueryTimeout string `json:"queryTimeoutSeconds,omitempty"` + TxTimeout string `json:"txTimeoutSeconds,omitempty"` }{ Proxy: Proxy(*cfg), } - if d := cfg.QueryTimeoutSeconds.Get(); d != 0 { - tmp.QueryTimeoutSeconds = d.String() + if d := cfg.QueryTimeout; d != 0 { + tmp.QueryTimeout = d.String() } - if d := cfg.TxTimeoutSeconds.Get(); d != 0 { - tmp.TxTimeoutSeconds = d.String() + if d := cfg.TxTimeout; d != 0 { + tmp.TxTimeout = d.String() } return json.Marshal(&tmp) } +func (cfg *OltpConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + OltpConfig + QueryTimeout string `json:"queryTimeoutSeconds,omitempty"` + TxTimeout string `json:"txTimeoutSeconds,omitempty"` + } + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.QueryTimeout != "" { + cfg.QueryTimeout, err = time.ParseDuration(tmp.QueryTimeout) + if err != nil { + return err + } + } + + if tmp.TxTimeout != "" { + cfg.TxTimeout, err = time.ParseDuration(tmp.TxTimeout) + if err != nil { + return err + } + } + + return nil +} + // HotRowProtectionConfig contains the config for hot row protection. type HotRowProtectionConfig struct { // Mode can be disable, dryRun or enable. Default is disable. @@ -501,97 +622,177 @@ type HotRowProtectionConfig struct { // HealthcheckConfig contains the config for healthcheck. type HealthcheckConfig struct { - IntervalSeconds flagutil.DeprecatedFloat64Seconds `json:"intervalSeconds,omitempty"` - DegradedThresholdSeconds flagutil.DeprecatedFloat64Seconds `json:"degradedThresholdSeconds,omitempty"` - UnhealthyThresholdSeconds flagutil.DeprecatedFloat64Seconds `json:"unhealthyThresholdSeconds,omitempty"` + Interval time.Duration + DegradedThreshold time.Duration + UnhealthyThreshold time.Duration } func (cfg *HealthcheckConfig) MarshalJSON() ([]byte, error) { - type Proxy HealthcheckConfig - - tmp := struct { - Proxy + var tmp struct { IntervalSeconds string `json:"intervalSeconds,omitempty"` DegradedThresholdSeconds string `json:"degradedThresholdSeconds,omitempty"` UnhealthyThresholdSeconds string `json:"unhealthyThresholdSeconds,omitempty"` - }{ - Proxy: Proxy(*cfg), } - if d := cfg.IntervalSeconds.Get(); d != 0 { + if d := cfg.Interval; d != 0 { tmp.IntervalSeconds = d.String() } - if d := cfg.DegradedThresholdSeconds.Get(); d != 0 { + if d := cfg.DegradedThreshold; d != 0 { tmp.DegradedThresholdSeconds = d.String() } - if d := cfg.UnhealthyThresholdSeconds.Get(); d != 0 { + if d := cfg.UnhealthyThreshold; d != 0 { tmp.UnhealthyThresholdSeconds = d.String() } return json.Marshal(&tmp) } +func (cfg *HealthcheckConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + Interval string `json:"intervalSeconds,omitempty"` + DegradedThreshold string `json:"degradedThresholdSeconds,omitempty"` + UnhealthyThreshold string `json:"unhealthyThresholdSeconds,omitempty"` + } + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.Interval != "" { + cfg.Interval, err = time.ParseDuration(tmp.Interval) + if err != nil { + return err + } + } + + if tmp.DegradedThreshold != "" { + cfg.DegradedThreshold, err = time.ParseDuration(tmp.DegradedThreshold) + if err != nil { + return err + } + } + + if tmp.UnhealthyThreshold != "" { + cfg.UnhealthyThreshold, err = time.ParseDuration(tmp.UnhealthyThreshold) + if err != nil { + return err + } + } + + return nil +} + // GracePeriodsConfig contains various grace periods. // TODO(sougou): move lameduck here? type GracePeriodsConfig struct { - ShutdownSeconds flagutil.DeprecatedFloat64Seconds `json:"shutdownSeconds,omitempty"` - TransitionSeconds flagutil.DeprecatedFloat64Seconds `json:"transitionSeconds,omitempty"` + Shutdown time.Duration + Transition time.Duration } func (cfg *GracePeriodsConfig) MarshalJSON() ([]byte, error) { - type Proxy GracePeriodsConfig - - tmp := struct { - Proxy + var tmp struct { ShutdownSeconds string `json:"shutdownSeconds,omitempty"` TransitionSeconds string `json:"transitionSeconds,omitempty"` - }{ - Proxy: Proxy(*cfg), } - if d := cfg.ShutdownSeconds.Get(); d != 0 { + if d := cfg.Shutdown; d != 0 { tmp.ShutdownSeconds = d.String() } - if d := cfg.TransitionSeconds.Get(); d != 0 { + if d := cfg.Transition; d != 0 { tmp.TransitionSeconds = d.String() } return json.Marshal(&tmp) } +func (cfg *GracePeriodsConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + Shutdown string `json:"shutdownSeconds,omitempty"` + Transition string `json:"transitionSeconds,omitempty"` + } + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.Shutdown != "" { + cfg.Shutdown, err = time.ParseDuration(tmp.Shutdown) + if err != nil { + return err + } + } + + if tmp.Transition != "" { + cfg.Transition, err = time.ParseDuration(tmp.Transition) + if err != nil { + return err + } + } + + return nil +} + // ReplicationTrackerConfig contains the config for the replication tracker. type ReplicationTrackerConfig struct { // Mode can be disable, polling or heartbeat. Default is disable. - Mode string `json:"mode,omitempty"` - HeartbeatIntervalSeconds flagutil.DeprecatedFloat64Seconds `json:"heartbeatIntervalSeconds,omitempty"` - HeartbeatOnDemandSeconds flagutil.DeprecatedFloat64Seconds `json:"heartbeatOnDemandSeconds,omitempty"` + Mode string `json:"mode,omitempty"` + HeartbeatInterval time.Duration + HeartbeatOnDemand time.Duration } func (cfg *ReplicationTrackerConfig) MarshalJSON() ([]byte, error) { - type Proxy ReplicationTrackerConfig - tmp := struct { - Proxy + Mode string `json:"mode,omitempty"` HeartbeatIntervalSeconds string `json:"heartbeatIntervalSeconds,omitempty"` HeartbeatOnDemandSeconds string `json:"heartbeatOnDemandSeconds,omitempty"` }{ - Proxy: Proxy(*cfg), + Mode: cfg.Mode, } - if d := cfg.HeartbeatIntervalSeconds.Get(); d != 0 { + if d := cfg.HeartbeatInterval; d != 0 { tmp.HeartbeatIntervalSeconds = d.String() } - if d := cfg.HeartbeatOnDemandSeconds.Get(); d != 0 { + if d := cfg.HeartbeatOnDemand; d != 0 { tmp.HeartbeatOnDemandSeconds = d.String() } return json.Marshal(&tmp) } +func (cfg *ReplicationTrackerConfig) UnmarshalJSON(data []byte) (err error) { + var tmp struct { + Mode string `json:"mode,omitempty"` + HeartbeatInterval string `json:"heartbeatIntervalSeconds,omitempty"` + HeartbeatOnDemand string `json:"heartbeatOnDemandSeconds,omitempty"` + } + + if err = json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.HeartbeatInterval != "" { + cfg.HeartbeatInterval, err = time.ParseDuration(tmp.HeartbeatInterval) + if err != nil { + return err + } + } + + if tmp.HeartbeatOnDemand != "" { + cfg.HeartbeatOnDemand, err = time.ParseDuration(tmp.HeartbeatOnDemand) + if err != nil { + return err + } + } + + cfg.Mode = tmp.Mode + + return nil +} + // TransactionLimitConfig captures configuration of transaction pool slots // limiter configuration. type TransactionLimitConfig struct { @@ -634,9 +835,9 @@ func (c *TabletConfig) Clone() *TabletConfig { func (c *TabletConfig) SetTxTimeoutForWorkload(val time.Duration, workload querypb.ExecuteOptions_Workload) { switch workload { case querypb.ExecuteOptions_OLAP: - _ = c.Olap.TxTimeoutSeconds.Set(val.String()) + c.Olap.TxTimeout = val case querypb.ExecuteOptions_OLTP: - _ = c.Oltp.TxTimeoutSeconds.Set(val.String()) + c.Oltp.TxTimeout = val default: panic(fmt.Sprintf("unsupported workload type: %v", workload)) } @@ -649,9 +850,9 @@ func (c *TabletConfig) TxTimeoutForWorkload(workload querypb.ExecuteOptions_Work case querypb.ExecuteOptions_DBA: return 0 case querypb.ExecuteOptions_OLAP: - return c.Olap.TxTimeoutSeconds.Get() + return c.Olap.TxTimeout default: - return c.Oltp.TxTimeoutSeconds.Get() + return c.Oltp.TxTimeout } } @@ -742,54 +943,36 @@ func (c *TabletConfig) verifyTxThrottlerConfig() error { // They actually get overwritten during Init. var defaultConfig = TabletConfig{ OltpReadPool: ConnPoolConfig{ - Size: 16, - // TODO (ajm188): remove the zero-value ones after these are durations. - // See the comment below in GracePeriodsConfig as to why they are needed - // for now. - TimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-query-pool-timeout", 0), - IdleTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-idle-timeout", 30*time.Minute), - MaxLifetimeSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-pool-conn-max-lifetime", 0), - MaxWaiters: 5000, + Size: 16, + IdleTimeout: 30 * time.Minute, + MaxWaiters: 5000, }, OlapReadPool: ConnPoolConfig{ - Size: 200, - // TODO (ajm188): remove the zero-value ones after these are durations. - // See the comment below in GracePeriodsConfig as to why they are needed - // for now. - TimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-stream-pool-timeout", 0), - IdleTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-stream-pool-idle-timeout", 30*time.Minute), + Size: 200, + IdleTimeout: 30 * time.Minute, }, TxPool: ConnPoolConfig{ - Size: 20, - TimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-txpool-timeout", time.Second), - // No actual flag for this one, but has non-zero value - IdleTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-txpool-idle-timeout", 30*time.Minute), - MaxWaiters: 5000, + Size: 20, + Timeout: time.Second, + IdleTimeout: 30 * time.Minute, + MaxWaiters: 5000, }, Olap: OlapConfig{ - TxTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-olap-transaction-timeout", 30*time.Second), + TxTimeout: 30 * time.Second, }, Oltp: OltpConfig{ - QueryTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-query-timeout", 30*time.Second), - TxTimeoutSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-transaction-timeout", 30*time.Second), - MaxRows: 10000, + QueryTimeout: 30 * time.Second, + TxTimeout: 30 * time.Second, + MaxRows: 10000, }, Healthcheck: HealthcheckConfig{ - IntervalSeconds: flagutil.NewDeprecatedFloat64Seconds("health_check_interval", 20*time.Second), - DegradedThresholdSeconds: flagutil.NewDeprecatedFloat64Seconds("degraded_threshold", 30*time.Second), - UnhealthyThresholdSeconds: flagutil.NewDeprecatedFloat64Seconds("unhealthy_threshold", 2*time.Hour), - }, - GracePeriods: GracePeriodsConfig{ - // TODO (ajm188) remove after these are durations. it's not necessary - // for production code because it's the zero value, but it's required - // for tests to pass (which require the name field to be present for - // deep equality). - ShutdownSeconds: flagutil.NewDeprecatedFloat64Seconds("shutdown_grace_period", 0), + Interval: 20 * time.Second, + DegradedThreshold: 30 * time.Second, + UnhealthyThreshold: 2 * time.Hour, }, ReplicationTracker: ReplicationTrackerConfig{ - Mode: Disable, - HeartbeatIntervalSeconds: flagutil.NewDeprecatedFloat64Seconds("heartbeat_interval", 250*time.Millisecond), - HeartbeatOnDemandSeconds: flagutil.NewDeprecatedFloat64Seconds("heartbeat_on_demand_duration", 0), + Mode: Disable, + HeartbeatInterval: 250 * time.Millisecond, }, HotRowProtection: HotRowProtectionConfig{ Mode: Disable, @@ -813,8 +996,8 @@ var defaultConfig = TabletConfig{ QueryCacheMemory: 32 * 1024 * 1024, // 32 mb for our query cache // The doorkeeper for the plan cache is disabled by default in endtoend tests to ensure // results are consistent between runs. - QueryCacheDoorkeeper: !servenv.TestingEndtoend, - SchemaReloadIntervalSeconds: flagutil.NewDeprecatedFloat64Seconds("queryserver-config-schema-reload-time", 30*time.Minute), + QueryCacheDoorkeeper: !servenv.TestingEndtoend, + SchemaReloadInterval: 30 * time.Minute, // SchemaChangeReloadTimeout is used for the signal reload operation where we have to query mysqld. // The queries during the signal reload operation are typically expected to have low load, // but in busy systems with many tables, some queries may take longer than anticipated. diff --git a/go/vt/vttablet/tabletserver/tabletenv/config_test.go b/go/vt/vttablet/tabletserver/tabletenv/config_test.go index e472cbb4789..c6f65cb94cb 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config_test.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config_test.go @@ -47,8 +47,11 @@ func TestConfigParse(t *testing.T) { }, }, OltpReadPool: ConnPoolConfig{ - Size: 16, - MaxWaiters: 40, + Size: 16, + MaxWaiters: 40, + Timeout: 10 * time.Second, + IdleTimeout: 20 * time.Second, + MaxLifetime: 50 * time.Second, }, RowStreamer: RowStreamerConfig{ MaxInnoDBTrxHistLen: 1000, @@ -56,10 +59,6 @@ func TestConfigParse(t *testing.T) { }, } - _ = cfg.OltpReadPool.TimeoutSeconds.Set("10s") - _ = cfg.OltpReadPool.IdleTimeoutSeconds.Set("20s") - _ = cfg.OltpReadPool.MaxLifetimeSeconds.Set("50s") - gotBytes, err := yaml2.Marshal(&cfg) require.NoError(t, err) wantBytes := `db: @@ -109,9 +108,9 @@ txPool: {} user: c oltpReadPool: size: 16 - idleTimeoutSeconds: 20 + idleTimeoutSeconds: 20s maxWaiters: 40 - maxLifetimeSeconds: 50 + maxLifetimeSeconds: 50s `) gotCfg := cfg gotCfg.DB = cfg.DB.Clone() @@ -178,17 +177,17 @@ func TestClone(t *testing.T) { cfg1 := &TabletConfig{ OltpReadPool: ConnPoolConfig{ - Size: 16, - MaxWaiters: 40, + Size: 16, + MaxWaiters: 40, + Timeout: 10 * time.Second, + IdleTimeout: 20 * time.Second, + MaxLifetime: 50 * time.Second, }, RowStreamer: RowStreamerConfig{ MaxInnoDBTrxHistLen: 1000000, MaxMySQLReplLagSecs: 43200, }, } - _ = cfg1.OltpReadPool.TimeoutSeconds.Set("10s") - _ = cfg1.OltpReadPool.IdleTimeoutSeconds.Set("20s") - _ = cfg1.OltpReadPool.MaxLifetimeSeconds.Set("50s") cfg2 := cfg1.Clone() assert.Equal(t, cfg1, cfg2) @@ -206,14 +205,14 @@ func TestFlags(t *testing.T) { // Simple Init. Init() - _ = want.OlapReadPool.IdleTimeoutSeconds.Set("30m") - _ = want.TxPool.IdleTimeoutSeconds.Set("30m") + want.OlapReadPool.IdleTimeout = 30 * time.Minute + want.TxPool.IdleTimeout = 30 * time.Minute want.HotRowProtection.Mode = Disable want.Consolidator = Enable - _ = want.Healthcheck.IntervalSeconds.Set("20s") - _ = want.Healthcheck.DegradedThresholdSeconds.Set("30s") - _ = want.Healthcheck.UnhealthyThresholdSeconds.Set("2h") - _ = want.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") + want.Healthcheck.Interval = 20 * time.Second + want.Healthcheck.DegradedThreshold = 30 * time.Second + want.Healthcheck.UnhealthyThreshold = 2 * time.Hour + want.ReplicationTracker.HeartbeatInterval = time.Second want.ReplicationTracker.Mode = Disable assert.Equal(t, want.DB, currentConfig.DB) assert.Equal(t, want, currentConfig) @@ -269,52 +268,52 @@ func TestFlags(t *testing.T) { enableHeartbeat = true heartbeatInterval = 1 * time.Second currentConfig.ReplicationTracker.Mode = "" - currentConfig.ReplicationTracker.HeartbeatIntervalSeconds.Set("0s") + currentConfig.ReplicationTracker.HeartbeatInterval = 0 Init() want.ReplicationTracker.Mode = Heartbeat - want.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") + want.ReplicationTracker.HeartbeatInterval = time.Second assert.Equal(t, want, currentConfig) enableHeartbeat = false heartbeatInterval = 1 * time.Second currentConfig.ReplicationTracker.Mode = "" - currentConfig.ReplicationTracker.HeartbeatIntervalSeconds.Set("0s") + currentConfig.ReplicationTracker.HeartbeatInterval = 0 Init() want.ReplicationTracker.Mode = Disable - want.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") + want.ReplicationTracker.HeartbeatInterval = time.Second assert.Equal(t, want, currentConfig) enableReplicationReporter = true heartbeatInterval = 1 * time.Second currentConfig.ReplicationTracker.Mode = "" - currentConfig.ReplicationTracker.HeartbeatIntervalSeconds.Set("0s") + currentConfig.ReplicationTracker.HeartbeatInterval = 0 Init() want.ReplicationTracker.Mode = Polling - want.ReplicationTracker.HeartbeatIntervalSeconds.Set("1s") + want.ReplicationTracker.HeartbeatInterval = time.Second assert.Equal(t, want, currentConfig) - healthCheckInterval = 1 * time.Second - currentConfig.Healthcheck.IntervalSeconds.Set("0s") + healthCheckInterval = time.Second + currentConfig.Healthcheck.Interval = 0 Init() - want.Healthcheck.IntervalSeconds.Set("1s") + want.Healthcheck.Interval = time.Second assert.Equal(t, want, currentConfig) degradedThreshold = 2 * time.Second - currentConfig.Healthcheck.DegradedThresholdSeconds.Set("0s") + currentConfig.Healthcheck.DegradedThreshold = 0 Init() - want.Healthcheck.DegradedThresholdSeconds.Set("2s") + want.Healthcheck.DegradedThreshold = 2 * time.Second assert.Equal(t, want, currentConfig) unhealthyThreshold = 3 * time.Second - currentConfig.Healthcheck.UnhealthyThresholdSeconds.Set("0s") + currentConfig.Healthcheck.UnhealthyThreshold = 0 Init() - want.Healthcheck.UnhealthyThresholdSeconds.Set("3s") + want.Healthcheck.UnhealthyThreshold = 3 * time.Second assert.Equal(t, want, currentConfig) transitionGracePeriod = 4 * time.Second - currentConfig.GracePeriods.TransitionSeconds.Set("0s") + currentConfig.GracePeriods.Transition = 0 Init() - want.GracePeriods.TransitionSeconds.Set("4s") + want.GracePeriods.Transition = 4 * time.Second assert.Equal(t, want, currentConfig) currentConfig.SanitizeLogMessages = false @@ -425,7 +424,6 @@ func TestVerifyTxThrottlerConfig(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.Name, func(t *testing.T) { t.Parallel() diff --git a/go/vt/vttablet/tabletserver/tabletenv/env.go b/go/vt/vttablet/tabletserver/tabletenv/env.go index c7202080c4d..27b4330c735 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/env.go +++ b/go/vt/vttablet/tabletserver/tabletenv/env.go @@ -22,39 +22,44 @@ import ( "vitess.io/vitess/go/tb" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vtenv" ) // Env defines the functions supported by TabletServer -// that the sub-componennts need to access. +// that the sub-components need to access. type Env interface { CheckMySQL() Config() *TabletConfig Exporter() *servenv.Exporter Stats() *Stats LogError() + Environment() *vtenv.Environment } type testEnv struct { config *TabletConfig exporter *servenv.Exporter stats *Stats + env *vtenv.Environment } // NewEnv creates an Env that can be used for tabletserver subcomponents // without an actual TabletServer. -func NewEnv(config *TabletConfig, exporterName string) Env { +func NewEnv(env *vtenv.Environment, config *TabletConfig, exporterName string) Env { exporter := servenv.NewExporter(exporterName, "Tablet") return &testEnv{ config: config, exporter: exporter, stats: NewStats(exporter), + env: env, } } -func (*testEnv) CheckMySQL() {} -func (te *testEnv) Config() *TabletConfig { return te.config } -func (te *testEnv) Exporter() *servenv.Exporter { return te.exporter } -func (te *testEnv) Stats() *Stats { return te.stats } +func (*testEnv) CheckMySQL() {} +func (te *testEnv) Config() *TabletConfig { return te.config } +func (te *testEnv) Exporter() *servenv.Exporter { return te.exporter } +func (te *testEnv) Stats() *Stats { return te.stats } +func (te *testEnv) Environment() *vtenv.Environment { return te.env } func (te *testEnv) LogError() { if x := recover(); x != nil { diff --git a/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go b/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go index 84de50aae74..51e056687b5 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go +++ b/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go @@ -17,7 +17,6 @@ limitations under the License. package tabletenv import ( - "bytes" "context" "encoding/json" "errors" @@ -54,7 +53,7 @@ func TestLogStats(t *testing.T) { } func testFormat(stats *LogStats, params url.Values) string { - var b bytes.Buffer + var b strings.Builder stats.Logf(&b, params) return b.String() } @@ -183,7 +182,6 @@ func TestLogStatsFilter(t *testing.T) { if got != want { t.Errorf("logstats format: got:\n%q\nwant:\n%q\n", got, want) } - } func TestLogStatsFormatQuerySources(t *testing.T) { diff --git a/go/vt/vttablet/tabletserver/tabletenv/seconds.go b/go/vt/vttablet/tabletserver/tabletenv/seconds.go index 205b571c9b1..ae11121f2de 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/seconds.go +++ b/go/vt/vttablet/tabletserver/tabletenv/seconds.go @@ -23,7 +23,7 @@ import ( ) // Seconds provides convenience functions for extracting -// duration from flaot64 seconds values. +// duration from float64 seconds values. type Seconds float64 // SecondsVar is like a flag.Float64Var, but it works for Seconds. diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 25eb4da7168..6ecc46c68ab 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -33,10 +33,9 @@ import ( "syscall" "time" + "vitess.io/vitess/go/acl" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/pools/smartconnpool" - - "vitess.io/vitess/go/acl" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/tb" @@ -52,6 +51,7 @@ import ( "vitess.io/vitess/go/vt/tableacl" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/onlineddl" "vitess.io/vitess/go/vt/vttablet/queryservice" @@ -128,6 +128,8 @@ type TabletServer struct { // This field is only stored for testing checkMysqlGaugeFunc *stats.GaugeFunc + + env *vtenv.Environment } var _ queryservice.QueryService = (*TabletServer)(nil) @@ -138,8 +140,8 @@ var _ queryservice.QueryService = (*TabletServer)(nil) var RegisterFunctions []func(Controller) // NewServer creates a new TabletServer based on the command line flags. -func NewServer(ctx context.Context, name string, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer { - return NewTabletServer(ctx, name, tabletenv.NewCurrentConfig(), topoServer, alias) +func NewServer(ctx context.Context, env *vtenv.Environment, name string, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer { + return NewTabletServer(ctx, env, name, tabletenv.NewCurrentConfig(), topoServer, alias) } var ( @@ -149,7 +151,7 @@ var ( // NewTabletServer creates an instance of TabletServer. Only the first // instance of TabletServer will expose its state variables. -func NewTabletServer(ctx context.Context, name string, config *tabletenv.TabletConfig, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer { +func NewTabletServer(ctx context.Context, env *vtenv.Environment, name string, config *tabletenv.TabletConfig, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer { exporter := servenv.NewExporter(name, "Tablet") tsv := &TabletServer{ exporter: exporter, @@ -160,21 +162,22 @@ func NewTabletServer(ctx context.Context, name string, config *tabletenv.TabletC enableHotRowProtection: config.HotRowProtection.Mode != tabletenv.Disable, topoServer: topoServer, alias: alias.CloneVT(), + env: env, } - tsv.QueryTimeout.Store(config.Oltp.QueryTimeoutSeconds.Get().Nanoseconds()) + tsv.QueryTimeout.Store(config.Oltp.QueryTimeout.Nanoseconds()) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(ctx, topoServer, "TabletSrvTopo") }) tabletTypeFunc := func() topodatapb.TabletType { - if tsv.sm == nil { + if tsv.sm == nil || tsv.sm.Target() == nil { return topodatapb.TabletType_UNKNOWN } return tsv.sm.Target().TabletType } - tsv.statelessql = NewQueryList("oltp-stateless") - tsv.statefulql = NewQueryList("oltp-stateful") - tsv.olapql = NewQueryList("olap") + tsv.statelessql = NewQueryList("oltp-stateless", env.Parser()) + tsv.statefulql = NewQueryList("oltp-stateful", env.Parser()) + tsv.olapql = NewQueryList("olap", env.Parser()) tsv.se = schema.NewEngine(tsv) tsv.hs = newHealthStreamer(tsv, alias, tsv.se) tsv.rt = repltracker.NewReplTracker(tsv, alias) @@ -187,8 +190,8 @@ func NewTabletServer(ctx context.Context, name string, config *tabletenv.TabletC tsv.te = NewTxEngine(tsv) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) - tsv.onlineDDLExecutor = onlineddl.NewExecutor(tsv, alias, topoServer, tsv.lagThrottler, tabletTypeFunc, tsv.onlineDDLExecutorToggleTableBuffer) tsv.tableGC = gc.NewTableGC(tsv, topoServer, tsv.lagThrottler) + tsv.onlineDDLExecutor = onlineddl.NewExecutor(tsv, alias, topoServer, tsv.lagThrottler, tabletTypeFunc, tsv.onlineDDLExecutorToggleTableBuffer, tsv.tableGC.RequestChecks) tsv.sm = &stateManager{ statelessql: tsv.statelessql, @@ -223,6 +226,8 @@ func NewTabletServer(ctx context.Context, name string, config *tabletenv.TabletC tsv.registerHealthzHealthHandler() tsv.registerDebugHealthHandler() tsv.registerQueryzHandler() + tsv.registerQuerylogzHandler() + tsv.registerTxlogzHandler() tsv.registerQueryListHandlers([]*QueryList{tsv.statelessql, tsv.statefulql, tsv.olapql}) tsv.registerTwopczHandler() tsv.registerMigrationStatusHandler() @@ -238,11 +243,11 @@ func (tsv *TabletServer) loadQueryTimeout() time.Duration { // onlineDDLExecutorToggleTableBuffer is called by onlineDDLExecutor as a callback function. onlineDDLExecutor // uses it to start/stop query buffering for a given table. -// It is onlineDDLExecutor's responsibility to make sure beffering is stopped after some definite amount of time. +// It is onlineDDLExecutor's responsibility to make sure buffering is stopped after some definite amount of time. // There are two layers to buffering/unbuffering: // 1. the creation and destruction of a QueryRuleSource. The existence of such source affects query plan rules // for all new queries (see Execute() function and call to GetPlan()) -// 2. affecting already existing rules: a Rule has a concext.WithCancel, that is cancelled by onlineDDLExecutor +// 2. affecting already existing rules: a Rule has a context.WithCancel, that is cancelled by onlineDDLExecutor func (tsv *TabletServer) onlineDDLExecutorToggleTableBuffer(bufferingCtx context.Context, tableName string, timeout time.Duration, bufferQueries bool) { queryRuleSource := fmt.Sprintf("onlineddl/%s", tableName) @@ -301,6 +306,11 @@ func (tsv *TabletServer) Stats() *tabletenv.Stats { return tsv.stats } +// Environment satisfies tabletenv.Env. +func (tsv *TabletServer) Environment() *vtenv.Environment { + return tsv.env +} + // LogError satisfies tabletenv.Env. func (tsv *TabletServer) LogError() { if x := recover(); x != nil { @@ -516,7 +526,11 @@ func (tsv *TabletServer) begin(ctx context.Context, target *querypb.Target, save logStats.OriginalSQL = beginSQL if beginSQL != "" { tsv.stats.QueryTimings.Record("BEGIN", startTime) - tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), startTime) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), startTime) } else { logStats.Method = "" } @@ -550,6 +564,24 @@ func (tsv *TabletServer) getPriorityFromOptions(options *querypb.ExecuteOptions) return optionsPriority } +// resolveTargetType returns the appropriate target tablet type for a +// TabletServer request. If the caller has a local context then it's +// an internal request and the target is the local tablet's current +// target. If it's not a local context then there should always be a +// non-nil target specified. +func (tsv *TabletServer) resolveTargetType(ctx context.Context, target *querypb.Target) (topodatapb.TabletType, error) { + if target != nil { + return target.TabletType, nil + } + if !tabletenv.IsLocalContext(ctx) { + return topodatapb.TabletType_UNKNOWN, ErrNoTarget + } + if tsv.sm.Target() == nil { + return topodatapb.TabletType_UNKNOWN, nil // This is true, and does not block the request + } + return tsv.sm.Target().TabletType, nil +} + // Commit commits the specified transaction. func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, transactionID int64) (newReservedID int64, err error) { err = tsv.execRequest( @@ -572,7 +604,11 @@ func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, tra // handlePanicAndSendLogStats doesn't log the no-op. if commitSQL != "" { tsv.stats.QueryTimings.Record("COMMIT", startTime) - tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), startTime) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), startTime) } else { logStats.Method = "" } @@ -590,7 +626,11 @@ func (tsv *TabletServer) Rollback(ctx context.Context, target *querypb.Target, t target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("ROLLBACK", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) logStats.TransactionID = transactionID newReservedID, err = tsv.te.Rollback(ctx, transactionID) if newReservedID > 0 { @@ -801,18 +841,22 @@ func (tsv *TabletServer) execute(ctx context.Context, target *querypb.Target, sq return err } } + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } qre := &QueryExecutor{ - query: query, - marginComments: comments, - bindVars: bindVariables, - connID: connID, - options: options, - plan: plan, - ctx: ctx, - logStats: logStats, - tsv: tsv, - tabletType: target.GetTabletType(), - setting: connSetting, + query: query, + marginComments: comments, + bindVars: bindVariables, + connID: connID, + options: options, + plan: plan, + ctx: ctx, + logStats: logStats, + tsv: tsv, + targetTabletType: targetType, + setting: connSetting, } result, err = qre.Execute() if err != nil { @@ -904,16 +948,17 @@ func (tsv *TabletServer) streamExecute(ctx context.Context, target *querypb.Targ } } qre := &QueryExecutor{ - query: query, - marginComments: comments, - bindVars: bindVariables, - connID: connID, - options: options, - plan: plan, - ctx: ctx, - logStats: logStats, - tsv: tsv, - setting: connSetting, + query: query, + marginComments: comments, + bindVars: bindVariables, + connID: connID, + options: options, + plan: plan, + ctx: ctx, + logStats: logStats, + tsv: tsv, + targetTabletType: target.GetTabletType(), + setting: connSetting, } return qre.Stream(callback) }, @@ -1204,7 +1249,11 @@ func (tsv *TabletServer) ReserveBeginExecute(ctx context.Context, target *queryp target, options, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("RESERVE", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) connID, sessionStateChanges, err = tsv.te.ReserveBegin(ctx, options, preQueries, postBeginQueries) if err != nil { return err @@ -1250,7 +1299,11 @@ func (tsv *TabletServer) ReserveBeginStreamExecute( target, options, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("RESERVE", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) connID, sessionStateChanges, err = tsv.te.ReserveBegin(ctx, options, preQueries, postBeginQueries) if err != nil { return err @@ -1304,7 +1357,11 @@ func (tsv *TabletServer) ReserveExecute(ctx context.Context, target *querypb.Tar target, options, allowOnShutdown, func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("RESERVE", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) state.ReservedID, err = tsv.te.Reserve(ctx, options, transactionID, preQueries) if err != nil { return err @@ -1355,7 +1412,11 @@ func (tsv *TabletServer) ReserveStreamExecute( target, options, allowOnShutdown, func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("RESERVE", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) state.ReservedID, err = tsv.te.Reserve(ctx, options, transactionID, preQueries) if err != nil { return err @@ -1385,7 +1446,11 @@ func (tsv *TabletServer) Release(ctx context.Context, target *querypb.Target, tr target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tsv.stats.QueryTimings.Record("RELEASE", time.Now()) - defer tsv.stats.QueryTimingsByTabletType.Record(target.TabletType.String(), time.Now()) + targetType, err := tsv.resolveTargetType(ctx, target) + if err != nil { + return err + } + defer tsv.stats.QueryTimingsByTabletType.Record(targetType.String(), time.Now()) logStats.TransactionID = transactionID logStats.ReservedID = reservedID if reservedID != 0 { @@ -1393,7 +1458,7 @@ func (tsv *TabletServer) Release(ctx context.Context, target *querypb.Target, tr return tsv.te.Release(reservedID) } // Rollback to cleanup the transaction before returning to the pool. - _, err := tsv.te.Rollback(ctx, transactionID) + _, err = tsv.te.Rollback(ctx, transactionID) return err }, ) @@ -1469,6 +1534,7 @@ func (tsv *TabletServer) execRequest( span.Annotate("workload_name", options.WorkloadName) } trace.AnnotateSQL(span, sqlparser.Preview(sql)) + // With a tabletenv.LocalContext() the target will be nil. if target != nil { span.Annotate("cell", target.Cell) span.Annotate("shard", target.Shard) @@ -1512,13 +1578,13 @@ func (tsv *TabletServer) handlePanicAndSendLogStats( // not a concern. var messagef, logMessage, query, truncatedQuery string messagef = fmt.Sprintf("Uncaught panic for %%v:\n%v\n%s", x, tb.Stack(4) /* Skip the last 4 boiler-plate frames. */) - query = queryAsString(sql, bindVariables, tsv.TerseErrors, false) + query = queryAsString(sql, bindVariables, tsv.TerseErrors, false, tsv.env.Parser()) terr := vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "%s", fmt.Sprintf(messagef, query)) if tsv.TerseErrors == tsv.Config().SanitizeLogMessages { - truncatedQuery = queryAsString(sql, bindVariables, tsv.TerseErrors, true) + truncatedQuery = queryAsString(sql, bindVariables, tsv.TerseErrors, true, tsv.env.Parser()) logMessage = fmt.Sprintf(messagef, truncatedQuery) } else { - truncatedQuery = queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true) + truncatedQuery = queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true, tsv.env.Parser()) logMessage = fmt.Sprintf(messagef, truncatedQuery) } log.Error(logMessage) @@ -1578,20 +1644,20 @@ func (tsv *TabletServer) convertAndLogError(ctx context.Context, sql string, bin sqlState := sqlErr.SQLState() errnum := sqlErr.Number() if tsv.TerseErrors && errCode != vtrpcpb.Code_FAILED_PRECONDITION { - err = vterrors.Errorf(errCode, "(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.TerseErrors, false)) + err = vterrors.Errorf(errCode, "(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.TerseErrors, false, tsv.env.Parser())) if logMethod != nil { - message = fmt.Sprintf("(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true)) + message = fmt.Sprintf("(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true, tsv.env.Parser())) } } else { - err = vterrors.Errorf(errCode, "%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, queryAsString(sql, bindVariables, false, false)) + err = vterrors.Errorf(errCode, "%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, queryAsString(sql, bindVariables, false, false, tsv.env.Parser())) if logMethod != nil { - message = fmt.Sprintf("%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true)) + message = fmt.Sprintf("%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true, tsv.env.Parser())) } } } else { err = vterrors.Errorf(errCode, "%v%s", err.Error(), callerID) if logMethod != nil { - message = fmt.Sprintf("%v: %v", err, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true)) + message = fmt.Sprintf("%v: %v", err, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages, true, tsv.env.Parser())) } } @@ -1775,6 +1841,18 @@ func (tsv *TabletServer) registerQueryzHandler() { }) } +func (tsv *TabletServer) registerQuerylogzHandler() { + tsv.exporter.HandleFunc("/querylogz", func(w http.ResponseWriter, r *http.Request) { + ch := tabletenv.StatsLogger.Subscribe("querylogz") + defer tabletenv.StatsLogger.Unsubscribe(ch) + querylogzHandler(ch, w, r, tsv.env.Parser()) + }) +} + +func (tsv *TabletServer) registerTxlogzHandler() { + tsv.exporter.HandleFunc("/txlogz", txlogzHandler) +} + func (tsv *TabletServer) registerQueryListHandlers(queryLists []*QueryList) { tsv.exporter.HandleFunc("/livequeryz/", func(w http.ResponseWriter, r *http.Request) { livequeryzHandler(queryLists, w, r) @@ -2023,7 +2101,7 @@ func (tsv *TabletServer) ConsolidatorMode() string { // If sanitize is false it also includes the bind variables. // If truncateForLog is true, it truncates the sql query and the // bind variables. -func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable, sanitize bool, truncateForLog bool) string { +func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable, sanitize bool, truncateForLog bool, parser *sqlparser.Parser) string { // Add the bind vars unless this needs to be sanitized, e.g. for log messages bvBuf := &bytes.Buffer{} fmt.Fprintf(bvBuf, "BindVars: {") @@ -2047,7 +2125,7 @@ func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable, s // Truncate the bind vars if necessary bv := bvBuf.String() - maxLen := sqlparser.GetTruncateErrLen() + maxLen := parser.GetTruncateErrLen() if truncateForLog && maxLen > 0 && len(bv) > maxLen { if maxLen <= 12 { bv = sqlparser.TruncationText @@ -2058,7 +2136,7 @@ func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable, s // Truncate the sql query if necessary if truncateForLog { - sql = sqlparser.TruncateForLog(sql) + sql = parser.TruncateForLog(sql) } // sql is the normalized query without the bind vars diff --git a/go/vt/vttablet/tabletserver/tabletserver_test.go b/go/vt/vttablet/tabletserver/tabletserver_test.go index d2fb10e5a77..97777c0245f 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_test.go @@ -30,10 +30,11 @@ import ( "testing" "time" + "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" - "vitess.io/vitess/go/vt/sidecardb" - "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/test/utils" @@ -441,9 +442,9 @@ func TestTabletServerConcludeTransaction(t *testing.T) { func TestTabletServerBeginFail(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TxPool.Size = 1 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg := tabletenv.NewDefaultConfig() + cfg.TxPool.Size = 1 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -563,6 +564,78 @@ func TestTabletServerCommitPrepared(t *testing.T) { require.NoError(t, err) } +// TestTabletServerWithNilTarget confirms that a nil target is +// handled correctly. This means that when a local context is +// used, the target type is inferred from the local tablet's +// latest target type. +// And if it's not a local context then we return an error. +func TestTabletServerWithNilTarget(t *testing.T) { + // A non-nil target is required when not using a local context. + ctx := tabletenv.LocalContext() + db, tsv := setupTabletServerTest(t, ctx, "") + defer tsv.StopService() + defer db.Close() + + // With a nil target, the local tablet's latest target type is + // what should be used as the inferred target type for our local + // calls. + target := (*querypb.Target)(nil) + localTargetType := topodatapb.TabletType_RDONLY // Use a non-default type + err := tsv.SetServingType(localTargetType, time.Now(), true, "test") + require.NoError(t, err) + + baseKey := "TabletServerTest" // Our TabletServer's name + fullKey := fmt.Sprintf("%s.%s", baseKey, localTargetType.String()) + + executeSQL := "select * from test_table limit 1000" + executeSQLResult := &sqltypes.Result{ + Fields: []*querypb.Field{ + {Type: sqltypes.VarBinary}, + }, + Rows: [][]sqltypes.Value{ + {sqltypes.NewVarBinary("row01")}, + }, + } + // BEGIN gets transmuted to this since it's a RDONLY tablet. + db.AddQuery("start transaction read only", &sqltypes.Result{}) + db.AddQuery(executeSQL, executeSQLResult) + + expectedCount := tsv.stats.QueryTimingsByTabletType.Counts()[fullKey] + + state, err := tsv.Begin(ctx, target, nil) + require.NoError(t, err) + expectedCount++ + require.Equal(t, expectedCount, tsv.stats.QueryTimingsByTabletType.Counts()[fullKey]) + + _, err = tsv.Execute(ctx, target, executeSQL, nil, state.TransactionID, 0, nil) + require.NoError(t, err) + expectedCount++ + require.Equal(t, expectedCount, tsv.stats.QueryTimingsByTabletType.Counts()[fullKey]) + + _, err = tsv.Rollback(ctx, target, state.TransactionID) + require.NoError(t, err) + expectedCount++ + require.Equal(t, expectedCount, tsv.stats.QueryTimingsByTabletType.Counts()[fullKey]) + + state, err = tsv.Begin(ctx, target, nil) + require.NoError(t, err) + expectedCount++ + require.Equal(t, expectedCount, tsv.stats.QueryTimingsByTabletType.Counts()[fullKey]) + + _, err = tsv.Commit(ctx, target, state.TransactionID) + require.NoError(t, err) + expectedCount++ + require.Equal(t, expectedCount, tsv.stats.QueryTimingsByTabletType.Counts()[fullKey]) + + // Finally be sure that we return an error now as expected when NOT + // using a local context but passing a nil target. + nonLocalCtx := context.Background() + _, err = tsv.Begin(nonLocalCtx, target, nil) + require.True(t, errors.Is(err, ErrNoTarget)) + _, err = tsv.resolveTargetType(nonLocalCtx, target) + require.True(t, errors.Is(err, ErrNoTarget)) +} + func TestSmallerTimeout(t *testing.T) { testcases := []struct { t1, t2, want time.Duration @@ -874,12 +947,12 @@ func TestSerializeTransactionsSameRow(t *testing.T) { // The actual execution looks like this: // tx1 | tx3 // tx2 - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Enable - config.HotRowProtection.MaxConcurrency = 1 + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Enable + cfg.HotRowProtection.MaxConcurrency = 1 // Reduce the txpool to 2 because we should never consume more than two slots. - config.TxPool.Size = 2 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg.TxPool.Size = 2 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -982,11 +1055,11 @@ func TestSerializeTransactionsSameRow(t *testing.T) { func TestDMLQueryWithoutWhereClause(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Enable - config.HotRowProtection.MaxConcurrency = 1 - config.TxPool.Size = 2 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Enable + cfg.HotRowProtection.MaxConcurrency = 1 + cfg.TxPool.Size = 2 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -1009,12 +1082,12 @@ func TestSerializeTransactionsSameRow_ConcurrentTransactions(t *testing.T) { // Out of these three, two can run in parallel because we increased the // ConcurrentTransactions limit to 2. // One out of the three transaction will always get serialized though. - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Enable - config.HotRowProtection.MaxConcurrency = 2 + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Enable + cfg.HotRowProtection.MaxConcurrency = 2 // Reduce the txpool to 2 because we should never consume more than two slots. - config.TxPool.Size = 2 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg.TxPool.Size = 2 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -1146,11 +1219,11 @@ func TestSerializeTransactionsSameRow_TooManyPendingRequests(t *testing.T) { // serialized. // Since we start to queue before the transaction pool would queue, we need // to enforce an upper limit as well to protect vttablet. - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Enable - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 1 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Enable + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 1 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -1230,10 +1303,10 @@ func TestSerializeTransactionsSameRow_RequestCanceled(t *testing.T) { // tx1 and tx2 run against the same row. // tx2 is blocked on tx1. Eventually, tx2 is canceled and its request fails. // Only after that tx1 commits and finishes. - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Enable - config.HotRowProtection.MaxConcurrency = 1 - db, tsv := setupTabletServerTestCustom(t, ctx, config, "") + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Enable + cfg.HotRowProtection.MaxConcurrency = 1 + db, tsv := setupTabletServerTestCustom(t, ctx, cfg, "", vtenv.NewTestEnv()) defer tsv.StopService() defer db.Close() @@ -1486,12 +1559,60 @@ func TestHandleExecUnknownError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() logStats := tabletenv.NewLogStats(ctx, "TestHandleExecError") - config := tabletenv.NewDefaultConfig() - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) defer tsv.handlePanicAndSendLogStats("select * from test_table", nil, logStats) panic("unknown exec error") } +// TestHandlePanicAndSendLogStatsMessageTruncation tests that when an error truncation +// length is set and a panic occurs, the code in handlePanicAndSendLogStats will +// truncate the error text in logs, but will not truncate the error text in the +// error value. +func TestHandlePanicAndSendLogStatsMessageTruncation(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tl := newTestLogger() + defer tl.Close() + logStats := tabletenv.NewLogStats(ctx, "TestHandlePanicAndSendLogStatsMessageTruncation") + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: config.DefaultMySQLVersion, + TruncateErrLen: 32, + }) + require.NoError(t, err) + + db, tsv := setupTabletServerTestCustom(t, ctx, tabletenv.NewDefaultConfig(), "", env) + defer tsv.StopService() + defer db.Close() + + longSql := "select * from test_table_loooooooooooooooooooooooooooooooooooong" + longBv := map[string]*querypb.BindVariable{ + "bv1": sqltypes.Int64BindVariable(1111111111), + "bv2": sqltypes.Int64BindVariable(2222222222), + "bv3": sqltypes.Int64BindVariable(3333333333), + "bv4": sqltypes.Int64BindVariable(4444444444), + } + + defer func() { + err := logStats.Error + want := "Uncaught panic for Sql: \"select * from test_table_loooooooooooooooooooooooooooooooooooong\", BindVars: {bv1: \"type:INT64 value:\\\"1111111111\\\"\"bv2: \"type:INT64 value:\\\"2222222222\\\"\"bv3: \"type:INT64 value:\\\"3333333333\\\"\"bv4: \"type:INT64 value:\\\"4444444444\\\"\"}" + require.Error(t, err) + assert.Contains(t, err.Error(), want) + want = "Uncaught panic for Sql: \"select * from test_t [TRUNCATED]\", BindVars: {bv1: \"typ [TRUNCATED]" + gotWhatWeWant := false + for _, log := range tl.getLogs() { + if strings.HasPrefix(log, want) { + gotWhatWeWant = true + break + } + } + assert.True(t, gotWhatWeWant) + }() + + defer tsv.handlePanicAndSendLogStats(longSql, longBv, logStats) + panic("panic from TestHandlePanicAndSendLogStatsMessageTruncation") +} + func TestQueryAsString(t *testing.T) { longSql := "select * from test_table_loooooooooooooooooooooooooooooooooooong" longBv := map[string]*querypb.BindVariable{ @@ -1500,23 +1621,25 @@ func TestQueryAsString(t *testing.T) { "bv3": sqltypes.Int64BindVariable(3333333333), "bv4": sqltypes.Int64BindVariable(4444444444), } - origTruncateErrLen := sqlparser.GetTruncateErrLen() - sqlparser.SetTruncateErrLen(32) - defer sqlparser.SetTruncateErrLen(origTruncateErrLen) + parser, err := sqlparser.New(sqlparser.Options{ + MySQLServerVersion: config.DefaultMySQLVersion, + TruncateErrLen: 32, + }) + require.NoError(t, err) - query := queryAsString(longSql, longBv, true, true) + query := queryAsString(longSql, longBv, true, true, parser) want := "Sql: \"select * from test_t [TRUNCATED]\", BindVars: {[REDACTED]}" assert.Equal(t, want, query) - query = queryAsString(longSql, longBv, true, false) + query = queryAsString(longSql, longBv, true, false, parser) want = "Sql: \"select * from test_table_loooooooooooooooooooooooooooooooooooong\", BindVars: {[REDACTED]}" assert.Equal(t, want, query) - query = queryAsString(longSql, longBv, false, true) + query = queryAsString(longSql, longBv, false, true, parser) want = "Sql: \"select * from test_t [TRUNCATED]\", BindVars: {bv1: \"typ [TRUNCATED]" assert.Equal(t, want, query) - query = queryAsString(longSql, longBv, false, false) + query = queryAsString(longSql, longBv, false, false, parser) want = "Sql: \"select * from test_table_loooooooooooooooooooooooooooooooooooong\", BindVars: {bv1: \"type:INT64 value:\\\"1111111111\\\"\"bv2: \"type:INT64 value:\\\"2222222222\\\"\"bv3: \"type:INT64 value:\\\"3333333333\\\"\"bv4: \"type:INT64 value:\\\"4444444444\\\"\"}" assert.Equal(t, want, query) } @@ -1606,8 +1729,8 @@ func (tl *testLogger) getLogs() []string { func TestHandleExecTabletError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError( @@ -1629,10 +1752,10 @@ func TestHandleExecTabletError(t *testing.T) { func TestTerseErrors(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = true - config.SanitizeLogMessages = false - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = true + cfg.SanitizeLogMessages = false + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() @@ -1663,10 +1786,10 @@ func TestTerseErrors(t *testing.T) { func TestSanitizeLogMessages(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = false - config.SanitizeLogMessages = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = false + cfg.SanitizeLogMessages = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() @@ -1697,9 +1820,9 @@ func TestSanitizeLogMessages(t *testing.T) { func TestTerseErrorsNonSQLError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError( @@ -1721,10 +1844,10 @@ func TestTerseErrorsNonSQLError(t *testing.T) { func TestSanitizeLogMessagesNonSQLError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = false - config.SanitizeLogMessages = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = false + cfg.SanitizeLogMessages = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError( @@ -1746,10 +1869,10 @@ func TestSanitizeLogMessagesNonSQLError(t *testing.T) { func TestSanitizeMessagesBindVars(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = true - config.SanitizeLogMessages = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = true + cfg.SanitizeLogMessages = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() @@ -1777,10 +1900,10 @@ func TestSanitizeMessagesBindVars(t *testing.T) { func TestSanitizeMessagesNoBindVars(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = true - config.SanitizeLogMessages = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = true + cfg.SanitizeLogMessages = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError(ctx, "", nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "sensitive message"), nil) @@ -1796,9 +1919,9 @@ func TestSanitizeMessagesNoBindVars(t *testing.T) { func TestTruncateErrorLen(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TruncateErrorLen = 32 - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TruncateErrorLen = 32 + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError( @@ -1820,19 +1943,23 @@ func TestTruncateErrorLen(t *testing.T) { func TestTruncateMessages(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = false + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = false // Sanitize the log messages, which means that the bind vars are omitted - config.SanitizeLogMessages = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg.SanitizeLogMessages = true + env, err := vtenv.New(vtenv.Options{ + MySQLServerVersion: config.DefaultMySQLVersion, + TruncateErrLen: 52, + }) + require.NoError(t, err) + tsv := NewTabletServer(ctx, env, "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() - sqlparser.SetTruncateErrLen(52) sql := "select * from test_table where xyz = :vtg1 order by abc desc" sqlErr := sqlerror.NewSQLError(10, "HY000", "sensitive message") sqlErr.Query = "select * from test_table where xyz = 'this is kinda long eh'" - err := tsv.convertAndLogError( + err = tsv.convertAndLogError( ctx, sql, map[string]*querypb.BindVariable{"vtg1": sqltypes.StringBindVariable("this is kinda long eh")}, @@ -1852,7 +1979,7 @@ func TestTruncateMessages(t *testing.T) { t.Errorf("log got '%s', want '%s'", tl.getLog(0), wantLog) } - sqlparser.SetTruncateErrLen(140) + env.Parser().SetTruncateErrLen(140) err = tsv.convertAndLogError( ctx, sql, @@ -1872,15 +1999,14 @@ func TestTruncateMessages(t *testing.T) { if wantLog != tl.getLog(1) { t.Errorf("log got '%s', want '%s'", tl.getLog(1), wantLog) } - sqlparser.SetTruncateErrLen(0) } func TestTerseErrorsIgnoreFailoverInProgress(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - config := tabletenv.NewDefaultConfig() - config.TerseErrors = true - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + cfg.TerseErrors = true + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) tl := newTestLogger() defer tl.Close() err := tsv.convertAndLogError(ctx, "select * from test_table where id = :a", @@ -1921,8 +2047,8 @@ func TestACLHUP(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() tableacl.Register("simpleacl", &simpleacl.Factory{}) - config := tabletenv.NewDefaultConfig() - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + cfg := tabletenv.NewDefaultConfig() + tsv := NewTabletServer(ctx, vtenv.NewTestEnv(), "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) f, err := os.CreateTemp("", "tableacl") require.NoError(t, err) @@ -2431,14 +2557,14 @@ func TestDatabaseNameReplaceByKeyspaceNameReserveBeginExecuteMethod(t *testing.T } func setupTabletServerTest(t testing.TB, ctx context.Context, keyspaceName string) (*fakesqldb.DB, *TabletServer) { - config := tabletenv.NewDefaultConfig() - return setupTabletServerTestCustom(t, ctx, config, keyspaceName) + cfg := tabletenv.NewDefaultConfig() + return setupTabletServerTestCustom(t, ctx, cfg, keyspaceName, vtenv.NewTestEnv()) } -func setupTabletServerTestCustom(t testing.TB, ctx context.Context, config *tabletenv.TabletConfig, keyspaceName string) (*fakesqldb.DB, *TabletServer) { +func setupTabletServerTestCustom(t testing.TB, ctx context.Context, cfg *tabletenv.TabletConfig, keyspaceName string, env *vtenv.Environment) (*fakesqldb.DB, *TabletServer) { db := setupFakeDB(t) - sidecardb.AddSchemaInitQueries(db, true) - tsv := NewTabletServer(ctx, "TabletServerTest", config, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) + sidecardb.AddSchemaInitQueries(db, true, env.Parser()) + tsv := NewTabletServer(ctx, env, "TabletServerTest", cfg, memorytopo.NewServer(ctx, ""), &topodatapb.TabletAlias{}) require.Equal(t, StateNotConnected, tsv.sm.State()) dbcfgs := newDBConfigs(db) target := &querypb.Target{ @@ -2585,7 +2711,8 @@ func addTabletServerSupportedQueries(db *fakesqldb.DB) { "rollback": {}, fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, } - sidecardb.AddSchemaInitQueries(db, true) + parser := sqlparser.NewTestParser() + sidecardb.AddSchemaInitQueries(db, true, parser) for query, result := range queryResultMap { db.AddQuery(query, result) } diff --git a/go/vt/vttablet/tabletserver/testutils_test.go b/go/vt/vttablet/tabletserver/testutils_test.go index 4760558f6ec..464e84ab47f 100644 --- a/go/vt/vttablet/tabletserver/testutils_test.go +++ b/go/vt/vttablet/tabletserver/testutils_test.go @@ -30,7 +30,7 @@ import ( var errRejected = errors.New("rejected") func newDBConfigs(db *fakesqldb.DB) *dbconfigs.DBConfigs { - params, _ := db.ConnParams().MysqlParams() + params := db.ConnParams() cp := *params return dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb") } diff --git a/go/vt/vttablet/tabletserver/throttle/base/http.go b/go/vt/vttablet/tabletserver/throttle/base/http.go index 6f657766ad1..bbf4662d6cf 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/http.go +++ b/go/vt/vttablet/tabletserver/throttle/base/http.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package base diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_health.go b/go/vt/vttablet/tabletserver/throttle/base/metric_health.go index e970888bf13..458e8e28264 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_health.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_health.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package base diff --git a/go/vt/vttablet/tabletserver/throttle/base/metric_health_test.go b/go/vt/vttablet/tabletserver/throttle/base/metric_health_test.go index d11ecd7b8e5..a1a1ad4e0c0 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/metric_health_test.go +++ b/go/vt/vttablet/tabletserver/throttle/base/metric_health_test.go @@ -1,9 +1,43 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ package base import ( diff --git a/go/vt/vttablet/tabletserver/throttle/base/recent_app.go b/go/vt/vttablet/tabletserver/throttle/base/recent_app.go index 2c629fbff25..64527c4cc1c 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/recent_app.go +++ b/go/vt/vttablet/tabletserver/throttle/base/recent_app.go @@ -1,9 +1,43 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ package base import ( diff --git a/go/vt/vttablet/tabletserver/throttle/base/throttle_metric.go b/go/vt/vttablet/tabletserver/throttle/base/throttle_metric.go index ff6e1b146d9..3d4c4f95a2e 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/throttle_metric.go +++ b/go/vt/vttablet/tabletserver/throttle/base/throttle_metric.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package base @@ -30,7 +65,7 @@ var ErrNoSuchMetric = errors.New("No such metric") // ErrInvalidCheckType is an internal error indicating an unknown check type var ErrInvalidCheckType = errors.New("Unknown throttler check type") -// IsDialTCPError sees if th egiven error indicates a TCP issue +// IsDialTCPError sees if the given error indicates a TCP issue func IsDialTCPError(e error) bool { if e == nil { return false diff --git a/go/vt/vttablet/tabletserver/throttle/base/throttle_metric_app.go b/go/vt/vttablet/tabletserver/throttle/base/throttle_metric_app.go index ce77f7068b6..482f319365f 100644 --- a/go/vt/vttablet/tabletserver/throttle/base/throttle_metric_app.go +++ b/go/vt/vttablet/tabletserver/throttle/base/throttle_metric_app.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package base diff --git a/go/vt/vttablet/tabletserver/throttle/check.go b/go/vt/vttablet/tabletserver/throttle/check.go index dd209a0c423..9dfbade8af6 100644 --- a/go/vt/vttablet/tabletserver/throttle/check.go +++ b/go/vt/vttablet/tabletserver/throttle/check.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package throttle @@ -11,7 +46,6 @@ import ( "fmt" "net/http" "strings" - "sync/atomic" "time" "vitess.io/vitess/go/stats" @@ -114,7 +148,7 @@ func (check *ThrottlerCheck) Check(ctx context.Context, appName string, storeTyp } checkResult = check.checkAppMetricResult(ctx, appName, storeType, storeName, metricResultFunc, flags) - atomic.StoreInt64(&check.throttler.lastCheckTimeNano, time.Now().UnixNano()) + check.throttler.lastCheckTimeNano.Store(time.Now().UnixNano()) go func(statusCode int) { stats.GetOrNewCounter("ThrottlerCheckAnyTotal", "total number of checks").Add(1) diff --git a/go/vt/vttablet/tabletserver/throttle/check_result.go b/go/vt/vttablet/tabletserver/throttle/check_result.go index 3bc162b623a..41a1b240934 100644 --- a/go/vt/vttablet/tabletserver/throttle/check_result.go +++ b/go/vt/vttablet/tabletserver/throttle/check_result.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package throttle diff --git a/go/vt/vttablet/tabletserver/throttle/client.go b/go/vt/vttablet/tabletserver/throttle/client.go index 41888340b5a..546d75c040d 100644 --- a/go/vt/vttablet/tabletserver/throttle/client.go +++ b/go/vt/vttablet/tabletserver/throttle/client.go @@ -86,7 +86,7 @@ func NewBackgroundClient(throttler *Throttler, appName throttlerapp.Name, checkT // ThrottleCheckOK checks the throttler, and returns 'true' when the throttler is satisfied. // It does not sleep. // The function caches results for a brief amount of time, hence it's safe and efficient to -// be called very frequenty. +// be called very frequently. // The function is not thread safe. func (c *Client) ThrottleCheckOK(ctx context.Context, overrideAppName throttlerapp.Name) (throttleCheckOK bool) { if c == nil { @@ -117,7 +117,7 @@ func (c *Client) ThrottleCheckOK(ctx context.Context, overrideAppName throttlera } -// ThrottleCheckOKOrWait checks the throttler; if throttler is satisfied, the function returns 'true' mmediately, +// ThrottleCheckOKOrWait checks the throttler; if throttler is satisfied, the function returns 'true' immediately, // otherwise it briefly sleeps and returns 'false'. // Non-empty appName overrides the default appName. // The function is not thread safe. @@ -129,7 +129,7 @@ func (c *Client) ThrottleCheckOKOrWaitAppName(ctx context.Context, appName throt return ok } -// ThrottleCheckOKOrWait checks the throttler; if throttler is satisfied, the function returns 'true' mmediately, +// ThrottleCheckOKOrWait checks the throttler; if throttler is satisfied, the function returns 'true' immediately, // otherwise it briefly sleeps and returns 'false'. // The function is not thread safe. func (c *Client) ThrottleCheckOKOrWait(ctx context.Context) bool { diff --git a/go/vt/vttablet/tabletserver/throttle/config/config.go b/go/vt/vttablet/tabletserver/throttle/config/config.go index b1f3ad61f80..f6234955cc4 100644 --- a/go/vt/vttablet/tabletserver/throttle/config/config.go +++ b/go/vt/vttablet/tabletserver/throttle/config/config.go @@ -1,17 +1,49 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ -package config +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub -// Instance is the one configuration for the throttler -var Instance = &ConfigurationSettings{} + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +package config -// Settings returns the settings of the global instance of Configuration -func Settings() *ConfigurationSettings { - return Instance +// NewConfigurationSettings creates new throttler configuration settings. +func NewConfigurationSettings() *ConfigurationSettings { + return &ConfigurationSettings{} } // ConfigurationSettings models a set of configurable values, that can be diff --git a/go/vt/vttablet/tabletserver/throttle/config/mysql_config.go b/go/vt/vttablet/tabletserver/throttle/config/mysql_config.go index 3e3e82adff4..3aa0607fb28 100644 --- a/go/vt/vttablet/tabletserver/throttle/config/mysql_config.go +++ b/go/vt/vttablet/tabletserver/throttle/config/mysql_config.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package config diff --git a/go/vt/vttablet/tabletserver/throttle/config/store_config.go b/go/vt/vttablet/tabletserver/throttle/config/store_config.go index 9a19025df05..7e5594050d9 100644 --- a/go/vt/vttablet/tabletserver/throttle/config/store_config.go +++ b/go/vt/vttablet/tabletserver/throttle/config/store_config.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package config diff --git a/go/vt/vttablet/tabletserver/throttle/mysql.go b/go/vt/vttablet/tabletserver/throttle/mysql.go index 350ad465b73..81a967ddacb 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package throttle @@ -16,9 +51,9 @@ import ( func aggregateMySQLProbes( ctx context.Context, - probes *mysql.Probes, + probes mysql.Probes, clusterName string, - instanceResultsMap mysql.InstanceMetricResultMap, + tabletResultsMap mysql.TabletResultMap, ignoreHostsCount int, IgnoreDialTCPErrors bool, ignoreHostsThreshold float64, @@ -26,13 +61,13 @@ func aggregateMySQLProbes( // probes is known not to change. It can be *replaced*, but not changed. // so it's safe to iterate it probeValues := []float64{} - for _, probe := range *probes { - instanceMetricResult, ok := instanceResultsMap[mysql.GetClusterInstanceKey(clusterName, &probe.Key)] + for _, probe := range probes { + tabletMetricResult, ok := tabletResultsMap[mysql.GetClusterTablet(clusterName, probe.Alias)] if !ok { return base.NoMetricResultYet } - value, err := instanceMetricResult.Get() + value, err := tabletMetricResult.Get() if err != nil { if IgnoreDialTCPErrors && base.IsDialTCPError(err) { continue @@ -42,7 +77,7 @@ func aggregateMySQLProbes( ignoreHostsCount = ignoreHostsCount - 1 continue } - return instanceMetricResult + return tabletMetricResult } // No error diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/instance_key.go b/go/vt/vttablet/tabletserver/throttle/mysql/instance_key.go deleted file mode 100644 index adcd6f422fb..00000000000 --- a/go/vt/vttablet/tabletserver/throttle/mysql/instance_key.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - Copyright 2015 Shlomi Noach, courtesy Booking.com - See https://github.com/github/freno/blob/master/LICENSE -*/ - -package mysql - -import ( - "fmt" - "strconv" - "strings" -) - -// InstanceKey is an instance indicator, identified by hostname and port -type InstanceKey struct { - Hostname string - Port int -} - -// SelfInstanceKey is a special indicator for "this instance", e.g. denoting the MySQL server associated with local tablet -// The values of this key are immaterial and are intentionally descriptive -var SelfInstanceKey = &InstanceKey{Hostname: "(self)", Port: 1} - -// newRawInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306 -// It expects such format and returns with error if input differs in format -func newRawInstanceKey(hostPort string) (*InstanceKey, error) { - tokens := strings.SplitN(hostPort, ":", 2) - if len(tokens) != 2 { - return nil, fmt.Errorf("Cannot parse InstanceKey from %s. Expected format is host:port", hostPort) - } - instanceKey := &InstanceKey{Hostname: tokens[0]} - var err error - if instanceKey.Port, err = strconv.Atoi(tokens[1]); err != nil { - return instanceKey, fmt.Errorf("Invalid port: %s", tokens[1]) - } - - return instanceKey, nil -} - -// ParseInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306 or some.hostname -// `defaultPort` is used if `hostPort` does not include a port. -func ParseInstanceKey(hostPort string, defaultPort int) (*InstanceKey, error) { - if !strings.Contains(hostPort, ":") { - return &InstanceKey{Hostname: hostPort, Port: defaultPort}, nil - } - return newRawInstanceKey(hostPort) -} - -// Equals tests equality between this key and another key -func (i *InstanceKey) Equals(other *InstanceKey) bool { - if other == nil { - return false - } - return i.Hostname == other.Hostname && i.Port == other.Port -} - -// SmallerThan returns true if this key is dictionary-smaller than another. -// This is used for consistent sorting/ordering; there's nothing magical about it. -func (i *InstanceKey) SmallerThan(other *InstanceKey) bool { - if i.Hostname < other.Hostname { - return true - } - if i.Hostname == other.Hostname && i.Port < other.Port { - return true - } - return false -} - -// IsValid uses simple heuristics to see whether this key represents an actual instance -func (i *InstanceKey) IsValid() bool { - if i.Hostname == "_" { - return false - } - return len(i.Hostname) > 0 && i.Port > 0 -} - -// IsSelf checks if this is the special "self" instance key -func (i *InstanceKey) IsSelf() bool { - if SelfInstanceKey == i { - return true - } - return SelfInstanceKey.Equals(i) -} - -// StringCode returns an official string representation of this key -func (i *InstanceKey) StringCode() string { - return fmt.Sprintf("%s:%d", i.Hostname, i.Port) -} - -// DisplayString returns a user-friendly string representation of this key -func (i *InstanceKey) DisplayString() string { - return i.StringCode() -} - -// String returns a user-friendly string representation of this key -func (i InstanceKey) String() string { - return i.StringCode() -} diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/instance_key_test.go b/go/vt/vttablet/tabletserver/throttle/mysql/instance_key_test.go deleted file mode 100644 index a8d3424c36a..00000000000 --- a/go/vt/vttablet/tabletserver/throttle/mysql/instance_key_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2017 GitHub Inc. - - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE -*/ - -package mysql - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewRawInstanceKey(t *testing.T) { - { - key, err := newRawInstanceKey("127.0.0.1:3307") - assert.NoError(t, err) - assert.Equal(t, key.Hostname, "127.0.0.1") - assert.Equal(t, key.Port, 3307) - } - { - _, err := newRawInstanceKey("127.0.0.1:abcd") - assert.Error(t, err) - } - { - _, err := newRawInstanceKey("127.0.0.1:") - assert.Error(t, err) - } - { - _, err := newRawInstanceKey("127.0.0.1") - assert.Error(t, err) - } -} - -func TestParseInstanceKey(t *testing.T) { - { - key, err := ParseInstanceKey("127.0.0.1:3307", 3306) - assert.NoError(t, err) - assert.Equal(t, "127.0.0.1", key.Hostname) - assert.Equal(t, 3307, key.Port) - } - { - key, err := ParseInstanceKey("127.0.0.1", 3306) - assert.NoError(t, err) - assert.Equal(t, "127.0.0.1", key.Hostname) - assert.Equal(t, 3306, key.Port) - } -} - -func TestEquals(t *testing.T) { - { - expect := &InstanceKey{Hostname: "127.0.0.1", Port: 3306} - key, err := ParseInstanceKey("127.0.0.1", 3306) - assert.NoError(t, err) - assert.True(t, key.Equals(expect)) - } -} - -func TestStringCode(t *testing.T) { - { - key := &InstanceKey{Hostname: "127.0.0.1", Port: 3306} - stringCode := key.StringCode() - assert.Equal(t, "127.0.0.1:3306", stringCode) - } -} diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/mysql_inventory.go b/go/vt/vttablet/tabletserver/throttle/mysql/mysql_inventory.go index ace9a2853a7..744bcc99a44 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql/mysql_inventory.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql/mysql_inventory.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package mysql @@ -10,35 +45,35 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base" ) -// ClusterInstanceKey combines a cluster name with an instance key -type ClusterInstanceKey struct { +// ClusterTablet combines a cluster name with a tablet alias +type ClusterTablet struct { ClusterName string - Key InstanceKey + Alias string } -// GetClusterInstanceKey creates a ClusterInstanceKey object -func GetClusterInstanceKey(clusterName string, key *InstanceKey) ClusterInstanceKey { - return ClusterInstanceKey{ClusterName: clusterName, Key: *key} +// GetClusterTablet creates a GetClusterTablet object +func GetClusterTablet(clusterName string, alias string) ClusterTablet { + return ClusterTablet{ClusterName: clusterName, Alias: alias} } -// InstanceMetricResultMap maps a cluster-instance to a result -type InstanceMetricResultMap map[ClusterInstanceKey]base.MetricResult +// TabletResultMap maps a cluster-tablet to a result +type TabletResultMap map[ClusterTablet]base.MetricResult // Inventory has the operational data about probes, their metrics, and relevant configuration type Inventory struct { - ClustersProbes map[string](*Probes) + ClustersProbes map[string](Probes) IgnoreHostsCount map[string]int IgnoreHostsThreshold map[string]float64 - InstanceKeyMetrics InstanceMetricResultMap + TabletMetrics TabletResultMap } // NewInventory creates a Inventory func NewInventory() *Inventory { inventory := &Inventory{ - ClustersProbes: make(map[string](*Probes)), + ClustersProbes: make(map[string](Probes)), IgnoreHostsCount: make(map[string]int), IgnoreHostsThreshold: make(map[string]float64), - InstanceKeyMetrics: make(map[ClusterInstanceKey]base.MetricResult), + TabletMetrics: make(map[ClusterTablet]base.MetricResult), } return inventory } diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/mysql_throttle_metric.go b/go/vt/vttablet/tabletserver/throttle/mysql/mysql_throttle_metric.go index 8c8a5cc4b32..966c7a93d7f 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql/mysql_throttle_metric.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql/mysql_throttle_metric.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package mysql @@ -20,9 +55,9 @@ import ( type MetricsQueryType int const ( - // MetricsQueryTypeDefault indictes the default, internal implementation. Specifically, our throttler runs a replication lag query + // MetricsQueryTypeDefault indicates the default, internal implementation. Specifically, our throttler runs a replication lag query MetricsQueryTypeDefault MetricsQueryType = iota - // MetricsQueryTypeShowGlobal indicatesa SHOW GLOBAL (STATUS|VARIABLES) query + // MetricsQueryTypeShowGlobal indicates SHOW GLOBAL (STATUS|VARIABLES) query MetricsQueryTypeShowGlobal // MetricsQueryTypeSelect indicates a custom SELECT query MetricsQueryTypeSelect @@ -33,7 +68,7 @@ const ( var mysqlMetricCache = cache.New(cache.NoExpiration, 10*time.Second) func getMySQLMetricCacheKey(probe *Probe) string { - return fmt.Sprintf("%s:%s", probe.Key, probe.MetricQuery) + return fmt.Sprintf("%s:%s", probe.Alias, probe.MetricQuery) } func cacheMySQLThrottleMetric(probe *Probe, mySQLThrottleMetric *MySQLThrottleMetric) *MySQLThrottleMetric { @@ -71,10 +106,10 @@ func GetMetricsQueryType(query string) MetricsQueryType { return MetricsQueryTypeUnknown } -// MySQLThrottleMetric has the probed metric for a mysql instance +// MySQLThrottleMetric has the probed metric for a tablet type MySQLThrottleMetric struct { // nolint:revive ClusterName string - Key InstanceKey + Alias string Value float64 Err error } @@ -84,9 +119,9 @@ func NewMySQLThrottleMetric() *MySQLThrottleMetric { return &MySQLThrottleMetric{Value: 0} } -// GetClusterInstanceKey returns the ClusterInstanceKey part of the metric -func (metric *MySQLThrottleMetric) GetClusterInstanceKey() ClusterInstanceKey { - return GetClusterInstanceKey(metric.ClusterName, &metric.Key) +// GetClusterTablet returns the ClusterTablet part of the metric +func (metric *MySQLThrottleMetric) GetClusterTablet() ClusterTablet { + return GetClusterTablet(metric.ClusterName, metric.Alias) } // Get implements MetricResult @@ -105,7 +140,7 @@ func ReadThrottleMetric(probe *Probe, clusterName string, overrideGetMetricFunc started := time.Now() mySQLThrottleMetric = NewMySQLThrottleMetric() mySQLThrottleMetric.ClusterName = clusterName - mySQLThrottleMetric.Key = probe.Key + mySQLThrottleMetric.Alias = probe.Alias defer func(metric *MySQLThrottleMetric, started time.Time) { go func() { diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/probe.go b/go/vt/vttablet/tabletserver/throttle/mysql/probe.go index 53b835497b4..8c3e069c0d1 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql/probe.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql/probe.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package mysql @@ -14,45 +49,35 @@ import ( // Probe is the minimal configuration required to connect to a MySQL server type Probe struct { - Key InstanceKey + Alias string MetricQuery string Tablet *topodatapb.Tablet - TabletHost string - TabletPort int CacheMillis int QueryInProgress int64 } -// Probes maps instances to probe(s) -type Probes map[InstanceKey](*Probe) +// Probes maps tablet aliases to probe(s) +type Probes map[string](*Probe) // ClusterProbes has the probes for a specific cluster type ClusterProbes struct { ClusterName string IgnoreHostsCount int IgnoreHostsThreshold float64 - InstanceProbes *Probes + TabletProbes Probes } // NewProbes creates Probes -func NewProbes() *Probes { - return &Probes{} +func NewProbes() Probes { + return Probes{} } // NewProbe creates Probe func NewProbe() *Probe { - config := &Probe{ - Key: InstanceKey{}, - } - return config + return &Probe{} } // String returns a human readable string of this struct func (p *Probe) String() string { - return fmt.Sprintf("%s, tablet=%s:%d", p.Key.DisplayString(), p.TabletHost, p.TabletPort) -} - -// Equals checks if this probe has same instance key as another -func (p *Probe) Equals(other *Probe) bool { - return p.Key.Equals(&other.Key) + return fmt.Sprintf("probe alias=%s", p.Alias) } diff --git a/go/vt/vttablet/tabletserver/throttle/mysql/probe_test.go b/go/vt/vttablet/tabletserver/throttle/mysql/probe_test.go index cb63441d419..8f489f39258 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql/probe_test.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql/probe_test.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package mysql @@ -14,6 +49,5 @@ import ( func TestNewProbe(t *testing.T) { c := NewProbe() - assert.Equal(t, "", c.Key.Hostname) - assert.Equal(t, 0, c.Key.Port) + assert.Equal(t, "", c.Alias) } diff --git a/go/vt/vttablet/tabletserver/throttle/mysql_test.go b/go/vt/vttablet/tabletserver/throttle/mysql_test.go index e90f9a69614..15d6feab03f 100644 --- a/go/vt/vttablet/tabletserver/throttle/mysql_test.go +++ b/go/vt/vttablet/tabletserver/throttle/mysql_test.go @@ -1,7 +1,42 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package throttle @@ -17,64 +52,64 @@ import ( ) var ( - key1 = mysql.InstanceKey{Hostname: "10.0.0.1", Port: 3306} - key2 = mysql.InstanceKey{Hostname: "10.0.0.2", Port: 3306} - key3 = mysql.InstanceKey{Hostname: "10.0.0.3", Port: 3306} - key4 = mysql.InstanceKey{Hostname: "10.0.0.4", Port: 3306} - key5 = mysql.InstanceKey{Hostname: "10.0.0.5", Port: 3306} + alias1 = "zone1-0001" + alias2 = "zone1-0002" + alias3 = "zone1-0003" + alias4 = "zone1-0004" + alias5 = "zone1-0005" ) func TestAggregateMySQLProbesNoErrors(t *testing.T) { ctx := context.Background() clusterName := "c0" - key1cluster := mysql.GetClusterInstanceKey(clusterName, &key1) - key2cluster := mysql.GetClusterInstanceKey(clusterName, &key2) - key3cluster := mysql.GetClusterInstanceKey(clusterName, &key3) - key4cluster := mysql.GetClusterInstanceKey(clusterName, &key4) - key5cluster := mysql.GetClusterInstanceKey(clusterName, &key5) - instanceResultsMap := mysql.InstanceMetricResultMap{ + key1cluster := mysql.GetClusterTablet(clusterName, alias1) + key2cluster := mysql.GetClusterTablet(clusterName, alias2) + key3cluster := mysql.GetClusterTablet(clusterName, alias3) + key4cluster := mysql.GetClusterTablet(clusterName, alias4) + key5cluster := mysql.GetClusterTablet(clusterName, alias5) + tabletResultsMap := mysql.TabletResultMap{ key1cluster: base.NewSimpleMetricResult(1.2), key2cluster: base.NewSimpleMetricResult(1.7), key3cluster: base.NewSimpleMetricResult(0.3), key4cluster: base.NewSimpleMetricResult(0.6), key5cluster: base.NewSimpleMetricResult(1.1), } - var probes mysql.Probes = map[mysql.InstanceKey](*mysql.Probe){} - for clusterKey := range instanceResultsMap { - probes[clusterKey.Key] = &mysql.Probe{Key: clusterKey.Key} + var probes mysql.Probes = map[string](*mysql.Probe){} + for clusterKey := range tabletResultsMap { + probes[clusterKey.Alias] = &mysql.Probe{Alias: clusterKey.Alias} } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 0, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 0, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.7) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 1, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 1, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.2) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 2, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 2, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.1) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 3, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 3, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.6) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 4, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 4, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.3) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 5, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 5, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.3) @@ -84,54 +119,54 @@ func TestAggregateMySQLProbesNoErrors(t *testing.T) { func TestAggregateMySQLProbesNoErrorsIgnoreHostsThreshold(t *testing.T) { ctx := context.Background() clusterName := "c0" - key1cluster := mysql.GetClusterInstanceKey(clusterName, &key1) - key2cluster := mysql.GetClusterInstanceKey(clusterName, &key2) - key3cluster := mysql.GetClusterInstanceKey(clusterName, &key3) - key4cluster := mysql.GetClusterInstanceKey(clusterName, &key4) - key5cluster := mysql.GetClusterInstanceKey(clusterName, &key5) - instanceResultsMap := mysql.InstanceMetricResultMap{ + key1cluster := mysql.GetClusterTablet(clusterName, alias1) + key2cluster := mysql.GetClusterTablet(clusterName, alias2) + key3cluster := mysql.GetClusterTablet(clusterName, alias3) + key4cluster := mysql.GetClusterTablet(clusterName, alias4) + key5cluster := mysql.GetClusterTablet(clusterName, alias5) + tableteResultsMap := mysql.TabletResultMap{ key1cluster: base.NewSimpleMetricResult(1.2), key2cluster: base.NewSimpleMetricResult(1.7), key3cluster: base.NewSimpleMetricResult(0.3), key4cluster: base.NewSimpleMetricResult(0.6), key5cluster: base.NewSimpleMetricResult(1.1), } - var probes mysql.Probes = map[mysql.InstanceKey](*mysql.Probe){} - for clusterKey := range instanceResultsMap { - probes[clusterKey.Key] = &mysql.Probe{Key: clusterKey.Key} + var probes mysql.Probes = map[string](*mysql.Probe){} + for clusterKey := range tableteResultsMap { + probes[clusterKey.Alias] = &mysql.Probe{Alias: clusterKey.Alias} } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 0, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 0, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.7) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 1, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 1, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.2) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 2, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 2, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.1) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 3, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 3, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.6) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 4, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 4, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.6) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 5, false, 1.0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tableteResultsMap, 5, false, 1.0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 0.6) @@ -141,56 +176,56 @@ func TestAggregateMySQLProbesNoErrorsIgnoreHostsThreshold(t *testing.T) { func TestAggregateMySQLProbesWithErrors(t *testing.T) { ctx := context.Background() clusterName := "c0" - key1cluster := mysql.GetClusterInstanceKey(clusterName, &key1) - key2cluster := mysql.GetClusterInstanceKey(clusterName, &key2) - key3cluster := mysql.GetClusterInstanceKey(clusterName, &key3) - key4cluster := mysql.GetClusterInstanceKey(clusterName, &key4) - key5cluster := mysql.GetClusterInstanceKey(clusterName, &key5) - instanceResultsMap := mysql.InstanceMetricResultMap{ + key1cluster := mysql.GetClusterTablet(clusterName, alias1) + key2cluster := mysql.GetClusterTablet(clusterName, alias2) + key3cluster := mysql.GetClusterTablet(clusterName, alias3) + key4cluster := mysql.GetClusterTablet(clusterName, alias4) + key5cluster := mysql.GetClusterTablet(clusterName, alias5) + tabletResultsMap := mysql.TabletResultMap{ key1cluster: base.NewSimpleMetricResult(1.2), key2cluster: base.NewSimpleMetricResult(1.7), key3cluster: base.NewSimpleMetricResult(0.3), key4cluster: base.NoSuchMetric, key5cluster: base.NewSimpleMetricResult(1.1), } - var probes mysql.Probes = map[mysql.InstanceKey](*mysql.Probe){} - for clusterKey := range instanceResultsMap { - probes[clusterKey.Key] = &mysql.Probe{Key: clusterKey.Key} + var probes mysql.Probes = map[string](*mysql.Probe){} + for clusterKey := range tabletResultsMap { + probes[clusterKey.Alias] = &mysql.Probe{Alias: clusterKey.Alias} } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 0, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 0, false, 0) _, err := worstMetric.Get() assert.Error(t, err) assert.Equal(t, err, base.ErrNoSuchMetric) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 1, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 1, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.7) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 2, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 2, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.2) } - instanceResultsMap[key1cluster] = base.NoSuchMetric + tabletResultsMap[key1cluster] = base.NoSuchMetric { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 0, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 0, false, 0) _, err := worstMetric.Get() assert.Error(t, err) assert.Equal(t, err, base.ErrNoSuchMetric) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 1, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 1, false, 0) _, err := worstMetric.Get() assert.Error(t, err) assert.Equal(t, err, base.ErrNoSuchMetric) } { - worstMetric := aggregateMySQLProbes(ctx, &probes, clusterName, instanceResultsMap, 2, false, 0) + worstMetric := aggregateMySQLProbes(ctx, probes, clusterName, tabletResultsMap, 2, false, 0) value, err := worstMetric.Get() assert.NoError(t, err) assert.Equal(t, value, 1.7) diff --git a/go/vt/vttablet/tabletserver/throttle/throttler.go b/go/vt/vttablet/tabletserver/throttle/throttler.go index b8d84b1ed5e..8f6db91936d 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttler.go +++ b/go/vt/vttablet/tabletserver/throttle/throttler.go @@ -1,17 +1,50 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This codebase originates from https://github.com/github/freno, See https://github.com/github/freno/blob/master/LICENSE +/* + MIT License + + Copyright (c) 2017 GitHub + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. */ package throttle import ( "context" - "encoding/json" "errors" "fmt" - "io" "math" "math/rand" "net/http" @@ -26,6 +59,7 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/protoutil" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/textutil" "vitess.io/vitess/go/timer" @@ -36,6 +70,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/heartbeat" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -47,15 +82,15 @@ import ( ) const ( - leaderCheckInterval = 5 * time.Second - mysqlCollectInterval = 250 * time.Millisecond - mysqlDormantCollectInterval = 5 * time.Second - mysqlRefreshInterval = 10 * time.Second - mysqlAggregateInterval = 125 * time.Millisecond - - aggregatedMetricsExpiration = 5 * time.Second + leaderCheckInterval = 5 * time.Second + mysqlCollectInterval = 250 * time.Millisecond + mysqlDormantCollectInterval = 5 * time.Second + mysqlRefreshInterval = 10 * time.Second + mysqlAggregateInterval = 125 * time.Millisecond throttledAppsSnapshotInterval = 5 * time.Second - recentAppsExpiration = time.Hour * 24 + + aggregatedMetricsExpiration = 5 * time.Second + recentAppsExpiration = time.Hour * 24 nonDeprioritizedAppMapExpiration = time.Second @@ -81,19 +116,7 @@ func init() { } func registerThrottlerFlags(fs *pflag.FlagSet) { - fs.StringVar(&throttleTabletTypes, "throttle_tablet_types", throttleTabletTypes, "Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' aways implicitly included") - - fs.Duration("throttle_threshold", 0, "Replication lag threshold for default lag throttling") - fs.String("throttle_metrics_query", "", "Override default heartbeat/lag metric. Use either `SELECT` (must return single row, single value) or `SHOW GLOBAL ... LIKE ...` queries. Set -throttle_metrics_threshold respectively.") - fs.Float64("throttle_metrics_threshold", 0, "Override default throttle threshold, respective to --throttle_metrics_query") - fs.Bool("throttle_check_as_check_self", false, "Should throttler/check return a throttler/check-self result (changes throttler behavior for writes)") - fs.Bool("throttler-config-via-topo", false, "Deprecated, will be removed in v19. Assumed to be 'true'") - - fs.MarkDeprecated("throttle_threshold", "Replication lag threshold for default lag throttling") - fs.MarkDeprecated("throttle_metrics_query", "Override default heartbeat/lag metric. Use either `SELECT` (must return single row, single value) or `SHOW GLOBAL ... LIKE ...` queries. Set -throttle_metrics_threshold respectively.") - fs.MarkDeprecated("throttle_metrics_threshold", "Override default throttle threshold, respective to --throttle_metrics_query") - fs.MarkDeprecated("throttle_check_as_check_self", "Should throttler/check return a throttler/check-self result (changes throttler behavior for writes)") - fs.MarkDeprecated("throttler-config-via-topo", "Assumed to be 'true'") + fs.StringVar(&throttleTabletTypes, "throttle_tablet_types", throttleTabletTypes, "Comma separated VTTablet types to be considered by the throttler. default: 'replica'. example: 'replica,rdonly'. 'replica' always implicitly included") } var ( @@ -130,18 +153,27 @@ type Throttler struct { isLeader atomic.Bool isOpen atomic.Bool - env tabletenv.Env - pool *connpool.Pool - tabletTypeFunc func() topodatapb.TabletType - ts throttlerTopoService - srvTopoServer srvtopo.Server - heartbeatWriter heartbeat.HeartbeatWriter + leaderCheckInterval time.Duration + mysqlCollectInterval time.Duration + mysqlDormantCollectInterval time.Duration + mysqlRefreshInterval time.Duration + mysqlAggregateInterval time.Duration + throttledAppsSnapshotInterval time.Duration + + configSettings *config.ConfigurationSettings + env tabletenv.Env + pool *connpool.Pool + tabletTypeFunc func() topodatapb.TabletType + ts throttlerTopoService + srvTopoServer srvtopo.Server + heartbeatWriter heartbeat.HeartbeatWriter + overrideTmClient tmclient.TabletManagerClient // recentCheckTickerValue is an ever increasing number, incrementing once per second. - recentCheckTickerValue int64 + recentCheckTickerValue atomic.Int64 // recentCheckValue is set to match or exceed recentCheckTickerValue whenever a "check" was made (other than by the throttler itself). // when recentCheckValue < recentCheckTickerValue that means there hasn't been a recent check. - recentCheckValue int64 + recentCheckValue atomic.Int64 throttleTabletTypesMap map[topodatapb.TabletType]bool @@ -162,14 +194,15 @@ type Throttler struct { recentApps *cache.Cache metricsHealth *cache.Cache - lastCheckTimeNano int64 + lastCheckTimeNano atomic.Int64 - initMutex sync.Mutex - enableMutex sync.Mutex - cancelOpenContext context.CancelFunc - cancelEnableContext context.CancelFunc - throttledAppsMutex sync.Mutex - watchSrvKeyspaceOnce sync.Once + initMutex sync.Mutex + enableMutex sync.Mutex + cancelOpenContext context.CancelFunc + cancelEnableContext context.CancelFunc + throttledAppsMutex sync.Mutex + + readSelfThrottleMetric func(context.Context, *mysql.Probe) *mysql.MySQLThrottleMetric // overwritten by unit test nonLowPriorityAppRequestsThrottled *cache.Cache httpClient *http.Client @@ -202,8 +235,8 @@ func NewThrottler(env tabletenv.Env, srvTopoServer srvtopo.Server, ts *topo.Serv ts: ts, heartbeatWriter: heartbeatWriter, pool: connpool.NewPool(env, "ThrottlerPool", tabletenv.ConnPoolConfig{ - Size: 2, - IdleTimeoutSeconds: env.Config().OltpReadPool.IdleTimeoutSeconds, + Size: 2, + IdleTimeout: env.Config().OltpReadPool.IdleTimeout, }), } @@ -224,7 +257,17 @@ func NewThrottler(env tabletenv.Env, srvTopoServer srvtopo.Server, ts *topo.Serv throttler.initThrottleTabletTypes() throttler.check = NewThrottlerCheck(throttler) + throttler.leaderCheckInterval = leaderCheckInterval + throttler.mysqlCollectInterval = mysqlCollectInterval + throttler.mysqlDormantCollectInterval = mysqlDormantCollectInterval + throttler.mysqlRefreshInterval = mysqlRefreshInterval + throttler.mysqlAggregateInterval = mysqlAggregateInterval + throttler.throttledAppsSnapshotInterval = throttledAppsSnapshotInterval + throttler.StoreMetricsThreshold(defaultThrottleLagThreshold.Seconds()) //default + throttler.readSelfThrottleMetric = func(ctx context.Context, p *mysql.Probe) *mysql.MySQLThrottleMetric { + return throttler.readSelfMySQLThrottleMetric(ctx, p) + } return throttler } @@ -267,7 +310,7 @@ func (throttler *Throttler) GetMetricsThreshold() float64 { func (throttler *Throttler) initConfig() { log.Infof("Throttler: initializing config") - config.Instance = &config.ConfigurationSettings{ + throttler.configSettings = &config.ConfigurationSettings{ Stores: config.StoresSettings{ MySQL: config.MySQLConfigurationSettings{ IgnoreDialTCPErrors: true, @@ -275,12 +318,12 @@ func (throttler *Throttler) initConfig() { }, }, } - config.Instance.Stores.MySQL.Clusters[selfStoreName] = &config.MySQLClusterConfigurationSettings{ + throttler.configSettings.Stores.MySQL.Clusters[selfStoreName] = &config.MySQLClusterConfigurationSettings{ MetricQuery: throttler.GetMetricsQuery(), ThrottleThreshold: &throttler.MetricsThreshold, IgnoreHostsCount: 0, } - config.Instance.Stores.MySQL.Clusters[shardStoreName] = &config.MySQLClusterConfigurationSettings{ + throttler.configSettings.Stores.MySQL.Clusters[shardStoreName] = &config.MySQLClusterConfigurationSettings{ MetricQuery: throttler.GetMetricsQuery(), ThrottleThreshold: &throttler.MetricsThreshold, IgnoreHostsCount: 0, @@ -296,7 +339,7 @@ func (throttler *Throttler) readThrottlerConfig(ctx context.Context) (*topodatap return throttler.normalizeThrottlerConfig(srvks.ThrottlerConfig), nil } -// normalizeThrottlerConfig noramlizes missing throttler config information, as needed. +// normalizeThrottlerConfig normalizes missing throttler config information, as needed. func (throttler *Throttler) normalizeThrottlerConfig(throttlerConfig *topodatapb.ThrottlerConfig) *topodatapb.ThrottlerConfig { if throttlerConfig == nil { throttlerConfig = &topodatapb.ThrottlerConfig{} @@ -314,9 +357,10 @@ func (throttler *Throttler) normalizeThrottlerConfig(throttlerConfig *topodatapb } func (throttler *Throttler) WatchSrvKeyspaceCallback(srvks *topodatapb.SrvKeyspace, err error) bool { - log.Infof("Throttler: WatchSrvKeyspaceCallback called with: %+v", srvks) if err != nil { - log.Errorf("WatchSrvKeyspaceCallback error: %v", err) + if !topo.IsErrType(err, topo.Interrupted) && !errors.Is(err, context.Canceled) { + log.Errorf("WatchSrvKeyspaceCallback error: %v", err) + } return false } throttlerConfig := throttler.normalizeThrottlerConfig(srvks.ThrottlerConfig) @@ -325,7 +369,6 @@ func (throttler *Throttler) WatchSrvKeyspaceCallback(srvks *topodatapb.SrvKeyspa // Throttler is enabled and we should apply the config change // through Operate() or else we get into race conditions. go func() { - log.Infof("Throttler: submitting a throttler config apply message with: %+v", throttlerConfig) throttler.throttlerConfigChan <- throttlerConfig }() } else { @@ -354,9 +397,9 @@ func (throttler *Throttler) applyThrottlerConfig(ctx context.Context, throttlerC throttler.ThrottleApp(appRule.Name, protoutil.TimeFromProto(appRule.ExpiresAt).UTC(), appRule.Ratio, appRule.Exempt) } if throttlerConfig.Enabled { - go throttler.Enable(ctx) + go throttler.Enable() } else { - go throttler.Disable(ctx) + go throttler.Disable() } } @@ -384,49 +427,95 @@ func (throttler *Throttler) IsRunning() bool { // Enable activates the throttler probes; when enabled, the throttler responds to check queries based on // the collected metrics. -func (throttler *Throttler) Enable(ctx context.Context) bool { +// The function returns a WaitGroup that can be used to wait for the throttler to be fully disabled, ie when +// the Operate() goroutine function terminates and caches are invalidated. +func (throttler *Throttler) Enable() *sync.WaitGroup { throttler.enableMutex.Lock() defer throttler.enableMutex.Unlock() - isEnabled := throttler.isEnabled.Swap(true) - if isEnabled { + if wasEnabled := throttler.isEnabled.Swap(true); wasEnabled { log.Infof("Throttler: already enabled") - return false + return nil } log.Infof("Throttler: enabling") - ctx, throttler.cancelEnableContext = context.WithCancel(ctx) + wg := &sync.WaitGroup{} + var ctx context.Context + ctx, throttler.cancelEnableContext = context.WithCancel(context.Background()) throttler.check.SelfChecks(ctx) - throttler.Operate(ctx) + throttler.Operate(ctx, wg) // Make a one-time request for a lease of heartbeats - go throttler.heartbeatWriter.RequestHeartbeats() + throttler.requestHeartbeats() - return true + return wg } -// Disable deactivates the probes and associated operations. When disabled, the throttler reponds to check +// Disable deactivates the probes and associated operations. When disabled, the throttler responds to check // queries with "200 OK" irrespective of lag or any other metrics. -func (throttler *Throttler) Disable(ctx context.Context) bool { +func (throttler *Throttler) Disable() bool { throttler.enableMutex.Lock() defer throttler.enableMutex.Unlock() - isEnabled := throttler.isEnabled.Swap(false) - if !isEnabled { + if wasEnabled := throttler.isEnabled.Swap(false); !wasEnabled { log.Infof("Throttler: already disabled") return false } log.Infof("Throttler: disabling") // _ = throttler.updateConfig(ctx, false, throttler.MetricsThreshold.Get()) // TODO(shlomi) - throttler.aggregatedMetrics.Flush() - throttler.recentApps.Flush() - throttler.nonLowPriorityAppRequestsThrottled.Flush() - // we do not flush throttler.throttledApps because this is data submitted by the user; the user expects the data to survive a disable+enable throttler.cancelEnableContext() return true } +// retryReadAndApplyThrottlerConfig() is called by Open(), read throttler config from topo, applies it, and starts watching +// for topo changes. +// But also, we're in an Open() function, which blocks state manager's operation, and affects +// opening of all other components. We thus read the throttler config in the background. +// However, we want to handle a situation where the read errors out. +// So we kick a loop that keeps retrying reading the config, for as long as this throttler is open. +func (throttler *Throttler) retryReadAndApplyThrottlerConfig(ctx context.Context) { + var watchSrvKeyspaceOnce sync.Once + retryInterval := 10 * time.Second + retryTicker := time.NewTicker(retryInterval) + defer retryTicker.Stop() + for { + if !throttler.IsOpen() { + // Throttler is not open so no need to keep retrying. + log.Warningf("Throttler.retryReadAndApplyThrottlerConfig(): throttler no longer seems to be open, exiting") + return + } + + requestCtx, requestCancel := context.WithTimeout(ctx, 5*time.Second) + defer requestCancel() + throttlerConfig, err := throttler.readThrottlerConfig(requestCtx) + if err == nil { + log.Infof("Throttler.retryReadAndApplyThrottlerConfig(): success reading throttler config: %+v", throttlerConfig) + // It's possible that during a retry-sleep, the throttler is closed and opened again, leading + // to two (or more) instances of this goroutine. That's not a big problem; it's fine if all + // attempt to read the throttler config; but we just want to ensure they don't step on each other + // while applying the changes. + throttler.initMutex.Lock() + defer throttler.initMutex.Unlock() + throttler.applyThrottlerConfig(ctx, throttlerConfig) // may issue an Enable + go watchSrvKeyspaceOnce.Do(func() { + // We start watching SrvKeyspace only after we know it's been created. Now is that time! + // We watch using the given ctx, which is cancelled when the throttler is Close()d. + throttler.srvTopoServer.WatchSrvKeyspace(ctx, throttler.cell, throttler.keyspace, throttler.WatchSrvKeyspaceCallback) + }) + return + } + log.Errorf("Throttler.retryReadAndApplyThrottlerConfig(): error reading throttler config. Will retry in %v. Err=%+v", retryInterval, err) + select { + case <-ctx.Done(): + // Throttler is not open so no need to keep retrying. + log.Infof("Throttler.retryReadAndApplyThrottlerConfig(): throttler no longer seems to be open, exiting") + return + case <-retryTicker.C: + } + } +} + // Open opens database pool and initializes the schema func (throttler *Throttler) Open() error { log.Infof("Throttler: started execution of Open. Acquiring initMutex lock") @@ -451,52 +540,7 @@ func (throttler *Throttler) Open() error { throttler.ThrottleApp("always-throttled-app", time.Now().Add(time.Hour*24*365*10), DefaultThrottleRatio, false) - log.Infof("Throttler: throttler-config-via-topo detected") - // We want to read throttler config from topo and apply it. - // But also, we're in an Open() function, which blocks state manager's operation, and affects - // opening of all other components. We thus read the throttler config in the background. - // However, we want to handle a situation where the read errors out. - // So we kick a loop that keeps retrying reading the config, for as long as this throttler is open. - retryReadAndApplyThrottlerConfig := func(ctx context.Context) { - retryInterval := 10 * time.Second - retryTicker := time.NewTicker(retryInterval) - defer retryTicker.Stop() - for { - if !throttler.IsOpen() { - // Throttler is not open so no need to keep retrying. - log.Errorf("Throttler.retryReadAndApplyThrottlerConfig(): throttler no longer seems to be open, exiting") - return - } - - requestCtx, requestCancel := context.WithTimeout(ctx, 5*time.Second) - defer requestCancel() - throttlerConfig, err := throttler.readThrottlerConfig(requestCtx) - if err == nil { - log.Errorf("Throttler.retryReadAndApplyThrottlerConfig(): success reading throttler config: %+v", throttlerConfig) - // It's possible that during a retry-sleep, the throttler is closed and opened again, leading - // to two (or more) instances of this goroutine. That's not a big problem; it's fine if all - // attempt to read the throttler config; but we just want to ensure they don't step on each other - // while applying the changes. - throttler.initMutex.Lock() - defer throttler.initMutex.Unlock() - throttler.applyThrottlerConfig(ctx, throttlerConfig) // may issue an Enable - go throttler.watchSrvKeyspaceOnce.Do(func() { - // We start watching SrvKeyspace only after we know it's been created. Now is that time! - throttler.srvTopoServer.WatchSrvKeyspace(context.Background(), throttler.cell, throttler.keyspace, throttler.WatchSrvKeyspaceCallback) - }) - return - } - log.Errorf("Throttler.retryReadAndApplyThrottlerConfig(): error reading throttler config. Will retry in %v. Err=%+v", retryInterval, err) - select { - case <-ctx.Done(): - // Throttler is not open so no need to keep retrying. - log.Errorf("Throttler.retryReadAndApplyThrottlerConfig(): throttler no longer seems to be open, exiting") - return - case <-retryTicker.C: - } - } - } - go retryReadAndApplyThrottlerConfig(ctx) + go throttler.retryReadAndApplyThrottlerConfig(ctx) return nil } @@ -512,19 +556,31 @@ func (throttler *Throttler) Close() { log.Infof("Throttler: throttler is not open") return } - ctx := context.Background() - throttler.Disable(ctx) + throttler.Disable() throttler.isLeader.Store(false) - log.Infof("Throttler: closing pool") - throttler.pool.Close() - throttler.cancelOpenContext() + // The below " != nil " checks are relevant to unit tests, where perhaps not all + // fields are supplied. + if throttler.pool != nil { + log.Infof("Throttler: closing pool") + throttler.pool.Close() + } + if throttler.cancelOpenContext != nil { + throttler.cancelOpenContext() + } log.Infof("Throttler: finished execution of Close") } +// requestHeartbeats sends a heartbeat lease request to the heartbeat writer. +// This action is recorded in stats. +func (throttler *Throttler) requestHeartbeats() { + go throttler.heartbeatWriter.RequestHeartbeats() + go stats.GetOrNewCounter("ThrottlerHeartbeatRequests", "heartbeat requests").Add(1) +} + func (throttler *Throttler) generateSelfMySQLThrottleMetricFunc(ctx context.Context, probe *mysql.Probe) func() *mysql.MySQLThrottleMetric { f := func() *mysql.MySQLThrottleMetric { - return throttler.readSelfMySQLThrottleMetric(ctx, probe) + return throttler.readSelfThrottleMetric(ctx, probe) } return f } @@ -533,7 +589,7 @@ func (throttler *Throttler) generateSelfMySQLThrottleMetricFunc(ctx context.Cont func (throttler *Throttler) readSelfMySQLThrottleMetric(ctx context.Context, probe *mysql.Probe) *mysql.MySQLThrottleMetric { metric := &mysql.MySQLThrottleMetric{ ClusterName: selfStoreName, - Key: *mysql.SelfInstanceKey, + Alias: "", Value: 0, Err: nil, } @@ -559,7 +615,7 @@ func (throttler *Throttler) readSelfMySQLThrottleMetric(ctx context.Context, pro switch metricsQueryType { case mysql.MetricsQueryTypeSelect: // We expect a single row, single column result. - // The "for" iteration below is just a way to get first result without knowning column name + // The "for" iteration below is just a way to get first result without knowing column name for k := range row { metric.Value, metric.Err = row.ToFloat64(k) } @@ -588,131 +644,127 @@ func (throttler *Throttler) ThrottledApps() (result []base.AppThrottle) { // isDormant returns true when the last check was more than dormantPeriod ago func (throttler *Throttler) isDormant() bool { - lastCheckTime := time.Unix(0, atomic.LoadInt64(&throttler.lastCheckTimeNano)) + lastCheckTime := time.Unix(0, throttler.lastCheckTimeNano.Load()) return time.Since(lastCheckTime) > dormantPeriod } // Operate is the main entry point for the throttler operation and logic. It will // run the probes, collect metrics, refresh inventory, etc. -func (throttler *Throttler) Operate(ctx context.Context) { +func (throttler *Throttler) Operate(ctx context.Context, wg *sync.WaitGroup) { tickers := [](*timer.SuspendableTicker){} addTicker := func(d time.Duration) *timer.SuspendableTicker { t := timer.NewSuspendableTicker(d, false) tickers = append(tickers, t) return t } - leaderCheckTicker := addTicker(leaderCheckInterval) - mysqlCollectTicker := addTicker(mysqlCollectInterval) - mysqlDormantCollectTicker := addTicker(mysqlDormantCollectInterval) - mysqlRefreshTicker := addTicker(mysqlRefreshInterval) - mysqlAggregateTicker := addTicker(mysqlAggregateInterval) - throttledAppsTicker := addTicker(throttledAppsSnapshotInterval) + leaderCheckTicker := addTicker(throttler.leaderCheckInterval) + mysqlCollectTicker := addTicker(throttler.mysqlCollectInterval) + mysqlDormantCollectTicker := addTicker(throttler.mysqlDormantCollectInterval) + mysqlRefreshTicker := addTicker(throttler.mysqlRefreshInterval) + mysqlAggregateTicker := addTicker(throttler.mysqlAggregateInterval) + throttledAppsTicker := addTicker(throttler.throttledAppsSnapshotInterval) recentCheckTicker := addTicker(time.Second) - tmClient := tmclient.NewTabletManagerClient() - + wg.Add(1) go func() { + defer func() { + throttler.aggregatedMetrics.Flush() + throttler.recentApps.Flush() + throttler.nonLowPriorityAppRequestsThrottled.Flush() + wg.Done() + }() + // we do not flush throttler.throttledApps because this is data submitted by the user; the user expects the data to survive a disable+enable + defer log.Infof("Throttler: Operate terminated, tickers stopped") - defer tmClient.Close() for _, t := range tickers { defer t.Stop() // since we just started the tickers now, speed up the ticks by forcing an immediate tick go t.TickNow() } + tmClient := throttler.overrideTmClient + if tmClient == nil { + // This is the normal production behavior. + // throttler.overrideTmClient != nil only in unit testing + tmClient = tmclient.NewTabletManagerClient() + defer tmClient.Close() + } + for { select { case <-ctx.Done(): return case <-leaderCheckTicker.C: - { - func() { - throttler.initMutex.Lock() - defer throttler.initMutex.Unlock() - - // sparse - shouldBeLeader := false - if throttler.IsOpen() { - if throttler.tabletTypeFunc() == topodatapb.TabletType_PRIMARY { - shouldBeLeader = true - } - } - - isLeader := throttler.isLeader.Swap(shouldBeLeader) - transitionedIntoLeader := false - if shouldBeLeader && !isLeader { - log.Infof("Throttler: transition into leadership") - transitionedIntoLeader = true - } - if !shouldBeLeader && isLeader { - log.Infof("Throttler: transition out of leadership") - } - - if transitionedIntoLeader { - // transitioned into leadership, let's speed up the next 'refresh' and 'collect' ticks - go mysqlRefreshTicker.TickNow() - go throttler.heartbeatWriter.RequestHeartbeats() - } - }() - } + func() { + throttler.initMutex.Lock() + defer throttler.initMutex.Unlock() + + // sparse + shouldBeLeader := false + if throttler.IsOpen() && throttler.tabletTypeFunc() == topodatapb.TabletType_PRIMARY { + shouldBeLeader = true + } + + isLeader := throttler.isLeader.Swap(shouldBeLeader) + transitionedIntoLeader := false + if shouldBeLeader && !isLeader { + log.Infof("Throttler: transition into leadership") + transitionedIntoLeader = true + } + if !shouldBeLeader && isLeader { + log.Infof("Throttler: transition out of leadership") + } + + if transitionedIntoLeader { + // transitioned into leadership, let's speed up the next 'refresh' and 'collect' ticks + go mysqlRefreshTicker.TickNow() + throttler.requestHeartbeats() + } + }() case <-mysqlCollectTicker.C: - { - if throttler.IsOpen() { - // frequent - if !throttler.isDormant() { - throttler.collectMySQLMetrics(ctx, tmClient) - } + if throttler.IsOpen() { + // frequent + if !throttler.isDormant() { + throttler.collectMySQLMetrics(ctx, tmClient) } } case <-mysqlDormantCollectTicker.C: - { - if throttler.IsOpen() { - // infrequent - if throttler.isDormant() { - throttler.collectMySQLMetrics(ctx, tmClient) - } + if throttler.IsOpen() { + // infrequent + if throttler.isDormant() { + throttler.collectMySQLMetrics(ctx, tmClient) } } case metric := <-throttler.mysqlThrottleMetricChan: - { - // incoming MySQL metric, frequent, as result of collectMySQLMetrics() - throttler.mysqlInventory.InstanceKeyMetrics[metric.GetClusterInstanceKey()] = metric - } + // incoming MySQL metric, frequent, as result of collectMySQLMetrics() + throttler.mysqlInventory.TabletMetrics[metric.GetClusterTablet()] = metric case <-mysqlRefreshTicker.C: - { - // sparse - if throttler.IsOpen() { - throttler.refreshMySQLInventory(ctx) - } + // sparse + if throttler.IsOpen() { + throttler.refreshMySQLInventory(ctx) } case probes := <-throttler.mysqlClusterProbesChan: - { - // incoming structural update, sparse, as result of refreshMySQLInventory() - throttler.updateMySQLClusterProbes(ctx, probes) - } + // incoming structural update, sparse, as result of refreshMySQLInventory() + throttler.updateMySQLClusterProbes(ctx, probes) case <-mysqlAggregateTicker.C: - { - if throttler.IsOpen() { - throttler.aggregateMySQLMetrics(ctx) - } + if throttler.IsOpen() { + throttler.aggregateMySQLMetrics(ctx) } case <-throttledAppsTicker.C: - { - if throttler.IsOpen() { - go throttler.expireThrottledApps() - } + if throttler.IsOpen() { + go throttler.expireThrottledApps() } case throttlerConfig := <-throttler.throttlerConfigChan: throttler.applyThrottlerConfig(ctx, throttlerConfig) case <-recentCheckTicker.C: // Increment recentCheckTickerValue by one. - atomic.AddInt64(&throttler.recentCheckTickerValue, 1) + throttler.recentCheckTickerValue.Add(1) } } }() } -func (throttler *Throttler) generateTabletHTTPProbeFunction(ctx context.Context, tmClient tmclient.TabletManagerClient, clusterName string, probe *mysql.Probe) (probeFunc func() *mysql.MySQLThrottleMetric) { +func (throttler *Throttler) generateTabletProbeFunction(ctx context.Context, clusterName string, tmClient tmclient.TabletManagerClient, probe *mysql.Probe) (probeFunc func() *mysql.MySQLThrottleMetric) { return func() *mysql.MySQLThrottleMetric { // Some reasonable timeout, to ensure we release connections even if they're hanging (otherwise grpc-go keeps polling those connections forever) ctx, cancel := context.WithTimeout(ctx, 4*mysqlCollectInterval) @@ -721,58 +773,27 @@ func (throttler *Throttler) generateTabletHTTPProbeFunction(ctx context.Context, // Hit a tablet's `check-self` via HTTP, and convert its CheckResult JSON output into a MySQLThrottleMetric mySQLThrottleMetric := mysql.NewMySQLThrottleMetric() mySQLThrottleMetric.ClusterName = clusterName - mySQLThrottleMetric.Key = probe.Key - - { - req := &tabletmanagerdatapb.CheckThrottlerRequest{} // We leave AppName empty; it will default to VitessName anyway, and we can save some proto space - if resp, gRPCErr := tmClient.CheckThrottler(ctx, probe.Tablet, req); gRPCErr == nil { - mySQLThrottleMetric.Value = resp.Value - if resp.StatusCode == http.StatusInternalServerError { - mySQLThrottleMetric.Err = fmt.Errorf("Status code: %d", resp.StatusCode) - } - if resp.RecentlyChecked { - // We have just probed a tablet, and it reported back that someone just recently "check"ed it. - // We therefore renew the heartbeats lease. - go throttler.heartbeatWriter.RequestHeartbeats() - } - return mySQLThrottleMetric - - // } else { - // In v18 we need to be backwards compatible. If we have a gRPC error it might be because the replica is v17 and - // does not support CheckThrottler() RPC. This is why: - // 1. We fall back to HTTP - // 2. We don't log an error (it would just spam the logs) - // In v19 we will remove all HTTP code, and will *potentially* log an error. - // log.Errorf("error in GRPC call to tablet %v: %v", probe.Tablet.GetAlias(), gRPCErr) - } - } - // Backwards compatibility to v17: if the underlying tablets do not support CheckThrottler gRPC, attempt a HTTP cehck: - tabletCheckSelfURL := fmt.Sprintf("http://%s:%d/throttler/check-self?app=%s", probe.TabletHost, probe.TabletPort, throttlerapp.VitessName) - resp, err := throttler.httpClient.Get(tabletCheckSelfURL) - if err != nil { - mySQLThrottleMetric.Err = err - return mySQLThrottleMetric - } - defer resp.Body.Close() - b, err := io.ReadAll(resp.Body) - if err != nil { - mySQLThrottleMetric.Err = err + mySQLThrottleMetric.Alias = probe.Alias + + if probe.Tablet == nil { + mySQLThrottleMetric.Err = fmt.Errorf("found nil tablet reference for alias %v, hostname %v", probe.Alias, probe.Tablet.Hostname) return mySQLThrottleMetric } - checkResult := &CheckResult{} - if err := json.Unmarshal(b, checkResult); err != nil { - mySQLThrottleMetric.Err = err + req := &tabletmanagerdatapb.CheckThrottlerRequest{} // We leave AppName empty; it will default to VitessName anyway, and we can save some proto space + resp, gRPCErr := tmClient.CheckThrottler(ctx, probe.Tablet, req) + if gRPCErr != nil { + mySQLThrottleMetric.Err = fmt.Errorf("gRPC error accessing tablet %v. Err=%v", probe.Alias, gRPCErr) return mySQLThrottleMetric } - mySQLThrottleMetric.Value = checkResult.Value - - if checkResult.StatusCode == http.StatusInternalServerError { - mySQLThrottleMetric.Err = fmt.Errorf("Status code: %d", checkResult.StatusCode) + mySQLThrottleMetric.Value = resp.Value + if resp.StatusCode == http.StatusInternalServerError { + mySQLThrottleMetric.Err = fmt.Errorf("Status code: %d", resp.StatusCode) } - if checkResult.RecentlyChecked { + if resp.RecentlyChecked { // We have just probed a tablet, and it reported back that someone just recently "check"ed it. // We therefore renew the heartbeats lease. - go throttler.heartbeatWriter.RequestHeartbeats() + throttler.requestHeartbeats() + go stats.GetOrNewCounter("ThrottlerProbeRecentlyChecked", "probe recently checked").Add(1) } return mySQLThrottleMetric } @@ -782,33 +803,33 @@ func (throttler *Throttler) collectMySQLMetrics(ctx context.Context, tmClient tm // synchronously, get lists of probes for clusterName, probes := range throttler.mysqlInventory.ClustersProbes { clusterName := clusterName - probes := probes - go func() { - // probes is known not to change. It can be *replaced*, but not changed. - // so it's safe to iterate it - for _, probe := range *probes { - probe := probe - go func() { - // Avoid querying the same server twice at the same time. If previous read is still there, - // we avoid re-reading it. - if !atomic.CompareAndSwapInt64(&probe.QueryInProgress, 0, 1) { - return - } - defer atomic.StoreInt64(&probe.QueryInProgress, 0) - - var throttleMetricFunc func() *mysql.MySQLThrottleMetric - if clusterName == selfStoreName { - // Throttler is probing its own tablet's metrics: - throttleMetricFunc = throttler.generateSelfMySQLThrottleMetricFunc(ctx, probe) - } else { - // Throttler probing other tablets: - throttleMetricFunc = throttler.generateTabletHTTPProbeFunction(ctx, tmClient, clusterName, probe) - } - throttleMetrics := mysql.ReadThrottleMetric(probe, clusterName, throttleMetricFunc) - throttler.mysqlThrottleMetricChan <- throttleMetrics - }() - } - }() + // probes is known not to change. It can be *replaced*, but not changed. + // so it's safe to iterate it + for _, probe := range probes { + go func(probe *mysql.Probe) { + // Avoid querying the same server twice at the same time. If previous read is still there, + // we avoid re-reading it. + if !atomic.CompareAndSwapInt64(&probe.QueryInProgress, 0, 1) { + return + } + defer atomic.StoreInt64(&probe.QueryInProgress, 0) + + var throttleMetricFunc func() *mysql.MySQLThrottleMetric + if clusterName == selfStoreName { + // Throttler is probing its own tablet's metrics: + throttleMetricFunc = throttler.generateSelfMySQLThrottleMetricFunc(ctx, probe) + } else { + // Throttler probing other tablets: + throttleMetricFunc = throttler.generateTabletProbeFunction(ctx, clusterName, tmClient, probe) + } + throttleMetrics := mysql.ReadThrottleMetric(probe, clusterName, throttleMetricFunc) + select { + case <-ctx.Done(): + return + case throttler.mysqlThrottleMetricChan <- throttleMetrics: + } + }(probe) + } } return nil } @@ -818,85 +839,96 @@ func (throttler *Throttler) refreshMySQLInventory(ctx context.Context) error { // distribute the query/threshold from the throttler down to the cluster settings and from there to the probes metricsQuery := throttler.GetMetricsQuery() metricsThreshold := throttler.MetricsThreshold.Load() - addInstanceKey := func(tablet *topodatapb.Tablet, tabletHost string, tabletPort int, key *mysql.InstanceKey, clusterName string, clusterSettings *config.MySQLClusterConfigurationSettings, probes *mysql.Probes) { + addProbe := func(alias string, tablet *topodatapb.Tablet, clusterName string, clusterSettings *config.MySQLClusterConfigurationSettings, probes mysql.Probes) bool { for _, ignore := range clusterSettings.IgnoreHosts { - if strings.Contains(key.StringCode(), ignore) { - log.Infof("Throttler: instance key ignored: %+v", key) - return + if strings.Contains(alias, ignore) { + log.Infof("Throttler: tablet ignored: %+v", alias) + return false } } - if !key.IsValid() && !key.IsSelf() { - log.Infof("Throttler: read invalid instance key: [%+v] for cluster %+v", key, clusterName) - return + if clusterName != selfStoreName { + if alias == "" { + log.Errorf("Throttler: got empty alias for cluster: %+v", clusterName) + return false + } + if tablet == nil { + log.Errorf("Throttler: got nil tablet for alias: %v in cluster: %+v", alias, clusterName) + return false + } } probe := &mysql.Probe{ - Key: *key, + Alias: alias, Tablet: tablet, - TabletHost: tabletHost, - TabletPort: tabletPort, MetricQuery: clusterSettings.MetricQuery, CacheMillis: clusterSettings.CacheMillis, } - (*probes)[*key] = probe + probes[alias] = probe + return true + } + + attemptWriteProbes := func(clusterProbes *mysql.ClusterProbes) error { + select { + case <-ctx.Done(): + return ctx.Err() + case throttler.mysqlClusterProbesChan <- clusterProbes: + return nil + } } - for clusterName, clusterSettings := range config.Settings().Stores.MySQL.Clusters { + for clusterName, clusterSettings := range throttler.configSettings.Stores.MySQL.Clusters { clusterName := clusterName - clusterSettings := clusterSettings clusterSettings.MetricQuery = metricsQuery clusterSettings.ThrottleThreshold.Store(metricsThreshold) + + clusterSettingsCopy := *clusterSettings // config may dynamically change, but internal structure (config.Settings().Stores.MySQL.Clusters in our case) // is immutable and can only be _replaced_. Hence, it's safe to read in a goroutine: - go func() { - throttler.mysqlClusterThresholds.Set(clusterName, math.Float64frombits(clusterSettings.ThrottleThreshold.Load()), cache.DefaultExpiration) + collect := func() error { + throttler.mysqlClusterThresholds.Set(clusterName, math.Float64frombits(clusterSettingsCopy.ThrottleThreshold.Load()), cache.DefaultExpiration) clusterProbes := &mysql.ClusterProbes{ ClusterName: clusterName, - IgnoreHostsCount: clusterSettings.IgnoreHostsCount, - InstanceProbes: mysql.NewProbes(), + IgnoreHostsCount: clusterSettingsCopy.IgnoreHostsCount, + TabletProbes: mysql.NewProbes(), } if clusterName == selfStoreName { // special case: just looking at this tablet's MySQL server. // We will probe this "cluster" (of one server) is a special way. - addInstanceKey(nil, "", 0, mysql.SelfInstanceKey, clusterName, clusterSettings, clusterProbes.InstanceProbes) - throttler.mysqlClusterProbesChan <- clusterProbes - return + addProbe("", nil, clusterName, &clusterSettingsCopy, clusterProbes.TabletProbes) + return attemptWriteProbes(clusterProbes) } if !throttler.isLeader.Load() { // This tablet may have used to be the primary, but it isn't now. It may have a recollection // of previous clusters it used to probe. It may have recollection of specific probes for such clusters. - // This now ensures any existing cluster probes are overrridden with an empty list of probes. - // `clusterProbes` was created above as empty, and identificable via `clusterName`. This will in turn + // This now ensures any existing cluster probes are overridden with an empty list of probes. + // `clusterProbes` was created above as empty, and identifiable via `clusterName`. This will in turn // be used to overwrite throttler.mysqlInventory.ClustersProbes[clusterProbes.ClusterName] in // updateMySQLClusterProbes(). - throttler.mysqlClusterProbesChan <- clusterProbes + return attemptWriteProbes(clusterProbes) // not the leader (primary tablet)? Then no more work for us. - return } // The primary tablet is also in charge of collecting the shard's metrics - err := func() error { - ctx, cancel := context.WithTimeout(ctx, mysqlRefreshInterval) - defer cancel() + ctx, cancel := context.WithTimeout(ctx, mysqlRefreshInterval) + defer cancel() - tabletAliases, err := throttler.ts.FindAllTabletAliasesInShard(ctx, throttler.keyspace, throttler.shard) + tabletAliases, err := throttler.ts.FindAllTabletAliasesInShard(ctx, throttler.keyspace, throttler.shard) + if err != nil { + return err + } + for _, tabletAlias := range tabletAliases { + tablet, err := throttler.ts.GetTablet(ctx, tabletAlias) if err != nil { return err } - for _, tabletAlias := range tabletAliases { - tablet, err := throttler.ts.GetTablet(ctx, tabletAlias) - if err != nil { - return err - } - if throttler.throttleTabletTypesMap[tablet.Type] { - key := mysql.InstanceKey{Hostname: tablet.MysqlHostname, Port: int(tablet.MysqlPort)} - addInstanceKey(tablet.Tablet, tablet.Hostname, int(tablet.PortMap["vt"]), &key, clusterName, clusterSettings, clusterProbes.InstanceProbes) - } + if throttler.throttleTabletTypesMap[tablet.Type] { + addProbe(topoproto.TabletAliasString(tabletAlias), tablet.Tablet, clusterName, &clusterSettingsCopy, clusterProbes.TabletProbes) } - throttler.mysqlClusterProbesChan <- clusterProbes - return nil - }() - if err != nil { + } + return attemptWriteProbes(clusterProbes) + } + go func() { + if err := collect(); err != nil { log.Errorf("refreshMySQLInventory: %+v", err) } }() @@ -906,7 +938,7 @@ func (throttler *Throttler) refreshMySQLInventory(ctx context.Context) error { // synchronous update of inventory func (throttler *Throttler) updateMySQLClusterProbes(ctx context.Context, clusterProbes *mysql.ClusterProbes) error { - throttler.mysqlInventory.ClustersProbes[clusterProbes.ClusterName] = clusterProbes.InstanceProbes + throttler.mysqlInventory.ClustersProbes[clusterProbes.ClusterName] = clusterProbes.TabletProbes throttler.mysqlInventory.IgnoreHostsCount[clusterProbes.ClusterName] = clusterProbes.IgnoreHostsCount throttler.mysqlInventory.IgnoreHostsThreshold[clusterProbes.ClusterName] = clusterProbes.IgnoreHostsThreshold return nil @@ -918,7 +950,7 @@ func (throttler *Throttler) aggregateMySQLMetrics(ctx context.Context) error { metricName := fmt.Sprintf("mysql/%s", clusterName) ignoreHostsCount := throttler.mysqlInventory.IgnoreHostsCount[clusterName] ignoreHostsThreshold := throttler.mysqlInventory.IgnoreHostsThreshold[clusterName] - aggregatedMetric := aggregateMySQLProbes(ctx, probes, clusterName, throttler.mysqlInventory.InstanceKeyMetrics, ignoreHostsCount, config.Settings().Stores.MySQL.IgnoreDialTCPErrors, ignoreHostsThreshold) + aggregatedMetric := aggregateMySQLProbes(ctx, probes, clusterName, throttler.mysqlInventory.TabletMetrics, ignoreHostsCount, throttler.configSettings.Stores.MySQL.IgnoreDialTCPErrors, ignoreHostsThreshold) throttler.aggregatedMetrics.Set(metricName, aggregatedMetric, cache.DefaultExpiration) } return nil @@ -960,7 +992,7 @@ func (throttler *Throttler) expireThrottledApps() { } } -// ThrottleApp instructs the throttler to begin throttling an app, to som eperiod and with some ratio. +// ThrottleApp instructs the throttler to begin throttling an app, to some period and with some ratio. func (throttler *Throttler) ThrottleApp(appName string, expireAt time.Time, ratio float64, exempt bool) (appThrottle *base.AppThrottle) { throttler.throttledAppsMutex.Lock() defer throttler.throttledAppsMutex.Unlock() @@ -996,7 +1028,7 @@ func (throttler *Throttler) ThrottleApp(appName string, expireAt time.Time, rati func (throttler *Throttler) UnthrottleApp(appName string) (appThrottle *base.AppThrottle) { throttler.throttledApps.Delete(appName) // the app is likely to check - go throttler.heartbeatWriter.RequestHeartbeats() + throttler.requestHeartbeats() return base.NewAppThrottle(appName, time.Now(), 0, false) } @@ -1143,20 +1175,21 @@ func (throttler *Throttler) checkStore(ctx context.Context, appName string, stor return okMetricCheckResult } if !flags.SkipRequestHeartbeats && !throttlerapp.VitessName.Equals(appName) { - go throttler.heartbeatWriter.RequestHeartbeats() + throttler.requestHeartbeats() // This check was made by someone other than the throttler itself, i.e. this came from online-ddl or vreplication or other. // We mark the fact that someone just made a check. If this is a REPLICA or RDONLY tables, this will be reported back // to the PRIMARY so that it knows it must renew the heartbeat lease. - atomic.StoreInt64(&throttler.recentCheckValue, 1+atomic.LoadInt64(&throttler.recentCheckTickerValue)) + throttler.recentCheckValue.Store(1 + throttler.recentCheckTickerValue.Load()) } checkResult = throttler.check.Check(ctx, appName, "mysql", storeName, remoteAddr, flags) - if atomic.LoadInt64(&throttler.recentCheckValue) >= atomic.LoadInt64(&throttler.recentCheckTickerValue) { + if throttler.recentCheckValue.Load() >= throttler.recentCheckTickerValue.Load() { // This indicates someone, who is not "vitess" ie not internal to the throttling logic, did a _recent_ `check`. // This could be online-ddl, or vreplication or whoever else. // If this tablet is a REPLICA or RDONLY, we want to advertise to the PRIMARY that someone did a recent check, // so that the PRIMARY knows it must renew the heartbeat lease. checkResult.RecentlyChecked = true + go stats.GetOrNewCounter("ThrottlerRecentlyChecked", "recently checked").Add(1) } return checkResult diff --git a/go/vt/vttablet/tabletserver/throttle/throttler_test.go b/go/vt/vttablet/tabletserver/throttle/throttler_test.go index c47466df522..25de8ca96f5 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttler_test.go +++ b/go/vt/vttablet/tabletserver/throttle/throttler_test.go @@ -1,7 +1,17 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ package throttle @@ -9,6 +19,7 @@ package throttle import ( "context" "fmt" + "net/http" "sync/atomic" "testing" "time" @@ -18,9 +29,14 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/config" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/mysql" + "vitess.io/vitess/go/vt/vttablet/tmclient" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) @@ -28,6 +44,23 @@ const ( waitForProbesTimeout = 30 * time.Second ) +type fakeTMClient struct { + tmclient.TabletManagerClient +} + +func (c *fakeTMClient) Close() { +} + +func (c *fakeTMClient) CheckThrottler(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.CheckThrottlerRequest) (*tabletmanagerdatapb.CheckThrottlerResponse, error) { + resp := &tabletmanagerdatapb.CheckThrottlerResponse{ + StatusCode: http.StatusOK, + Value: 0, + Threshold: 1, + RecentlyChecked: true, + } + return resp, nil +} + type FakeTopoServer struct { } @@ -64,6 +97,66 @@ type FakeHeartbeatWriter struct { func (w FakeHeartbeatWriter) RequestHeartbeats() { } +func newTestThrottler() *Throttler { + metricsQuery := "select 1" + configSettings := config.NewConfigurationSettings() + configSettings.Stores.MySQL.Clusters = map[string]*config.MySQLClusterConfigurationSettings{ + selfStoreName: {}, + shardStoreName: {}, + } + for _, s := range configSettings.Stores.MySQL.Clusters { + s.MetricQuery = metricsQuery + s.ThrottleThreshold = &atomic.Uint64{} + s.ThrottleThreshold.Store(1) + } + env := tabletenv.NewEnv(vtenv.NewTestEnv(), nil, "TabletServerTest") + throttler := &Throttler{ + mysqlClusterProbesChan: make(chan *mysql.ClusterProbes), + mysqlClusterThresholds: cache.New(cache.NoExpiration, 0), + heartbeatWriter: FakeHeartbeatWriter{}, + ts: &FakeTopoServer{}, + mysqlInventory: mysql.NewInventory(), + pool: connpool.NewPool(env, "ThrottlerPool", tabletenv.ConnPoolConfig{}), + tabletTypeFunc: func() topodatapb.TabletType { return topodatapb.TabletType_PRIMARY }, + overrideTmClient: &fakeTMClient{}, + } + throttler.configSettings = configSettings + throttler.mysqlThrottleMetricChan = make(chan *mysql.MySQLThrottleMetric) + throttler.mysqlInventoryChan = make(chan *mysql.Inventory, 1) + throttler.mysqlClusterProbesChan = make(chan *mysql.ClusterProbes) + throttler.throttlerConfigChan = make(chan *topodatapb.ThrottlerConfig) + throttler.mysqlInventory = mysql.NewInventory() + + throttler.throttledApps = cache.New(cache.NoExpiration, 0) + throttler.mysqlClusterThresholds = cache.New(cache.NoExpiration, 0) + throttler.aggregatedMetrics = cache.New(10*aggregatedMetricsExpiration, 0) + throttler.recentApps = cache.New(recentAppsExpiration, 0) + throttler.metricsHealth = cache.New(cache.NoExpiration, 0) + throttler.nonLowPriorityAppRequestsThrottled = cache.New(nonDeprioritizedAppMapExpiration, 0) + throttler.metricsQuery.Store(metricsQuery) + throttler.initThrottleTabletTypes() + throttler.check = NewThrottlerCheck(throttler) + + // High contention & racy itnervals: + throttler.leaderCheckInterval = 10 * time.Millisecond + throttler.mysqlCollectInterval = 10 * time.Millisecond + throttler.mysqlDormantCollectInterval = 10 * time.Millisecond + throttler.mysqlRefreshInterval = 10 * time.Millisecond + throttler.mysqlAggregateInterval = 10 * time.Millisecond + throttler.throttledAppsSnapshotInterval = 10 * time.Millisecond + + throttler.readSelfThrottleMetric = func(ctx context.Context, p *mysql.Probe) *mysql.MySQLThrottleMetric { + return &mysql.MySQLThrottleMetric{ + ClusterName: selfStoreName, + Alias: "", + Value: 1, + Err: nil, + } + } + + return throttler +} + func TestIsAppThrottled(t *testing.T) { throttler := Throttler{ throttledApps: cache.New(cache.NoExpiration, 0), @@ -129,17 +222,18 @@ func TestIsAppExempted(t *testing.T) { // `PRIMARY` tablet, probes other tablets). On the leader, the list is expected to be non-empty. func TestRefreshMySQLInventory(t *testing.T) { metricsQuery := "select 1" - config.Settings().Stores.MySQL.Clusters = map[string]*config.MySQLClusterConfigurationSettings{ + configSettings := config.NewConfigurationSettings() + clusters := map[string]*config.MySQLClusterConfigurationSettings{ selfStoreName: {}, "ks1": {}, "ks2": {}, } - clusters := config.Settings().Stores.MySQL.Clusters for _, s := range clusters { s.MetricQuery = metricsQuery s.ThrottleThreshold = &atomic.Uint64{} s.ThrottleThreshold.Store(1) } + configSettings.Stores.MySQL.Clusters = clusters throttler := &Throttler{ mysqlClusterProbesChan: make(chan *mysql.ClusterProbes), @@ -147,20 +241,21 @@ func TestRefreshMySQLInventory(t *testing.T) { ts: &FakeTopoServer{}, mysqlInventory: mysql.NewInventory(), } + throttler.configSettings = configSettings throttler.metricsQuery.Store(metricsQuery) throttler.initThrottleTabletTypes() validateClusterProbes := func(t *testing.T, ctx context.Context) { testName := fmt.Sprintf("leader=%t", throttler.isLeader.Load()) t.Run(testName, func(t *testing.T) { - // validateProbesCount expectes number of probes according to cluster name and throttler's leadership status - validateProbesCount := func(t *testing.T, clusterName string, probes *mysql.Probes) { + // validateProbesCount expects number of probes according to cluster name and throttler's leadership status + validateProbesCount := func(t *testing.T, clusterName string, probes mysql.Probes) { if clusterName == selfStoreName { - assert.Equal(t, 1, len(*probes)) + assert.Equal(t, 1, len(probes)) } else if throttler.isLeader.Load() { - assert.NotZero(t, len(*probes)) + assert.NotZero(t, len(probes)) } else { - assert.Empty(t, *probes) + assert.Empty(t, probes) } } t.Run("waiting for probes", func(t *testing.T) { @@ -170,7 +265,7 @@ func TestRefreshMySQLInventory(t *testing.T) { for { select { case probes := <-throttler.mysqlClusterProbesChan: - // Worth noting that in this unit test, the throttler is _closed_. Its own Operate() function does + // Worth noting that in this unit test, the throttler is _closed_ and _disabled_. Its own Operate() function does // not run, and therefore there is none but us to both populate `mysqlClusterProbesChan` as well as // read from it. We do not compete here with any other goroutine. assert.NotNil(t, probes) @@ -178,7 +273,7 @@ func TestRefreshMySQLInventory(t *testing.T) { throttler.updateMySQLClusterProbes(ctx, probes) numClusterProbesResults++ - validateProbesCount(t, probes.ClusterName, probes.InstanceProbes) + validateProbesCount(t, probes.ClusterName, probes.TabletProbes) if numClusterProbesResults == len(clusters) { // Achieved our goal @@ -219,3 +314,120 @@ func TestRefreshMySQLInventory(t *testing.T) { validateClusterProbes(t, ctx) }) } + +// runThrottler opens and enables the throttler, therby making it run the Operate() function, for a given amount of time. +// Optionally, running a given function halfway while the throttler is still open and running. +func runThrottler(t *testing.T, throttler *Throttler, timeout time.Duration, f func(*testing.T)) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + assert.False(t, throttler.IsOpen()) + assert.False(t, throttler.IsEnabled()) + + throttler.isOpen.Swap(true) + defer throttler.isOpen.Swap(false) + assert.True(t, throttler.IsOpen()) + assert.False(t, throttler.IsEnabled()) + + wg := throttler.Enable() + require.NotNil(t, wg) + defer wg.Wait() + defer throttler.Disable() + assert.True(t, throttler.IsEnabled()) + + // Enabling again does nothing: + wg2 := throttler.Enable() + assert.Nil(t, wg2) + + if f != nil { + time.Sleep(timeout / 2) + f(t) + } + + <-ctx.Done() + assert.Error(t, ctx.Err()) + + throttler.Disable() + assert.False(t, throttler.IsEnabled()) +} + +// TestRace merely lets the throttler run with aggressive intervals for a few seconds, so as to detect race conditions. +// This is relevant to `go test -race` +func TestRace(t *testing.T) { + throttler := newTestThrottler() + runThrottler(t, throttler, 5*time.Second, nil) +} + +// TestProbes enables a throttler for a few seocnds, and afterwards expects to find probes and metrics. +func TestProbesWhileOperating(t *testing.T) { + throttler := newTestThrottler() + + t.Run("aggregated", func(t *testing.T) { + assert.Equal(t, 0, throttler.aggregatedMetrics.ItemCount()) + }) + runThrottler(t, throttler, 5*time.Second, func(t *testing.T) { + t.Run("aggregated", func(t *testing.T) { + assert.Equal(t, 2, throttler.aggregatedMetrics.ItemCount()) // flushed upon Disable() + aggr := throttler.aggregatedMetricsSnapshot() + assert.Equal(t, 2, len(aggr)) // "self" and "shard" clusters + for clusterName, metricResult := range aggr { + val, err := metricResult.Get() + assert.NoError(t, err) + switch clusterName { + case "mysql/self": + assert.Equal(t, float64(1), val) + case "mysql/shard": + assert.Equal(t, float64(0), val) + default: + assert.Failf(t, "unknown clusterName", "%v", clusterName) + } + } + }) + }) +} + +// TestProbesPostDisable runs the throttler for some time, and then investigates the internal throttler maps and values. +func TestProbesPostDisable(t *testing.T) { + throttler := newTestThrottler() + runThrottler(t, throttler, 2*time.Second, nil) + + probes := throttler.mysqlInventory.ClustersProbes + assert.NotEmpty(t, probes) + + selfProbes := probes[selfStoreName] + t.Run("self", func(t *testing.T) { + assert.NotEmpty(t, selfProbes) + require.Equal(t, 1, len(selfProbes)) // should always be true once refreshMySQLInventory() runs + probe, ok := selfProbes[""] + assert.True(t, ok) + assert.NotNil(t, probe) + + assert.Equal(t, "", probe.Alias) + assert.Nil(t, probe.Tablet) + assert.Equal(t, "select 1", probe.MetricQuery) + assert.Zero(t, atomic.LoadInt64(&probe.QueryInProgress)) + }) + + shardProbes := probes[shardStoreName] + t.Run("shard", func(t *testing.T) { + assert.NotEmpty(t, shardProbes) + assert.Equal(t, 2, len(shardProbes)) // see fake FindAllTabletAliasesInShard above + for _, probe := range shardProbes { + require.NotNil(t, probe) + assert.NotEmpty(t, probe.Alias) + assert.NotNil(t, probe.Tablet) + assert.Equal(t, "select 1", probe.MetricQuery) + assert.Zero(t, atomic.LoadInt64(&probe.QueryInProgress)) + } + }) + + t.Run("metrics", func(t *testing.T) { + assert.Equal(t, 3, len(throttler.mysqlInventory.TabletMetrics)) // 1 self tablet + 2 shard tablets + }) + + t.Run("aggregated", func(t *testing.T) { + assert.Zero(t, throttler.aggregatedMetrics.ItemCount()) // flushed upon Disable() + aggr := throttler.aggregatedMetricsSnapshot() + assert.Empty(t, aggr) + }) +} diff --git a/go/vt/vttablet/tabletserver/throttle/throttlerapp/app.go b/go/vt/vttablet/tabletserver/throttle/throttlerapp/app.go index cc86ad0620b..4f1f5857837 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttlerapp/app.go +++ b/go/vt/vttablet/tabletserver/throttle/throttlerapp/app.go @@ -73,7 +73,7 @@ var ( ) // ExemptFromChecks returns 'true' for apps that should skip the throttler checks. The throttler should -// always repsond with automated "OK" to those apps, without delay. These apps also do not cause a heartbeat renewal. +// always respond with automated "OK" to those apps, without delay. These apps also do not cause a heartbeat renewal. func ExemptFromChecks(appName string) bool { return exemptFromChecks[appName] } diff --git a/go/vt/vttablet/tabletserver/throttle/throttlerapp/app_test.go b/go/vt/vttablet/tabletserver/throttle/throttlerapp/app_test.go index bd14624f49b..c468009c793 100644 --- a/go/vt/vttablet/tabletserver/throttle/throttlerapp/app_test.go +++ b/go/vt/vttablet/tabletserver/throttle/throttlerapp/app_test.go @@ -1,7 +1,17 @@ /* - Copyright 2017 GitHub Inc. +Copyright 2023 The Vitess Authors. - Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ package throttlerapp diff --git a/go/vt/vttablet/tabletserver/tx/api.go b/go/vt/vttablet/tabletserver/tx/api.go index a06923776c0..a392e530ffa 100644 --- a/go/vt/vttablet/tabletserver/tx/api.go +++ b/go/vt/vttablet/tabletserver/tx/api.go @@ -126,7 +126,7 @@ func (p *Properties) RecordQuery(query string) { func (p *Properties) InTransaction() bool { return p != nil } // String returns a printable version of the transaction -func (p *Properties) String(sanitize bool) string { +func (p *Properties) String(sanitize bool, parser *sqlparser.Parser) string { if p == nil { return "" } @@ -135,7 +135,7 @@ func (p *Properties) String(sanitize bool) string { sb := strings.Builder{} for _, query := range p.Queries { if sanitize { - query, _ = sqlparser.RedactSQLQuery(query) + query, _ = parser.RedactSQLQuery(query) } sb.WriteString(query) sb.WriteString(";") diff --git a/go/vt/vttablet/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go index fe8f1aa0b6e..7e8ecc06a75 100644 --- a/go/vt/vttablet/tabletserver/tx_engine.go +++ b/go/vt/vttablet/tabletserver/tx_engine.go @@ -97,7 +97,7 @@ func NewTxEngine(env tabletenv.Env) *TxEngine { config := env.Config() te := &TxEngine{ env: env, - shutdownGracePeriod: config.GracePeriods.ShutdownSeconds.Get(), + shutdownGracePeriod: config.GracePeriods.Shutdown, reservedConnStats: env.Exporter().NewTimings("ReservedConnections", "Reserved connections stats", "operation"), } limiter := txlimiter.New(env) @@ -124,8 +124,8 @@ func NewTxEngine(env tabletenv.Env) *TxEngine { // the TxPreparedPool. te.preparedPool = NewTxPreparedPool(config.TxPool.Size - 2) readPool := connpool.NewPool(env, "TxReadPool", tabletenv.ConnPoolConfig{ - Size: 3, - IdleTimeoutSeconds: env.Config().TxPool.IdleTimeoutSeconds, + Size: 3, + IdleTimeout: env.Config().TxPool.IdleTimeout, }) te.twoPC = NewTwoPC(readPool) te.state = NotServing diff --git a/go/vt/vttablet/tabletserver/tx_engine_test.go b/go/vt/vttablet/tabletserver/tx_engine_test.go index 6ddf2f5a9d3..7a97a8f331f 100644 --- a/go/vt/vttablet/tabletserver/tx_engine_test.go +++ b/go/vt/vttablet/tabletserver/tx_engine_test.go @@ -25,6 +25,7 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" "github.com/stretchr/testify/assert" @@ -43,12 +44,12 @@ func TestTxEngineClose(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() ctx := context.Background() - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - config.TxPool.Size = 10 - _ = config.Oltp.TxTimeoutSeconds.Set("100ms") - _ = config.GracePeriods.ShutdownSeconds.Set("0s") - te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + cfg.TxPool.Size = 10 + cfg.Oltp.TxTimeout = 100 * time.Millisecond + cfg.GracePeriods.Shutdown = 0 + te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) // Normal close. te.AcceptReadWrite() @@ -149,9 +150,9 @@ func TestTxEngineBegin(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() db.AddQueryPattern(".*", &sqltypes.Result{}) - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) for _, exec := range []func() (int64, string, error){ func() (int64, string, error) { @@ -195,9 +196,9 @@ func TestTxEngineRenewFails(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() db.AddQueryPattern(".*", &sqltypes.Result{}) - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) te.AcceptReadOnly() options := &querypb.ExecuteOptions{} connID, _, err := te.ReserveBegin(ctx, options, nil, nil) @@ -530,12 +531,12 @@ func TestWithInnerTests(outerT *testing.T) { } func setupTxEngine(db *fakesqldb.DB) *TxEngine { - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - config.TxPool.Size = 10 - config.Oltp.TxTimeoutSeconds.Set("100ms") - _ = config.GracePeriods.ShutdownSeconds.Set("0s") - te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + cfg.TxPool.Size = 10 + cfg.Oltp.TxTimeout = 100 * time.Millisecond + cfg.GracePeriods.Shutdown = 0 + te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) return te } @@ -565,9 +566,9 @@ func TestTxEngineFailReserve(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() db.AddQueryPattern(".*", &sqltypes.Result{}) - config := tabletenv.NewDefaultConfig() - config.DB = newDBConfigs(db) - te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.DB = newDBConfigs(db) + te := NewTxEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) options := &querypb.ExecuteOptions{} _, err := te.Reserve(ctx, options, 0, nil) diff --git a/go/vt/vttablet/tabletserver/tx_executor.go b/go/vt/vttablet/tabletserver/tx_executor.go index 9dc92506e84..93d18a200f9 100644 --- a/go/vt/vttablet/tabletserver/tx_executor.go +++ b/go/vt/vttablet/tabletserver/tx_executor.go @@ -235,7 +235,7 @@ func (txe *TxExecutor) ConcludeTransaction(dtid string) error { }) } -// ReadTransaction returns the metadata for the sepcified dtid. +// ReadTransaction returns the metadata for the specified dtid. func (txe *TxExecutor) ReadTransaction(dtid string) (*querypb.TransactionMetadata, error) { if !txe.te.twopcEnabled { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled") diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index f42e3c95408..52f356e0cca 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -130,7 +130,7 @@ func (tp *TxPool) Shutdown(ctx context.Context) { func (tp *TxPool) transactionKiller() { defer tp.env.LogError() for _, conn := range tp.scp.GetElapsedTimeout(vterrors.TxKillerRollback) { - log.Warningf("killing transaction (exceeded timeout: %v): %s", conn.timeout, conn.String(tp.env.Config().SanitizeLogMessages)) + log.Warningf("killing transaction (exceeded timeout: %v): %s", conn.timeout, conn.String(tp.env.Config().SanitizeLogMessages, tp.env.Environment().Parser())) switch { case conn.IsTainted(): conn.Close() diff --git a/go/vt/vttablet/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go index 3515310c481..9edbe5b3592 100644 --- a/go/vt/vttablet/tabletserver/tx_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_pool_test.go @@ -24,7 +24,9 @@ import ( "time" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/dbconfigs" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" @@ -215,7 +217,8 @@ func primeTxPoolWithConnection(t *testing.T, ctx context.Context) (*fakesqldb.DB txPool, _ := newTxPool() // Set the capacity to 1 to ensure that the db connection is reused. txPool.scp.conns.SetCapacity(1) - txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + txPool.Open(params, params, params) // Run a query to trigger a database connection. That connection will be // reused by subsequent transactions. @@ -303,7 +306,7 @@ func TestTxPoolWaitTimeoutError(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().TxPool.TimeoutSeconds.Set("1s") + env.Config().TxPool.Timeout = time.Second // given db, txPool, _, closer := setupWithEnv(t, env) defer closer() @@ -374,7 +377,8 @@ func TestTxPoolGetConnRecentlyRemovedTransaction(t *testing.T) { assertErrorMatch(id, "pool closed") txPool, _ = newTxPool() - txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + txPool.Open(params, params, params) conn1, _, _, _ = txPool.Begin(ctx, &querypb.ExecuteOptions{}, false, 0, nil, nil) id = conn1.ReservedID() @@ -389,7 +393,7 @@ func TestTxPoolGetConnRecentlyRemovedTransaction(t *testing.T) { env.Config().SetTxTimeoutForWorkload(1*time.Millisecond, querypb.ExecuteOptions_OLTP) env.Config().SetTxTimeoutForWorkload(1*time.Millisecond, querypb.ExecuteOptions_OLAP) txPool, _ = newTxPoolWithEnv(env) - txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + txPool.Open(params, params, params) defer txPool.Close() conn1, _, _, err = txPool.Begin(ctx, &querypb.ExecuteOptions{}, false, 0, nil, nil) @@ -425,7 +429,7 @@ func TestTxTimeoutKillsTransactions(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("1s") + env.Config().Oltp.TxTimeout = time.Second _, txPool, limiter, closer := setupWithEnv(t, env) defer closer() startingKills := txPool.env.Stats().KillCounters.Counts()["Transactions"] @@ -474,7 +478,7 @@ func TestTxTimeoutDoesNotKillShortLivedTransactions(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("1s") + env.Config().Oltp.TxTimeout = time.Second _, txPool, _, closer := setupWithEnv(t, env) defer closer() startingKills := txPool.env.Stats().KillCounters.Counts()["Transactions"] @@ -507,8 +511,8 @@ func TestTxTimeoutKillsOlapTransactions(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("1s") - _ = env.Config().Olap.TxTimeoutSeconds.Set("2s") + env.Config().Oltp.TxTimeout = time.Second + env.Config().Olap.TxTimeout = 2 * time.Second _, txPool, _, closer := setupWithEnv(t, env) defer closer() startingKills := txPool.env.Stats().KillCounters.Counts()["Transactions"] @@ -545,8 +549,8 @@ func TestTxTimeoutNotEnforcedForZeroLengthTimeouts(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 2 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("0s") - _ = env.Config().Olap.TxTimeoutSeconds.Set("0s") + env.Config().Oltp.TxTimeout = 0 + env.Config().Olap.TxTimeout = 0 _, txPool, _, closer := setupWithEnv(t, env) defer closer() startingKills := txPool.env.Stats().KillCounters.Counts()["Transactions"] @@ -588,8 +592,8 @@ func TestTxTimeoutReservedConn(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("1s") - _ = env.Config().Olap.TxTimeoutSeconds.Set("2s") + env.Config().Oltp.TxTimeout = time.Second + env.Config().Olap.TxTimeout = 2 * time.Second _, txPool, _, closer := setupWithEnv(t, env) defer closer() startingRcKills := txPool.env.Stats().KillCounters.Counts()["ReservedConnection"] @@ -631,8 +635,8 @@ func TestTxTimeoutReusedReservedConn(t *testing.T) { env := newEnv("TabletServerTest") env.Config().TxPool.Size = 1 env.Config().TxPool.MaxWaiters = 0 - _ = env.Config().Oltp.TxTimeoutSeconds.Set("1s") - _ = env.Config().Olap.TxTimeoutSeconds.Set("2s") + env.Config().Oltp.TxTimeout = time.Second + env.Config().Olap.TxTimeout = 2 * time.Second _, txPool, _, closer := setupWithEnv(t, env) defer closer() startingRcKills := txPool.env.Stats().KillCounters.Counts()["ReservedConnection"] @@ -812,15 +816,15 @@ func newTxPoolWithEnv(env tabletenv.Env) (*TxPool, *fakeLimiter) { } func newEnv(exporterName string) tabletenv.Env { - config := tabletenv.NewDefaultConfig() - config.TxPool.Size = 300 - _ = config.Oltp.TxTimeoutSeconds.Set("30s") - _ = config.TxPool.TimeoutSeconds.Set("40s") - config.TxPool.MaxWaiters = 500000 - _ = config.OltpReadPool.IdleTimeoutSeconds.Set("30s") - _ = config.OlapReadPool.IdleTimeoutSeconds.Set("30s") - _ = config.TxPool.IdleTimeoutSeconds.Set("30s") - env := tabletenv.NewEnv(config, exporterName) + cfg := tabletenv.NewDefaultConfig() + cfg.TxPool.Size = 300 + cfg.Oltp.TxTimeout = 30 * time.Second + cfg.TxPool.Timeout = 40 * time.Second + cfg.TxPool.MaxWaiters = 500000 + cfg.OltpReadPool.IdleTimeout = 30 * time.Second + cfg.OlapReadPool.IdleTimeout = 30 * time.Second + cfg.TxPool.IdleTimeout = 30 * time.Second + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, exporterName) return env } @@ -869,7 +873,8 @@ func setup(t *testing.T) (*fakesqldb.DB, *TxPool, *fakeLimiter, func()) { db.AddQueryPattern(".*", &sqltypes.Result{}) txPool, limiter := newTxPool() - txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + txPool.Open(params, params, params) return db, txPool, limiter, func() { txPool.Close() @@ -882,7 +887,8 @@ func setupWithEnv(t *testing.T, env tabletenv.Env) (*fakesqldb.DB, *TxPool, *fak db.AddQueryPattern(".*", &sqltypes.Result{}) txPool, limiter := newTxPoolWithEnv(env) - txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) + params := dbconfigs.New(db.ConnParams()) + txPool.Open(params, params, params) return db, txPool, limiter, func() { txPool.Close() diff --git a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go index 3a4133b54d6..46c95193f6f 100644 --- a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go +++ b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go @@ -20,6 +20,7 @@ import ( "testing" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" @@ -38,16 +39,16 @@ func createCallers(username, principal, component, subcomponent string) (*queryp } func TestTxLimiter_DisabledAllowsAll(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.TxPool.Size = 10 - config.TransactionLimitPerUser = 0.1 - config.EnableTransactionLimit = false - config.EnableTransactionLimitDryRun = false - config.TransactionLimitByUsername = false - config.TransactionLimitByPrincipal = false - config.TransactionLimitByComponent = false - config.TransactionLimitBySubcomponent = false - limiter := New(tabletenv.NewEnv(config, "TabletServerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.TxPool.Size = 10 + cfg.TransactionLimitPerUser = 0.1 + cfg.EnableTransactionLimit = false + cfg.EnableTransactionLimitDryRun = false + cfg.TransactionLimitByUsername = false + cfg.TransactionLimitByPrincipal = false + cfg.TransactionLimitByComponent = false + cfg.TransactionLimitBySubcomponent = false + limiter := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) im, ef := createCallers("", "", "", "") for i := 0; i < 5; i++ { if got, want := limiter.Get(im, ef), true; got != want { @@ -58,18 +59,18 @@ func TestTxLimiter_DisabledAllowsAll(t *testing.T) { } func TestTxLimiter_LimitsOnlyOffendingUser(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.TxPool.Size = 10 - config.TransactionLimitPerUser = 0.3 - config.EnableTransactionLimit = true - config.EnableTransactionLimitDryRun = false - config.TransactionLimitByUsername = true - config.TransactionLimitByPrincipal = false - config.TransactionLimitByComponent = false - config.TransactionLimitBySubcomponent = false + cfg := tabletenv.NewDefaultConfig() + cfg.TxPool.Size = 10 + cfg.TransactionLimitPerUser = 0.3 + cfg.EnableTransactionLimit = true + cfg.EnableTransactionLimitDryRun = false + cfg.TransactionLimitByUsername = true + cfg.TransactionLimitByPrincipal = false + cfg.TransactionLimitByComponent = false + cfg.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewEnv(config, "TabletServerTest")) + newlimiter := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) @@ -117,25 +118,25 @@ func TestTxLimiter_LimitsOnlyOffendingUser(t *testing.T) { t.Errorf("Get(im1, ef1) after releasing: got %v, want %v", got, want) } - // Rejection coutner for user 1 should still be 1. + // Rejection count for user 1 should still be 1. if got, want := limiter.rejections.Counts()[key1], int64(1); got != want { t.Errorf("Rejections count for %s: got %d, want %d", key1, got, want) } } func TestTxLimiterDryRun(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.TxPool.Size = 10 - config.TransactionLimitPerUser = 0.3 - config.EnableTransactionLimit = true - config.EnableTransactionLimitDryRun = true - config.TransactionLimitByUsername = true - config.TransactionLimitByPrincipal = false - config.TransactionLimitByComponent = false - config.TransactionLimitBySubcomponent = false + cfg := tabletenv.NewDefaultConfig() + cfg.TxPool.Size = 10 + cfg.TransactionLimitPerUser = 0.3 + cfg.EnableTransactionLimit = true + cfg.EnableTransactionLimitDryRun = true + cfg.TransactionLimitByUsername = true + cfg.TransactionLimitByPrincipal = false + cfg.TransactionLimitByComponent = false + cfg.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewEnv(config, "TabletServerTest")) + newlimiter := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) diff --git a/go/vt/vttablet/tabletserver/txlogz.go b/go/vt/vttablet/tabletserver/txlogz.go index 04a2147a7e0..8d1b88c8c85 100644 --- a/go/vt/vttablet/tabletserver/txlogz.go +++ b/go/vt/vttablet/tabletserver/txlogz.go @@ -31,7 +31,6 @@ import ( "vitess.io/vitess/go/vt/logz" querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -72,10 +71,6 @@ var ( `)) ) -func init() { - servenv.HTTPHandleFunc("/txlogz", txlogzHandler) -} - // txlogzHandler serves a human readable snapshot of the // current transaction log. // Endpoint: /txlogz?timeout=%d&limit=%d diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go index ec1ab47758c..10428ed67c7 100644 --- a/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go @@ -51,7 +51,7 @@ import ( // - Waiting transactions are unblocked if their context is done. // - Both the local queue (per row range) and global queue (whole process) are // limited to avoid that queued transactions can consume the full capacity -// of vttablet. This is important if the capaciy is finite. For example, the +// of vttablet. This is important if the capacity is finite. For example, the // number of RPCs in flight could be limited by the RPC subsystem. type TxSerializer struct { env tabletenv.Env @@ -151,7 +151,7 @@ func (txs *TxSerializer) Wait(ctx context.Context, key, table string) (done Done if err != nil { if waited { // Waiting failed early e.g. due a canceled context and we did NOT get the - // slot. Call "done" now because we don'txs return it to the caller. + // slot. Call "done" now because we do not return it to the caller. txs.unlockLocked(key, false /* returnSlot */) } return nil, waited, err @@ -273,15 +273,18 @@ func (txs *TxSerializer) unlockLocked(key string, returnSlot bool) { delete(txs.queues, key) if q.max > 1 { + var formattedKey = key var logMsg string + if txs.env.Config().SanitizeLogMessages { - logMsg = fmt.Sprintf("%v simultaneous transactions (%v in total) for the same row range (%v) would have been queued.", q.max, q.count, txs.sanitizeKey(key)) - } else { - logMsg = fmt.Sprintf("%v simultaneous transactions (%v in total) for the same row range (%v) would have been queued.", q.max, q.count, key) + formattedKey = txs.sanitizeKey(key) } + if txs.dryRun { + logMsg = fmt.Sprintf("%v simultaneous transactions (%v in total) for the same row range (%v) would have been queued.", q.max, q.count, formattedKey) txs.logDryRun.Infof(logMsg) } else { + logMsg = fmt.Sprintf("%v simultaneous transactions (%v in total) for the same row range (%v) have been queued.", q.max, q.count, formattedKey) txs.log.Infof(logMsg) } } diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go index d495800e141..e1b4b5a7612 100644 --- a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go @@ -17,6 +17,7 @@ limitations under the License. package txserializer import ( + "context" "fmt" "net/http" "net/http/httptest" @@ -25,9 +26,8 @@ import ( "testing" "time" - "context" - "vitess.io/vitess/go/streamlog" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -44,11 +44,11 @@ func resetVariables(txs *TxSerializer) { } func TestTxSerializer_NoHotRow(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 5 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -76,11 +76,11 @@ func TestTxSerializerRedactDebugUI(t *testing.T) { streamlog.SetRedactDebugUIQueries(false) }() - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 5 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -103,8 +103,8 @@ func TestTxSerializerRedactDebugUI(t *testing.T) { } func TestKeySanitization(t *testing.T) { - config := tabletenv.NewDefaultConfig() - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) // with a where clause key := "t1 where c1='foo'" want := "t1 ... [REDACTED]" @@ -122,11 +122,11 @@ func TestKeySanitization(t *testing.T) { } func TestTxSerializer(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 2 - config.HotRowProtection.MaxGlobalQueueSize = 3 - config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 2 + cfg.HotRowProtection.MaxGlobalQueueSize = 3 + cfg.HotRowProtection.MaxConcurrency = 1 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -195,11 +195,11 @@ func TestTxSerializer(t *testing.T) { func TestTxSerializer_ConcurrentTransactions(t *testing.T) { // Allow up to 2 concurrent transactions per hot row. - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 3 - config.HotRowProtection.MaxGlobalQueueSize = 3 - config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 3 + cfg.HotRowProtection.MaxGlobalQueueSize = 3 + cfg.HotRowProtection.MaxConcurrency = 2 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -318,11 +318,11 @@ func testHTTPHandler(txs *TxSerializer, count int, redacted bool) error { // tx1 and tx2 are allowed to run concurrently while tx3 and tx4 are queued. // tx3 will get canceled and tx4 will be unblocked once tx1 is done. func TestTxSerializerCancel(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 4 - config.HotRowProtection.MaxGlobalQueueSize = 4 - config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 4 + cfg.HotRowProtection.MaxGlobalQueueSize = 4 + cfg.HotRowProtection.MaxConcurrency = 2 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) // tx3 and tx4 will record their number once they're done waiting. @@ -418,12 +418,12 @@ func TestTxSerializerCancel(t *testing.T) { // TestTxSerializerDryRun verifies that the dry-run mode does not serialize // the two concurrent transactions for the same key. func TestTxSerializerDryRun(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.Mode = tabletenv.Dryrun - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 2 - config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.Mode = tabletenv.Dryrun + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 2 + cfg.HotRowProtection.MaxConcurrency = 1 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -489,11 +489,11 @@ func TestTxSerializerDryRun(t *testing.T) { // reject transactions although they may succeed within the txpool constraints // and RPC deadline. func TestTxSerializerGlobalQueueOverflow(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 1 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) // tx1. done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") @@ -530,22 +530,22 @@ func TestTxSerializerGlobalQueueOverflow(t *testing.T) { } func TestTxSerializerPending(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 1 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) if got, want := txs.Pending("t1 where1"), 0; got != want { t.Errorf("there should be no pending transaction: got = %v, want = %v", got, want) } } func BenchmarkTxSerializer_NoHotRow(b *testing.B) { - config := tabletenv.NewDefaultConfig() - config.HotRowProtection.MaxQueueSize = 1 - config.HotRowProtection.MaxGlobalQueueSize = 1 - config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) + cfg := tabletenv.NewDefaultConfig() + cfg.HotRowProtection.MaxQueueSize = 1 + cfg.HotRowProtection.MaxGlobalQueueSize = 1 + cfg.HotRowProtection.MaxConcurrency = 5 + txs := New(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "TxSerializerTest")) b.ResetTimer() diff --git a/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go index 1e503dc7020..3b298cacddf 100644 --- a/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go +++ b/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go @@ -59,6 +59,14 @@ func (m *MockHealthCheck) CacheStatus() discovery.TabletsCacheStatusList { return ret0 } +// HealthyStatus mocks base method. +func (m *MockHealthCheck) HealthyStatus() discovery.TabletsCacheStatusList { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthyStatus") + ret0, _ := ret[0].(discovery.TabletsCacheStatusList) + return ret0 +} + // CacheStatus indicates an expected call of CacheStatus. func (mr *MockHealthCheckMockRecorder) CacheStatus() *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go index 3ffb3a78a1a..aeb75d258a3 100644 --- a/go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go +++ b/go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go @@ -12,6 +12,7 @@ import ( discovery "vitess.io/vitess/go/vt/discovery" throttlerdata "vitess.io/vitess/go/vt/proto/throttlerdata" + topodata "vitess.io/vitess/go/vt/proto/topodata" ) // MockThrottlerInterface is a mock of ThrottlerInterface interface. @@ -63,6 +64,20 @@ func (mr *MockThrottlerInterfaceMockRecorder) GetConfiguration() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfiguration", reflect.TypeOf((*MockThrottlerInterface)(nil).GetConfiguration)) } +// MaxLag mocks base method. +func (m *MockThrottlerInterface) MaxLag(tabletType topodata.TabletType) uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MaxLag", tabletType) + ret0, _ := ret[0].(uint32) + return ret0 +} + +// MaxLag indicates an expected call of LastMaxLagNotIgnoredForTabletType. +func (mr *MockThrottlerInterfaceMockRecorder) MaxLag(tabletType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxLag", reflect.TypeOf((*MockThrottlerInterface)(nil).MaxLag), tabletType) +} + // MaxRate mocks base method. func (m *MockThrottlerInterface) MaxRate() int64 { m.ctrl.T.Helper() diff --git a/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go index 92976bbedf2..4a682ffd298 100644 --- a/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go +++ b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -22,6 +22,7 @@ import ( "reflect" "strings" "sync" + "sync/atomic" "time" "vitess.io/vitess/go/stats" @@ -81,14 +82,7 @@ type ThrottlerInterface interface { GetConfiguration() *throttlerdatapb.Configuration UpdateConfiguration(configuration *throttlerdatapb.Configuration, copyZeroValues bool) error ResetConfiguration() -} - -// TopologyWatcherInterface defines the public interface that is implemented by -// discovery.LegacyTopologyWatcher. It is only used here to allow mocking out -// go/vt/discovery.LegacyTopologyWatcher. -type TopologyWatcherInterface interface { - Start() - Stop() + MaxLag(tabletType topodatapb.TabletType) uint32 } // TxThrottlerName is the name the wrapped go/vt/throttler object will be registered with @@ -175,6 +169,10 @@ type txThrottlerStateImpl struct { // tabletTypes stores the tablet types for throttling tabletTypes map[topodatapb.TabletType]bool + + maxLag int64 + done chan bool + waitForTermination sync.WaitGroup } // NewTxThrottler tries to construct a txThrottler from the relevant @@ -253,7 +251,7 @@ func (t *txThrottler) Throttle(priority int, workload string) (result bool) { // Throttle according to both what the throttler state says and the priority. Workloads with lower priority value // are less likely to be throttled. - result = t.state.throttle() && rand.Intn(sqlparser.MaxPriorityValue) < priority + result = rand.Intn(sqlparser.MaxPriorityValue) < priority && t.state.throttle() t.requestsTotal.Add(workload, 1) if result { @@ -292,6 +290,7 @@ func newTxThrottlerState(txThrottler *txThrottler, config *tabletenv.TabletConfi tabletTypes: tabletTypes, throttler: t, txThrottler: txThrottler, + done: make(chan bool, 1), } // get cells from topo if none defined in tabletenv config @@ -306,6 +305,8 @@ func newTxThrottlerState(txThrottler *txThrottler, config *tabletenv.TabletConfi state.stopHealthCheck = cancel state.initHealthCheckStream(txThrottler.topoServer, target) go state.healthChecksProcessor(ctx, txThrottler.topoServer, target) + state.waitForTermination.Add(1) + go state.updateMaxLag() return state, nil } @@ -364,7 +365,35 @@ func (ts *txThrottlerStateImpl) throttle() bool { // Serialize calls to ts.throttle.Throttle() ts.throttleMu.Lock() defer ts.throttleMu.Unlock() - return ts.throttler.Throttle(0 /* threadId */) > 0 + + maxLag := atomic.LoadInt64(&ts.maxLag) + + return maxLag > ts.config.TxThrottlerConfig.TargetReplicationLagSec && + ts.throttler.Throttle(0 /* threadId */) > 0 +} + +func (ts *txThrottlerStateImpl) updateMaxLag() { + defer ts.waitForTermination.Done() + // We use half of the target lag to ensure we have enough resolution to see changes in lag below that value + ticker := time.NewTicker(time.Duration(ts.config.TxThrottlerConfig.TargetReplicationLagSec/2) * time.Second) + defer ticker.Stop() +outerloop: + for { + select { + case <-ticker.C: + var maxLag uint32 + + for tabletType := range ts.tabletTypes { + maxLagPerTabletType := ts.throttler.MaxLag(tabletType) + if maxLagPerTabletType > maxLag { + maxLag = maxLagPerTabletType + } + } + atomic.StoreInt64(&ts.maxLag, int64(maxLag)) + case <-ts.done: + break outerloop + } + } } func (ts *txThrottlerStateImpl) deallocateResources() { @@ -372,6 +401,8 @@ func (ts *txThrottlerStateImpl) deallocateResources() { ts.closeHealthCheckStream() ts.healthCheck = nil + ts.done <- true + ts.waitForTermination.Wait() // After ts.healthCheck is closed txThrottlerStateImpl.StatsUpdate() is guaranteed not // to be executing, so we can safely close the throttler. ts.throttler.Close() diff --git a/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go index 268a37437d9..fe352cf96f4 100644 --- a/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go +++ b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and @@ -22,6 +22,7 @@ package txthrottler import ( "context" + "sync/atomic" "testing" "time" @@ -33,6 +34,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" @@ -40,16 +42,16 @@ import ( ) func TestDisabledThrottler(t *testing.T) { - config := tabletenv.NewDefaultConfig() - config.EnableTxThrottler = false - env := tabletenv.NewEnv(config, t.Name()) + cfg := tabletenv.NewDefaultConfig() + cfg.EnableTxThrottler = false + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, t.Name()) throttler := NewTxThrottler(env, nil) throttler.InitDBConfig(&querypb.Target{ Keyspace: "keyspace", Shard: "shard", }) assert.Nil(t, throttler.Open()) - assert.False(t, throttler.Throttle(0, "some_workload")) + assert.False(t, throttler.Throttle(0, "some-workload")) throttlerImpl, _ := throttler.(*txThrottler) assert.Zero(t, throttlerImpl.throttlerRunning.Get()) throttler.Close() @@ -79,34 +81,51 @@ func TestEnabledThrottler(t *testing.T) { return mockThrottler, nil } - call0 := mockThrottler.EXPECT().UpdateConfiguration(gomock.Any(), true /* copyZeroValues */) - call1 := mockThrottler.EXPECT().Throttle(0) - call1.Return(0 * time.Second) + var calls []*gomock.Call + + call := mockThrottler.EXPECT().UpdateConfiguration(gomock.Any(), true /* copyZeroValues */) + calls = append(calls, call) + + // 1 + call = mockThrottler.EXPECT().Throttle(0) + call.Return(0 * time.Second) + calls = append(calls, call) + tabletStats := &discovery.TabletHealth{ Target: &querypb.Target{ Cell: "cell1", TabletType: topodatapb.TabletType_REPLICA, }, } - call2 := mockThrottler.EXPECT().RecordReplicationLag(gomock.Any(), tabletStats) - call3 := mockThrottler.EXPECT().Throttle(0) - call3.Return(1 * time.Second) - call4 := mockThrottler.EXPECT().Throttle(0) - call4.Return(1 * time.Second) - calllast := mockThrottler.EXPECT().Close() + call = mockThrottler.EXPECT().RecordReplicationLag(gomock.Any(), tabletStats) + calls = append(calls, call) + + // 2 + call = mockThrottler.EXPECT().Throttle(0) + call.Return(1 * time.Second) + calls = append(calls, call) + + // 3 + // Nothing gets mocked here because the order of evaluation in txThrottler.Throttle() evaluates first + // whether the priority allows for throttling or not, so no need to mock calls in mockThrottler.Throttle() + + // 4 + // Nothing gets mocked here because the order of evaluation in txThrottlerStateImpl.Throttle() evaluates first + // whether there is lag or not, so no call to the underlying mockThrottler is issued. - call1.After(call0) - call2.After(call1) - call3.After(call2) - call4.After(call3) - calllast.After(call4) + call = mockThrottler.EXPECT().Close() + calls = append(calls, call) - config := tabletenv.NewDefaultConfig() - config.EnableTxThrottler = true - config.TxThrottlerTabletTypes = &topoproto.TabletTypeListFlag{topodatapb.TabletType_REPLICA} + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } + + cfg := tabletenv.NewDefaultConfig() + cfg.EnableTxThrottler = true + cfg.TxThrottlerTabletTypes = &topoproto.TabletTypeListFlag{topodatapb.TabletType_REPLICA} - env := tabletenv.NewEnv(config, t.Name()) + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, t.Name()) throttler := NewTxThrottler(env, ts) throttlerImpl, _ := throttler.(*txThrottler) assert.NotNil(t, throttlerImpl) @@ -117,13 +136,20 @@ func TestEnabledThrottler(t *testing.T) { }) assert.Nil(t, throttlerImpl.Open()) - throttlerStateImpl := throttlerImpl.state.(*txThrottlerStateImpl) + throttlerStateImpl, ok := throttlerImpl.state.(*txThrottlerStateImpl) + assert.True(t, ok) assert.Equal(t, map[topodatapb.TabletType]bool{topodatapb.TabletType_REPLICA: true}, throttlerStateImpl.tabletTypes) assert.Equal(t, int64(1), throttlerImpl.throttlerRunning.Get()) - assert.False(t, throttlerImpl.Throttle(100, "some_workload")) - assert.Equal(t, int64(1), throttlerImpl.requestsTotal.Counts()["some_workload"]) - assert.Zero(t, throttlerImpl.requestsThrottled.Counts()["some_workload"]) + // Stop the go routine that keeps updating the cached shard's max lag to prevent it from changing the value in a + // way that will interfere with how we manipulate that value in our tests to evaluate different cases: + throttlerStateImpl.done <- true + + // 1 should not throttle due to return value of underlying Throttle(), despite high lag + atomic.StoreInt64(&throttlerStateImpl.maxLag, 20) + assert.False(t, throttlerImpl.Throttle(100, "some-workload")) + assert.Equal(t, int64(1), throttlerImpl.requestsTotal.Counts()["some-workload"]) + assert.Zero(t, throttlerImpl.requestsThrottled.Counts()["some-workload"]) throttlerImpl.state.StatsUpdate(tabletStats) // This calls replication lag thing assert.Equal(t, map[string]int64{"cell1.REPLICA": 1}, throttlerImpl.healthChecksReadTotal.Counts()) @@ -139,16 +165,23 @@ func TestEnabledThrottler(t *testing.T) { assert.Equal(t, map[string]int64{"cell1.REPLICA": 1, "cell2.RDONLY": 1}, throttlerImpl.healthChecksReadTotal.Counts()) assert.Equal(t, map[string]int64{"cell1.REPLICA": 1}, throttlerImpl.healthChecksRecordedTotal.Counts()) - // The second throttle call should reject. - assert.True(t, throttlerImpl.Throttle(100, "some_workload")) - assert.Equal(t, int64(2), throttlerImpl.requestsTotal.Counts()["some_workload"]) - assert.Equal(t, int64(1), throttlerImpl.requestsThrottled.Counts()["some_workload"]) + // 2 should throttle due to return value of underlying Throttle(), high lag & priority = 100 + assert.True(t, throttlerImpl.Throttle(100, "some-workload")) + assert.Equal(t, int64(2), throttlerImpl.requestsTotal.Counts()["some-workload"]) + assert.Equal(t, int64(1), throttlerImpl.requestsThrottled.Counts()["some-workload"]) - // This call should not throttle due to priority. Check that's the case and counters agree. - assert.False(t, throttlerImpl.Throttle(0, "some_workload")) - assert.Equal(t, int64(3), throttlerImpl.requestsTotal.Counts()["some_workload"]) - assert.Equal(t, int64(1), throttlerImpl.requestsThrottled.Counts()["some_workload"]) - throttlerImpl.Close() + // 3 should not throttle despite return value of underlying Throttle() and high lag, due to priority = 0 + assert.False(t, throttlerImpl.Throttle(0, "some-workload")) + assert.Equal(t, int64(3), throttlerImpl.requestsTotal.Counts()["some-workload"]) + assert.Equal(t, int64(1), throttlerImpl.requestsThrottled.Counts()["some-workload"]) + + // 4 should not throttle despite return value of underlying Throttle() and priority = 100, due to low lag + atomic.StoreInt64(&throttlerStateImpl.maxLag, 1) + assert.False(t, throttler.Throttle(100, "some-workload")) + assert.Equal(t, int64(4), throttlerImpl.requestsTotal.Counts()["some-workload"]) + assert.Equal(t, int64(1), throttlerImpl.requestsThrottled.Counts()["some-workload"]) + + throttler.Close() assert.Zero(t, throttlerImpl.throttlerRunning.Get()) } @@ -168,8 +201,8 @@ func TestFetchKnownCells(t *testing.T) { } func TestDryRunThrottler(t *testing.T) { - config := tabletenv.NewDefaultConfig() - env := tabletenv.NewEnv(config, t.Name()) + cfg := tabletenv.NewDefaultConfig() + env := tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, t.Name()) testCases := []struct { Name string diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 2862601bf1b..398f7b4e27e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -432,7 +432,7 @@ func (vse *Engine) setWatch() { } var vschema *vindexes.VSchema if v != nil { - vschema = vindexes.BuildVSchema(v) + vschema = vindexes.BuildVSchema(v, vse.env.Environment().Parser()) if err != nil { log.Errorf("Error building vschema: %v", err) vse.vschemaErrors.Add(1) @@ -590,9 +590,13 @@ func (vse *Engine) getMySQLEndpoint(ctx context.Context, db dbconfigs.Connector) // mapPKEquivalentCols gets a PK equivalent from mysqld for the table // and maps the column names to field indexes in the MinimalTable struct. -func (vse *Engine) mapPKEquivalentCols(ctx context.Context, table *binlogdatapb.MinimalTable) ([]int, error) { - mysqld := mysqlctl.NewMysqld(vse.env.Config().DB) - pkeColNames, indexName, err := mysqld.GetPrimaryKeyEquivalentColumns(ctx, vse.env.Config().DB.DBName, table.Name) +func (vse *Engine) mapPKEquivalentCols(ctx context.Context, db dbconfigs.Connector, table *binlogdatapb.MinimalTable) ([]int, error) { + conn, err := db.Connect(ctx) + if err != nil { + return nil, err + } + defer conn.Close() + pkeColNames, indexName, err := mysqlctl.GetPrimaryKeyEquivalentColumns(ctx, conn.ExecuteFetch, vse.env.Config().DB.DBName, table.Name) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go index 36bcc8f181a..35bea172cd0 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go @@ -243,7 +243,7 @@ func TestVStreamerWaitForMySQL(t *testing.T) { testDB.AddQuery(replicaLagQuery, sbmres) for _, tt := range tests { - tt.fields.cp = testDB.ConnParams() + tt.fields.cp = dbconfigs.New(testDB.ConnParams()) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() t.Run(tt.name, func(t *testing.T) { diff --git a/go/vt/vttablet/tabletserver/vstreamer/fuzz.go b/go/vt/vttablet/tabletserver/vstreamer/fuzz.go index 90387e97f2c..83369f27d5e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/fuzz.go +++ b/go/vt/vttablet/tabletserver/vstreamer/fuzz.go @@ -24,6 +24,7 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -65,7 +66,7 @@ func Fuzz(data []byte) int { if err != nil { return -1 } - _, _ = buildPlan(t1, testLocalVSchema, &binlogdatapb.Filter{ + _, _ = buildPlan(t1, testLocalVSchema, sqlparser.NewTestParser(), &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{ {Match: str1, Filter: str2}, }, diff --git a/go/vt/vttablet/tabletserver/vstreamer/helper_event_test.go b/go/vt/vttablet/tabletserver/vstreamer/helper_event_test.go new file mode 100644 index 00000000000..0b479bd588c --- /dev/null +++ b/go/vt/vttablet/tabletserver/vstreamer/helper_event_test.go @@ -0,0 +1,589 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vstreamer + +// This file contains the test framework for testing the event generation logic in vstreamer. +// The test framework is designed to be used in the following way: +// 1. Define a TestSpec with the following fields: +// - ddls: a list of create table statements for the tables to be used in the test +// - tests: a list of test cases, each test case is a list of TestQuery +// - options: test-specific options, if any +// 2. Call ts.Init() to initialize the test. +// 3. Call ts.Run() to run the test. This will run the queries and validate the events. +// 4. Call ts.Close() to clean up the tables created in the test. +// The test framework will take care of creating the tables, running the queries, and validating the events for +// simpler cases. For more complex cases, the test framework provides hooks to customize the event generation. + +// Note: To simplify the initial implementation, the test framework is designed to be used in the vstreamer package only. +// It makes several assumptions about how the test cases are written. For example, queries are expected to +// use single quotes for string literals, for example: +// `"insert into t1 values (1, 'blob1', 'aaa')"`. +// The test framework will not work if the queries use double quotes for string literals at the moment. + +import ( + "context" + "fmt" + "slices" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/schemadiff" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" +) + +const ( + lengthInt = 11 + lengthBlob = 65535 + lengthText = 262140 + lengthSet = 56 +) + +func getDefaultCollationID() int64 { + return 45 // utf8mb4_general_ci +} + +var ( + // noEvents is used to indicate that a query is expected to generate no events. + noEvents = []TestRowEvent{} +) + +// TestColumn has all the attributes of a column required for the test cases. +type TestColumn struct { + name, dataType, colType string + len, collationID int64 + dataTypeLowered string + skip bool + collationName string +} + +// TestFieldEvent has all the attributes of a table required for creating a field event. +type TestFieldEvent struct { + table, db string + cols []*TestColumn +} + +// TestQuery represents a database query and the expected events it generates. +type TestQuery struct { + query string + events []TestRowEvent +} + +// TestRowChange represents the before and after state of a row due to a dml +type TestRowChange struct { + before []string + after []string +} + +// TestRowEventSpec is used for defining a custom row event. +type TestRowEventSpec struct { + table string + changes []TestRowChange +} + +// Generates a string representation for a custom row event. +func (s *TestRowEventSpec) String() string { + ev := &binlogdata.RowEvent{ + TableName: s.table, + } + var rowChanges []*binlogdata.RowChange + if s.changes != nil && len(s.changes) > 0 { + for _, c := range s.changes { + rowChange := binlogdata.RowChange{} + if c.before != nil && len(c.before) > 0 { + rowChange.Before = &query.Row{} + for _, val := range c.before { + rowChange.Before.Lengths = append(rowChange.Before.Lengths, int64(len(val))) + rowChange.Before.Values = append(rowChange.Before.Values, []byte(val)...) + } + } + if c.after != nil && len(c.after) > 0 { + rowChange.After = &query.Row{} + for _, val := range c.after { + rowChange.After.Lengths = append(rowChange.After.Lengths, int64(len(val))) + rowChange.After.Values = append(rowChange.After.Values, []byte(val)...) + } + } + rowChanges = append(rowChanges, &rowChange) + } + ev.RowChanges = rowChanges + } + vEvent := &binlogdata.VEvent{ + Type: binlogdata.VEventType_ROW, + RowEvent: ev, + } + return vEvent.String() +} + +// TestRowEvent is used to define either the actual row event string (the `event` field) or a custom row event +// (the `spec` field). Only one should be specified. If a test validates `flags` of a RowEvent then it is set. +type TestRowEvent struct { + event string + spec *TestRowEventSpec + flags int +} + +// TestSpecOptions has any non-standard test-specific options which can modify the event generation behaviour. +type TestSpecOptions struct { + noblob bool + filter *binlogdata.Filter +} + +// TestSpec is defined one per unit test. +type TestSpec struct { + // test=specific parameters + t *testing.T + ddls []string // create table statements + tests [][]*TestQuery // list of input queries and expected events for each query + options *TestSpecOptions // test-specific options + + // internal state + inited bool // whether the test has been initialized + tables []string // list of tables in the schema (created in `ddls`) + pkColumns map[string][]string // map of table name to primary key columns + schema *schemadiff.Schema // parsed schema from `ddls` using `schemadiff` + fieldEvents map[string]*TestFieldEvent // map of table name to field event for the table + fieldEventsSent map[string]bool // whether the field event has been sent for the table in the test + state map[string]*query.Row // last row inserted for each table. Useful to generate events only for inserts + metadata map[string][]string // list of enum/set values for enum/set columns +} + +func (ts *TestSpec) getCurrentState(table string) *query.Row { + return ts.state[table] +} + +func (ts *TestSpec) setCurrentState(table string, row *query.Row) { + ts.state[table] = row +} + +// Init() initializes the test. It creates the tables and sets up the internal state. +func (ts *TestSpec) Init() error { + var err error + if ts.inited { + return nil + } + defer func() { ts.inited = true }() + if ts.options == nil { + ts.options = &TestSpecOptions{} + } + ts.schema, err = schemadiff.NewSchemaFromQueries(schemadiff.NewTestEnv(), ts.ddls) + if err != nil { + return err + } + ts.fieldEvents = make(map[string]*TestFieldEvent) + ts.fieldEventsSent = make(map[string]bool) + ts.state = make(map[string]*query.Row) + ts.metadata = make(map[string][]string) + ts.pkColumns = make(map[string][]string) + // create tables + require.Equal(ts.t, len(ts.ddls), len(ts.schema.Tables()), "number of tables in ddls and schema do not match") + for i, t := range ts.schema.Tables() { + execStatement(ts.t, ts.ddls[i]) + fe := ts.getFieldEvent(t) + ts.fieldEvents[t.Name()] = fe + + var pkColumns []string + var hasPK bool + for _, index := range t.TableSpec.Indexes { + require.NotNil(ts.t, index.Info, "index.Info is nil") + if index.Info.Type == sqlparser.IndexTypePrimary { + for _, col := range index.Columns { + pkColumns = append(pkColumns, col.Column.String()) + } + hasPK = true + } + } + if !hasPK { + // add all columns as pk columns + for _, col := range t.TableSpec.Columns { + pkColumns = append(pkColumns, col.Name.String()) + } + } + ts.pkColumns[t.Name()] = pkColumns + } + engine.se.Reload(context.Background()) + return nil +} + +// Close() should be called (via defer) at the end of the test to clean up the tables created in the test. +func (ts *TestSpec) Close() { + dropStatement := fmt.Sprintf("drop tables %s", strings.Join(ts.schema.TableNames(), ", ")) + execStatement(ts.t, dropStatement) +} + +func (ts *TestSpec) getBindVarsForInsert(stmt sqlparser.Statement) (string, map[string]string) { + bv := make(map[string]string) + ins := stmt.(*sqlparser.Insert) + tn, err := ins.Table.TableName() + require.NoError(ts.t, err) + table := tn.Name.String() + fe := ts.fieldEvents[table] + vals, ok := ins.Rows.(sqlparser.Values) + require.True(ts.t, ok, "insert statement does not have values") + for _, val := range vals { + for i, v := range val { + bufV := sqlparser.NewTrackedBuffer(nil) + v.Format(bufV) + s := bufV.String() + switch fe.cols[i].dataTypeLowered { + case "varchar", "char", "binary", "varbinary", "blob", "text": + s = strings.Trim(s, "'") + case "set", "enum": + s = ts.getMetadataMap(table, fe.cols[i], s) + } + bv[fe.cols[i].name] = s + } + } + return table, bv +} + +func (ts *TestSpec) getBindVarsForUpdate(stmt sqlparser.Statement) (string, map[string]string) { + bv := make(map[string]string) + upd := stmt.(*sqlparser.Update) + //buf := sqlparser.NewTrackedBuffer(nil) + table := sqlparser.String(upd.TableExprs[0].(*sqlparser.AliasedTableExpr).Expr) + //upd.TableExprs[0].(*sqlparser.AliasedTableExpr).Expr.Format(buf) + //table := buf.String() + fe, ok := ts.fieldEvents[table] + require.True(ts.t, ok, "field event for table %s not found", table) + index := int64(0) + state := ts.getCurrentState(table) + for i, col := range fe.cols { + bv[col.name] = string(state.Values[index : index+state.Lengths[i]]) + index += state.Lengths[i] + } + for _, expr := range upd.Exprs { + bufV := sqlparser.NewTrackedBuffer(nil) + bufN := sqlparser.NewTrackedBuffer(nil) + expr.Expr.Format(bufV) + expr.Name.Format(bufN) + bv[bufN.String()] = strings.Trim(bufV.String(), "'") + } + return table, bv +} + +// Run() runs the test. It first initializes the test, then runs the queries and validates the events. +func (ts *TestSpec) Run() { + require.NoError(ts.t, engine.se.Reload(context.Background())) + if !ts.inited { + require.NoError(ts.t, ts.Init()) + } + var testcases []testcase + for _, t := range ts.tests { + var tc testcase + var input []string + var output []string + for _, tq := range t { + var table string + input = append(input, tq.query) + switch { + case tq.events != nil && len(tq.events) == 0: // when an input query is expected to generate no events + continue + case tq.events != nil && // when we define the actual events either as a serialized string or as a TestRowEvent + (len(tq.events) > 0 && + !(len(tq.events) == 1 && tq.events[0].event == "" && tq.events[0].spec == nil)): + for _, e := range tq.events { + if e.event != "" { + output = append(output, e.event) + } else if e.spec != nil { + output = append(output, e.spec.String()) + } else { + panic("invalid event") + } + } + continue + default: + // when we don't define the actual events, we generate them based on the input query + flags := 0 + if len(tq.events) == 1 { + flags = tq.events[0].flags + } + stmt, err := sqlparser.NewTestParser().Parse(tq.query) + require.NoError(ts.t, err) + bv := make(map[string]string) + isRowEvent := false + switch stmt.(type) { + case *sqlparser.Begin: + output = append(output, "begin") + case *sqlparser.Commit: + output = append(output, "gtid", "commit") + case *sqlparser.Insert: + isRowEvent = true + table, bv = ts.getBindVarsForInsert(stmt) + case *sqlparser.Update: + isRowEvent = true + table, bv = ts.getBindVarsForUpdate(stmt) + case *sqlparser.Delete: + isRowEvent = true + del := stmt.(*sqlparser.Delete) + table = del.TableExprs[0].(*sqlparser.AliasedTableExpr).As.String() + default: + require.FailNowf(ts.t, "unsupported statement type", "stmt: %s", stmt) + } + if isRowEvent { + fe := ts.fieldEvents[table] + if fe == nil { + require.FailNowf(ts.t, "field event for table %s not found", table) + } + if !ts.fieldEventsSent[table] { + output = append(output, fe.String()) + ts.fieldEventsSent[table] = true + } + output = append(output, ts.getRowEvent(table, bv, fe, stmt, uint32(flags))) + } + } + + } + tc.input = input + tc.output = append(tc.output, output) + testcases = append(testcases, tc) + } + runCases(ts.t, ts.options.filter, testcases, "current", nil) +} + +func (ts *TestSpec) getFieldEvent(table *schemadiff.CreateTableEntity) *TestFieldEvent { + var tfe TestFieldEvent + tfe.table = table.Name() + tfe.db = testenv.DBName + for _, col := range table.TableSpec.Columns { + tc := TestColumn{} + tc.name = col.Name.String() + sqlType := col.Type.SQLType() + tc.dataType = sqlType.String() + tc.dataTypeLowered = strings.ToLower(tc.dataType) + tc.collationName = col.Type.Options.Collate + switch tc.dataTypeLowered { + case "int32": + tc.len = lengthInt + tc.collationID = collations.CollationBinaryID + tc.colType = "int(11)" + case "varchar", "varbinary", "char", "binary": + l := *col.Type.Length + switch tc.dataTypeLowered { + case "binary", "varbinary": + tc.len = int64(l) + tc.collationID = collations.CollationBinaryID + default: + tc.len = 4 * int64(l) + tc.collationID = getDefaultCollationID() + if tc.dataTypeLowered == "char" && strings.Contains(tc.collationName, "bin") { + tc.dataType = "BINARY" + } + } + tc.colType = fmt.Sprintf("%s(%d)", tc.dataTypeLowered, l) + case "blob": + tc.len = lengthBlob + tc.collationID = collations.CollationBinaryID + tc.colType = "blob" + case "text": + tc.len = lengthText + tc.collationID = getDefaultCollationID() + tc.colType = "text" + case "set": + tc.len = lengthSet + tc.collationID = getDefaultCollationID() + tc.colType = fmt.Sprintf("%s(%s)", tc.dataTypeLowered, strings.Join(col.Type.EnumValues, ",")) + ts.metadata[getMetadataKey(table.Name(), tc.name)] = col.Type.EnumValues + case "enum": + tc.len = int64(len(col.Type.EnumValues) + 1) + tc.collationID = getDefaultCollationID() + tc.colType = fmt.Sprintf("%s(%s)", tc.dataTypeLowered, strings.Join(col.Type.EnumValues, ",")) + ts.metadata[getMetadataKey(table.Name(), tc.name)] = col.Type.EnumValues + default: + log.Infof(fmt.Sprintf("unknown sqlTypeString %s", tc.dataTypeLowered)) + } + tfe.cols = append(tfe.cols, &tc) + } + return &tfe +} + +func getMetadataKey(table, col string) string { + return fmt.Sprintf("%s:%s", table, col) +} + +func (ts *TestSpec) setMetadataMap(table, col, value string) { + values := strings.Split(value, ",") + valuesReversed := slices.Clone(values) + slices.Reverse(valuesReversed) + ts.metadata[getMetadataKey(table, col)] = valuesReversed +} + +func (ts *TestSpec) getMetadataMap(table string, col *TestColumn, value string) string { + var bits int64 + value = strings.Trim(value, "'") + meta := ts.metadata[getMetadataKey(table, col.name)] + values := strings.Split(value, ",") + for _, v := range values { + v2 := strings.Trim(v, "'") + for i, m := range meta { + m2 := strings.Trim(m, "'") + if m2 == v2 { + switch col.dataTypeLowered { + case "set": + bits |= 1 << uint(i) + case "enum": + bits = int64(i) + 1 + } + } + } + } + return strconv.FormatInt(bits, 10) +} + +func (ts *TestSpec) getRowEvent(table string, bv map[string]string, fe *TestFieldEvent, stmt sqlparser.Statement, flags uint32) string { + ev := &binlogdata.RowEvent{ + TableName: table, + RowChanges: []*binlogdata.RowChange{ + { + Before: nil, + After: nil, + }, + }, + Flags: flags, + } + var row query.Row + for i, col := range fe.cols { + if fe.cols[i].skip { + continue + } + if col.dataTypeLowered == "binary" { + bv[col.name] = strings.TrimSuffix(bv[col.name], "\\0") + } + val := []byte(bv[col.name]) + l := int64(len(val)) + if col.dataTypeLowered == "binary" { + for l < col.len { + val = append(val, "\x00"...) + l++ + } + } + row.Values = append(row.Values, val...) + row.Lengths = append(row.Lengths, l) + } + ev.RowChanges = ts.getRowChanges(table, stmt, &row) + vEvent := &binlogdata.VEvent{ + Type: binlogdata.VEventType_ROW, + RowEvent: ev, + } + return vEvent.String() +} + +func (ts *TestSpec) getRowChanges(table string, stmt sqlparser.Statement, row *query.Row) []*binlogdata.RowChange { + var rowChanges []*binlogdata.RowChange + var rowChange binlogdata.RowChange + switch stmt.(type) { + case *sqlparser.Insert: + rowChange.After = row + ts.setCurrentState(table, row) + case *sqlparser.Update: + rowChange = *ts.getRowChangeForUpdate(table, row) + ts.setCurrentState(table, row) + case *sqlparser.Delete: + rowChange.Before = row + ts.setCurrentState(table, nil) + } + rowChanges = append(rowChanges, &rowChange) + return rowChanges +} + +func (ts *TestSpec) getRowChangeForUpdate(table string, newState *query.Row) *binlogdata.RowChange { + var rowChange binlogdata.RowChange + var bitmap byte + var before, after query.Row + + currentState := ts.getCurrentState(table) + if currentState == nil { + return nil + } + var currentValueIndex int64 + var hasSkip bool + for i, l := range currentState.Lengths { + skip := false + isPKColumn := false + for _, pkColumn := range ts.pkColumns[table] { + if pkColumn == ts.fieldEvents[table].cols[i].name { + isPKColumn = true + break + } + } + if ts.options.noblob { + switch ts.fieldEvents[table].cols[i].dataTypeLowered { + case "blob", "text": + currentValue := currentState.Values[currentValueIndex : currentValueIndex+l] + newValue := newState.Values[currentValueIndex : currentValueIndex+l] + if string(currentValue) == string(newValue) { + skip = true + hasSkip = true + } + } + } + if skip && !isPKColumn { + before.Lengths = append(before.Lengths, -1) + } else { + before.Values = append(before.Values, currentState.Values[currentValueIndex:currentValueIndex+l]...) + before.Lengths = append(before.Lengths, l) + } + if skip { + after.Lengths = append(after.Lengths, -1) + } else { + after.Values = append(after.Values, newState.Values[currentValueIndex:currentValueIndex+l]...) + after.Lengths = append(after.Lengths, l) + bitmap |= 1 << uint(i) + } + currentValueIndex += l + } + rowChange.Before = &before + rowChange.After = &after + if hasSkip { + rowChange.DataColumns = &binlogdata.RowChange_Bitmap{ + Count: int64(len(currentState.Lengths)), + Cols: []byte{bitmap}, + } + } + return &rowChange +} + +func (ts *TestSpec) getBefore(table string) *query.Row { + currentState := ts.getCurrentState(table) + if currentState == nil { + return nil + } + var row query.Row + var currentValueIndex int64 + for i, l := range currentState.Lengths { + dataTypeIsRedacted := false + switch ts.fieldEvents[table].cols[i].dataTypeLowered { + case "blob", "text": + dataTypeIsRedacted = true + } + if ts.options.noblob && dataTypeIsRedacted { + row.Lengths = append(row.Lengths, -1) + } else { + row.Values = append(row.Values, currentState.Values[currentValueIndex:currentValueIndex+l]...) + row.Lengths = append(row.Lengths, l) + } + currentValueIndex += l + } + return &row +} diff --git a/go/vt/vttablet/tabletserver/vstreamer/local_vschema_test.go b/go/vt/vttablet/tabletserver/vstreamer/local_vschema_test.go index f514298e844..5d57effbadf 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/local_vschema_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/local_vschema_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -86,7 +87,7 @@ func TestFindColVindex(t *testing.T) { }, }, } - vschema := vindexes.BuildVSchema(testSrvVSchema) + vschema := vindexes.BuildVSchema(testSrvVSchema, sqlparser.NewTestParser()) testcases := []struct { keyspace string @@ -149,7 +150,7 @@ func TestFindOrCreateVindex(t *testing.T) { }, }, } - vschema := vindexes.BuildVSchema(testSrvVSchema) + vschema := vindexes.BuildVSchema(testSrvVSchema, sqlparser.NewTestParser()) lvs := &localVSchema{ keyspace: "ks1", @@ -204,7 +205,7 @@ func TestFindTable(t *testing.T) { }, }, } - vschema := vindexes.BuildVSchema(testSrvVSchema) + vschema := vindexes.BuildVSchema(testSrvVSchema, sqlparser.NewTestParser()) testcases := []struct { keyspace string diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_flaky_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_flaky_test.go index f3743c6de46..d4b8e62341a 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_flaky_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_flaky_test.go @@ -27,6 +27,7 @@ import ( _flag "vitess.io/vitess/go/internal/flag" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" ) @@ -90,10 +91,10 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams original, err := env.Dbcfgs.AppWithDB().MysqlParams() require.NoError(t, err) modified := modifier(*original) - config := env.TabletEnv.Config().Clone() - config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) + cfg := env.TabletEnv.Config().Clone() + cfg.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) - engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine, nil, env.Cells[0]) + engine := NewEngine(tabletenv.NewEnv(vtenv.NewTestEnv(), cfg, "VStreamerTest"), env.SrvTopo, env.SchemaEngine, nil, env.Cells[0]) engine.InitDBConfig(env.KeyspaceName, env.ShardName) engine.Open() return engine diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 30fbfdb7a01..5f4f9ea7fb7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -56,6 +57,8 @@ type Plan struct { // Filters is the list of filters to be applied to the columns // of the table. Filters []Filter + + env *vtenv.Environment } // Opcode enumerates the operators supported in a where clause @@ -162,14 +165,14 @@ func getOpcode(comparison *sqlparser.ComparisonExpr) (Opcode, error) { } // compare returns true after applying the comparison specified in the Filter to the actual data in the column -func compare(comparison Opcode, columnValue, filterValue sqltypes.Value, charset collations.ID) (bool, error) { +func compare(comparison Opcode, columnValue, filterValue sqltypes.Value, collationEnv *collations.Environment, charset collations.ID) (bool, error) { // use null semantics: return false if either value is null if columnValue.IsNull() || filterValue.IsNull() { return false, nil } // at this point neither values can be null // NullsafeCompare returns 0 if values match, -1 if columnValue < filterValue, 1 if columnValue > filterValue - result, err := evalengine.NullsafeCompare(columnValue, filterValue, charset) + result, err := evalengine.NullsafeCompare(columnValue, filterValue, collationEnv, charset) if err != nil { return false, err } @@ -228,7 +231,7 @@ func (plan *Plan) filter(values, result []sqltypes.Value, charsets []collations. return false, nil } default: - match, err := compare(filter.Opcode, values[filter.ColNum], filter.Value, charsets[filter.ColNum]) + match, err := compare(filter.Opcode, values[filter.ColNum], filter.Value, plan.env.CollationEnv(), charsets[filter.ColNum]) if err != nil { return false, err } @@ -284,11 +287,11 @@ func mustSendStmt(query mysql.Query, dbname string) bool { return true } -func mustSendDDL(query mysql.Query, dbname string, filter *binlogdatapb.Filter) bool { +func mustSendDDL(query mysql.Query, dbname string, filter *binlogdatapb.Filter, parser *sqlparser.Parser) bool { if query.Database != "" && query.Database != dbname { return false } - ast, err := sqlparser.Parse(query.SQL) + ast, err := parser.Parse(query.SQL) // If there was a parsing error, we send it through. Hopefully, // recipient can handle it. if err != nil { @@ -338,13 +341,13 @@ func ruleMatches(tableName string, filter *binlogdatapb.Filter) bool { // tableMatches is similar to buildPlan below and MatchTable in vreplication/table_plan_builder.go. func tableMatches(table sqlparser.TableName, dbname string, filter *binlogdatapb.Filter) bool { - if !table.Qualifier.IsEmpty() && table.Qualifier.String() != dbname { + if table.Qualifier.NotEmpty() && table.Qualifier.String() != dbname { return false } return ruleMatches(table.Name.String(), filter) } -func buildPlan(ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (*Plan, error) { +func buildPlan(env *vtenv.Environment, ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (*Plan, error) { for _, rule := range filter.Rules { switch { case strings.HasPrefix(rule.Match, "/"): @@ -356,9 +359,9 @@ func buildPlan(ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (* if !result { continue } - return buildREPlan(ti, vschema, rule.Filter) + return buildREPlan(env, ti, vschema, rule.Filter) case rule.Match == ti.Name: - return buildTablePlan(ti, vschema, rule.Filter) + return buildTablePlan(env, ti, vschema, rule.Filter) } } return nil, nil @@ -366,8 +369,9 @@ func buildPlan(ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (* // buildREPlan handles cases where Match has a regular expression. // If so, the Filter can be an empty string or a keyrange, like "-80". -func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) { +func buildREPlan(env *vtenv.Environment, ti *Table, vschema *localVSchema, filter string) (*Plan, error) { plan := &Plan{ + env: env, Table: ti, } plan.ColExprs = make([]ColExpr, len(ti.Fields)) @@ -409,8 +413,8 @@ func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) // BuildTablePlan handles cases where a specific table name is specified. // The filter must be a select statement. -func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, error) { - sel, fromTable, err := analyzeSelect(query) +func buildTablePlan(env *vtenv.Environment, ti *Table, vschema *localVSchema, query string) (*Plan, error) { + sel, fromTable, err := analyzeSelect(query, env.Parser()) if err != nil { log.Errorf("%s", err.Error()) return nil, err @@ -422,6 +426,7 @@ func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, erro plan := &Plan{ Table: ti, + env: env, } if err := plan.analyzeWhere(vschema, sel.Where); err != nil { log.Errorf("%s", err.Error()) @@ -439,8 +444,8 @@ func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, erro return plan, nil } -func analyzeSelect(query string) (sel *sqlparser.Select, fromTable sqlparser.IdentifierCS, err error) { - statement, err := sqlparser.Parse(query) +func analyzeSelect(query string, parser *sqlparser.Parser) (sel *sqlparser.Select, fromTable sqlparser.IdentifierCS, err error) { + statement, err := parser.Parse(query) if err != nil { return nil, fromTable, err } @@ -528,15 +533,18 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er if !ok { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) } - //StrVal is varbinary, we do not support varchar since we would have to implement all collation types + // StrVal is varbinary, we do not support varchar since we would have to implement all collation types if val.Type != sqlparser.IntVal && val.Type != sqlparser.StrVal { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) } - pv, err := evalengine.Translate(val, nil) + pv, err := evalengine.Translate(val, &evalengine.Config{ + Collation: plan.env.CollationEnv().DefaultConnectionCharset(), + Environment: plan.env, + }) if err != nil { return err } - env := evalengine.EmptyExpressionEnv() + env := evalengine.EmptyExpressionEnv(plan.env) resolved, err := env.Evaluate(pv) if err != nil { return err @@ -544,7 +552,7 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er plan.Filters = append(plan.Filters, Filter{ Opcode: opcode, ColNum: colnum, - Value: resolved.Value(collations.Default()), + Value: resolved.Value(plan.env.CollationEnv().DefaultConnectionCharset()), }) case *sqlparser.FuncExpr: if !expr.Name.EqualString("in_keyrange") { @@ -702,7 +710,7 @@ func (plan *Plan) analyzeExpr(vschema *localVSchema, selExpr sqlparser.SelectExp return ColExpr{}, fmt.Errorf("unsupported function: %v", sqlparser.String(inner)) } case *sqlparser.Literal: - //allow only intval 1 + // allow only intval 1 if inner.Type != sqlparser.IntVal { return ColExpr{}, fmt.Errorf("only integer literals are supported") } diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go index 03001362073..19fa5ee06a2 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go @@ -20,18 +20,17 @@ import ( "fmt" "testing" - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/vt/proto/topodata" - - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/test/utils" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/json2" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -86,7 +85,7 @@ func init() { "ks": &kspb, }, } - vschema := vindexes.BuildVSchema(srvVSchema) + vschema := vindexes.BuildVSchema(srvVSchema, sqlparser.NewTestParser()) testLocalVSchema = &localVSchema{ keyspace: "ks", vschema: vschema, @@ -167,7 +166,7 @@ func TestMustSendDDL(t *testing.T) { }} for _, tcase := range testcases { q := mysql.Query{SQL: tcase.sql, Database: tcase.db} - got := mustSendDDL(q, "mydb", filter) + got := mustSendDDL(q, "mydb", filter, sqlparser.NewTestParser()) if got != tcase.output { t.Errorf("%v: %v, want %v", q, got, tcase.output) } @@ -259,6 +258,7 @@ func TestPlanBuilder(t *testing.T) { Flags: uint32(querypb.MySqlFlag_BINARY_FLAG), }, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -289,6 +289,7 @@ func TestPlanBuilder(t *testing.T) { VindexColumns: []int{0}, KeyRange: nil, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -311,6 +312,7 @@ func TestPlanBuilder(t *testing.T) { Flags: uint32(querypb.MySqlFlag_BINARY_FLAG), }, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -333,6 +335,7 @@ func TestPlanBuilder(t *testing.T) { Flags: uint32(querypb.MySqlFlag_BINARY_FLAG), }, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -355,6 +358,7 @@ func TestPlanBuilder(t *testing.T) { Flags: uint32(querypb.MySqlFlag_NUM_FLAG), }, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -385,6 +389,7 @@ func TestPlanBuilder(t *testing.T) { VindexColumns: []int{0}, KeyRange: nil, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -415,6 +420,7 @@ func TestPlanBuilder(t *testing.T) { VindexColumns: []int{0}, KeyRange: nil, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -445,6 +451,7 @@ func TestPlanBuilder(t *testing.T) { VindexColumns: nil, KeyRange: nil, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t2, @@ -478,6 +485,7 @@ func TestPlanBuilder(t *testing.T) { VindexColumns: []int{0, 1}, KeyRange: nil, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -501,6 +509,7 @@ func TestPlanBuilder(t *testing.T) { }, }}, convertUsingUTF8Columns: map[string]bool{"val": true}, + env: vtenv.NewTestEnv(), }, }, { inTable: regional, @@ -524,6 +533,7 @@ func TestPlanBuilder(t *testing.T) { Vindex: testLocalVSchema.vschema.Keyspaces["ks"].Vindexes["region_vdx"], VindexColumns: []int{0, 1}, }}, + env: vtenv.NewTestEnv(), }, }, { inTable: t1, @@ -634,7 +644,7 @@ func TestPlanBuilder(t *testing.T) { }} for _, tcase := range testcases { t.Run(tcase.inRule.String(), func(t *testing.T) { - plan, err := buildPlan(tcase.inTable, testLocalVSchema, &binlogdatapb.Filter{ + plan, err := buildPlan(vtenv.NewTestEnv(), tcase.inTable, testLocalVSchema, &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{tcase.inRule}, }) @@ -731,7 +741,7 @@ func TestPlanBuilderFilterComparison(t *testing.T) { for _, tcase := range testcases { t.Run(tcase.name, func(t *testing.T) { - plan, err := buildPlan(t1, testLocalVSchema, &binlogdatapb.Filter{ + plan, err := buildPlan(vtenv.NewTestEnv(), t1, testLocalVSchema, &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{Match: "t1", Filter: tcase.inFilter}}, }) @@ -775,7 +785,7 @@ func TestCompare(t *testing.T) { } for _, tc := range testcases { t.Run("", func(t *testing.T) { - got, err := compare(tc.opcode, tc.columnValue, tc.filterValue, collations.CollationUtf8mb4ID) + got, err := compare(tc.opcode, tc.columnValue, tc.filterValue, collations.MySQL8(), collations.CollationUtf8mb4ID) require.NoError(t, err) require.Equal(t, tc.want, got) }) diff --git a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go index 91f319fa2c5..834b8b88378 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go @@ -62,7 +62,7 @@ func (rs *resultStreamer) Cancel() { } func (rs *resultStreamer) Stream() error { - _, fromTable, err := analyzeSelect(rs.query) + _, fromTable, err := analyzeSelect(rs.query, rs.vse.env.Environment().Parser()) if err != nil { return err } diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index bd259864981..c1685c61d13 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -19,6 +19,7 @@ package vstreamer import ( "context" "fmt" + "net/url" "sync" "time" @@ -35,7 +36,6 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" @@ -45,17 +45,6 @@ var ( rowStreamertHeartbeatInterval = 10 * time.Second ) -// RowStreamer exposes an externally usable interface to rowStreamer. -type RowStreamer interface { - Stream() error - Cancel() -} - -// NewRowStreamer returns a RowStreamer -func NewRowStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, query string, lastpk []sqltypes.Value, send func(*binlogdatapb.VStreamRowsResponse) error, vse *Engine, mode RowStreamerMode) RowStreamer { - return newRowStreamer(ctx, cp, se, query, lastpk, &localVSchema{vschema: &vindexes.VSchema{}}, send, vse, mode, nil) -} - type RowStreamerMode int32 const ( @@ -151,7 +140,7 @@ func (rs *rowStreamer) Stream() error { func (rs *rowStreamer) buildPlan() error { // This pre-parsing is required to extract the table name // and create its metadata. - sel, fromTable, err := analyzeSelect(rs.query) + sel, fromTable, err := analyzeSelect(rs.query, rs.se.Environment().Parser()) if err != nil { return err } @@ -165,7 +154,7 @@ func (rs *rowStreamer) buildPlan() error { // "puncture"; this is an event that is captured by vstreamer. The completion of the flow fixes the // puncture, and places a new table under the original table's name, but the way it is done does not // cause vstreamer to refresh schema state. - // There is therefore a reproducable valid sequence of events where vstreamer thinks a table does not + // There is therefore a reproducible valid sequence of events where vstreamer thinks a table does not // exist, where it in fact does exist. // For this reason we give vstreamer a "second chance" to review the up-to-date state of the schema. // In the future, we will reduce this operation to reading a single table rather than the entire schema. @@ -188,7 +177,7 @@ func (rs *rowStreamer) buildPlan() error { // This is because the row format of a read is identical // to the row format of a binlog event. So, the same // filtering will work. - rs.plan, err = buildTablePlan(ti, rs.vschema, rs.query) + rs.plan, err = buildTablePlan(rs.se.Environment(), ti, rs.vschema, rs.query) if err != nil { log.Errorf("%s", err.Error()) return err @@ -201,7 +190,12 @@ func (rs *rowStreamer) buildPlan() error { return err } } - + if s, found := directives.GetString("ukForce", ""); found { + st.PKIndexName, err = url.QueryUnescape(s) + if err != nil { + return err + } + } rs.pkColumns, err = rs.buildPKColumns(st) if err != nil { return err @@ -235,7 +229,7 @@ func (rs *rowStreamer) buildPKColumns(st *binlogdatapb.MinimalTable) ([]int, err var pkColumns = make([]int, 0) if len(st.PKColumns) == 0 { // Use a PK equivalent if one exists. - pkColumns, err := rs.vse.mapPKEquivalentCols(rs.ctx, st) + pkColumns, err := rs.vse.mapPKEquivalentCols(rs.ctx, rs.cp, st) if err == nil && len(pkColumns) != 0 { return pkColumns, nil } @@ -279,8 +273,11 @@ func (rs *rowStreamer) buildSelect(st *binlogdatapb.MinimalTable) (string, error // of the PK columns which are used in the ORDER BY clause below. var indexHint string if st.PKIndexName != "" { - indexHint = fmt.Sprintf(" force index (%s)", - sqlescape.EscapeID(sqlescape.UnescapeID(st.PKIndexName))) + escapedPKIndexName, err := sqlescape.EnsureEscaped(st.PKIndexName) + if err != nil { + return "", err + } + indexHint = fmt.Sprintf(" force index (%s)", escapedPKIndexName) } buf.Myprintf(" from %v%s", sqlparser.NewIdentifierCS(rs.plan.Table.Name), indexHint) if len(rs.lastpk) != 0 { diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 9828481397b..47efb466d3a 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -23,16 +23,68 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/log" - "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) +// TestRowStreamerQuery validates that the correct force index hint and order by is added to the rowstreamer query. +func TestRowStreamerQuery(t *testing.T) { + execStatements(t, []string{ + "create table t1(id int, uk1 int, val varbinary(128), primary key(id), unique key uk2 (uk1))", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + engine.se.Reload(context.Background()) + // We need to StreamRows, to get an initialized RowStreamer. + // Note that the query passed into StreamRows is overwritten while running the test. + err := engine.StreamRows(context.Background(), "select * from t1", nil, func(rows *binlogdatapb.VStreamRowsResponse) error { + type testCase struct { + directives string + sendQuerySuffix string + } + queryTemplate := "select %s id, uk1, val from t1" + getQuery := func(directives string) string { + return fmt.Sprintf(queryTemplate, directives) + } + sendQueryPrefix := "select /*+ MAX_EXECUTION_TIME(3600000) */ id, uk1, val from t1" + testCases := []testCase{ + {"", "force index (`PRIMARY`) order by id"}, + {"/*vt+ ukColumns=\"uk1\" ukForce=\"uk2\" */", "force index (`uk2`) order by uk1"}, + {"/*vt+ ukForce=\"uk2\" */", "force index (`uk2`) order by uk1"}, + {"/*vt+ ukColumns=\"uk1\" */", "order by uk1"}, + } + + for _, tc := range testCases { + t.Run(tc.directives, func(t *testing.T) { + var err error + var rs *rowStreamer + // Depending on the order of the test cases, the index of the engine.rowStreamers slice may change. + for _, rs2 := range engine.rowStreamers { + if rs2 != nil { + rs = rs2 + break + } + } + require.NotNil(t, rs) + rs.query = getQuery(tc.directives) + err = rs.buildPlan() + require.NoError(t, err) + want := fmt.Sprintf("%s %s", sendQueryPrefix, tc.sendQuerySuffix) + require.Equal(t, want, rs.sendQuery) + }) + } + return nil + }) + require.NoError(t, err) +} + func TestStreamRowsScan(t *testing.T) { if testing.Short() { t.Skip() @@ -206,7 +258,7 @@ func TestStreamRowsUnicode(t *testing.T) { engine = savedEngine }() engine = customEngine(t, func(in mysql.ConnParams) mysql.ConnParams { - in.Charset = "latin1" + in.Charset = collations.CollationLatin1Swedish return in }) defer engine.Close() diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go index b9a3a70ea98..1510dbb53ef 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go @@ -19,18 +19,21 @@ package vstreamer import ( "context" "fmt" + "strings" "sync/atomic" "time" "github.com/spf13/pflag" "vitess.io/vitess/go/mysql/replication" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" ) // If the current binary log is greater than this byte size, we @@ -77,7 +80,7 @@ func (conn *snapshotConn) streamWithSnapshot(ctx context.Context, table, query s // Rotating the log when it's above a certain size ensures that we are processing // a relatively small binary log that will be minimal in size and GTID events. // We only attempt to rotate it if the current log is of any significant size to - // avoid too many unecessary rotations. + // avoid too many unnecessary rotations. if rotatedLog, err = conn.limitOpenBinlogSize(); err != nil { // This is a best effort operation meant to lower overhead and improve performance. // Thus it should not be required, nor cause the operation to fail. @@ -112,18 +115,15 @@ func (conn *snapshotConn) startSnapshot(ctx context.Context, table string) (gtid defer func() { _, err := lockConn.ExecuteFetch("unlock tables", 0, false) if err != nil { - log.Warning("Unlock tables failed: %v", err) - } else { - log.Infof("Tables unlocked: %v", table) + log.Warning("Unlock tables (%s) failed: %v", table, err) } lockConn.Close() }() tableName := sqlparser.String(sqlparser.NewIdentifierCS(table)) - log.Infof("Locking table %s for copying", table) if _, err := lockConn.ExecuteFetch(fmt.Sprintf("lock tables %s read", tableName), 1, false); err != nil { - log.Infof("Error locking table %s to read", tableName) + log.Warningf("Error locking table %s to read: %v", tableName, err) return "", err } mpos, err := lockConn.PrimaryPosition() @@ -168,7 +168,7 @@ func (conn *snapshotConn) startSnapshotWithConsistentGTID(ctx context.Context) ( return replication.EncodePosition(mpos), nil } -// Close rollsback any open transactions and closes the connection. +// Close rolls back any open transactions and closes the connection. func (conn *snapshotConn) Close() { _, _ = conn.ExecuteFetch("rollback", 1, false) conn.Conn.Close() @@ -241,8 +241,44 @@ func (conn *snapshotConn) startSnapshotAllTables(ctx context.Context) (gtid stri log.Infof("Locking all tables") if _, err := lockConn.ExecuteFetch("FLUSH TABLES WITH READ LOCK", 1, false); err != nil { + attemptExplicitTablesLocks := false + if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERAccessDeniedError { + // Access denied. On some systems this is either because the user doesn't have SUPER or RELOAD privileges. + // On some other systems, namely RDS, the command is just unsupported. + // There is an alternative way: run a `LOCK TABLES tbl1 READ, tbl2 READ, ...` for all tables. It not as + // efficient, and make a huge query, but still better than nothing. + attemptExplicitTablesLocks = true + } log.Infof("Error locking all tables") - return "", err + if !attemptExplicitTablesLocks { + return "", err + } + // get list of all tables + rs, err := conn.ExecuteFetch("show full tables", -1, true) + if err != nil { + return "", err + } + + var lockClauses []string + for _, row := range rs.Rows { + tableName := row[0].ToString() + tableType := row[1].ToString() + if tableType != "BASE TABLE" { + continue + } + tableName = sqlparser.String(sqlparser.NewIdentifierCS(tableName)) + lockClause := fmt.Sprintf("%s read", tableName) + lockClauses = append(lockClauses, lockClause) + } + if len(lockClauses) > 0 { + query := fmt.Sprintf("lock tables %s", strings.Join(lockClauses, ",")) + if _, err := lockConn.ExecuteFetch(query, 1, false); err != nil { + log.Error(vterrors.Wrapf(err, "explicitly locking all %v tables", len(lockClauses))) + return "", err + } + } else { + log.Infof("explicit lock tables: no tables found") + } } mpos, err := lockConn.PrimaryPosition() if err != nil { diff --git a/go/vt/vttablet/tabletserver/vstreamer/tablestreamer.go b/go/vt/vttablet/tabletserver/vstreamer/tablestreamer.go index 0bbd265435b..80f850dae2e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/tablestreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/tablestreamer.go @@ -23,12 +23,12 @@ import ( "strings" "sync/atomic" - "vitess.io/vitess/go/vt/vttablet" - "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/mysqlctl/tmutils" + "vitess.io/vitess/go/vt/vttablet" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -117,12 +117,16 @@ func (ts *tableStreamer) Stream() error { return err } - rs, err := conn.ExecuteFetch("show tables", -1, true) + rs, err := conn.ExecuteFetch("show full tables", -1, true) if err != nil { return err } for _, row := range rs.Rows { tableName := row[0].ToString() + tableType := row[1].ToString() + if tableType != tmutils.TableBaseTable { + continue + } if schema2.IsInternalOperationTableName(tableName) { log.Infof("Skipping internal table %s", tableName) continue diff --git a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go index c40e180110f..9c77ca18594 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go +++ b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go @@ -20,6 +20,7 @@ package testenv import ( "context" "fmt" + "math/rand" "os" "regexp" "strings" @@ -30,6 +31,7 @@ import ( "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttest" @@ -39,6 +41,8 @@ import ( vttestpb "vitess.io/vitess/go/vt/proto/vttest" ) +const DBName = "vttest" + // Env contains all the env vars for a test against a mysql instance. type Env struct { cluster *vttest.LocalCluster @@ -63,7 +67,7 @@ type Env struct { // Init initializes an Env. func Init(ctx context.Context) (*Env, error) { te := &Env{ - KeyspaceName: "vttest", + KeyspaceName: DBName, ShardName: "0", Cells: []string{"cell1"}, } @@ -75,7 +79,9 @@ func Init(ctx context.Context) (*Env, error) { if err := te.TopoServ.CreateShard(ctx, te.KeyspaceName, te.ShardName); err != nil { panic(err) } - te.SrvTopo = srvtopo.NewResilientServer(ctx, te.TopoServ, "TestTopo") + // Add a random suffix to metric name to avoid panic. Another option would have been to generate a random string. + suffix := rand.Int() + te.SrvTopo = srvtopo.NewResilientServer(ctx, te.TopoServ, "TestTopo"+fmt.Sprint(suffix)) cfg := vttest.Config{ Topology: &vttestpb.VTTestTopology{ @@ -85,7 +91,7 @@ func Init(ctx context.Context) (*Env, error) { Shards: []*vttestpb.Shard{ { Name: "0", - DbNameOverride: "vttest", + DbNameOverride: DBName, }, }, }, @@ -103,9 +109,9 @@ func Init(ctx context.Context) (*Env, error) { return nil, fmt.Errorf("could not launch mysql: %v", err) } te.Dbcfgs = dbconfigs.NewTestDBConfigs(te.cluster.MySQLConnParams(), te.cluster.MySQLAppDebugConnParams(), te.cluster.DbName()) - config := tabletenv.NewDefaultConfig() - config.DB = te.Dbcfgs - te.TabletEnv = tabletenv.NewEnv(config, "VStreamerTest") + conf := tabletenv.NewDefaultConfig() + conf.DB = te.Dbcfgs + te.TabletEnv = tabletenv.NewEnv(vtenv.NewTestEnv(), conf, "VStreamerTest") te.Mysqld = mysqlctl.NewMysqld(te.Dbcfgs) pos, _ := te.Mysqld.PrimaryPosition() if strings.HasPrefix(strings.ToLower(pos.GTIDSet.Flavor()), string(mysqlctl.FlavorMariaDB)) { diff --git a/go/vt/vttablet/tabletserver/vstreamer/uvstreamer_flaky_test.go b/go/vt/vttablet/tabletserver/vstreamer/uvstreamer_flaky_test.go index 203052e981e..c515357a4ec 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/uvstreamer_flaky_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/uvstreamer_flaky_test.go @@ -71,11 +71,11 @@ const ( numInitialRows = 10 ) -type state struct { +type TestState struct { tables []string } -var testState = &state{} +var testState = &TestState{} var positions map[string]string var allEvents []*binlogdatapb.VEvent @@ -240,17 +240,17 @@ func TestVStreamCopyCompleteFlow(t *testing.T) { insertRow(t, "t1", 1, numInitialRows+4) insertRow(t, "t2", 2, numInitialRows+3) // savepoints should not be sent in the event stream - execStatement(t, ` -begin; -insert into t3 (id31, id32) values (12, 360); -savepoint a; -insert into t3 (id31, id32) values (13, 390); -rollback work to savepoint a; -savepoint b; -insert into t3 (id31, id32) values (13, 390); -release savepoint b; -commit;" -`) + execStatements(t, []string{ + "begin", + "insert into t3 (id31, id32) values (12, 360)", + "savepoint a", + "insert into t3 (id31, id32) values (13, 390)", + "rollback work to savepoint a", + "savepoint b", + "insert into t3 (id31, id32) values (13, 390)", + "release savepoint b", + "commit", + }) } numCopyEvents := 3 /*t1,t2,t3*/ * (numInitialRows + 1 /*FieldEvent*/ + 1 /*LastPKEvent*/ + 1 /*TestEvent: Copy Start*/ + 2 /*begin,commit*/ + 3 /* LastPK Completed*/) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index f210e756da1..9c63f8a499c 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -211,7 +211,7 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog // GTID->DDL // GTID->OTHER // HEARTBEAT is issued if there's inactivity, which is likely - // to heppend between one group of events and another. + // to happen between one group of events and another. // // Buffering only takes row or statement lengths into consideration. // Length of other events is considered negligible. @@ -503,7 +503,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e Type: binlogdatapb.VEventType_COMMIT, }) case sqlparser.StmtDDL: - if mustSendDDL(q, vs.cp.DBName(), vs.filter) { + if mustSendDDL(q, vs.cp.DBName(), vs.filter, vs.vse.env.Environment().Parser()) { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_GTID, Gtid: replication.EncodePosition(vs.pos), @@ -520,7 +520,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e Type: binlogdatapb.VEventType_OTHER, }) } - if schema.MustReloadSchemaOnDDL(q.SQL, vs.cp.DBName()) { + if schema.MustReloadSchemaOnDDL(q.SQL, vs.cp.DBName(), vs.vse.env.Environment().Parser()) { vs.se.ReloadAt(context.Background(), vs.pos) } case sqlparser.StmtSavepoint: @@ -682,7 +682,7 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { // Build a normal table plan, which means, return all rows // and columns as is. Special handling is done when we actually // receive the row event. We'll build a JOURNAL event instead. - plan, err := buildREPlan(table, nil, "") + plan, err := buildREPlan(vs.se.Environment(), table, nil, "") if err != nil { return err } @@ -716,7 +716,7 @@ func (vs *vstreamer) buildVersionPlan(id uint64, tm *mysql.TableMap) error { // Build a normal table plan, which means, return all rows // and columns as is. Special handling is done when we actually // receive the row event. We'll build a JOURNAL event instead. - plan, err := buildREPlan(table, nil, "") + plan, err := buildREPlan(vs.se.Environment(), table, nil, "") if err != nil { return err } @@ -738,7 +738,7 @@ func (vs *vstreamer) buildTablePlan(id uint64, tm *mysql.TableMap) (*binlogdatap Name: tm.Name, Fields: cols, } - plan, err := buildPlan(table, vs.vschema, vs.filter) + plan, err := buildPlan(vs.se.Environment(), table, vs.vschema, vs.filter) if err != nil { return nil, err } @@ -764,15 +764,16 @@ func (vs *vstreamer) buildTablePlan(id uint64, tm *mysql.TableMap) (*binlogdatap func (vs *vstreamer) buildTableColumns(tm *mysql.TableMap) ([]*querypb.Field, error) { var fields []*querypb.Field for i, typ := range tm.Types { - t, err := sqltypes.MySQLToType(int64(typ), 0) + t, err := sqltypes.MySQLToType(typ, 0) if err != nil { return nil, fmt.Errorf("unsupported type: %d, position: %d", typ, i) } + coll := collations.CollationForType(t, vs.se.Environment().CollationEnv().DefaultConnectionCharset()) fields = append(fields, &querypb.Field{ Name: fmt.Sprintf("@%d", i+1), Type: t, - Charset: uint32(collations.DefaultCollationForType(t)), - Flags: mysql.FlagsForColumn(t, collations.DefaultCollationForType(t)), + Charset: uint32(coll), + Flags: mysql.FlagsForColumn(t, coll), }) } st, err := vs.se.GetTableForPos(sqlparser.NewIdentifierCS(tm.Name), replication.EncodePosition(vs.pos)) @@ -956,7 +957,7 @@ func (vs *vstreamer) rebuildPlans() error { // cause that to change. continue } - newPlan, err := buildPlan(plan.Table, vs.vschema, vs.filter) + newPlan, err := buildPlan(vs.se.Environment(), plan.Table, vs.vschema, vs.filter) if err != nil { return err } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go similarity index 82% rename from go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go rename to go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 0eda0d6c52e..4d1983cd4d3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -26,6 +26,8 @@ import ( "testing" "time" + "github.com/prometheus/common/version" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" @@ -57,22 +59,15 @@ func checkIfOptionIsSupported(t *testing.T, variable string) bool { return false } -type TestColumn struct { - name, dataType, colType string - len, charset int64 -} - -type TestFieldEvent struct { - table, db string - cols []*TestColumn -} - func (tfe *TestFieldEvent) String() string { s := fmt.Sprintf("type:FIELD field_event:{table_name:\"%s\"", tfe.table) fld := "" for _, col := range tfe.cols { + if col.skip { + continue + } fld += fmt.Sprintf(" fields:{name:\"%s\" type:%s table:\"%s\" org_table:\"%s\" database:\"%s\" org_name:\"%s\" column_length:%d charset:%d", - col.name, col.dataType, tfe.table, tfe.table, tfe.db, col.name, col.len, col.charset) + col.name, col.dataType, tfe.table, tfe.table, tfe.db, col.name, col.len, col.collationID) if col.colType != "" { fld += fmt.Sprintf(" column_type:\"%s\"", col.colType) } @@ -94,166 +89,103 @@ func TestNoBlob(t *testing.T) { env = nil newEngine(t, ctx, "noblob") defer func() { + if engine != nil { + engine.Close() + } + if env != nil { + env.Close() + } engine = oldEngine env = oldEnv }() - execStatements(t, []string{ - "create table t1(id int, blb blob, val varchar(4), primary key(id))", - "create table t2(id int, txt text, val varchar(4), unique key(id, val))", - }) - defer execStatements(t, []string{ - "drop table t1", - "drop table t2", - }) - engine.se.Reload(context.Background()) - queries := []string{ - "begin", - "insert into t1 values (1, 'blob1', 'aaa')", - "update t1 set val = 'bbb'", - "commit", - "begin", - "insert into t2 values (1, 'text1', 'aaa')", - "update t2 set val = 'bbb'", - "commit", - } - fe1 := &TestFieldEvent{ - table: "t1", - db: "vttest", - cols: []*TestColumn{ - {name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, - {name: "blb", dataType: "BLOB", colType: "blob", len: 65535, charset: 63}, - {name: "val", dataType: "VARCHAR", colType: "varchar(4)", len: 16, charset: 45}, + ts := &TestSpec{ + t: t, + ddls: []string{ + // t1 has a blob column and a primary key. The blob column will not be in update row events. + "create table t1(id int, blb blob, val varchar(4), primary key(id))", + // t2 has a text column and no primary key. The text column will be in update row events. + "create table t2(id int, txt text, val varchar(4), unique key(id, val))", + // t3 has a text column and a primary key. The text column will not be in update row events. + "create table t3(id int, txt text, val varchar(4), primary key(id))", }, - } - fe2 := &TestFieldEvent{ - table: "t2", - db: "vttest", - cols: []*TestColumn{ - {name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, - {name: "txt", dataType: "TEXT", colType: "text", len: 262140, charset: 45}, - {name: "val", dataType: "VARCHAR", colType: "varchar(4)", len: 16, charset: 45}, + options: &TestSpecOptions{ + noblob: true, }, } - - testcases := []testcase{{ - input: queries, - output: [][]string{{ - "begin", - fe1.String(), - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:5 lengths:3 values:"1blob1aaa"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:-1 lengths:3 values:"1aaa"} after:{lengths:1 lengths:-1 lengths:3 values:"1bbb"} data_columns:{count:3 cols:"\x05"}}}`, - "gtid", - "commit", - }, { - "begin", - fe2.String(), - `type:ROW row_event:{table_name:"t2" row_changes:{after:{lengths:1 lengths:5 lengths:3 values:"1text1aaa"}}}`, - `type:ROW row_event:{table_name:"t2" row_changes:{before:{lengths:1 lengths:5 lengths:3 values:"1text1aaa"} after:{lengths:1 lengths:-1 lengths:3 values:"1bbb"} data_columns:{count:3 cols:"\x05"}}}`, - "gtid", - "commit", - }}, + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 'blob1', 'aaa')", nil}, + {"update t1 set val = 'bbb'", nil}, + {"commit", nil}, + }, {{"begin", nil}, + {"insert into t2 values (1, 'text1', 'aaa')", nil}, + {"update t2 set val = 'bbb'", nil}, + {"commit", nil}, + }, {{"begin", nil}, + {"insert into t3 values (1, 'text1', 'aaa')", nil}, + {"update t3 set val = 'bbb'", nil}, + {"commit", nil}, }} - runCases(t, nil, testcases, "current", nil) + ts.Run() } +// TestSetAndEnum confirms that the events for set and enum columns are correct. func TestSetAndEnum(t *testing.T) { - execStatements(t, []string{ - "create table t1(id int, val binary(4), color set('red','green','blue'), size enum('S','M','L'), primary key(id))", - }) - defer execStatements(t, []string{ - "drop table t1", - }) - engine.se.Reload(context.Background()) - queries := []string{ - "begin", - "insert into t1 values (1, 'aaa', 'red,blue', 'S')", - "insert into t1 values (2, 'bbb', 'green', 'M')", - "insert into t1 values (3, 'ccc', 'red,blue,green', 'L')", - "commit", - } - - fe := &TestFieldEvent{ - table: "t1", - db: "vttest", - cols: []*TestColumn{ - {name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, - {name: "val", dataType: "BINARY", colType: "binary(4)", len: 4, charset: 63}, - {name: "color", dataType: "SET", colType: "set('red','green','blue')", len: 56, charset: 45}, - {name: "size", dataType: "ENUM", colType: "enum('S','M','L')", len: 4, charset: 45}, + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id int, val binary(4), color set('red','green','blue'), size enum('S','M','L'), primary key(id))", }, } - - testcases := []testcase{{ - input: queries, - output: [][]string{{ - `begin`, - fe.String(), - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"1aaa\x0051"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"2bbb\x0022"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"3ccc\x0073"}}}`, - `gtid`, - `commit`, - }}, + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 'aaa', 'red,blue', 'S')", nil}, + {"insert into t1 values (2, 'bbb', 'green', 'M')", nil}, + {"insert into t1 values (3, 'ccc', 'red,blue,green', 'L')", nil}, + {"commit", nil}, }} - runCases(t, nil, testcases, "current", nil) + ts.Run() } +// TestCellValuePadding tests that the events are correctly padded for binary columns. func TestCellValuePadding(t *testing.T) { - - execStatements(t, []string{ - "create table t1(id int, val binary(4), primary key(val))", - "create table t2(id int, val char(4), primary key(val))", - "create table t3(id int, val char(4) collate utf8mb4_bin, primary key(val))", - }) - defer execStatements(t, []string{ - "drop table t1", - "drop table t2", - "drop table t3", - }) - engine.se.Reload(context.Background()) - queries := []string{ - "begin", - "insert into t1 values (1, 'aaa\000')", - "insert into t1 values (2, 'bbb\000')", - "update t1 set id = 11 where val = 'aaa\000'", - "insert into t2 values (1, 'aaa')", - "insert into t2 values (2, 'bbb')", - "update t2 set id = 11 where val = 'aaa'", - "insert into t3 values (1, 'aaa')", - "insert into t3 values (2, 'bb')", - "update t3 set id = 11 where val = 'aaa'", - "commit", - } - - testcases := []testcase{{ - input: queries, - output: [][]string{{ - `begin`, - `type:FIELD field_event:{table_name:"t1" fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:BINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:4 charset:63 column_type:"binary(4)"}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"1aaa\x00"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"2bbb\x00"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:4 values:"1aaa\x00"} after:{lengths:2 lengths:4 values:"11aaa\x00"}}}`, - `type:FIELD field_event:{table_name:"t2" fields:{name:"id" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:CHAR table:"t2" org_table:"t2" database:"vttest" org_name:"val" column_length:16 charset:45 column_type:"char(4)"}}`, - `type:ROW row_event:{table_name:"t2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"t2" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`, - `type:ROW row_event:{table_name:"t2" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:2 lengths:3 values:"11aaa"}}}`, - `type:FIELD field_event:{table_name:"t3" fields:{name:"id" type:INT32 table:"t3" org_table:"t3" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:BINARY table:"t3" org_table:"t3" database:"vttest" org_name:"val" column_length:16 charset:45 column_type:"char(4)"}}`, - `type:ROW row_event:{table_name:"t3" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"t3" row_changes:{after:{lengths:1 lengths:2 values:"2bb"}}}`, - `type:ROW row_event:{table_name:"t3" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:2 lengths:3 values:"11aaa"}}}`, - `gtid`, - `commit`, + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id int, val binary(4), primary key(val))", + "create table t2(id int, val char(4), primary key(val))", + "create table t3(id int, val char(4) collate utf8mb4_bin, primary key(val))"}, + } + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 'aaa\000')", nil}, + {"insert into t1 values (2, 'bbb\000')", nil}, + {"update t1 set id = 11 where val = 'aaa\000'", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"1", "aaa\x00"}, after: []string{"11", "aaa\x00"}}}}}, + }}, + {"insert into t2 values (1, 'aaa')", nil}, + {"insert into t2 values (2, 'bbb')", nil}, + {"update t2 set id = 11 where val = 'aaa'", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t2", changes: []TestRowChange{{before: []string{"1", "aaa"}, after: []string{"11", "aaa"}}}}}, }}, + {"insert into t3 values (1, 'aaa')", nil}, + {"insert into t3 values (2, 'bb')", nil}, + {"update t3 set id = 11 where val = 'aaa'", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t3", changes: []TestRowChange{{before: []string{"1", "aaa"}, after: []string{"11", "aaa"}}}}}, + }}, + {"commit", nil}, }} - runCases(t, nil, testcases, "current", nil) + ts.Run() } func TestSetStatement(t *testing.T) { - - if testing.Short() { - t.Skip() - } if !checkIfOptionIsSupported(t, "log_builtin_as_identified_by_password") { // the combination of setting this option and support for "set password" only works on a few flavors log.Info("Cannot test SetStatement on this flavor") @@ -296,45 +228,25 @@ func TestSetForeignKeyCheck(t *testing.T) { testRowEventFlags = true defer func() { testRowEventFlags = false }() - execStatements(t, []string{ - "create table t1(id int, val binary(4), primary key(id))", - }) - defer execStatements(t, []string{ - "drop table t1", - }) - engine.se.Reload(context.Background()) - queries := []string{ - "begin", - "insert into t1 values (1, 'aaa')", - "set @@session.foreign_key_checks=1", - "insert into t1 values (2, 'bbb')", - "set @@session.foreign_key_checks=0", - "insert into t1 values (3, 'ccc')", - "commit", - } - - fe := &TestFieldEvent{ - table: "t1", - db: "vttest", - cols: []*TestColumn{ - {name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, - {name: "val", dataType: "BINARY", colType: "binary(4)", len: 4, charset: 63}, + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id int, val binary(4), primary key(id))", }, } - - testcases := []testcase{{ - input: queries, - output: [][]string{{ - `begin`, - fe.String(), - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"1aaa\x00"}} flags:1}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"2bbb\x00"}} flags:1}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"3ccc\x00"}} flags:3}`, - `gtid`, - `commit`, - }}, + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 'aaa')", []TestRowEvent{{flags: 1}}}, + {"set @@session.foreign_key_checks=1", noEvents}, + {"insert into t1 values (2, 'bbb')", []TestRowEvent{{flags: 1}}}, + {"set @@session.foreign_key_checks=0", noEvents}, + {"insert into t1 values (3, 'ccc')", []TestRowEvent{{flags: 3}}}, + {"commit", nil}, }} - runCases(t, nil, testcases, "current", nil) + ts.Run() + } func TestStmtComment(t *testing.T) { @@ -711,225 +623,179 @@ func TestVStreamCopyWithDifferentFilters(t *testing.T) { } } +// TestFilteredVarBinary confirms that adding a filter using a varbinary column results in the correct set of events. func TestFilteredVarBinary(t *testing.T) { - if testing.Short() { - t.Skip() + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id1 int, val varbinary(128), primary key(id1))", + }, + options: &TestSpecOptions{ + filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select id1, val from t1 where val = 'newton'", + }}, + }, + }, } - - execStatements(t, []string{ - "create table t1(id1 int, val varbinary(128), primary key(id1))", - }) - defer execStatements(t, []string{ - "drop table t1", - }) - engine.se.Reload(context.Background()) - - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "t1", - Filter: "select id1, val from t1 where val = 'newton'", + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 'kepler')", noEvents}, + {"insert into t1 values (2, 'newton')", nil}, + {"insert into t1 values (3, 'newton')", nil}, + {"insert into t1 values (4, 'kepler')", noEvents}, + {"insert into t1 values (5, 'newton')", nil}, + {"update t1 set val = 'newton' where id1 = 1", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"1", "newton"}}}}}, }}, - } - - testcases := []testcase{{ - input: []string{ - "begin", - "insert into t1 values (1, 'kepler')", - "insert into t1 values (2, 'newton')", - "insert into t1 values (3, 'newton')", - "insert into t1 values (4, 'kepler')", - "insert into t1 values (5, 'newton')", - "update t1 set val = 'newton' where id1 = 1", - "update t1 set val = 'kepler' where id1 = 2", - "update t1 set val = 'newton' where id1 = 2", - "update t1 set val = 'kepler' where id1 = 1", - "delete from t1 where id1 in (2,3)", - "commit", - }, - output: [][]string{{ - `begin`, - `type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"2newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"3newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"5newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"1newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"2newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"2newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"1newton"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"2newton"}} row_changes:{before:{lengths:1 lengths:6 values:"3newton"}}}`, - `gtid`, - `commit`, + {"update t1 set val = 'kepler' where id1 = 2", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"2", "newton"}}}}}, + }}, + {"update t1 set val = 'newton' where id1 = 2", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"2", "newton"}}}}}, + }}, + {"update t1 set val = 'kepler' where id1 = 1", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"1", "newton"}}}}}, }}, + {"delete from t1 where id1 in (2,3)", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"2", "newton"}}, {before: []string{"3", "newton"}}}}}, + }}, + {"commit", nil}, }} - runCases(t, filter, testcases, "", nil) + ts.Run() } +// TestFilteredInt confirms that adding a filter using an int column results in the correct set of events. func TestFilteredInt(t *testing.T) { - if testing.Short() { - t.Skip() + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", + }, + options: &TestSpecOptions{ + filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select id1, val from t1 where id2 = 200", + }}, + }, + }, } - engine.se.Reload(context.Background()) - - execStatements(t, []string{ - "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", - }) - defer execStatements(t, []string{ - "drop table t1", - }) - engine.se.Reload(context.Background()) - - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "t1", - Filter: "select id1, val from t1 where id2 = 200", + defer ts.Close() + require.NoError(t, ts.Init()) + ts.fieldEvents["t1"].cols[1].skip = true + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into t1 values (1, 100, 'aaa')", noEvents}, + {"insert into t1 values (2, 200, 'bbb')", nil}, + {"insert into t1 values (3, 100, 'ccc')", noEvents}, + {"insert into t1 values (4, 200, 'ddd')", nil}, + {"insert into t1 values (5, 200, 'eee')", nil}, + {"update t1 set val = 'newddd' where id1 = 4", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"4", "ddd"}, after: []string{"4", "newddd"}}}}}, }}, - } - - testcases := []testcase{{ - input: []string{ - "begin", - "insert into t1 values (1, 100, 'aaa')", - "insert into t1 values (2, 200, 'bbb')", - "insert into t1 values (3, 100, 'ccc')", - "insert into t1 values (4, 200, 'ddd')", - "insert into t1 values (5, 200, 'eee')", - "update t1 set val = 'newddd' where id1 = 4", - "update t1 set id2 = 200 where id1 = 1", - "update t1 set id2 = 100 where id1 = 2", - "update t1 set id2 = 100 where id1 = 1", - "update t1 set id2 = 200 where id1 = 2", - "commit", - }, - output: [][]string{{ - `begin`, - `type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"4ddd"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"5eee"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"4ddd"} after:{lengths:1 lengths:6 values:"4newddd"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"2bbb"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`, - `gtid`, - `commit`, + {"update t1 set id2 = 200 where id1 = 1", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"1", "aaa"}}}}}, + }}, + {"update t1 set id2 = 100 where id1 = 2", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"2", "bbb"}}}}}, }}, + {"update t1 set id2 = 100 where id1 = 1", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{before: []string{"1", "aaa"}}}}}, + }}, + {"update t1 set id2 = 200 where id1 = 2", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "t1", changes: []TestRowChange{{after: []string{"2", "bbb"}}}}}, + }}, + {"commit", nil}, }} - runCases(t, filter, testcases, "", nil) + ts.Run() } +// TestSavepoint confirms that rolling back to a savepoint drops the dmls that were executed during the savepoint. func TestSavepoint(t *testing.T) { - if testing.Short() { - t.Skip() - } - - execStatements(t, []string{ - "create table stream1(id int, val varbinary(128), primary key(id))", - "create table stream2(id int, val varbinary(128), primary key(id))", - }) - defer execStatements(t, []string{ - "drop table stream1", - "drop table stream2", - }) - engine.se.Reload(context.Background()) - testcases := []testcase{{ - input: []string{ - "begin", - "insert into stream1 values (1, 'aaa')", - "savepoint a", - "insert into stream1 values (2, 'aaa')", - "rollback work to savepoint a", - "savepoint b", - "update stream1 set val='bbb' where id = 1", - "release savepoint b", - "commit", + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table stream1(id int, val varbinary(128), primary key(id))", }, - output: [][]string{{ - `begin`, - `type:FIELD field_event:{table_name:"stream1" fields:{name:"id" type:INT32 table:"stream1" org_table:"stream1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream1" org_table:"stream1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`, - `type:ROW row_event:{table_name:"stream1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"stream1" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:1 lengths:3 values:"1bbb"}}}`, - `gtid`, - `commit`, + } + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into stream1 values (1, 'aaa')", nil}, + {"savepoint a", noEvents}, + {"insert into stream1 values (2, 'aaa')", noEvents}, + {"rollback work to savepoint a", noEvents}, + {"savepoint b", noEvents}, + {"update stream1 set val='bbb' where id = 1", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "stream1", changes: []TestRowChange{{before: []string{"1", "aaa"}, after: []string{"1", "bbb"}}}}}, }}, + {"release savepoint b", noEvents}, + {"commit", nil}, }} - runCases(t, nil, testcases, "current", nil) + ts.Run() } +// TestSavepointWithFilter tests that using savepoints with both filtered and unfiltered tables works as expected. func TestSavepointWithFilter(t *testing.T) { - if testing.Short() { - t.Skip() - } - - execStatements(t, []string{ - "create table stream1(id int, val varbinary(128), primary key(id))", - "create table stream2(id int, val varbinary(128), primary key(id))", - }) - defer execStatements(t, []string{ - "drop table stream1", - "drop table stream2", - }) - engine.se.Reload(context.Background()) - testcases := []testcase{{ - input: []string{ - "begin", - "insert into stream1 values (1, 'aaa')", - "savepoint a", - "insert into stream1 values (2, 'aaa')", - "savepoint b", - "insert into stream1 values (3, 'aaa')", - "savepoint c", - "insert into stream1 values (4, 'aaa')", - "savepoint d", - "commit", - - "begin", - "insert into stream1 values (5, 'aaa')", - "savepoint d", - "insert into stream1 values (6, 'aaa')", - "savepoint c", - "insert into stream1 values (7, 'aaa')", - "savepoint b", - "insert into stream1 values (8, 'aaa')", - "savepoint a", - "commit", - - "begin", - "insert into stream1 values (9, 'aaa')", - "savepoint a", - "insert into stream2 values (1, 'aaa')", - "savepoint b", - "insert into stream1 values (10, 'aaa')", - "savepoint c", - "insert into stream2 values (2, 'aaa')", - "savepoint d", - "commit", + ts := &TestSpec{ + t: t, + ddls: []string{ + "create table stream1(id int, val varbinary(128), primary key(id))", + "create table stream2(id int, val varbinary(128), primary key(id))", }, - output: [][]string{{ - `begin`, - `gtid`, - `commit`, - }, { - `begin`, - `gtid`, - `commit`, - }, { - `begin`, - `type:FIELD field_event:{table_name:"stream2" fields:{name:"id" type:INT32 table:"stream2" org_table:"stream2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream2" org_table:"stream2" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`, - `type:ROW row_event:{table_name:"stream2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`, - `type:ROW row_event:{table_name:"stream2" row_changes:{after:{lengths:1 lengths:3 values:"2aaa"}}}`, - `gtid`, - `commit`, + options: &TestSpecOptions{ + filter: &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "stream2", + Filter: "select * from stream2", + }}, + }, + }, + } + defer ts.Close() + require.NoError(t, ts.Init()) + ts.tests = [][]*TestQuery{{ + {"begin", nil}, + {"insert into stream1 values (1, 'aaa')", noEvents}, + {"savepoint a", noEvents}, + {"insert into stream1 values (2, 'aaa')", noEvents}, + {"savepoint b", noEvents}, + {"insert into stream1 values (3, 'aaa')", noEvents}, + {"savepoint c", noEvents}, + {"insert into stream1 values (4, 'aaa')", noEvents}, + {"savepoint d", noEvents}, + {"commit", nil}, + }, { + {"begin", nil}, + {"insert into stream1 values (5, 'aaa')", noEvents}, + {"savepoint d", noEvents}, + {"insert into stream1 values (6, 'aaa')", noEvents}, + {"savepoint c", noEvents}, + {"insert into stream1 values (7, 'aaa')", noEvents}, + {"savepoint b", noEvents}, + {"insert into stream1 values (8, 'aaa')", noEvents}, + {"savepoint a", noEvents}, + {"commit", nil}, + }, { + {"begin", nil}, + {"insert into stream1 values (9, 'aaa')", noEvents}, + {"savepoint a", noEvents}, + {"insert into stream2 values (1, 'aaa')", nil}, + {"savepoint b", noEvents}, + {"insert into stream1 values (10, 'aaa')", noEvents}, + {"savepoint c", noEvents}, + {"insert into stream2 values (2, 'aaa')", []TestRowEvent{ + {spec: &TestRowEventSpec{table: "stream2", changes: []TestRowChange{{after: []string{"2", "aaa"}}}}}, }}, + {"savepoint d", noEvents}, + {"commit", nil}, }} - - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "stream2", - Filter: "select * from stream2", - }}, - } - runCases(t, filter, testcases, "current", nil) + ts.Run() } func TestStatements(t *testing.T) { @@ -1685,6 +1551,9 @@ func TestBestEffortNameInFieldEvent(t *testing.T) { // test that vstreamer ignores tables created by OnlineDDL func TestInternalTables(t *testing.T) { + if version.GoOS == "darwin" { + t.Skip("internal online ddl table matching doesn't work on Mac because it is case insensitive") + } if testing.Short() { t.Skip() } @@ -1991,6 +1860,12 @@ func TestMinimalMode(t *testing.T) { env = nil newEngine(t, ctx, "minimal") defer func() { + if engine != nil { + engine.Close() + } + if env != nil { + env.Close() + } engine = oldEngine env = oldEnv }() @@ -2158,11 +2033,11 @@ func TestGeneratedColumns(t *testing.T) { table: "t1", db: "vttest", cols: []*TestColumn{ - {name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, - {name: "val", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, charset: 63}, - {name: "val2", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, charset: 63}, - {name: "val3", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, charset: 63}, - {name: "id2", dataType: "INT32", colType: "int(11)", len: 11, charset: 63}, + {name: "id", dataType: "INT32", colType: "int(11)", len: 11, collationID: 63}, + {name: "val", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, collationID: 63}, + {name: "val2", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, collationID: 63}, + {name: "val3", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, collationID: 63}, + {name: "id2", dataType: "INT32", colType: "int(11)", len: 11, collationID: 63}, }, } @@ -2205,8 +2080,8 @@ func TestGeneratedInvisiblePrimaryKey(t *testing.T) { table: "t1", db: "vttest", cols: []*TestColumn{ - {name: "my_row_id", dataType: "UINT64", colType: "bigint unsigned", len: 20, charset: 63}, - {name: "val", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, charset: 63}, + {name: "my_row_id", dataType: "UINT64", colType: "bigint unsigned", len: 20, collationID: 63}, + {name: "val", dataType: "VARBINARY", colType: "varbinary(6)", len: 6, collationID: 63}, }, } @@ -2288,10 +2163,16 @@ func expectLog(ctx context.Context, t *testing.T, input any, ch <-chan []*binlog break } } + + numEventsToMatch := len(evs) if len(wantset) != len(evs) { - t.Fatalf("%v: evs\n%v, want\n%v, >> got length %d, wanted length %d", input, evs, wantset, len(evs), len(wantset)) + log.Warningf("%v: evs\n%v, want\n%v, >> got length %d, wanted length %d", input, evs, wantset, len(evs), len(wantset)) + if len(wantset) < len(evs) { + numEventsToMatch = len(wantset) + } } - for i, want := range wantset { + for i := 0; i < numEventsToMatch; i++ { + want := wantset[i] // CurrentTime is not testable. evs[i].CurrentTime = 0 evs[i].Keyspace = "" @@ -2350,6 +2231,9 @@ func expectLog(ctx context.Context, t *testing.T, input any, ch <-chan []*binlog } } } + if len(wantset) != len(evs) { + t.Fatalf("%v: evs\n%v, want\n%v, got length %d, wanted length %d", input, evs, wantset, len(evs), len(wantset)) + } } } @@ -2385,7 +2269,7 @@ func vstream(ctx context.Context, t *testing.T, pos string, tablePKs []*binlogda timer := time.NewTimer(2 * time.Second) defer timer.Stop() - t.Logf("Received events: %v", evs) + log.Infof("Received events: %v", evs) select { case ch <- evs: case <-ctx.Done(): diff --git a/go/vt/vttablet/tmrpctest/test_tm_rpc.go b/go/vt/vttablet/tmrpctest/test_tm_rpc.go index 2393a3fb2f0..f3a9e842753 100644 --- a/go/vt/vttablet/tmrpctest/test_tm_rpc.go +++ b/go/vt/vttablet/tmrpctest/test_tm_rpc.go @@ -1039,7 +1039,12 @@ func tmRPCTestPopulateReparentJournal(ctx context.Context, t *testing.T, client func tmRPCTestPopulateReparentJournalPanic(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.Tablet) { err := client.PopulateReparentJournal(ctx, tablet, testTimeCreatedNS, testActionName, testPrimaryAlias, testReplicationPosition) - expectHandleRPCPanic(t, "PopulateReparentJournal", false /*verbose*/, err) + expectHandleRPCPanic(t, "PopulateReparentJournal", true /*verbose*/, err) +} + +func tmRPCTestWaitForPositionPanic(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.Tablet) { + err := client.WaitForPosition(ctx, tablet, testReplicationPosition) + expectHandleRPCPanic(t, "WaitForPosition", true /*verbose*/, err) } var testInitReplicaCalled = false @@ -1239,7 +1244,7 @@ func tmRPCTestPromoteReplicaPanic(ctx context.Context, t *testing.T, client tmcl // Backup / restore related methods // -var testBackupConcurrency = int64(24) +var testBackupConcurrency = int32(24) var testBackupAllowPrimary = false var testBackupCalled = false var testRestoreFromBackupCalled = false @@ -1256,7 +1261,7 @@ func (fra *fakeRPCTM) Backup(ctx context.Context, logger logutil.Logger, request } func tmRPCTestBackup(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.Tablet) { - req := &tabletmanagerdatapb.BackupRequest{Concurrency: int64(testBackupConcurrency), AllowPrimary: testBackupAllowPrimary} + req := &tabletmanagerdatapb.BackupRequest{Concurrency: testBackupConcurrency, AllowPrimary: testBackupAllowPrimary} stream, err := client.Backup(ctx, tablet, req) if err != nil { t.Fatalf("Backup failed: %v", err) @@ -1266,7 +1271,7 @@ func tmRPCTestBackup(ctx context.Context, t *testing.T, client tmclient.TabletMa } func tmRPCTestBackupPanic(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.Tablet) { - req := &tabletmanagerdatapb.BackupRequest{Concurrency: int64(testBackupConcurrency), AllowPrimary: testBackupAllowPrimary} + req := &tabletmanagerdatapb.BackupRequest{Concurrency: testBackupConcurrency, AllowPrimary: testBackupAllowPrimary} stream, err := client.Backup(ctx, tablet, req) if err != nil { t.Fatalf("Backup failed: %v", err) @@ -1447,6 +1452,7 @@ func Run(t *testing.T, client tmclient.TabletManagerClient, tablet *topodatapb.T tmRPCTestResetReplicationPanic(ctx, t, client, tablet) tmRPCTestInitPrimaryPanic(ctx, t, client, tablet) tmRPCTestPopulateReparentJournalPanic(ctx, t, client, tablet) + tmRPCTestWaitForPositionPanic(ctx, t, client, tablet) tmRPCTestDemotePrimaryPanic(ctx, t, client, tablet) tmRPCTestUndoDemotePrimaryPanic(ctx, t, client, tablet) tmRPCTestSetReplicationSourcePanic(ctx, t, client, tablet) diff --git a/go/vt/vttest/local_cluster.go b/go/vt/vttest/local_cluster.go index 9d84cb7fceb..da19d1c6fb8 100644 --- a/go/vt/vttest/local_cluster.go +++ b/go/vt/vttest/local_cluster.go @@ -18,7 +18,6 @@ package vttest import ( "bufio" - "bytes" "context" "encoding/json" "fmt" @@ -36,14 +35,15 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "vitess.io/vitess/go/constants/sidecar" - - "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/proto/logutil" + "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/vt/vtctl/vtctlclient" vschemapb "vitess.io/vitess/go/vt/proto/vschema" @@ -110,6 +110,10 @@ type Config struct { // cluster startup if the data directory does not already exist. PersistentMode bool + // VtCombo bind address. + // vtcombo will bind to this address when running the servenv. + VtComboBindAddress string + // MySQL protocol bind address. // vtcombo will bind to this address when exposing the mysql protocol socket MySQLBindHost string @@ -173,12 +177,12 @@ func (cfg *Config) InitSchemas(keyspace, schema string, vschema *vschemapb.Keysp // Write the schema if set. if schema != "" { ksDir := path.Join(schemaDir, keyspace) - err := os.Mkdir(ksDir, os.ModeDir|0775) + err := os.Mkdir(ksDir, os.ModeDir|0o775) if err != nil { return err } fileName := path.Join(ksDir, "schema.sql") - err = os.WriteFile(fileName, []byte(schema), 0666) + err = os.WriteFile(fileName, []byte(schema), 0o666) if err != nil { return err } @@ -191,7 +195,7 @@ func (cfg *Config) InitSchemas(keyspace, schema string, vschema *vschemapb.Keysp if err != nil { return err } - if err := os.WriteFile(vschemaFilePath, vschemaJSON, 0644); err != nil { + if err := os.WriteFile(vschemaFilePath, vschemaJSON, 0o644); err != nil { return err } } @@ -277,7 +281,11 @@ type LocalCluster struct { // cluster access should be performed through the vtgate port. func (db *LocalCluster) MySQLConnParams() mysql.ConnParams { connParams := db.mysql.Params(db.DbName()) - connParams.Charset = db.Config.Charset + ch, err := collations.MySQL8().ParseConnectionCharset(db.Config.Charset) + if err != nil { + panic(err) + } + connParams.Charset = ch return connParams } @@ -298,7 +306,11 @@ func (db *LocalCluster) MySQLCleanConnParams() mysql.ConnParams { mysqlctl = toxiproxy.mysqlctl } connParams := mysqlctl.Params(db.DbName()) - connParams.Charset = db.Config.Charset + ch, err := collations.MySQL8().ParseConnectionCharset(db.Config.Charset) + if err != nil { + panic(err) + } + connParams.Charset = ch return connParams } @@ -489,11 +501,6 @@ func (db *LocalCluster) loadSchema(shouldRunDatabaseMigrations bool) error { } for _, kpb := range db.Topology.Keyspaces { - if kpb.ServedFrom != "" { - // redirected keyspaces have no underlying database - continue - } - keyspace := kpb.Name keyspaceDir := path.Join(db.SchemaDir, keyspace) @@ -549,11 +556,12 @@ func (db *LocalCluster) createVTSchema() error { return db.ExecuteFetch(query, "") } - if err := sidecardb.Init(context.Background(), sidecardbExec); err != nil { + if err := sidecardb.Init(context.Background(), vtenv.NewTestEnv(), sidecardbExec); err != nil { return err } return nil } + func (db *LocalCluster) createDatabases() error { log.Info("Creating databases in cluster...") @@ -565,9 +573,6 @@ func (db *LocalCluster) createDatabases() error { var sql []string for _, kpb := range db.Topology.Keyspaces { - if kpb.ServedFrom != "" { - continue - } for _, dbname := range db.shardNames(kpb) { sql = append(sql, fmt.Sprintf("create database `%s`", dbname)) } @@ -641,6 +646,7 @@ func (db *LocalCluster) JSONConfig() any { } config := map[string]any{ + "bind_address": db.vt.BindAddress, "port": db.vt.Port, "socket": db.mysql.UnixSocket(), "vtcombo_mysql_port": db.Env.PortForProtocol("vtcombo_mysql_port", ""), @@ -697,7 +703,7 @@ func dirExist(dir string) bool { // statements in the SQL file. func LoadSQLFile(filename, sourceroot string) ([]string, error) { var ( - cmd bytes.Buffer + cmd strings.Builder sql []string inSQ bool inDQ bool @@ -783,7 +789,7 @@ func (db *LocalCluster) VTProcess() *VtProcess { // a pointer to the interface. To read this vschema, the caller must convert it to a map func (vt *VtProcess) ReadVSchema() (*interface{}, error) { httpClient := &http.Client{Timeout: 5 * time.Second} - resp, err := httpClient.Get(fmt.Sprintf("http://%s:%d/debug/vschema", "127.0.0.1", vt.Port)) + resp, err := httpClient.Get(fmt.Sprintf("http://%s:%d/debug/vschema", vt.BindAddress, vt.Port)) if err != nil { return nil, err } diff --git a/go/vt/vttest/plugin_consultopo.go b/go/vt/vttest/plugin_consultopo.go index cb10acc2cd2..3d47ee51681 100644 --- a/go/vt/vttest/plugin_consultopo.go +++ b/go/vt/vttest/plugin_consultopo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/vt/vttest/plugin_zk2topo.go b/go/vt/vttest/plugin_zk2topo.go index 3859454f7bd..7f1f81a5701 100644 --- a/go/vt/vttest/plugin_zk2topo.go +++ b/go/vt/vttest/plugin_zk2topo.go @@ -7,7 +7,7 @@ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreedto in writing, software +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and diff --git a/go/vt/vttest/randomdata.go b/go/vt/vttest/randomdata.go index 19eaeb98fb0..0848f8cb709 100644 --- a/go/vt/vttest/randomdata.go +++ b/go/vt/vttest/randomdata.go @@ -151,9 +151,6 @@ func (db *LocalCluster) populateShard(dbname string, rng *rand.Rand) error { func (db *LocalCluster) populateWithRandomData() error { rng := rand.New(rand.NewSource(int64(db.Seed.RngSeed))) for _, kpb := range db.Topology.Keyspaces { - if kpb.ServedFrom != "" { - continue - } for _, dbname := range db.shardNames(kpb) { if err := db.populateShard(dbname, rng); err != nil { return err diff --git a/go/vt/vttest/vtprocess.go b/go/vt/vttest/vtprocess.go index 2053973b766..808a9510cbe 100644 --- a/go/vt/vttest/vtprocess.go +++ b/go/vt/vttest/vtprocess.go @@ -50,6 +50,7 @@ type VtProcess struct { Binary string ExtraArgs []string Env []string + BindAddress string Port int PortGrpc int HealthCheck HealthChecker @@ -91,7 +92,7 @@ func (vtp *VtProcess) IsHealthy() bool { // Address returns the main address for this Vitess process. // This is usually the main HTTP endpoint for the service. func (vtp *VtProcess) Address() string { - return fmt.Sprintf("localhost:%d", vtp.Port) + return fmt.Sprintf("%s:%d", vtp.BindAddress, vtp.Port) } // WaitTerminate attempts to gracefully shutdown the Vitess process by sending @@ -128,7 +129,7 @@ func (vtp *VtProcess) WaitStart() (err error) { vtp.proc = exec.Command( vtp.Binary, "--port", fmt.Sprintf("%d", vtp.Port), - "--bind-address", "127.0.0.1", + "--bind-address", vtp.BindAddress, "--log_dir", vtp.LogDirectory, "--alsologtostderr", ) @@ -141,8 +142,7 @@ func (vtp *VtProcess) WaitStart() (err error) { vtp.proc.Args = append(vtp.proc.Args, vtp.ExtraArgs...) vtp.proc.Env = append(vtp.proc.Env, os.Environ()...) vtp.proc.Env = append(vtp.proc.Env, vtp.Env...) - - if testing.Verbose() { + if !testing.Testing() || testing.Verbose() { vtp.proc.Stderr = os.Stderr vtp.proc.Stdout = os.Stdout } @@ -184,23 +184,28 @@ const ( // QueryServerArgs are the default arguments passed to all Vitess query servers var QueryServerArgs = []string{ "--queryserver-config-pool-size", "4", - "--queryserver-config-query-timeout", "300", - "--queryserver-config-schema-reload-time", "60", + "--queryserver-config-query-timeout", "300s", + "--queryserver-config-schema-reload-time", "60s", "--queryserver-config-stream-pool-size", "4", "--queryserver-config-transaction-cap", "4", - "--queryserver-config-transaction-timeout", "300", - "--queryserver-config-txpool-timeout", "300", + "--queryserver-config-transaction-timeout", "300s", + "--queryserver-config-txpool-timeout", "300s", } // VtcomboProcess returns a VtProcess handle for a local `vtcombo` service, // configured with the given Config. // The process must be manually started by calling WaitStart() func VtcomboProcess(environment Environment, args *Config, mysql MySQLManager) (*VtProcess, error) { + vtcomboBindAddress := "127.0.0.1" + if args.VtComboBindAddress != "" { + vtcomboBindAddress = args.VtComboBindAddress + } vt := &VtProcess{ Name: "vtcombo", Directory: environment.Directory(), LogDirectory: environment.LogDirectory(), Binary: environment.BinaryPath("vtcombo"), + BindAddress: vtcomboBindAddress, Port: environment.PortForProtocol("vtcombo", ""), PortGrpc: environment.PortForProtocol("vtcombo", "grpc"), HealthCheck: environment.ProcessHealthCheck("vtcombo"), diff --git a/go/vt/vttls/vttls.go b/go/vt/vttls/vttls.go index 098ed67eec4..adaf2cca672 100644 --- a/go/vt/vttls/vttls.go +++ b/go/vt/vttls/vttls.go @@ -283,7 +283,7 @@ func loadTLSCertificate(cert, key string) (*[]tls.Certificate, error) { result, ok := tlsCertificates.Load(tlsIdentifier) if !ok { - return nil, vterrors.Errorf(vtrpc.Code_NOT_FOUND, "Cannot find loaded tls certificate with cert: %s, key%s", cert, key) + return nil, vterrors.Errorf(vtrpc.Code_NOT_FOUND, "Cannot find loaded tls certificate with cert: %s, key: %s", cert, key) } return result.(*[]tls.Certificate), nil diff --git a/go/vt/wrangler/external_cluster_test.go b/go/vt/wrangler/external_cluster_test.go index 3c878411b6b..9876e2bf999 100644 --- a/go/vt/wrangler/external_cluster_test.go +++ b/go/vt/wrangler/external_cluster_test.go @@ -4,13 +4,13 @@ import ( "context" "testing" - "vitess.io/vitess/go/test/utils" - "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" ) func TestVitessCluster(t *testing.T) { @@ -18,7 +18,7 @@ func TestVitessCluster(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") tmc := newTestWranglerTMClient() - wr := New(logutil.NewConsoleLogger(), ts, tmc) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmc) name, topoType, topoServer, topoRoot := "c1", "x", "y", "z" t.Run("Zero clusters to start", func(t *testing.T) { diff --git a/go/vt/wrangler/fake_dbclient_test.go b/go/vt/wrangler/fake_dbclient_test.go index 7bcc5f5bcf2..7fce5ce9afc 100644 --- a/go/vt/wrangler/fake_dbclient_test.go +++ b/go/vt/wrangler/fake_dbclient_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/assert" "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/sqltypes" ) @@ -160,6 +161,22 @@ func (dc *fakeDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Resul return qr, err } +func (dc *fakeDBClient) ExecuteFetchMulti(query string, maxrows int) ([]*sqltypes.Result, error) { + queries, err := sqlparser.NewTestParser().SplitStatementToPieces(query) + if err != nil { + return nil, err + } + results := make([]*sqltypes.Result, 0, len(queries)) + for _, query := range queries { + qr, err := dc.executeFetch(query, maxrows) + if err != nil { + return nil, err + } + results = append(results, qr) + } + return results, nil +} + // ExecuteFetch is part of the DBClient interface func (dc *fakeDBClient) executeFetch(query string, maxrows int) (*sqltypes.Result, error) { if dbrs := dc.queries[query]; dbrs != nil { diff --git a/go/vt/wrangler/fake_tablet_test.go b/go/vt/wrangler/fake_tablet_test.go index 66d5cf474d6..b70a64d644e 100644 --- a/go/vt/wrangler/fake_tablet_test.go +++ b/go/vt/wrangler/fake_tablet_test.go @@ -23,29 +23,29 @@ import ( "testing" "time" - vdiff2 "vitess.io/vitess/go/vt/vttablet/tabletmanager/vdiff" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" - "github.com/stretchr/testify/require" "google.golang.org/grpc" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/grpctmserver" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" "vitess.io/vitess/go/vt/vttablet/tabletconntest" "vitess.io/vitess/go/vt/vttablet/tabletmanager" + vdiff2 "vitess.io/vitess/go/vt/vttablet/tabletmanager/vdiff" "vitess.io/vitess/go/vt/vttablet/tabletservermock" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/vttablet/tmclienttest" - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - // import the gRPC client implementation for tablet manager _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" @@ -190,7 +190,6 @@ func (ft *fakeTablet) StartActionLoop(t *testing.T, wr *Wrangler) { ft.Tablet.PortMap["vt"] = vtPort ft.Tablet.PortMap["grpc"] = gRPCPort ft.Tablet.Hostname = "127.0.0.1" - config := &tabletenv.TabletConfig{} // Create a test tm on that port, and re-read the record // (it has new ports and IP). ft.TM = &tabletmanager.TabletManager{ @@ -199,9 +198,10 @@ func (ft *fakeTablet) StartActionLoop(t *testing.T, wr *Wrangler) { MysqlDaemon: ft.FakeMysqlDaemon, DBConfigs: &dbconfigs.DBConfigs{}, QueryServiceControl: tabletservermock.NewController(), - VDiffEngine: vdiff2.NewEngine(config, wr.TopoServer(), ft.Tablet), + VDiffEngine: vdiff2.NewEngine(wr.TopoServer(), ft.Tablet, collations.MySQL8(), sqlparser.NewTestParser()), + Env: vtenv.NewTestEnv(), } - if err := ft.TM.Start(ft.Tablet, 0); err != nil { + if err := ft.TM.Start(ft.Tablet, nil); err != nil { t.Fatal(err) } ft.Tablet = ft.TM.Tablet() diff --git a/go/vt/wrangler/keyspace.go b/go/vt/wrangler/keyspace.go index 7f3f00da4f8..a5f7d6ae0bf 100644 --- a/go/vt/wrangler/keyspace.go +++ b/go/vt/wrangler/keyspace.go @@ -44,7 +44,7 @@ const ( // validateNewWorkflow ensures that the specified workflow doesn't already exist // in the keyspace. func (wr *Wrangler) validateNewWorkflow(ctx context.Context, keyspace, workflow string) error { - allshards, err := wr.ts.FindAllShardsInKeyspace(ctx, keyspace) + allshards, err := wr.ts.FindAllShardsInKeyspace(ctx, keyspace, nil) if err != nil { return err } diff --git a/go/vt/wrangler/log_recorder_test.go b/go/vt/wrangler/log_recorder_test.go index 5eaecdac702..852b80876a4 100644 --- a/go/vt/wrangler/log_recorder_test.go +++ b/go/vt/wrangler/log_recorder_test.go @@ -19,7 +19,7 @@ package wrangler import ( "testing" - "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/assert" ) func TestLogRecorder(t *testing.T) { diff --git a/go/vt/wrangler/materializer.go b/go/vt/wrangler/materializer.go index 990492bd191..5b159e35a11 100644 --- a/go/vt/wrangler/materializer.go +++ b/go/vt/wrangler/materializer.go @@ -39,6 +39,7 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/tmutils" "vitess.io/vitess/go/vt/schema" + "vitess.io/vitess/go/vt/schemadiff" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" @@ -177,7 +178,7 @@ func (wr *Wrangler) MoveTables(ctx context.Context, workflow, sourceKeyspace, ta return err } if len(tables) > 0 { - err = wr.validateSourceTablesExist(ctx, sourceKeyspace, ksTables, tables) + err = wr.validateSourceTablesExist(sourceKeyspace, ksTables, tables) if err != nil { return err } @@ -192,7 +193,7 @@ func (wr *Wrangler) MoveTables(ctx context.Context, workflow, sourceKeyspace, ta excludeTables = strings.TrimSpace(excludeTables) if excludeTables != "" { excludeTablesList = strings.Split(excludeTables, ",") - err = wr.validateSourceTablesExist(ctx, sourceKeyspace, ksTables, excludeTablesList) + err = wr.validateSourceTablesExist(sourceKeyspace, ksTables, excludeTablesList) if err != nil { return err } @@ -317,13 +318,11 @@ func (wr *Wrangler) MoveTables(ctx context.Context, workflow, sourceKeyspace, ta return err } } - if vschema != nil { - // We added to the vschema. - if err := wr.ts.SaveVSchema(ctx, targetKeyspace, vschema); err != nil { - return err - } - } + // We added to the vschema. + if err := wr.ts.SaveVSchema(ctx, targetKeyspace, vschema); err != nil { + return err + } } if err := wr.ts.RebuildSrvVSchema(ctx, nil); err != nil { return err @@ -367,7 +366,7 @@ func (wr *Wrangler) MoveTables(ctx context.Context, workflow, sourceKeyspace, ta return nil } -func (wr *Wrangler) validateSourceTablesExist(ctx context.Context, sourceKeyspace string, ksTables, tables []string) error { +func (wr *Wrangler) validateSourceTablesExist(sourceKeyspace string, ksTables, tables []string) error { // validate that tables provided are present in the source keyspace var missingTables []string for _, table := range tables { @@ -446,7 +445,7 @@ func (wr *Wrangler) checkIfPreviousJournalExists(ctx context.Context, mz *materi mu sync.Mutex exists bool tablets []string - ws = workflow.NewServer(wr.ts, wr.tmc) + ws = workflow.NewServer(wr.env, wr.ts, wr.tmc) ) err := forAllSources(func(si *topo.ShardInfo) error { @@ -541,7 +540,7 @@ func (wr *Wrangler) prepareCreateLookup(ctx context.Context, keyspace string, sp return nil, nil, nil, fmt.Errorf("vindex %s is not a lookup type", vindex.Type) } - targetKeyspace, targetTableName, err = sqlparser.ParseTable(vindex.Params["table"]) + targetKeyspace, targetTableName, err = wr.env.Parser().ParseTable(vindex.Params["table"]) if err != nil || targetKeyspace == "" { return nil, nil, nil, fmt.Errorf("vindex table name must be in the form .
{{.ExecuteTime.Seconds}} {{.CommitTime.Seconds}} {{.StmtType}}{{.SQL | truncateQuery | unquote | cssWrappable}}{{.SQL | .Parser.TruncateForUI | unquote | cssWrappable}} {{.ShardQueries}} {{.RowsAffected}} {{.ErrorStr}}
{{.MysqlResponseTime.Seconds}} {{.WaitingForConnection.Seconds}} {{.PlanType}}{{.OriginalSQL | truncateQuery | unquote | cssWrappable}}{{.OriginalSQL | .Parser.TruncateForUI | unquote | cssWrappable}} {{.NumberOfQueries}} {{.FmtQuerySources}} {{.RowsAffected}}
. Got: %v", vindex.Params["table"]) } @@ -838,7 +837,7 @@ func (wr *Wrangler) ExternalizeVindex(ctx context.Context, qualifiedVindexName s return fmt.Errorf("vindex %s not found in vschema", qualifiedVindexName) } - targetKeyspace, targetTableName, err := sqlparser.ParseTable(sourceVindex.Params["table"]) + targetKeyspace, targetTableName, err := wr.env.Parser().ParseTable(sourceVindex.Params["table"]) if err != nil || targetKeyspace == "" { return fmt.Errorf("vindex table name must be in the form .
. Got: %v", sourceVindex.Params["table"]) } @@ -1065,7 +1064,7 @@ func (wr *Wrangler) buildMaterializer(ctx context.Context, ms *vtctldatapb.Mater if err != nil { return nil, err } - targetVSchema, err := vindexes.BuildKeyspaceSchema(vschema, ms.TargetKeyspace) + targetVSchema, err := vindexes.BuildKeyspaceSchema(vschema, ms.TargetKeyspace, wr.env.Parser()) if err != nil { return nil, err } @@ -1221,7 +1220,7 @@ func (mz *materializer) deploySchema(ctx context.Context) error { if createDDL == createDDLAsCopy || createDDL == createDDLAsCopyDropConstraint || createDDL == createDDLAsCopyDropForeignKeys { if ts.SourceExpression != "" { // Check for table if non-empty SourceExpression. - sourceTableName, err := sqlparser.TableFromStatement(ts.SourceExpression) + sourceTableName, err := mz.wr.env.Parser().TableFromStatement(ts.SourceExpression) if err != nil { return err } @@ -1237,7 +1236,7 @@ func (mz *materializer) deploySchema(ctx context.Context) error { } if createDDL == createDDLAsCopyDropConstraint { - strippedDDL, err := stripTableConstraints(ddl) + strippedDDL, err := stripTableConstraints(ddl, mz.wr.env.Parser()) if err != nil { return err } @@ -1246,7 +1245,7 @@ func (mz *materializer) deploySchema(ctx context.Context) error { } if createDDL == createDDLAsCopyDropForeignKeys { - strippedDDL, err := stripTableForeignKeys(ddl) + strippedDDL, err := stripTableForeignKeys(ddl, mz.wr.env.Parser()) if err != nil { return err } @@ -1260,6 +1259,22 @@ func (mz *materializer) deploySchema(ctx context.Context) error { } if len(applyDDLs) > 0 { + if mz.ms.AtomicCopy { + // AtomicCopy suggests we may be interested in Foreign Key support. As such, we want to + // normalize the source schema: ensure the order of table definitions is compatible with + // the constraints graph. We want to first create the parents, then the children. + // We use schemadiff to normalize the schema. + // For now, and because this is could have wider implications, we ignore any errors in + // reading the source schema. + env := schemadiff.NewEnv(mz.wr.env, mz.wr.env.CollationEnv().DefaultConnectionCharset()) + schema, err := schemadiff.NewSchemaFromQueries(env, applyDDLs) + if err != nil { + log.Error(vterrors.Wrapf(err, "AtomicCopy: failed to normalize schema via schemadiff")) + } else { + applyDDLs = schema.ToQueries() + log.Infof("AtomicCopy used, and schema was normalized via schemadiff. %v queries normalized", len(applyDDLs)) + } + } sql := strings.Join(applyDDLs, ";\n") _, err = mz.wr.tmc.ApplySchema(ctx, targetTablet.Tablet, &tmutils.SchemaChange{ @@ -1277,9 +1292,8 @@ func (mz *materializer) deploySchema(ctx context.Context) error { }) } -func stripTableForeignKeys(ddl string) (string, error) { - - ast, err := sqlparser.ParseStrictDDL(ddl) +func stripTableForeignKeys(ddl string, parser *sqlparser.Parser) (string, error) { + ast, err := parser.ParseStrictDDL(ddl) if err != nil { return "", err } @@ -1307,8 +1321,8 @@ func stripTableForeignKeys(ddl string) (string, error) { return newDDL, nil } -func stripTableConstraints(ddl string) (string, error) { - ast, err := sqlparser.ParseStrictDDL(ddl) +func stripTableConstraints(ddl string, parser *sqlparser.Parser) (string, error) { + ast, err := parser.ParseStrictDDL(ddl) if err != nil { return "", err } @@ -1354,7 +1368,7 @@ func (mz *materializer) generateInserts(ctx context.Context, sourceShards []*top } // Validate non-empty query. - stmt, err := sqlparser.Parse(ts.SourceExpression) + stmt, err := mz.wr.env.Parser().Parse(ts.SourceExpression) if err != nil { return "", err } diff --git a/go/vt/wrangler/materializer_env_test.go b/go/vt/wrangler/materializer_env_test.go index b98621ffa1b..3d5797a6bff 100644 --- a/go/vt/wrangler/materializer_env_test.go +++ b/go/vt/wrangler/materializer_env_test.go @@ -37,6 +37,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" _flag "vitess.io/vitess/go/internal/flag" @@ -129,7 +130,8 @@ func newTestMaterializerEnv(t *testing.T, ctx context.Context, ms *vtctldatapb.M cell: "cell", tmc: newTestMaterializerTMClient(), } - env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + parser := sqlparser.NewTestParser() + env.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) tabletID := 100 for _, shard := range sources { _ = env.addTablet(tabletID, env.ms.SourceKeyspace, shard, topodatapb.TabletType_PRIMARY) @@ -145,7 +147,7 @@ func newTestMaterializerEnv(t *testing.T, ctx context.Context, ms *vtctldatapb.M for _, ts := range ms.TableSettings { tableName := ts.TargetTable - table, err := sqlparser.TableFromStatement(ts.SourceExpression) + table, err := parser.TableFromStatement(ts.SourceExpression) if err == nil { tableName = table.Name.String() } diff --git a/go/vt/wrangler/materializer_test.go b/go/vt/wrangler/materializer_test.go index 242bca31e49..bdeee1e6ac3 100644 --- a/go/vt/wrangler/materializer_test.go +++ b/go/vt/wrangler/materializer_test.go @@ -33,8 +33,10 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -1541,7 +1543,7 @@ func TestCreateLookupVindexFailures(t *testing.T) { defer cancel() topoServ := memorytopo.NewServer(ctx, "cell") - wr := New(logutil.NewConsoleLogger(), topoServ, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), topoServ, nil) unique := map[string]*vschemapb.Vindex{ "v": { @@ -2541,7 +2543,7 @@ func TestMaterializerNoSourcePrimary(t *testing.T) { cell: "cell", tmc: newTestMaterializerTMClient(), } - env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + env.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) defer env.close() tabletID := 100 @@ -2870,7 +2872,7 @@ func TestStripForeignKeys(t *testing.T) { } for _, tc := range tcs { - newDDL, err := stripTableForeignKeys(tc.ddl) + newDDL, err := stripTableForeignKeys(tc.ddl, sqlparser.NewTestParser()) if tc.hasErr != (err != nil) { t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) } @@ -2944,7 +2946,7 @@ func TestStripConstraints(t *testing.T) { } for _, tc := range tcs { - newDDL, err := stripTableConstraints(tc.ddl) + newDDL, err := stripTableConstraints(tc.ddl, sqlparser.NewTestParser()) if tc.hasErr != (err != nil) { t.Fatalf("hasErr does not match: err: %v, tc: %+v", err, tc) } diff --git a/go/vt/wrangler/reparent.go b/go/vt/wrangler/reparent.go index fbeec55cbbc..1a3a45cf99b 100644 --- a/go/vt/wrangler/reparent.go +++ b/go/vt/wrangler/reparent.go @@ -26,7 +26,6 @@ import ( "time" "vitess.io/vitess/go/event" - "vitess.io/vitess/go/sets" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools/events" @@ -60,7 +59,7 @@ func (wr *Wrangler) InitShardPrimary(ctx context.Context, keyspace, shard string ev := &events.Reparent{} // do the work - err = grpcvtctldserver.NewVtctldServer(wr.ts).InitShardPrimaryLocked(ctx, ev, &vtctldatapb.InitShardPrimaryRequest{ + err = grpcvtctldserver.NewVtctldServer(wr.env, wr.ts).InitShardPrimaryLocked(ctx, ev, &vtctldatapb.InitShardPrimaryRequest{ Keyspace: keyspace, Shard: shard, PrimaryElectTabletAlias: primaryElectTabletAlias, @@ -76,16 +75,16 @@ func (wr *Wrangler) InitShardPrimary(ctx context.Context, keyspace, shard string // PlannedReparentShard will make the provided tablet the primary for the shard, // when both the current and new primary are reachable and in good shape. -func (wr *Wrangler) PlannedReparentShard(ctx context.Context, keyspace, shard string, primaryElectTabletAlias, avoidTabletAlias *topodatapb.TabletAlias, waitReplicasTimeout time.Duration) (err error) { +func (wr *Wrangler) PlannedReparentShard( + ctx context.Context, + keyspace, shard string, + opts reparentutil.PlannedReparentOptions, +) (err error) { _, err = reparentutil.NewPlannedReparenter(wr.ts, wr.tmc, wr.logger).ReparentShard( ctx, keyspace, shard, - reparentutil.PlannedReparentOptions{ - AvoidPrimaryAlias: avoidTabletAlias, - NewPrimaryAlias: primaryElectTabletAlias, - WaitReplicasTimeout: waitReplicasTimeout, - }, + opts, ) return err @@ -93,18 +92,12 @@ func (wr *Wrangler) PlannedReparentShard(ctx context.Context, keyspace, shard st // EmergencyReparentShard will make the provided tablet the primary for // the shard, when the old primary is completely unreachable. -func (wr *Wrangler) EmergencyReparentShard(ctx context.Context, keyspace, shard string, primaryElectTabletAlias *topodatapb.TabletAlias, waitReplicasTimeout time.Duration, ignoredTablets sets.Set[string], preventCrossCellPromotion bool, waitForAllTablets bool) (err error) { +func (wr *Wrangler) EmergencyReparentShard(ctx context.Context, keyspace, shard string, opts reparentutil.EmergencyReparentOptions) (err error) { _, err = reparentutil.NewEmergencyReparenter(wr.ts, wr.tmc, wr.logger).ReparentShard( ctx, keyspace, shard, - reparentutil.EmergencyReparentOptions{ - NewPrimaryAlias: primaryElectTabletAlias, - WaitReplicasTimeout: waitReplicasTimeout, - IgnoreReplicas: ignoredTablets, - PreventCrossCellPromotion: preventCrossCellPromotion, - WaitAllTablets: waitForAllTablets, - }, + opts, ) return err diff --git a/go/vt/wrangler/resharder.go b/go/vt/wrangler/resharder.go index a81c3e8d598..536f4c643cc 100644 --- a/go/vt/wrangler/resharder.go +++ b/go/vt/wrangler/resharder.go @@ -164,6 +164,9 @@ func (wr *Wrangler) buildResharder(ctx context.Context, keyspace, workflow strin return rs, nil } +// validateTargets ensures that the target shards have no existing +// VReplication workflow streams as that is an invalid starting +// state for the non-serving shards involved in a Reshard. func (rs *resharder) validateTargets(ctx context.Context) error { err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error { targetPrimary := rs.targetPrimaries[target.ShardName()] @@ -298,6 +301,8 @@ func (rs *resharder) copySchema(ctx context.Context) error { return err } +// createStreams creates all of the VReplication streams that +// need to now exist on the new shards. func (rs *resharder) createStreams(ctx context.Context) error { var excludeRules []*binlogdatapb.Rule for tableName, table := range rs.vschema.Tables { @@ -359,7 +364,14 @@ func (rs *resharder) createStreams(ctx context.Context) error { func (rs *resharder) startStreams(ctx context.Context) error { err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error { targetPrimary := rs.targetPrimaries[target.ShardName()] - query := fmt.Sprintf("update _vt.vreplication set state='Running' where db_name=%s", encodeString(targetPrimary.DbName())) + // This is the rare case where we truly want to update every stream/record + // because we've already confirmed that there were no existing workflows + // on the shards when we started, and we want to start all of the ones + // that we've created on the new shards as we're migrating them. + // We use the comment directive to indicate that this is intentional + // and OK. + query := fmt.Sprintf("update /*vt+ %s */ _vt.vreplication set state='Running' where db_name=%s", + vreplication.AllowUnsafeWriteCommentDirective, encodeString(targetPrimary.DbName())) if _, err := rs.wr.tmc.VReplicationExec(ctx, targetPrimary.Tablet, query); err != nil { return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetPrimary.Tablet, query) } diff --git a/go/vt/wrangler/resharder_env_test.go b/go/vt/wrangler/resharder_env_test.go index ee39c7e5eaa..3fbdde6f52e 100644 --- a/go/vt/wrangler/resharder_env_test.go +++ b/go/vt/wrangler/resharder_env_test.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" querypb "vitess.io/vitess/go/vt/proto/query" @@ -93,7 +94,7 @@ func newTestResharderEnv(t *testing.T, ctx context.Context, sources, targets []s cell: "cell", tmc: newTestResharderTMClient(), } - env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + env.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) initTopo(t, env.topoServ, "ks", sources, targets, []string{"cell"}) tabletID := 100 for _, shard := range sources { diff --git a/go/vt/wrangler/resharder_test.go b/go/vt/wrangler/resharder_test.go index 40d31d36e1f..8d5a90092f3 100644 --- a/go/vt/wrangler/resharder_test.go +++ b/go/vt/wrangler/resharder_test.go @@ -22,15 +22,15 @@ import ( "strings" "testing" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/vtgate/vindexes" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" - "vitess.io/vitess/go/vt/vtgate/vindexes" ) const rsSelectFrozenQuery = "select 1 from _vt.vreplication where db_name='vt_ks' and message='FROZEN' and workflow_sub_type != 1" @@ -104,8 +104,8 @@ func TestResharderOneToMany(t *testing.T) { tc.cells+`', '`+tc.tabletTypes+`', [0-9]*, 0, 'Stopped', 'vt_ks', 4, 0, false\)`+eol, &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, tc.cells, tc.tabletTypes, defaultOnDDL, true, false, false) require.NoError(t, err) @@ -143,7 +143,7 @@ func TestResharderManyToOne(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) @@ -185,8 +185,8 @@ func TestResharderManyToMany(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) @@ -240,8 +240,8 @@ func TestResharderOneRefTable(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) @@ -363,8 +363,8 @@ func TestResharderOneRefStream(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) @@ -442,8 +442,8 @@ func TestResharderNoRefStream(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, true, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) @@ -484,8 +484,8 @@ func TestResharderCopySchema(t *testing.T) { &sqltypes.Result{}, ) - env.tmc.expectVRQuery(200, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) - env.tmc.expectVRQuery(210, "update _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(200, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) + env.tmc.expectVRQuery(210, "update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state='Running' where db_name='vt_ks'", &sqltypes.Result{}) err := env.wr.Reshard(context.Background(), env.keyspace, env.workflow, env.sources, env.targets, false, "", "", defaultOnDDL, true, false, false) assert.NoError(t, err) diff --git a/go/vt/wrangler/schema.go b/go/vt/wrangler/schema.go index 84bc078f240..ae24106c97f 100644 --- a/go/vt/wrangler/schema.go +++ b/go/vt/wrangler/schema.go @@ -17,9 +17,9 @@ limitations under the License. package wrangler import ( - "bytes" "context" "fmt" + "strings" "sync" "text/template" "time" @@ -262,7 +262,7 @@ func (wr *Wrangler) CopySchemaShard(ctx context.Context, sourceTabletAlias *topo } } - // Notify Replicass to reload schema. This is best-effort. + // Notify Replicas to reload schema. This is best-effort. reloadCtx, cancel := context.WithTimeout(ctx, waitReplicasTimeout) defer cancel() resp, err := wr.VtctldServer().ReloadSchemaShard(reloadCtx, &vtctldatapb.ReloadSchemaShardRequest{ @@ -307,8 +307,8 @@ func (wr *Wrangler) applySQLShard(ctx context.Context, tabletInfo *topo.TabletIn // fillStringTemplate returns the string template filled func fillStringTemplate(tmpl string, vars any) (string, error) { myTemplate := template.Must(template.New("").Parse(tmpl)) - data := new(bytes.Buffer) - if err := myTemplate.Execute(data, vars); err != nil { + var data strings.Builder + if err := myTemplate.Execute(&data, vars); err != nil { return "", err } return data.String(), nil diff --git a/go/vt/wrangler/shard.go b/go/vt/wrangler/shard.go index c1c65b0407b..6b74f32031e 100644 --- a/go/vt/wrangler/shard.go +++ b/go/vt/wrangler/shard.go @@ -113,7 +113,7 @@ func (wr *Wrangler) DeleteShard(ctx context.Context, keyspace, shard string, rec // GetTabletMap ignores ErrNoNode, and it's good for // our purpose, it means a tablet was deleted but is // still referenced. - tabletMap, err := wr.ts.GetTabletMap(ctx, aliases) + tabletMap, err := wr.ts.GetTabletMap(ctx, aliases, nil) if err != nil { return fmt.Errorf("GetTabletMap() failed: %v", err) } diff --git a/go/vt/wrangler/split.go b/go/vt/wrangler/split.go index ba67fd8efef..543d50a808d 100644 --- a/go/vt/wrangler/split.go +++ b/go/vt/wrangler/split.go @@ -40,7 +40,7 @@ const ( // on a Shard. func (wr *Wrangler) SetSourceShards(ctx context.Context, keyspace, shard string, sources []*topodatapb.TabletAlias, tables []string) error { // Read the source tablets. - sourceTablets, err := wr.ts.GetTabletMap(ctx, sources) + sourceTablets, err := wr.ts.GetTabletMap(ctx, sources, nil) if err != nil { return err } diff --git a/go/vt/wrangler/stream_migrater_test.go b/go/vt/wrangler/stream_migrater_test.go index 98828261b27..6432a188f36 100644 --- a/go/vt/wrangler/stream_migrater_test.go +++ b/go/vt/wrangler/stream_migrater_test.go @@ -939,7 +939,7 @@ func TestStreamMigrateSyncFail(t *testing.T) { tme.dbTargetClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow in ('t1')", &sqltypes.Result{}, nil) tme.dbTargetClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow in ('t1')", &sqltypes.Result{}, nil) - tme.expectCancelMigration() + tme.expectCancelStreamMigrations() _, _, err = tme.wr.SwitchWrites(ctx, tme.targetKeyspace, "test", 1*time.Second, false, false, true, false, false) want := "does not match" @@ -1022,8 +1022,8 @@ func TestStreamMigrateCancel(t *testing.T) { // sm.migrateStreams->->restart source streams tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow != 'test_reverse'", resultid12, nil) tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow != 'test_reverse'", resultid12, nil) - tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', stop_pos = null, message = '' where id in (1, 2)", &sqltypes.Result{}, nil) - tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', stop_pos = null, message = '' where id in (1, 2)", &sqltypes.Result{}, nil) + tme.dbSourceClients[0].addQuery("update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state = 'Running', stop_pos = null, message = '' where id in (1, 2)", &sqltypes.Result{}, nil) + tme.dbSourceClients[1].addQuery("update /*vt+ ALLOW_UNSAFE_VREPLICATION_WRITE */ _vt.vreplication set state = 'Running', stop_pos = null, message = '' where id in (1, 2)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 1", runningResult(1), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 1", runningResult(1), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 2", runningResult(2), nil) @@ -1173,7 +1173,7 @@ func TestStreamMigrateCancelWithStoppedStreams(t *testing.T) { tme.dbTargetClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow in ('t1t2')", &sqltypes.Result{}, nil) tme.dbTargetClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow in ('t1t2')", &sqltypes.Result{}, nil) - tme.expectCancelMigration() + tme.expectCancelStreamMigrations() _, _, err = tme.wr.SwitchWrites(ctx, tme.targetKeyspace, "test", 1*time.Second, true, false, false, false, false) if err != nil { diff --git a/go/vt/wrangler/switcher_dry_run.go b/go/vt/wrangler/switcher_dry_run.go index 7b21ac65fe0..6c1b48bb5c6 100644 --- a/go/vt/wrangler/switcher_dry_run.go +++ b/go/vt/wrangler/switcher_dry_run.go @@ -24,7 +24,8 @@ import ( "strings" "time" - "vitess.io/vitess/go/maps2" + "golang.org/x/exp/maps" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/vt/vtctl/workflow" @@ -224,7 +225,7 @@ func (dr *switcherDryRun) stopStreams(ctx context.Context, sm *workflow.StreamMi } func (dr *switcherDryRun) cancelMigration(ctx context.Context, sm *workflow.StreamMigrator) { - dr.drLog.Log("Cancel stream migrations as requested") + dr.drLog.Log("Cancel migration as requested") } func (dr *switcherDryRun) lockKeyspace(ctx context.Context, keyspace, _ string) (context.Context, func(*error), error) { @@ -399,7 +400,7 @@ func (dr *switcherDryRun) resetSequences(ctx context.Context) error { } func (dr *switcherDryRun) initializeTargetSequences(ctx context.Context, sequencesByBackingTable map[string]*sequenceMetadata) error { - sortedBackingTableNames := maps2.Keys(sequencesByBackingTable) + sortedBackingTableNames := maps.Keys(sequencesByBackingTable) slices.Sort(sortedBackingTableNames) dr.drLog.Log(fmt.Sprintf("The following sequence backing tables used by tables being moved will be initialized: %s", strings.Join(sortedBackingTableNames, ","))) diff --git a/go/vt/wrangler/tablet_test.go b/go/vt/wrangler/tablet_test.go index 1350b6b574c..c5ae032fe07 100644 --- a/go/vt/wrangler/tablet_test.go +++ b/go/vt/wrangler/tablet_test.go @@ -25,6 +25,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" ) // TestInitTabletShardConversion makes sure InitTablet converts the @@ -36,7 +37,7 @@ func TestInitTabletShardConversion(t *testing.T) { cell := "cell1" ts := memorytopo.NewServer(ctx, cell) - wr := New(logutil.NewConsoleLogger(), ts, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, nil) tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ @@ -70,7 +71,7 @@ func TestDeleteTabletBasic(t *testing.T) { cell := "cell1" ts := memorytopo.NewServer(ctx, cell) - wr := New(logutil.NewConsoleLogger(), ts, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, nil) tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ @@ -102,7 +103,7 @@ func TestDeleteTabletTruePrimary(t *testing.T) { cell := "cell1" ts := memorytopo.NewServer(ctx, cell) - wr := New(logutil.NewConsoleLogger(), ts, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, nil) tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ @@ -149,7 +150,7 @@ func TestDeleteTabletFalsePrimary(t *testing.T) { cell := "cell1" ts := memorytopo.NewServer(ctx, cell) - wr := New(logutil.NewConsoleLogger(), ts, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, nil) tablet1 := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ @@ -201,7 +202,7 @@ func TestDeleteTabletShardNonExisting(t *testing.T) { cell := "cell1" ts := memorytopo.NewServer(ctx, cell) - wr := New(logutil.NewConsoleLogger(), ts, nil) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, nil) tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ diff --git a/go/vt/wrangler/testdata/show-80dash.json b/go/vt/wrangler/testdata/show-80dash.json new file mode 100644 index 00000000000..f1b14bfccf0 --- /dev/null +++ b/go/vt/wrangler/testdata/show-80dash.json @@ -0,0 +1,71 @@ +{ + "Workflow": "wrWorkflow", + "SourceLocation": { + "Keyspace": "source", + "Shards": [ + "0" + ] + }, + "TargetLocation": { + "Keyspace": "target", + "Shards": [ + "80-" + ] + }, + "MaxVReplicationLag": 0, + "MaxVReplicationTransactionLag": 0, + "Frozen": false, + "ShardStatuses": { + "80-/zone1-0000000210": { + "PrimaryReplicationStatuses": [ + { + "Shard": "80-", + "Tablet": "zone1-0000000210", + "ID": 1, + "Bls": { + "keyspace": "source", + "shard": "0", + "filter": { + "rules": [ + { + "match": "t1" + }, + { + "match": "t2" + } + ] + } + }, + "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", + "StopPos": "", + "State": "Copying", + "DBName": "vt_target", + "TransactionTimestamp": 0, + "TimeUpdated": 1234, + "TimeHeartbeat": 1234, + "TimeThrottled": 0, + "ComponentThrottled": "", + "Message": "", + "Tags": "", + "WorkflowType": "Materialize", + "WorkflowSubType": "None", + "CopyState": [ + { + "Table": "t1", + "LastPK": "pk1" + }, + { + "Table": "t2", + "LastPK": "pk2" + } + ], + "RowsCopied": 1000 + } + ], + "TabletControls": null, + "PrimaryIsServing": true + } + }, + "SourceTimeZone": "", + "TargetTimeZone": "" +} \ No newline at end of file diff --git a/go/vt/wrangler/testdata/show-all-shards.json b/go/vt/wrangler/testdata/show-all-shards.json new file mode 100644 index 00000000000..4868c164a72 --- /dev/null +++ b/go/vt/wrangler/testdata/show-all-shards.json @@ -0,0 +1,121 @@ +{ + "Workflow": "wrWorkflow", + "SourceLocation": { + "Keyspace": "source", + "Shards": [ + "0" + ] + }, + "TargetLocation": { + "Keyspace": "target", + "Shards": [ + "-80", + "80-" + ] + }, + "MaxVReplicationLag": 0, + "MaxVReplicationTransactionLag": 0, + "Frozen": false, + "ShardStatuses": { + "-80/zone1-0000000200": { + "PrimaryReplicationStatuses": [ + { + "Shard": "-80", + "Tablet": "zone1-0000000200", + "ID": 1, + "Bls": { + "keyspace": "source", + "shard": "0", + "filter": { + "rules": [ + { + "match": "t1" + }, + { + "match": "t2" + } + ] + } + }, + "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", + "StopPos": "", + "State": "Copying", + "DBName": "vt_target", + "TransactionTimestamp": 0, + "TimeUpdated": 1234, + "TimeHeartbeat": 1234, + "TimeThrottled": 0, + "ComponentThrottled": "", + "Message": "", + "Tags": "", + "WorkflowType": "Materialize", + "WorkflowSubType": "None", + "CopyState": [ + { + "Table": "t1", + "LastPK": "pk1" + }, + { + "Table": "t2", + "LastPK": "pk2" + } + ], + "RowsCopied": 1000 + } + ], + "TabletControls": null, + "PrimaryIsServing": true + }, + "80-/zone1-0000000210": { + "PrimaryReplicationStatuses": [ + { + "Shard": "80-", + "Tablet": "zone1-0000000210", + "ID": 1, + "Bls": { + "keyspace": "source", + "shard": "0", + "filter": { + "rules": [ + { + "match": "t1" + }, + { + "match": "t2" + } + ] + } + }, + "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", + "StopPos": "", + "State": "Copying", + "DBName": "vt_target", + "TransactionTimestamp": 0, + "TimeUpdated": 1234, + "TimeHeartbeat": 1234, + "TimeThrottled": 0, + "ComponentThrottled": "", + "Message": "", + "Tags": "", + "WorkflowType": "Materialize", + "WorkflowSubType": "None", + "CopyState": [ + { + "Table": "t1", + "LastPK": "pk1" + }, + { + "Table": "t2", + "LastPK": "pk2" + } + ], + "RowsCopied": 1000 + } + ], + "TabletControls": null, + "PrimaryIsServing": true + } + }, + "SourceTimeZone": "", + "TargetTimeZone": "" +} \ No newline at end of file diff --git a/go/vt/wrangler/testdata/show-dash80.json b/go/vt/wrangler/testdata/show-dash80.json new file mode 100644 index 00000000000..7e810278e36 --- /dev/null +++ b/go/vt/wrangler/testdata/show-dash80.json @@ -0,0 +1,71 @@ +{ + "Workflow": "wrWorkflow", + "SourceLocation": { + "Keyspace": "source", + "Shards": [ + "0" + ] + }, + "TargetLocation": { + "Keyspace": "target", + "Shards": [ + "-80" + ] + }, + "MaxVReplicationLag": 0, + "MaxVReplicationTransactionLag": 0, + "Frozen": false, + "ShardStatuses": { + "-80/zone1-0000000200": { + "PrimaryReplicationStatuses": [ + { + "Shard": "-80", + "Tablet": "zone1-0000000200", + "ID": 1, + "Bls": { + "keyspace": "source", + "shard": "0", + "filter": { + "rules": [ + { + "match": "t1" + }, + { + "match": "t2" + } + ] + } + }, + "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", + "StopPos": "", + "State": "Copying", + "DBName": "vt_target", + "TransactionTimestamp": 0, + "TimeUpdated": 1234, + "TimeHeartbeat": 1234, + "TimeThrottled": 0, + "ComponentThrottled": "", + "Message": "", + "Tags": "", + "WorkflowType": "Materialize", + "WorkflowSubType": "None", + "CopyState": [ + { + "Table": "t1", + "LastPK": "pk1" + }, + { + "Table": "t2", + "LastPK": "pk2" + } + ], + "RowsCopied": 1000 + } + ], + "TabletControls": null, + "PrimaryIsServing": true + } + }, + "SourceTimeZone": "", + "TargetTimeZone": "" +} \ No newline at end of file diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go index 787e4ce1946..0de8bfd78f3 100644 --- a/go/vt/wrangler/testlib/backup_test.go +++ b/go/vt/wrangler/testlib/backup_test.go @@ -27,13 +27,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql/replication" - - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/mysqlctl/backupstorage" @@ -41,12 +40,15 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) +const mysqlShutdownTimeout = 1 * time.Minute + type compressionDetails struct { CompressionEngineName string ExternalCompressorCmd string @@ -90,7 +92,7 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { db := fakesqldb.New(t) defer db.Close() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -263,7 +265,7 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { RelayLogInfoPath: path.Join(root, "relay-log.info"), } - err = destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* backupTime */, time.Time{} /* restoreToTimestamp */, "") + err = destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* backupTime */, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout) if err != nil { return err } @@ -302,7 +304,7 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { primary.FakeMysqlDaemon.SetReplicationPositionPos = primary.FakeMysqlDaemon.CurrentPrimaryPosition // restore primary from latest backup - require.NoError(t, primary.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, ""), + require.NoError(t, primary.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout), "RestoreData failed") // tablet was created as PRIMARY, so it's baseTabletType is PRIMARY assert.Equal(t, topodatapb.TabletType_PRIMARY, primary.Tablet.Type) @@ -318,7 +320,7 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { } // Test restore with the backup timestamp - require.NoError(t, primary.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, backupTime, time.Time{} /* restoreToTimestamp */, ""), + require.NoError(t, primary.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, backupTime, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout), "RestoreData with backup timestamp failed") assert.Equal(t, topodatapb.TabletType_PRIMARY, primary.Tablet.Type) assert.False(t, primary.FakeMysqlDaemon.Replicating) @@ -342,7 +344,7 @@ func TestBackupRestoreLagged(t *testing.T) { db := fakesqldb.New(t) defer db.Close() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -519,7 +521,7 @@ func TestBackupRestoreLagged(t *testing.T) { errCh = make(chan error, 1) go func(ctx context.Context, tablet *FakeTablet) { - errCh <- tablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "") + errCh <- tablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout) }(ctx, destTablet) timer = time.NewTicker(1 * time.Second) @@ -561,7 +563,7 @@ func TestRestoreUnreachablePrimary(t *testing.T) { db := fakesqldb.New(t) defer db.Close() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -713,7 +715,7 @@ func TestRestoreUnreachablePrimary(t *testing.T) { // set a short timeout so that we don't have to wait 30 seconds topo.RemoteOperationTimeout = 2 * time.Second // Restore should still succeed - require.NoError(t, destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "")) + require.NoError(t, destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout)) // verify the full status require.NoError(t, destTablet.FakeMysqlDaemon.CheckSuperQueryList(), "destTablet.FakeMysqlDaemon.CheckSuperQueryList failed") assert.True(t, destTablet.FakeMysqlDaemon.Replicating) @@ -736,7 +738,7 @@ func TestDisableActiveReparents(t *testing.T) { db := fakesqldb.New(t) defer db.Close() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -867,7 +869,7 @@ func TestDisableActiveReparents(t *testing.T) { RelayLogInfoPath: path.Join(root, "relay-log.info"), } - require.NoError(t, destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "")) + require.NoError(t, destTablet.TM.RestoreData(ctx, logutil.NewConsoleLogger(), 0 /* waitForBackupInterval */, false /* deleteBeforeRestore */, time.Time{} /* restoreFromBackupTs */, time.Time{} /* restoreToTimestamp */, "", mysqlShutdownTimeout)) // verify the full status require.NoError(t, destTablet.FakeMysqlDaemon.CheckSuperQueryList(), "destTablet.FakeMysqlDaemon.CheckSuperQueryList failed") assert.False(t, destTablet.FakeMysqlDaemon.Replicating) @@ -889,9 +891,9 @@ func needInnoDBRedoLogSubdir() (needIt bool, err error) { return needIt, err } versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch) - _, capableOf, _ := mysql.GetFlavor(versionStr, nil) + capableOf := mysql.ServerVersionCapableOf(versionStr) if capableOf == nil { return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr) } - return capableOf(mysql.DynamicRedoLogCapacityFlavorCapability) + return capableOf(capabilities.DynamicRedoLogCapacityFlavorCapability) } diff --git a/go/vt/wrangler/testlib/copy_schema_shard_test.go b/go/vt/wrangler/testlib/copy_schema_shard_test.go index 866ec2fe931..262a93f4e23 100644 --- a/go/vt/wrangler/testlib/copy_schema_shard_test.go +++ b/go/vt/wrangler/testlib/copy_schema_shard_test.go @@ -22,14 +22,14 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/mysqlctl/tmutils" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -56,7 +56,7 @@ func copySchema(t *testing.T, useShardAsSource bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() diff --git a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go index 99cc1839186..6cafe83b684 100644 --- a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go @@ -25,15 +25,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql/replication" - "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/sets" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl/reparentutil" "vitess.io/vitess/go/vt/vtctl/reparentutil/reparenttestutil" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -50,7 +51,7 @@ func TestEmergencyReparentShard(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -204,7 +205,7 @@ func TestEmergencyReparentShardPrimaryElectNotBest(t *testing.T) { discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create a primary, a couple good replicas oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) @@ -279,7 +280,13 @@ func TestEmergencyReparentShardPrimaryElectNotBest(t *testing.T) { defer moreAdvancedReplica.StopActionLoop(t) // run EmergencyReparentShard - err := wr.EmergencyReparentShard(ctx, newPrimary.Tablet.Keyspace, newPrimary.Tablet.Shard, newPrimary.Tablet.Alias, 10*time.Second, sets.New[string](), false, false) + err := wr.EmergencyReparentShard(ctx, newPrimary.Tablet.Keyspace, newPrimary.Tablet.Shard, reparentutil.EmergencyReparentOptions{ + NewPrimaryAlias: newPrimary.Tablet.Alias, + WaitAllTablets: false, + WaitReplicasTimeout: 10 * time.Second, + IgnoreReplicas: sets.New[string](), + PreventCrossCellPromotion: false, + }) cancel() assert.NoError(t, err) diff --git a/go/vt/wrangler/testlib/external_reparent_test.go b/go/vt/wrangler/testlib/external_reparent_test.go index c0152de3cf3..fdc1ca664ee 100644 --- a/go/vt/wrangler/testlib/external_reparent_test.go +++ b/go/vt/wrangler/testlib/external_reparent_test.go @@ -22,14 +22,14 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/discovery" - "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -50,7 +50,7 @@ func TestTabletExternallyReparentedBasic(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -143,7 +143,7 @@ func TestTabletExternallyReparentedToReplica(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary, a new primary, two good replicas, one bad replica oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) @@ -226,7 +226,7 @@ func TestTabletExternallyReparentedWithDifferentMysqlPort(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary, a new primary, two good replicas, one bad replica oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) @@ -319,7 +319,7 @@ func TestTabletExternallyReparentedContinueOnUnexpectedPrimary(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary, a new primary, two good replicas, one bad replica oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) @@ -405,7 +405,7 @@ func TestTabletExternallyReparentedRerun(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary, a new primary, and a good replica. oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) @@ -509,7 +509,7 @@ func TestRPCTabletExternallyReparentedDemotesPrimaryToConfiguredTabletType(t *te ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary and a new primary oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_SPARE, nil) diff --git a/go/vt/wrangler/testlib/fake_tablet.go b/go/vt/wrangler/testlib/fake_tablet.go index a1b30813f53..97d74edf3f7 100644 --- a/go/vt/wrangler/testlib/fake_tablet.go +++ b/go/vt/wrangler/testlib/fake_tablet.go @@ -35,6 +35,7 @@ import ( "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/grpctmserver" "vitess.io/vitess/go/vt/vttablet/tabletconntest" "vitess.io/vitess/go/vt/vttablet/tabletmanager" @@ -209,8 +210,9 @@ func (ft *FakeTablet) StartActionLoop(t *testing.T, wr *wrangler.Wrangler) { DBConfigs: &dbconfigs.DBConfigs{}, QueryServiceControl: tabletservermock.NewController(), VREngine: vreplication.NewTestEngine(wr.TopoServer(), ft.Tablet.Alias.Cell, ft.FakeMysqlDaemon, binlogplayer.NewFakeDBClient, binlogplayer.NewFakeDBClient, topoproto.TabletDbName(ft.Tablet), nil), + Env: vtenv.NewTestEnv(), } - if err := ft.TM.Start(ft.Tablet, 0); err != nil { + if err := ft.TM.Start(ft.Tablet, nil); err != nil { t.Fatalf("Error in tablet - %v, err - %v", topoproto.TabletAliasString(ft.Tablet.Alias), err.Error()) } ft.Tablet = ft.TM.Tablet() diff --git a/go/vt/wrangler/testlib/find_tablet_test.go b/go/vt/wrangler/testlib/find_tablet_test.go index 5b6f26f7056..069eb913ddf 100644 --- a/go/vt/wrangler/testlib/find_tablet_test.go +++ b/go/vt/wrangler/testlib/find_tablet_test.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -36,7 +37,7 @@ func TestFindTablet(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // Create an old primary, two good replicas oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) diff --git a/go/vt/wrangler/testlib/permissions_test.go b/go/vt/wrangler/testlib/permissions_test.go index 4a0e71512f3..ba110a30d87 100644 --- a/go/vt/wrangler/testlib/permissions_test.go +++ b/go/vt/wrangler/testlib/permissions_test.go @@ -24,12 +24,13 @@ import ( "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" querypb "vitess.io/vitess/go/vt/proto/query" @@ -47,7 +48,7 @@ func TestPermissions(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() diff --git a/go/vt/wrangler/testlib/planned_reparent_shard_test.go b/go/vt/wrangler/testlib/planned_reparent_shard_test.go index 0125e69cac0..7069df9d3e1 100644 --- a/go/vt/wrangler/testlib/planned_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/planned_reparent_shard_test.go @@ -24,6 +24,7 @@ import ( "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/vt/mysqlctl" + "vitess.io/vitess/go/vt/vtenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -51,7 +52,7 @@ func TestPlannedReparentShardNoPrimaryProvided(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -167,7 +168,7 @@ func TestPlannedReparentShardNoError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -303,7 +304,7 @@ func TestPlannedReparentInitialization(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -389,7 +390,7 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -497,7 +498,7 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -603,7 +604,7 @@ func TestPlannedReparentShardRelayLogError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -683,7 +684,7 @@ func TestPlannedReparentShardRelayLogErrorStartReplication(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -768,7 +769,7 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() @@ -908,7 +909,7 @@ func TestPlannedReparentShardSamePrimary(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() diff --git a/go/vt/wrangler/testlib/reparent_utils_test.go b/go/vt/wrangler/testlib/reparent_utils_test.go index 0d1d84e89f5..8dfc3efb20d 100644 --- a/go/vt/wrangler/testlib/reparent_utils_test.go +++ b/go/vt/wrangler/testlib/reparent_utils_test.go @@ -25,16 +25,14 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/replication" - - "vitess.io/vitess/go/vt/vtctl/reparentutil/reparenttestutil" - "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/vtctl/reparentutil" - "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl/reparentutil" + "vitess.io/vitess/go/vt/vtctl/reparentutil/reparenttestutil" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -51,7 +49,7 @@ func TestShardReplicationStatuses(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // create shard and tablets if _, err := ts.GetOrCreateShard(ctx, "test_keyspace", "0"); err != nil { @@ -135,7 +133,7 @@ func TestReparentTablet(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // create shard and tablets if _, err := ts.GetOrCreateShard(ctx, "test_keyspace", "0"); err != nil { @@ -192,7 +190,7 @@ func TestSetReplicationSource(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) // create shard and tablets _, err := ts.GetOrCreateShard(ctx, "test_keyspace", "0") diff --git a/go/vt/wrangler/testlib/shard_test.go b/go/vt/wrangler/testlib/shard_test.go index a0b1b0a3562..400071d9e3c 100644 --- a/go/vt/wrangler/testlib/shard_test.go +++ b/go/vt/wrangler/testlib/shard_test.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" @@ -35,7 +36,7 @@ func TestDeleteShardCleanup(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() diff --git a/go/vt/wrangler/testlib/version_test.go b/go/vt/wrangler/testlib/version_test.go index 102bcdfe6e5..c0ea92a5b46 100644 --- a/go/vt/wrangler/testlib/version_test.go +++ b/go/vt/wrangler/testlib/version_test.go @@ -27,10 +27,11 @@ import ( "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtenv" + "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo/memorytopo" - "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -70,7 +71,7 @@ func TestVersion(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2") - wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) + wr := wrangler.New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) vp := NewVtctlPipe(t, ts) defer vp.Close() diff --git a/go/vt/wrangler/testlib/vtctl_pipe.go b/go/vt/wrangler/testlib/vtctl_pipe.go index 38c535f005f..44a6931870a 100644 --- a/go/vt/wrangler/testlib/vtctl_pipe.go +++ b/go/vt/wrangler/testlib/vtctl_pipe.go @@ -35,6 +35,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtctl/grpcvtctlserver" "vitess.io/vitess/go/vt/vtctl/vtctlclient" + "vitess.io/vitess/go/vt/vtenv" // we need to import the grpcvtctlclient library so the gRPC // vtctl client is registered and can be used. @@ -76,7 +77,7 @@ func NewVtctlPipe(t *testing.T, ts *topo.Server) *VtctlPipe { // Create a gRPC server and listen on the port server := grpc.NewServer() - grpcvtctlserver.StartServer(server, ts) + grpcvtctlserver.StartServer(server, vtenv.NewTestEnv(), ts) go server.Serve(listener) // Create a VtctlClient gRPC client to talk to the fake server @@ -138,7 +139,7 @@ func (vp *VtctlPipe) run(args []string, outputFunc func(string)) error { } // RunAndStreamOutput returns the output of the vtctl command as a channel. -// When the channcel is closed, the command did finish. +// When the channel is closed, the command did finish. func (vp *VtctlPipe) RunAndStreamOutput(args []string) (logutil.EventStream, error) { actionTimeout := 30 * time.Second ctx := context.Background() diff --git a/go/vt/wrangler/traffic_switcher.go b/go/vt/wrangler/traffic_switcher.go index 654a5bd1588..a6b3587c3a1 100644 --- a/go/vt/wrangler/traffic_switcher.go +++ b/go/vt/wrangler/traffic_switcher.go @@ -26,10 +26,10 @@ import ( "sync" "time" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "vitess.io/vitess/go/json2" - "vitess.io/vitess/go/maps2" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -40,6 +40,7 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vtctl/workflow" "vitess.io/vitess/go/vt/vterrors" @@ -49,6 +50,7 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -221,7 +223,7 @@ func (wr *Wrangler) getWorkflowState(ctx context.Context, targetKeyspace, workfl return nil, nil, err } - ws := workflow.NewServer(wr.ts, wr.tmc) + ws := workflow.NewServer(wr.env, wr.ts, wr.tmc) state := &workflow.State{ Workflow: workflowName, SourceKeyspace: ts.SourceKeyspaceName(), @@ -437,11 +439,8 @@ func (wr *Wrangler) areTabletsAvailableToStreamFrom(ctx context.Context, ts *tra if ts.optCells != "" { cells = strings.Split(ts.optCells, ",") } - // FIXME: currently there is a default setting in the tablet that is used if user does not specify a tablet type, - // we use the value specified in the tablet flag `-vreplication_tablet_type` - // but ideally we should populate the vreplication table with a default value when we setup the workflow if tabletTypes == "" { - tabletTypes = "PRIMARY,REPLICA" + tabletTypes = "in_order:REPLICA,PRIMARY" // default } var wg sync.WaitGroup @@ -487,11 +486,11 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa ts, ws, err := wr.getWorkflowState(ctx, targetKeyspace, workflowName) _ = ws if err != nil { - handleError("failed to get the current workflow state", err) + return handleError("failed to get the current workflow state", err) } if ts == nil { errorMsg := fmt.Sprintf("workflow %s not found in keyspace %s", workflowName, targetKeyspace) - handleError("failed to get the current workflow state", fmt.Errorf(errorMsg)) + return handleError("failed to get the current workflow state", fmt.Errorf(errorMsg)) } var sw iswitcher @@ -508,7 +507,7 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa ts.Logger().Infof("Built switching metadata: %+v", ts) if err := ts.validate(ctx); err != nil { - handleError("workflow validation failed", err) + return handleError("workflow validation failed", err) } if reverseReplication { @@ -556,7 +555,7 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa } if !journalsExist { ts.Logger().Infof("No previous journals were found. Proceeding normally.") - sm, err := workflow.BuildStreamMigrator(ctx, ts, cancel) + sm, err := workflow.BuildStreamMigrator(ctx, ts, cancel, wr.env.Parser()) if err != nil { return handleError("failed to migrate the workflow streams", err) } @@ -621,6 +620,20 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa sw.cancelMigration(ctx, sm) return handleError("failed to create the reverse vreplication streams", err) } + + // Initialize any target sequences, if there are any, before allowing new writes. + if initializeTargetSequences && len(sequenceMetadata) > 0 { + ts.Logger().Infof("Initializing target sequences") + // Writes are blocked so we can safely initialize the sequence tables but + // we also want to use a shorter timeout than the parent context. + // We use at most half of the overall timeout. + initSeqCtx, cancel := context.WithTimeout(ctx, timeout/2) + defer cancel() + if err := sw.initializeTargetSequences(initSeqCtx, sequenceMetadata); err != nil { + sw.cancelMigration(ctx, sm) + return handleError(fmt.Sprintf("failed to initialize the sequences used in the %s keyspace", ts.TargetKeyspaceName()), err) + } + } } else { if cancel { return handleError("invalid cancel", fmt.Errorf("traffic switching has reached the point of no return, cannot cancel")) @@ -637,17 +650,6 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa if err := sw.createJournals(ctx, sourceWorkflows); err != nil { return handleError("failed to create the journal", err) } - // Initialize any target sequences, if there are any, before allowing new writes. - if initializeTargetSequences && len(sequenceMetadata) > 0 { - // Writes are blocked so we can safely initialize the sequence tables but - // we also want to use a shorter timeout than the parent context. - // We use up at most half of the overall timeout. - initSeqCtx, cancel := context.WithTimeout(ctx, timeout/2) - defer cancel() - if err := sw.initializeTargetSequences(initSeqCtx, sequenceMetadata); err != nil { - return handleError(fmt.Sprintf("failed to initialize the sequences used in the %s keyspace", ts.TargetKeyspaceName()), err) - } - } if err := sw.allowTargetWrites(ctx); err != nil { return handleError(fmt.Sprintf("failed to allow writes in the %s keyspace", ts.TargetKeyspaceName()), err) } @@ -655,7 +657,7 @@ func (wr *Wrangler) SwitchWrites(ctx context.Context, targetKeyspace, workflowNa return handleError("failed to update the routing rules", err) } if err := sw.streamMigraterfinalize(ctx, ts, sourceWorkflows); err != nil { - handleError("failed to finalize the traffic switch", err) + return handleError("failed to finalize the traffic switch", err) } if reverseReplication { if err := sw.startReverseVReplication(ctx); err != nil { @@ -859,8 +861,45 @@ func (wr *Wrangler) DropSources(ctx context.Context, targetKeyspace, workflowNam return sw.logs(), nil } +func (wr *Wrangler) getShardSubset(ctx context.Context, keyspace string, shardSubset []string) ([]string, error) { + if wr.WorkflowParams != nil && len(wr.WorkflowParams.ShardSubset) > 0 { + shardSubset = wr.WorkflowParams.ShardSubset + } + allShards, err := wr.ts.GetShardNames(ctx, keyspace) + if err != nil { + return nil, err + } + if len(allShards) == 0 { + return nil, fmt.Errorf("no shards found in keyspace %s", keyspace) + } + + if len(shardSubset) == 0 { + return allShards, nil + } + + existingShards := make(map[string]bool, len(allShards)) + for _, shard := range allShards { + existingShards[shard] = true + } + // Validate that the provided shards are part of the keyspace. + for _, shard := range shardSubset { + _, found := existingShards[shard] + if !found { + return nil, fmt.Errorf("shard %s not found in keyspace %s", shard, keyspace) + } + } + log.Infof("Selecting subset of shards in keyspace %s: %d from %d :: %+v", + keyspace, len(shardSubset), len(allShards), shardSubset) + return shardSubset, nil + +} + func (wr *Wrangler) buildTrafficSwitcher(ctx context.Context, targetKeyspace, workflowName string) (*trafficSwitcher, error) { - tgtInfo, err := workflow.LegacyBuildTargets(ctx, wr.ts, wr.tmc, targetKeyspace, workflowName) + shardSubset, err := wr.getShardSubset(ctx, targetKeyspace, nil) + if err != nil { + return nil, err + } + tgtInfo, err := workflow.LegacyBuildTargets(ctx, wr.ts, wr.tmc, targetKeyspace, workflowName, shardSubset) if err != nil { log.Infof("Error building targets: %s", err) return nil, err @@ -956,7 +995,7 @@ func (wr *Wrangler) buildTrafficSwitcher(ctx context.Context, targetKeyspace, wo if err != nil { return nil, err } - ts.sourceKSSchema, err = vindexes.BuildKeyspaceSchema(vs, ts.sourceKeyspace) + ts.sourceKSSchema, err = vindexes.BuildKeyspaceSchema(vs, ts.sourceKeyspace, wr.env.Parser()) if err != nil { return nil, err } @@ -1150,7 +1189,7 @@ func (ts *trafficSwitcher) switchShardReads(ctx context.Context, cells []string, // If so, it also returns the list of sourceWorkflows that need to be switched. func (ts *trafficSwitcher) checkJournals(ctx context.Context) (journalsExist bool, sourceWorkflows []string, err error) { var ( - ws = workflow.NewServer(ts.TopoServer(), ts.TabletManagerClient()) + ws = workflow.NewServer(ts.wr.env, ts.TopoServer(), ts.TabletManagerClient()) mu sync.Mutex ) @@ -1300,7 +1339,7 @@ func (ts *trafficSwitcher) cancelMigration(ctx context.Context, sm *workflow.Str ts.Logger().Errorf("Cancel migration failed:", err) } - sm.CancelMigration(ctx) + sm.CancelStreamMigrations(ctx) err = ts.ForAllTargets(func(target *workflow.MigrationTarget) error { query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s and workflow=%s", encodeString(target.GetPrimary().DbName()), encodeString(ts.WorkflowName())) @@ -1394,8 +1433,8 @@ func (ts *trafficSwitcher) createReverseVReplication(ctx context.Context) error Filter: filter, }) } - log.Infof("Creating reverse workflow vreplication stream on tablet %s: workflow %s, startPos %s", - source.GetPrimary().Alias, ts.ReverseWorkflowName(), target.Position) + log.Infof("Creating reverse workflow vreplication stream on tablet %s: workflow %s, startPos %s for target %s:%s, uid %d", + source.GetPrimary().Alias, ts.ReverseWorkflowName(), target.Position, ts.TargetKeyspaceName(), target.GetShard().ShardName(), uid) _, err := ts.VReplicationExec(ctx, source.GetPrimary().Alias, binlogplayer.CreateVReplicationState(ts.ReverseWorkflowName(), reverseBls, target.Position, binlogdatapb.VReplicationWorkflowState_Stopped, source.GetPrimary().DbName(), ts.workflowType, ts.workflowSubType)) @@ -1615,7 +1654,8 @@ func (ts *trafficSwitcher) deleteShardRoutingRules(ctx context.Context) error { func (ts *trafficSwitcher) startReverseVReplication(ctx context.Context) error { return ts.ForAllSources(func(source *workflow.MigrationSource) error { - query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s", encodeString(source.GetPrimary().DbName())) + query := fmt.Sprintf("update _vt.vreplication set state='Running', message='' where db_name=%s and workflow=%s", + encodeString(source.GetPrimary().DbName()), encodeString(ts.ReverseWorkflowName())) _, err := ts.VReplicationExec(ctx, source.GetPrimary().Alias, query) return err }) @@ -1734,23 +1774,33 @@ func getRenameFileName(tableName string) string { func (ts *trafficSwitcher) removeSourceTables(ctx context.Context, removalType workflow.TableRemovalType) error { err := ts.ForAllSources(func(source *workflow.MigrationSource) error { for _, tableName := range ts.Tables() { - query := fmt.Sprintf("drop table %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName))) + primaryDbName, err := sqlescape.EnsureEscaped(source.GetPrimary().DbName()) + if err != nil { + return err + } + tableNameEscaped, err := sqlescape.EnsureEscaped(tableName) + if err != nil { + return err + } + query := fmt.Sprintf("drop table %s.%s", primaryDbName, tableNameEscaped) if removalType == workflow.DropTable { ts.Logger().Infof("%s: Dropping table %s.%s\n", source.GetPrimary().String(), source.GetPrimary().DbName(), tableName) } else { - renameName := getRenameFileName(tableName) + renameName, err := sqlescape.EnsureEscaped(getRenameFileName(tableName)) + if err != nil { + return err + } ts.Logger().Infof("%s: Renaming table %s.%s to %s.%s\n", source.GetPrimary().String(), source.GetPrimary().DbName(), tableName, source.GetPrimary().DbName(), renameName) - query = fmt.Sprintf("rename table %s.%s TO %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName)), - sqlescape.EscapeID(sqlescape.UnescapeID(source.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(renameName))) + query = fmt.Sprintf("rename table %s.%s TO %s.%s", primaryDbName, tableNameEscaped, primaryDbName, renameName) } - _, err := ts.wr.ExecuteFetchAsDba(ctx, source.GetPrimary().Alias, query, 1, false, true) + _, err = ts.wr.tmc.ExecuteFetchAsDba(ctx, source.GetPrimary().Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ + Query: []byte(query), + MaxRows: 1, + ReloadSchema: true, + DisableForeignKeyChecks: true, + }) if err != nil { ts.Logger().Errorf("%s: Error removing table %s: %v", source.GetPrimary().String(), tableName, err) return err @@ -1845,12 +1895,23 @@ func (ts *trafficSwitcher) removeTargetTables(ctx context.Context) error { log.Infof("removeTargetTables") err := ts.ForAllTargets(func(target *workflow.MigrationTarget) error { for _, tableName := range ts.Tables() { - query := fmt.Sprintf("drop table %s.%s", - sqlescape.EscapeID(sqlescape.UnescapeID(target.GetPrimary().DbName())), - sqlescape.EscapeID(sqlescape.UnescapeID(tableName))) + primaryDbName, err := sqlescape.EnsureEscaped(target.GetPrimary().DbName()) + if err != nil { + return err + } + tableName, err := sqlescape.EnsureEscaped(tableName) + if err != nil { + return err + } + query := fmt.Sprintf("drop table %s.%s", primaryDbName, tableName) ts.Logger().Infof("%s: Dropping table %s.%s\n", target.GetPrimary().String(), target.GetPrimary().DbName(), tableName) - _, err := ts.wr.ExecuteFetchAsDba(ctx, target.GetPrimary().Alias, query, 1, false, true) + _, err = ts.wr.tmc.ExecuteFetchAsDba(ctx, target.GetPrimary().Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ + Query: []byte(query), + MaxRows: 1, + ReloadSchema: true, + DisableForeignKeyChecks: true, + }) if err != nil { ts.Logger().Errorf("%s: Error removing table %s: %v", target.GetPrimary().String(), tableName, err) @@ -1923,9 +1984,6 @@ func (ts *trafficSwitcher) addParticipatingTablesToKeyspace(ctx context.Context, if err := json2.Unmarshal([]byte(wrap), ks); err != nil { return err } - if err != nil { - return err - } for table, vtab := range ks.Tables { vschema.Tables[table] = vtab } @@ -2073,7 +2131,7 @@ func (ts *trafficSwitcher) getTargetSequenceMetadata(ctx context.Context) (map[s // error if any is seen. func (ts *trafficSwitcher) findSequenceUsageInKeyspace(vschema *vschemapb.Keyspace) (map[string]*sequenceMetadata, bool, error) { allFullyQualified := true - targets := maps2.Values(ts.Targets()) + targets := maps.Values(ts.Targets()) if len(targets) == 0 || targets[0].GetPrimary() == nil { // This should never happen return nil, false, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "no primary tablet found for target keyspace %s", ts.targetKeyspace) } @@ -2143,13 +2201,17 @@ func (ts *trafficSwitcher) initializeTargetSequences(ctx context.Context, sequen ) qr, terr := ts.wr.ExecuteFetchAsApp(ictx, primary.GetAlias(), true, query.Query, 1) if terr != nil || len(qr.Rows) != 1 { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s in order to initialize the backing sequence table: %v", - ts.targetKeyspace, sequenceMetadata.usingTableName, terr) + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s on tablet %s in order to initialize the backing sequence table: %v", + ts.targetKeyspace, sequenceMetadata.usingTableName, topoproto.TabletAliasString(primary.Alias), terr) } - maxID, terr := sqltypes.Proto3ToResult(qr).Rows[0][0].ToInt64() - if terr != nil { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s in order to initialize the backing sequence table: %v", - ts.targetKeyspace, sequenceMetadata.usingTableName, terr) + rawVal := sqltypes.Proto3ToResult(qr).Rows[0][0] + maxID := int64(0) + if !rawVal.IsNull() { // If it's NULL then there are no rows and 0 remains the max + maxID, terr = rawVal.ToInt64() + if terr != nil { + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to get the max used sequence value for target table %s.%s on tablet %s in order to initialize the backing sequence table: %v", + ts.targetKeyspace, sequenceMetadata.usingTableName, topoproto.TabletAliasString(primary.Alias), terr) + } } srMu.Lock() defer srMu.Unlock() diff --git a/go/vt/wrangler/traffic_switcher_env_test.go b/go/vt/wrangler/traffic_switcher_env_test.go index c8ec71dba96..3838ded0669 100644 --- a/go/vt/wrangler/traffic_switcher_env_test.go +++ b/go/vt/wrangler/traffic_switcher_env_test.go @@ -20,6 +20,8 @@ import ( "context" "fmt" "math/rand" + "strconv" + "strings" "sync" "testing" "time" @@ -40,6 +42,7 @@ import ( "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -56,7 +59,7 @@ import ( const ( streamInfoQuery = "select id, source, message, cell, tablet_types, workflow_type, workflow_sub_type, defer_secondary_keys from _vt.vreplication where workflow='%s' and db_name='vt_%s'" streamExtInfoQuery = "select id, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, time_heartbeat, time_throttled, component_throttled, message, tags, workflow_type, workflow_sub_type, defer_secondary_keys, rows_copied from _vt.vreplication where db_name = 'vt_%s' and workflow = '%s'" - copyStateQuery = "select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)" + copyStateQuery = "select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (%s) and id in (select max(id) from _vt.copy_state where vrepl_id in (%s) group by vrepl_id, table_name)" maxValForSequence = "select max(`id`) as maxval from `vt_%s`.`%s`" ) @@ -118,7 +121,7 @@ func newTestTableMigrater(ctx context.Context, t *testing.T) *testMigraterEnv { func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, targetShards []string, fmtQuery string) *testMigraterEnv { tme := &testMigraterEnv{} tme.ts = memorytopo.NewServer(ctx, "cell1", "cell2") - tme.wr = New(logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) + tme.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) tme.wr.sem = semaphore.NewWeighted(1) tme.sourceShards = sourceShards tme.targetShards = targetShards @@ -255,7 +258,7 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, "maxval", "int64", ), - "5", + "NULL", ), ) tme.tmeDB.AddQuery(fmt.Sprintf(maxValForSequence, "ks2", "t2"), @@ -271,7 +274,7 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, // Now tell the fakesqldb used by the global keyspace tablets to expect // the sequence management related queries against the target keyspace. gfdb.AddQuery( - sqlparser.BuildParsedQuery(sqlInitSequenceTable, sqlescape.EscapeID("vt_global"), sqlescape.EscapeID("t1_seq"), 6, 6, 6).Query, + sqlparser.BuildParsedQuery(sqlInitSequenceTable, sqlescape.EscapeID("vt_global"), sqlescape.EscapeID("t1_seq"), 1, 1, 1).Query, &sqltypes.Result{RowsAffected: 0}, ) gfdb.AddQuery( @@ -298,6 +301,7 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, for i, targetShard := range targetShards { var streamInfoRows []string var streamExtInfoRows []string + var vreplIDs []string for j, sourceShard := range sourceShards { bls := &binlogdatapb.BinlogSource{ Keyspace: "ks1", @@ -314,8 +318,10 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, } streamInfoRows = append(streamInfoRows, fmt.Sprintf("%d|%v||||1|0|0", j+1, bls)) streamExtInfoRows = append(streamExtInfoRows, fmt.Sprintf("%d|||||Running|vt_ks1|%d|%d|0|0||1||0", j+1, now, now)) - tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, j+1, j+1), noResult) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(j+1), 10)) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) tme.dbTargetClients[i].addInvariant(streamInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), @@ -332,6 +338,7 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, for i, sourceShard := range sourceShards { var streamInfoRows []string + var vreplIDs []string for j, targetShard := range targetShards { bls := &binlogdatapb.BinlogSource{ Keyspace: "ks2", @@ -347,8 +354,10 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, }, } streamInfoRows = append(streamInfoRows, fmt.Sprintf("%d|%v||||1|0|0", j+1, bls)) - tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, j+1, j+1), noResult) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(j+1), 10)) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) tme.dbSourceClients[i].addInvariant(reverseStreamInfoKs1, sqltypes.MakeTestResult(sqltypes.MakeTestFields( "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), @@ -373,7 +382,7 @@ func newTestTableMigraterCustom(ctx context.Context, t *testing.T, sourceShards, } // newTestTablePartialMigrater creates a test tablet migrater -// specifially for partial or shard by shard migrations. +// specifically for partial or shard by shard migrations. // The shards must be the same on the source and target, and we // must be moving a subset of them. // fmtQuery should be of the form: 'select a, b %s group by a'. @@ -382,7 +391,7 @@ func newTestTablePartialMigrater(ctx context.Context, t *testing.T, shards, shar require.Greater(t, len(shards), 1, "shard by shard migrations can only be done on sharded keyspaces") tme := &testMigraterEnv{} tme.ts = memorytopo.NewServer(ctx, "cell1", "cell2") - tme.wr = New(logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) + tme.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) tme.wr.sem = semaphore.NewWeighted(1) tme.sourceShards = shards tme.targetShards = shards @@ -467,9 +476,10 @@ func newTestTablePartialMigrater(ctx context.Context, t *testing.T, shards, shar now := time.Now().Unix() for i, shard := range shards { + var streamInfoRows []string + var streamExtInfoRows []string + var vreplIDs []string for _, shardToMove := range shardsToMove { - var streamInfoRows []string - var streamExtInfoRows []string if shardToMove == shard { bls := &binlogdatapb.BinlogSource{ Keyspace: "ks1", @@ -486,26 +496,31 @@ func newTestTablePartialMigrater(ctx context.Context, t *testing.T, shards, shar } streamInfoRows = append(streamInfoRows, fmt.Sprintf("%d|%v||||1|0|0", i+1, bls)) streamExtInfoRows = append(streamExtInfoRows, fmt.Sprintf("%d|||||Running|vt_ks1|%d|%d|0|0|||1||0", i+1, now, now)) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(i+1), 10)) } - tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, i+1, i+1), noResult) - tme.dbTargetClients[i].addInvariant(streamInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", - "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), - streamInfoRows...)) - tme.dbTargetClients[i].addInvariant(streamExtInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags|workflow_type|workflow_sub_type|defer_secondary_keys", - "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|int64|varchar|varchar|int64|int64|int64"), - streamExtInfoRows...)) - tme.dbTargetClients[i].addInvariant(reverseStreamExtInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags|workflow_type|workflow_sub_type|defer_secondary_keys", - "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|int64|varchar|varchar|int64|int64|int64"), - streamExtInfoRows...)) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) + log.Infof("Adding streamInfoKs2 invariant for shard %s, client %s,rows %q", + shard, tme.dbTargetClients[i].name, streamExtInfoRows) + tme.dbTargetClients[i].addInvariant(streamInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", + "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), + streamInfoRows...)) + tme.dbTargetClients[i].addInvariant(streamExtInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags|workflow_type|workflow_sub_type|defer_secondary_keys", + "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|int64|varchar|varchar|int64|int64|int64"), + streamExtInfoRows...)) + tme.dbTargetClients[i].addInvariant(reverseStreamExtInfoKs2, sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags|workflow_type|workflow_sub_type|defer_secondary_keys", + "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|int64|varchar|varchar|int64|int64|int64"), + streamExtInfoRows...)) } for i, shard := range shards { + var streamInfoRows []string + var vreplIDs []string for _, shardToMove := range shardsToMove { - var streamInfoRows []string if shardToMove == shard { bls := &binlogdatapb.BinlogSource{ Keyspace: "ks2", @@ -521,16 +536,17 @@ func newTestTablePartialMigrater(ctx context.Context, t *testing.T, shards, shar }, } streamInfoRows = append(streamInfoRows, fmt.Sprintf("%d|%v||||1|0|0", i+1, bls)) - tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, i+1, i+1), noResult) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(i+1), 10)) } - tme.dbSourceClients[i].addInvariant(reverseStreamInfoKs1, sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", - "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), - streamInfoRows...), - ) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) + tme.dbSourceClients[i].addInvariant(reverseStreamInfoKs1, sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", + "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), + streamInfoRows...), + ) } - tme.targetKeyspace = "ks2" return tme } @@ -538,7 +554,7 @@ func newTestTablePartialMigrater(ctx context.Context, t *testing.T, shards, shar func newTestShardMigrater(ctx context.Context, t *testing.T, sourceShards, targetShards []string) *testShardMigraterEnv { tme := &testShardMigraterEnv{} tme.ts = memorytopo.NewServer(ctx, "cell1", "cell2") - tme.wr = New(logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) + tme.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), tme.ts, tmclient.NewTabletManagerClient()) tme.sourceShards = sourceShards tme.targetShards = targetShards tme.tmeDB = fakesqldb.New(t) @@ -632,6 +648,7 @@ func newTestShardMigrater(ctx context.Context, t *testing.T, sourceShards, targe for i, targetShard := range targetShards { var rows, rowsRdOnly []string var streamExtInfoRows []string + var vreplIDs []string for j, sourceShard := range sourceShards { if !key.KeyRangeIntersect(tme.targetKeyRanges[i], tme.sourceKeyRanges[j]) { continue @@ -649,8 +666,10 @@ func newTestShardMigrater(ctx context.Context, t *testing.T, sourceShards, targe rows = append(rows, fmt.Sprintf("%d|%v||||1|0|0", j+1, bls)) rowsRdOnly = append(rows, fmt.Sprintf("%d|%v|||RDONLY|1|0|0", j+1, bls)) streamExtInfoRows = append(streamExtInfoRows, fmt.Sprintf("%d|||||Running|vt_ks1|%d|%d|0|0|||", j+1, now, now)) - tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, j+1, j+1), noResult) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(j+1), 10)) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbTargetClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) tme.dbTargetClients[i].addInvariant(streamInfoKs, sqltypes.MakeTestResult(sqltypes.MakeTestFields( "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), @@ -670,11 +689,14 @@ func newTestShardMigrater(ctx context.Context, t *testing.T, sourceShards, targe tme.targetKeyspace = "ks" for i, dbclient := range tme.dbSourceClients { var streamExtInfoRows []string + var vreplIDs []string dbclient.addInvariant(streamInfoKs, &sqltypes.Result{}) for j := range targetShards { streamExtInfoRows = append(streamExtInfoRows, fmt.Sprintf("%d|||||Running|vt_ks|%d|%d|0|0|||", j+1, now, now)) - tme.dbSourceClients[i].addInvariant(fmt.Sprintf(copyStateQuery, j+1, j+1), noResult) + vreplIDs = append(vreplIDs, strconv.FormatInt(int64(j+1), 10)) } + vreplIDsJoined := strings.Join(vreplIDs, ", ") + tme.dbSourceClients[i].addInvariant(fmt.Sprintf(copyStateQuery, vreplIDsJoined, vreplIDsJoined), noResult) tme.dbSourceClients[i].addInvariant(streamExtInfoKs, sqltypes.MakeTestResult(sqltypes.MakeTestFields( "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags", "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|varchar|varchar|varchar"), @@ -862,7 +884,7 @@ func (tme *testShardMigraterEnv) expectStartReverseVReplication() { // NOTE: this is not a faithful reproduction of what should happen. // The ids returned are not accurate. for _, dbclient := range tme.dbSourceClients { - dbclient.addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + dbclient.addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) dbclient.addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) dbclient.addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) dbclient.addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) @@ -891,7 +913,7 @@ func (tme *testShardMigraterEnv) expectDeleteTargetVReplication() { } } -func (tme *testShardMigraterEnv) expectCancelMigration() { +func (tme *testShardMigraterEnv) expectCancelStreamMigrations() { for _, dbclient := range tme.dbTargetClients { dbclient.addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test'", &sqltypes.Result{}, nil) } diff --git a/go/vt/wrangler/traffic_switcher_test.go b/go/vt/wrangler/traffic_switcher_test.go index 6c97758ad48..e1ae1ce908f 100644 --- a/go/vt/wrangler/traffic_switcher_test.go +++ b/go/vt/wrangler/traffic_switcher_test.go @@ -286,7 +286,7 @@ func TestTableMigrateMainflow(t *testing.T) { verifyQueries(t, tme.allDBClients) //------------------------------------------------------------------------------------------------------------------- - // Test SwitchWrites cancelation on failure. + // Test SwitchWrites cancellation on failure. tme.expectNoPreviousJournals() // Switch all the reads first. @@ -434,11 +434,11 @@ func TestTableMigrateMainflow(t *testing.T) { createJournals() startReverseVReplication := func() { - tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1'", resultid34, nil) + tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) - tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1'", resultid34, nil) + tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) @@ -607,7 +607,7 @@ func TestShardMigrateMainflow(t *testing.T) { verifyQueries(t, tme.allDBClients) //------------------------------------------------------------------------------------------------------------------- - // Test SwitchWrites cancelation on failure. + // Test SwitchWrites cancellation on failure. tme.expectNoPreviousJournals() // Switch all the reads first. @@ -731,11 +731,11 @@ func TestShardMigrateMainflow(t *testing.T) { createJournals() startReverseVReplication := func() { - tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) - tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) @@ -949,8 +949,10 @@ func testTableMigrateOneToMany(t *testing.T, keepData, keepRoutingRules bool) { tme.dbTargetClients[0].addQuery("select 1 from _vt.vreplication where db_name='vt_ks2' and workflow='test' and message!='FROZEN'", &sqltypes.Result{}, nil) tme.dbTargetClients[1].addQuery("select 1 from _vt.vreplication where db_name='vt_ks2' and workflow='test' and message!='FROZEN'", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1' and workflow = 'test_reverse'", &sqltypes.Result{}, nil) + tme.tmeDB.AddQuery("SET SESSION foreign_key_checks = OFF", &sqltypes.Result{}) tme.tmeDB.AddQuery(fmt.Sprintf("rename table `vt_ks1`.`t1` TO `vt_ks1`.`%s`", getRenameFileName("t1")), &sqltypes.Result{}) tme.tmeDB.AddQuery(fmt.Sprintf("rename table `vt_ks1`.`t2` TO `vt_ks1`.`%s`", getRenameFileName("t2")), &sqltypes.Result{}) + tme.tmeDB.AddQuery("SET SESSION foreign_key_checks = ON", &sqltypes.Result{}) tme.dbTargetClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks2' and workflow = 'test'", &sqltypes.Result{}, nil) // tme.dbTargetClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks2' and workflow = 'test'", &sqltypes.Result{}, nil) } @@ -1009,8 +1011,8 @@ func TestTableMigrateOneToManyDryRun(t *testing.T) { "\tKeyspace ks1, Shard 0 at Position MariaDB/5-456-892", "Wait for VReplication on stopped streams to catchup for up to 1s", "Create reverse replication workflow test_reverse", - "Create journal entries on source databases", "The following sequence backing tables used by tables being moved will be initialized: t1_seq,t2_seq", + "Create journal entries on source databases", "Enable writes on keyspace ks2 tables [t1,t2]", "Switch routing from keyspace ks1 to keyspace ks2", "Routing rules for tables [t1,t2] will be updated", @@ -1233,11 +1235,11 @@ func TestTableMigrateJournalExists(t *testing.T) { tme.dbSourceClients[1].addQueryRE(journal2, &sqltypes.Result{}, nil) // mi.startReverseVReplication - tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1'", resultid34, nil) + tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) - tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1'", resultid34, nil) + tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks1' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) @@ -1312,11 +1314,11 @@ func TestShardMigrateJournalExists(t *testing.T) { tme.dbSourceClients[1].addQueryRE(journal2, &sqltypes.Result{}, nil) // mi.startReverseVReplication - tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) - tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) @@ -1411,7 +1413,7 @@ func TestTableMigrateCancelDryRun(t *testing.T) { want := []string{ "Lock keyspace ks1", "Lock keyspace ks2", - "Cancel stream migrations as requested", + "Cancel migration as requested", "Unlock keyspace ks2", "Unlock keyspace ks1", } @@ -2043,11 +2045,11 @@ func TestShardMigrateNoAvailableTabletsForReverseReplication(t *testing.T) { createJournals() startReverseVReplication := func() { - tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[0].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[0].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[0].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) - tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks'", resultid34, nil) + tme.dbSourceClients[1].addQuery("select id from _vt.vreplication where db_name = 'vt_ks' and workflow = 'test_reverse'", resultid34, nil) tme.dbSourceClients[1].addQuery("update _vt.vreplication set state = 'Running', message = '' where id in (3, 4)", &sqltypes.Result{}, nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 3", runningResult(3), nil) tme.dbSourceClients[1].addQuery("select * from _vt.vreplication where id = 4", runningResult(4), nil) diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go index 2d6e49b73d7..70dee1261fc 100644 --- a/go/vt/wrangler/vdiff.go +++ b/go/vt/wrangler/vdiff.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql/sqlerror" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" @@ -92,6 +93,7 @@ type RowDiff struct { // vdiff contains the metadata for performing vdiff for one workflow. type vdiff struct { + env *vtenv.Environment ts *trafficSwitcher sourceCell string targetCell string @@ -142,6 +144,9 @@ type tableDiffer struct { // source Primitive and targetPrimitive are used for streaming sourcePrimitive engine.Primitive targetPrimitive engine.Primitive + + collationEnv *collations.Environment + parser *sqlparser.Parser } // shardStreamer streams rows from one shard. This works for @@ -207,6 +212,7 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflowName, sou } // Initialize vdiff df := &vdiff{ + env: wr.env, ts: ts, sourceCell: sourceCell, targetCell: targetCell, @@ -241,7 +247,7 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflowName, sou if err != nil { return nil, vterrors.Wrap(err, "GetSchema") } - if err = df.buildVDiffPlan(ctx, oneFilter, schm, df.tables); err != nil { + if err = df.buildVDiffPlan(oneFilter, schm, df.tables); err != nil { return nil, vterrors.Wrap(err, "buildVDiffPlan") } @@ -369,11 +375,11 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflowName, sou diffReports[table] = dr } if format == "json" { - json, err := json.MarshalIndent(diffReports, "", "") + j, err := json.MarshalIndent(diffReports, "", "") if err != nil { wr.Logger().Printf("Error converting report to json: %v", err.Error()) } - jsonOutput += string(json) + jsonOutput += string(j) wr.logger.Printf("%s", jsonOutput) } else { for table, dr := range diffReports { @@ -443,7 +449,7 @@ func (df *vdiff) diffTable(ctx context.Context, wr *Wrangler, table string, td * } // buildVDiffPlan builds all the differs. -func (df *vdiff) buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter, schm *tabletmanagerdatapb.SchemaDefinition, tablesToInclude []string) error { +func (df *vdiff) buildVDiffPlan(filter *binlogdatapb.Filter, schm *tabletmanagerdatapb.SchemaDefinition, tablesToInclude []string) error { df.differs = make(map[string]*tableDiffer) for _, table := range schm.TableDefinitions { rule, err := vreplication.MatchTable(table.Name, filter) @@ -485,8 +491,8 @@ func (df *vdiff) buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter // findPKs identifies PKs, determines any collations to be used for // them, and removes them from the columns used for data comparison. -func findPKs(table *tabletmanagerdatapb.TableDefinition, targetSelect *sqlparser.Select, td *tableDiffer) (sqlparser.OrderBy, error) { - columnCollations, err := getColumnCollations(table) +func findPKs(env *vtenv.Environment, table *tabletmanagerdatapb.TableDefinition, targetSelect *sqlparser.Select, td *tableDiffer) (sqlparser.OrderBy, error) { + columnCollations, err := getColumnCollations(env, table) if err != nil { return nil, err } @@ -528,11 +534,10 @@ func findPKs(table *tabletmanagerdatapb.TableDefinition, targetSelect *sqlparser } // getColumnCollations determines the proper collation to use for each -// column in the table definition leveraging MySQL's collation inheritence +// column in the table definition leveraging MySQL's collation inheritance // rules. -func getColumnCollations(table *tabletmanagerdatapb.TableDefinition) (map[string]collations.ID, error) { - collationEnv := collations.Local() - createstmt, err := sqlparser.Parse(table.Schema) +func getColumnCollations(venv *vtenv.Environment, table *tabletmanagerdatapb.TableDefinition) (map[string]collations.ID, error) { + createstmt, err := venv.Parser().Parse(table.Schema) if err != nil { return nil, err } @@ -540,36 +545,37 @@ func getColumnCollations(table *tabletmanagerdatapb.TableDefinition) (map[string if !ok { return nil, vterrors.Wrapf(err, "invalid table schema %s for table %s", table.Schema, table.Name) } - tableschema, err := schemadiff.NewCreateTableEntity(createtable) + env := schemadiff.NewEnv(venv, venv.CollationEnv().DefaultConnectionCharset()) + tableschema, err := schemadiff.NewCreateTableEntity(env, createtable) if err != nil { return nil, vterrors.Wrapf(err, "invalid table schema %s for table %s", table.Schema, table.Name) } tableCharset := tableschema.GetCharset() tableCollation := tableschema.GetCollation() // If no explicit collation is specified for the column then we need - // to walk the inheritence tree. + // to walk the inheritance tree. getColumnCollation := func(column *sqlparser.ColumnDefinition) collations.ID { // If there's an explicit collation listed then use that. if column.Type.Options.Collate != "" { - return collationEnv.LookupByName(strings.ToLower(column.Type.Options.Collate)) + return env.CollationEnv().LookupByName(strings.ToLower(column.Type.Options.Collate)) } // If the column has a charset listed then the default collation // for that charset is used. if column.Type.Charset.Name != "" { - return collationEnv.DefaultCollationForCharset(strings.ToLower(column.Type.Charset.Name)) + return env.CollationEnv().DefaultCollationForCharset(strings.ToLower(column.Type.Charset.Name)) } // If the table has an explicit collation listed then use that. if tableCollation != "" { - return collationEnv.LookupByName(strings.ToLower(tableCollation)) + return env.CollationEnv().LookupByName(strings.ToLower(tableCollation)) } // If the table has a charset listed then use the default collation // for that charset. if tableCharset != "" { - return collationEnv.DefaultCollationForCharset(strings.ToLower(tableCharset)) + return env.CollationEnv().DefaultCollationForCharset(strings.ToLower(tableCharset)) } // The table is using the global default charset and collation and - // we inherite that. - return collations.Default() + // we inherit that. + return env.CollationEnv().DefaultConnectionCharset() } columnCollations := make(map[string]collations.ID) @@ -646,7 +652,7 @@ func getColumnNameForSelectExpr(selectExpression sqlparser.SelectExpr) (string, // buildTablePlan builds one tableDiffer. func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, query string) (*tableDiffer, error) { - statement, err := sqlparser.Parse(query) + statement, err := df.env.Parser().Parse(query) if err != nil { return nil, err } @@ -655,7 +661,9 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer return nil, fmt.Errorf("unexpected: %v", sqlparser.String(statement)) } td := &tableDiffer{ - targetTable: table.Name, + targetTable: table.Name, + collationEnv: df.env.CollationEnv(), + parser: df.env.Parser(), } sourceSelect := &sqlparser.Select{} targetSelect := &sqlparser.Select{} @@ -672,14 +680,14 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer } case *sqlparser.AliasedExpr: var targetCol *sqlparser.ColName - if !selExpr.As.IsEmpty() { - targetCol = &sqlparser.ColName{Name: selExpr.As} - } else { + if selExpr.As.IsEmpty() { if colAs, ok := selExpr.Expr.(*sqlparser.ColName); ok { targetCol = colAs } else { return nil, fmt.Errorf("expression needs an alias: %v", sqlparser.String(selExpr)) } + } else { + targetCol = &sqlparser.ColName{Name: selExpr.As} } // If the input was "select a as b", then source will use "a" and target will use "b". sourceSelect.SelectExprs = append(sourceSelect.SelectExprs, selExpr) @@ -696,7 +704,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer aggregates = append(aggregates, engine.NewAggregateParam( /*opcode*/ opcode.AggregateSum, /*offset*/ len(sourceSelect.SelectExprs)-1, - /*alias*/ "")) + /*alias*/ "", df.env.CollationEnv())) } } default: @@ -735,7 +743,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer }, } - orderby, err := findPKs(table, targetSelect, td) + orderby, err := findPKs(df.env, table, targetSelect, td) if err != nil { return nil, err } @@ -751,31 +759,32 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer td.sourceExpression = sqlparser.String(sourceSelect) td.targetExpression = sqlparser.String(targetSelect) - td.sourcePrimitive = newMergeSorter(df.sources, td.comparePKs) - td.targetPrimitive = newMergeSorter(df.targets, td.comparePKs) + td.sourcePrimitive = newMergeSorter(df.sources, td.comparePKs, df.env.CollationEnv()) + td.targetPrimitive = newMergeSorter(df.targets, td.comparePKs, df.env.CollationEnv()) // If there were aggregate expressions, we have to re-aggregate // the results, which engine.OrderedAggregate can do. if len(aggregates) != 0 { td.sourcePrimitive = &engine.OrderedAggregate{ - Aggregates: aggregates, - GroupByKeys: pkColsToGroupByParams(td.pkCols), - Input: td.sourcePrimitive, + Aggregates: aggregates, + GroupByKeys: pkColsToGroupByParams(td.pkCols, td.collationEnv), + Input: td.sourcePrimitive, + CollationEnv: df.env.CollationEnv(), } } return td, nil } -func pkColsToGroupByParams(pkCols []int) []*engine.GroupByParams { +func pkColsToGroupByParams(pkCols []int, collationEnv *collations.Environment) []*engine.GroupByParams { var res []*engine.GroupByParams for _, col := range pkCols { - res = append(res, &engine.GroupByParams{KeyCol: col, WeightStringCol: -1, Type: evalengine.UnknownType()}) + res = append(res, &engine.GroupByParams{KeyCol: col, WeightStringCol: -1, Type: evalengine.Type{}, CollationEnv: collationEnv}) } return res } // newMergeSorter creates an engine.MergeSort based on the shard streamers and pk columns. -func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compareColInfo) *engine.MergeSort { +func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compareColInfo, collationEnv *collations.Environment) *engine.MergeSort { prims := make([]engine.StreamExecutor, 0, len(participants)) for _, participant := range participants { prims = append(prims, participant) @@ -784,11 +793,11 @@ func newMergeSorter(participants map[string]*shardStreamer, comparePKs []compare for _, cpk := range comparePKs { weightStringCol := -1 // if the collation is nil or unknown, use binary collation to compare as bytes - t := evalengine.Type{Type: sqltypes.Unknown, Coll: collations.CollationBinaryID} + var collation collations.ID = collations.CollationBinaryID if cpk.collation != collations.Unknown { - t.Coll = cpk.collation + collation = cpk.collation } - ob = append(ob, evalengine.OrderByParams{Col: cpk.colIndex, WeightStringCol: weightStringCol, Type: t}) + ob = append(ob, evalengine.OrderByParams{Col: cpk.colIndex, WeightStringCol: weightStringCol, Type: evalengine.NewType(sqltypes.Unknown, collation), CollationEnv: collationEnv}) } return &engine.MergeSort{ Primitives: prims, @@ -996,7 +1005,7 @@ func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, particip }() } -// syncTargets fast-forwards the vreplication to the source snapshot positons +// syncTargets fast-forwards the vreplication to the source snapshot positions // and waits for the selected tablets to catch up to that point. func (df *vdiff) syncTargets(ctx context.Context, filteredReplicationWaitTime time.Duration) error { waitCtx, cancel := context.WithTimeout(ctx, filteredReplicationWaitTime) @@ -1309,7 +1318,7 @@ func (td *tableDiffer) compare(sourceRow, targetRow []sqltypes.Value, cols []com if col.collation == collations.Unknown { collationID = collations.CollationBinaryID } - c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], collationID) + c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], td.collationEnv, collationID) if err != nil { return 0, err } @@ -1323,7 +1332,7 @@ func (td *tableDiffer) compare(sourceRow, targetRow []sqltypes.Value, cols []com func (td *tableDiffer) genRowDiff(queryStmt string, row []sqltypes.Value, debug, onlyPks bool) (*RowDiff, error) { drp := &RowDiff{} drp.Row = make(map[string]sqltypes.Value) - statement, err := sqlparser.Parse(queryStmt) + statement, err := td.parser.Parse(queryStmt) if err != nil { return nil, err } diff --git a/go/vt/wrangler/vdiff_env_test.go b/go/vt/wrangler/vdiff_env_test.go index 01f3a3a0f9e..ff0f97db769 100644 --- a/go/vt/wrangler/vdiff_env_test.go +++ b/go/vt/wrangler/vdiff_env_test.go @@ -24,12 +24,12 @@ import ( "testing" "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -78,7 +78,7 @@ func newTestVDiffEnv(t testing.TB, ctx context.Context, sourceShards, targetShar tabletType: topodatapb.TabletType_REPLICA, tmc: newTestVDiffTMClient(), } - env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + env.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) // Generate a unique dialer name. dialerName := fmt.Sprintf("VDiffTest-%s-%d", t.Name(), rand.Intn(1000000000)) diff --git a/go/vt/wrangler/vdiff_test.go b/go/vt/wrangler/vdiff_test.go index 28422b6cd4d..1b0071ebed7 100644 --- a/go/vt/wrangler/vdiff_test.go +++ b/go/vt/wrangler/vdiff_test.go @@ -23,8 +23,6 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/vtgate/evalengine" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -34,11 +32,14 @@ import ( tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/engine/opcode" ) func TestVDiffPlanSuccess(t *testing.T) { + collationEnv := collations.MySQL8() + parser := sqlparser.NewTestParser() schm := &tabletmanagerdatapb.SchemaDefinition{ TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ Name: "t1", @@ -97,8 +98,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -114,8 +117,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -131,8 +136,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -148,8 +155,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{1, collations.Unknown, true}}, pkCols: []int{1}, selectPks: []int{1}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -165,8 +174,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // non-pk text column. @@ -183,8 +194,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // non-pk text column, different order. @@ -201,8 +214,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{1, collations.Unknown, true}}, pkCols: []int{1}, selectPks: []int{1}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // pk text column. @@ -215,12 +230,14 @@ func TestVDiffPlanSuccess(t *testing.T) { targetTable: "pktext", sourceExpression: "select textcol, c2 from pktext order by textcol asc", targetExpression: "select textcol, c2 from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Default(), true}, {1, collations.Unknown, false}}, - comparePKs: []compareColInfo{{0, collations.Default(), true}}, + compareCols: []compareColInfo{{0, collationEnv.DefaultConnectionCharset(), true}, {1, collations.Unknown, false}}, + comparePKs: []compareColInfo{{0, collationEnv.DefaultConnectionCharset(), true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Default(), false}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Default(), false}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // pk text column, different order. @@ -233,12 +250,14 @@ func TestVDiffPlanSuccess(t *testing.T) { targetTable: "pktext", sourceExpression: "select c2, textcol from pktext order by textcol asc", targetExpression: "select c2, textcol from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Unknown, false}, {1, collations.Default(), true}}, - comparePKs: []compareColInfo{{1, collations.Default(), true}}, + compareCols: []compareColInfo{{0, collations.Unknown, false}, {1, collationEnv.DefaultConnectionCharset(), true}}, + comparePKs: []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), true}}, pkCols: []int{1}, selectPks: []int{1}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Default(), false}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Default(), false}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // text column as expression. @@ -251,12 +270,14 @@ func TestVDiffPlanSuccess(t *testing.T) { targetTable: "pktext", sourceExpression: "select c2, a + b as textcol from pktext order by textcol asc", targetExpression: "select c2, textcol from pktext order by textcol asc", - compareCols: []compareColInfo{{0, collations.Unknown, false}, {1, collations.Default(), true}}, - comparePKs: []compareColInfo{{1, collations.Default(), true}}, + compareCols: []compareColInfo{{0, collations.Unknown, false}, {1, collationEnv.DefaultConnectionCharset(), true}}, + comparePKs: []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), true}}, pkCols: []int{1}, selectPks: []int{1}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Default(), false}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collations.Default(), false}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{1, collationEnv.DefaultConnectionCharset(), false}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -271,8 +292,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}, {1, collations.Unknown, true}}, pkCols: []int{0, 1}, selectPks: []int{0, 1}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}, {1, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}, {1, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}, {1, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}, {1, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // in_keyrange @@ -289,8 +312,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // in_keyrange on RHS of AND. @@ -308,8 +333,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // in_keyrange on LHS of AND. @@ -327,8 +354,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // in_keyrange on cascaded AND expression @@ -346,8 +375,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // in_keyrange parenthesized @@ -365,8 +396,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // group by @@ -383,8 +416,10 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { // aggregations @@ -403,13 +438,16 @@ func TestVDiffPlanSuccess(t *testing.T) { selectPks: []int{0}, sourcePrimitive: &engine.OrderedAggregate{ Aggregates: []*engine.AggregateParams{ - engine.NewAggregateParam(opcode.AggregateSum, 2, ""), - engine.NewAggregateParam(opcode.AggregateSum, 3, ""), + engine.NewAggregateParam(opcode.AggregateSum, 2, "", collationEnv), + engine.NewAggregateParam(opcode.AggregateSum, 3, "", collationEnv), }, - GroupByKeys: []*engine.GroupByParams{{KeyCol: 0, WeightStringCol: -1, Type: evalengine.UnknownType()}}, - Input: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + GroupByKeys: []*engine.GroupByParams{{KeyCol: 0, WeightStringCol: -1, CollationEnv: collations.MySQL8()}}, + Input: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + CollationEnv: collationEnv, }, - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }, { input: &binlogdatapb.Rule{ @@ -425,16 +463,18 @@ func TestVDiffPlanSuccess(t *testing.T) { comparePKs: []compareColInfo{{0, collations.Unknown, true}}, pkCols: []int{0}, selectPks: []int{0}, - sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), - targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}), + sourcePrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + targetPrimitive: newMergeSorter(nil, []compareColInfo{{0, collations.Unknown, true}}, collationEnv), + collationEnv: collationEnv, + parser: parser, }, }} for _, tcase := range testcases { t.Run(tcase.input.Filter, func(t *testing.T) { filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} - df := &vdiff{sourceTimeZone: tcase.sourceTimeZone, targetTimeZone: "UTC"} - err := df.buildVDiffPlan(context.Background(), filter, schm, nil) + df := &vdiff{env: vtenv.NewTestEnv(), sourceTimeZone: tcase.sourceTimeZone, targetTimeZone: "UTC"} + err := df.buildVDiffPlan(filter, schm, nil) require.NoError(t, err, tcase.input) require.Equal(t, 1, len(df.differs), tcase.input) assert.Equal(t, tcase.td, df.differs[tcase.table], tcase.input) @@ -488,8 +528,8 @@ func TestVDiffPlanFailure(t *testing.T) { }} for _, tcase := range testcases { filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} - df := &vdiff{} - err := df.buildVDiffPlan(context.Background(), filter, schm, nil) + df := &vdiff{env: vtenv.NewTestEnv()} + err := df.buildVDiffPlan(filter, schm, nil) assert.EqualError(t, err, tcase.err, tcase.input) } } @@ -1079,9 +1119,10 @@ func TestVDiffFindPKs(t *testing.T) { }, } + env := vtenv.NewTestEnv() for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - _, err := findPKs(tc.table, tc.targetSelect, tc.tdIn) + _, err := findPKs(env, tc.table, tc.targetSelect, tc.tdIn) require.NoError(t, err) require.EqualValues(t, tc.tdOut, tc.tdIn) }) @@ -1118,30 +1159,30 @@ func TestVDiffPlanInclude(t *testing.T) { }}, } - df := &vdiff{} + df := &vdiff{env: vtenv.NewTestEnv()} rule := &binlogdatapb.Rule{ Match: "/.*", } filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{rule}} var err error - err = df.buildVDiffPlan(context.Background(), filter, schm, []string{"t2"}) + err = df.buildVDiffPlan(filter, schm, []string{"t2"}) require.NoError(t, err) require.Equal(t, 1, len(df.differs)) - err = df.buildVDiffPlan(context.Background(), filter, schm, []string{"t2", "t3"}) + err = df.buildVDiffPlan(filter, schm, []string{"t2", "t3"}) require.NoError(t, err) require.Equal(t, 2, len(df.differs)) - err = df.buildVDiffPlan(context.Background(), filter, schm, []string{"t1", "t2", "t3"}) + err = df.buildVDiffPlan(filter, schm, []string{"t1", "t2", "t3"}) require.NoError(t, err) require.Equal(t, 3, len(df.differs)) - err = df.buildVDiffPlan(context.Background(), filter, schm, []string{"t1", "t2", "t3", "t4"}) + err = df.buildVDiffPlan(filter, schm, []string{"t1", "t2", "t3", "t4"}) require.NoError(t, err) require.Equal(t, 4, len(df.differs)) - err = df.buildVDiffPlan(context.Background(), filter, schm, []string{"t1", "t2", "t3", "t5"}) + err = df.buildVDiffPlan(filter, schm, []string{"t1", "t2", "t3", "t5"}) require.Error(t, err) } func TestGetColumnCollations(t *testing.T) { - collationEnv := collations.Local() + collationEnv := collations.MySQL8() tests := []struct { name string table *tabletmanagerdatapb.TableDefinition @@ -1162,7 +1203,7 @@ func TestGetColumnCollations(t *testing.T) { }, want: map[string]collations.ID{ "c1": collations.Unknown, - "name": collations.Default(), + "name": collationEnv.DefaultConnectionCharset(), }, }, { @@ -1171,8 +1212,8 @@ func TestGetColumnCollations(t *testing.T) { Schema: "create table t1 (c1 varchar(10), name varchar(10), primary key(c1))", }, want: map[string]collations.ID{ - "c1": collations.Default(), - "name": collations.Default(), + "c1": collationEnv.DefaultConnectionCharset(), + "name": collationEnv.DefaultConnectionCharset(), }, }, { @@ -1182,7 +1223,7 @@ func TestGetColumnCollations(t *testing.T) { }, want: map[string]collations.ID{ "c1": collations.Unknown, - "name": collations.Default(), + "name": collationEnv.DefaultConnectionCharset(), }, }, { @@ -1237,9 +1278,10 @@ func TestGetColumnCollations(t *testing.T) { }, }, } + env := vtenv.NewTestEnv() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := getColumnCollations(tt.table) + got, err := getColumnCollations(env, tt.table) if (err != nil) != tt.wantErr { t.Errorf("getColumnCollations() error = %v, wantErr = %t", err, tt.wantErr) return diff --git a/go/vt/wrangler/vexec.go b/go/vt/wrangler/vexec.go index 0734fa7b593..2c279c5c6cf 100644 --- a/go/vt/wrangler/vexec.go +++ b/go/vt/wrangler/vexec.go @@ -44,6 +44,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" workflow2 "vitess.io/vitess/go/vt/vtctl/workflow" vtctldvexec "vitess.io/vitess/go/vt/vtctl/workflow/vexec" // renamed to avoid a collision with the vexec struct in this package ) @@ -158,7 +159,7 @@ func (wr *Wrangler) VExec(ctx context.Context, workflow, keyspace, query string, if wr.VExecFunc != nil { return wr.VExecFunc(ctx, workflow, keyspace, query, dryRun) } - results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, dryRun) + results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, dryRun, nil) retResults := make(map[*topo.TabletInfo]*sqltypes.Result) for tablet, result := range results { retResults[tablet] = sqltypes.Proto3ToResult(result) @@ -167,10 +168,13 @@ func (wr *Wrangler) VExec(ctx context.Context, workflow, keyspace, query string, } // runVexec is the main function that runs a dry or wet execution of 'query' on backend shards. -func (wr *Wrangler) runVexec(ctx context.Context, workflow, keyspace, query string, callback func(context.Context, *topo.TabletInfo) (*querypb.QueryResult, error), dryRun bool) (map[*topo.TabletInfo]*querypb.QueryResult, error) { +func (wr *Wrangler) runVexec(ctx context.Context, workflow, keyspace, query string, + callback func(context.Context, *topo.TabletInfo) (*querypb.QueryResult, error), + dryRun bool, shards []string) (map[*topo.TabletInfo]*querypb.QueryResult, error) { + vx := newVExec(ctx, workflow, keyspace, query, wr) - if err := vx.getPrimaries(); err != nil { + if err := vx.getPrimaries(shards); err != nil { return nil, err } if callback == nil { // Using legacy SQL query path @@ -275,7 +279,7 @@ func (vx *vexec) execCallback(callback func(context.Context, *topo.TabletInfo) ( // parseQuery parses the input query func (vx *vexec) parseQuery() (err error) { - if vx.stmt, err = sqlparser.Parse(vx.query); err != nil { + if vx.stmt, err = vx.wr.SQLParser().Parse(vx.query); err != nil { return err } if vx.tableName, err = extractTableName(vx.stmt); err != nil { @@ -285,15 +289,13 @@ func (vx *vexec) parseQuery() (err error) { } // getPrimaries identifies primary tablet for all shards relevant to our keyspace -func (vx *vexec) getPrimaries() error { +func (vx *vexec) getPrimaries(shards []string) error { var err error - shards, err := vx.wr.ts.GetShardNames(vx.ctx, vx.keyspace) + shards, err = vx.wr.getShardSubset(vx.ctx, vx.keyspace, shards) if err != nil { return err } - if len(shards) == 0 { - return fmt.Errorf("no shards found in keyspace %s", vx.keyspace) - } + var allPrimaries []*topo.TabletInfo var primary *topo.TabletInfo for _, shard := range shards { @@ -340,10 +342,11 @@ func (wr *Wrangler) convertQueryResultToSQLTypesResult(results map[*topo.TabletI // rpcReq is an optional argument for any actions that use the new RPC path. Today // that is only the update action. When using the SQL interface this is ignored and // you can pass nil. -func (wr *Wrangler) WorkflowAction(ctx context.Context, workflow, keyspace, action string, dryRun bool, rpcReq any) (map[*topo.TabletInfo]*sqltypes.Result, error) { +func (wr *Wrangler) WorkflowAction(ctx context.Context, workflow, keyspace, action string, dryRun bool, rpcReq any, + shards []string) (map[*topo.TabletInfo]*sqltypes.Result, error) { switch action { case "show": - replStatus, err := wr.ShowWorkflow(ctx, workflow, keyspace) + replStatus, err := wr.ShowWorkflow(ctx, workflow, keyspace, shards) if err != nil { return nil, err } @@ -358,7 +361,7 @@ func (wr *Wrangler) WorkflowAction(ctx context.Context, workflow, keyspace, acti return nil, err default: } - results, err := wr.execWorkflowAction(ctx, workflow, keyspace, action, dryRun, rpcReq) + results, err := wr.execWorkflowAction(ctx, workflow, keyspace, action, dryRun, rpcReq, shards) if err != nil { return nil, err } @@ -390,7 +393,7 @@ func (wr *Wrangler) getWorkflowActionQuery(action string) (string, error) { // canRestartWorkflow validates that, for an atomic copy workflow, none of the streams are still in the copy phase. // Since we copy all tables in a single snapshot, we cannot restart a workflow which broke before all tables were copied. func (wr *Wrangler) canRestartWorkflow(ctx context.Context, workflow, keyspace string) error { - res, err := wr.ShowWorkflow(ctx, workflow, keyspace) + res, err := wr.ShowWorkflow(ctx, workflow, keyspace, nil) if err != nil { return err } @@ -409,7 +412,8 @@ func (wr *Wrangler) canRestartWorkflow(ctx context.Context, workflow, keyspace s return nil } -func (wr *Wrangler) execWorkflowAction(ctx context.Context, workflow, keyspace, action string, dryRun bool, rpcReq any) (map[*topo.TabletInfo]*querypb.QueryResult, error) { +func (wr *Wrangler) execWorkflowAction(ctx context.Context, workflow, keyspace, action string, dryRun bool, rpcReq any, + shards []string) (map[*topo.TabletInfo]*querypb.QueryResult, error) { var callback func(context.Context, *topo.TabletInfo) (*querypb.QueryResult, error) = nil query, err := wr.getWorkflowActionQuery(action) if err != nil { @@ -452,7 +456,7 @@ func (wr *Wrangler) execWorkflowAction(ctx context.Context, workflow, keyspace, wr.Logger().Printf("On the following tablets in the %s keyspace for workflow %s:\n", keyspace, workflow) vx := newVExec(ctx, workflow, keyspace, "", wr) - if err := vx.getPrimaries(); err != nil { + if err := vx.getPrimaries(shards); err != nil { return nil, err } tablets := vx.primaries @@ -475,13 +479,15 @@ func (wr *Wrangler) execWorkflowAction(ctx context.Context, workflow, keyspace, } } - return wr.runVexec(ctx, workflow, keyspace, query, callback, dryRun) + return wr.runVexec(ctx, workflow, keyspace, query, callback, dryRun, shards) } -// WorkflowTagAction sets or clears the tags for a workflow in a keyspace +// WorkflowTagAction sets or clears the tags for a workflow in a keyspace. func (wr *Wrangler) WorkflowTagAction(ctx context.Context, keyspace string, workflow string, tags string) (map[*topo.TabletInfo]*sqltypes.Result, error) { + // A WHERE clause with the correct workflow name is automatically added + // to the query later on in vexec.addDefaultWheres(). query := fmt.Sprintf("update _vt.vreplication set tags = %s", encodeString(tags)) - results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, false) + results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, false, nil) return wr.convertQueryResultToSQLTypesResult(results), err } @@ -582,7 +588,7 @@ type ReplicationStatus struct { deferSecondaryKeys bool } -func (wr *Wrangler) getReplicationStatusFromRow(ctx context.Context, row sqltypes.RowNamedValues, primary *topo.TabletInfo) (*ReplicationStatus, string, error) { +func (wr *Wrangler) getReplicationStatusFromRow(ctx context.Context, row sqltypes.RowNamedValues, copyStates []copyState, primary *topo.TabletInfo) (*ReplicationStatus, string, error) { var err error var id int32 var timeUpdated, transactionTimestamp, timeHeartbeat, timeThrottled int64 @@ -661,9 +667,6 @@ func (wr *Wrangler) getReplicationStatusFromRow(ctx context.Context, row sqltype workflowSubType, _ = row.ToInt32("workflow_sub_type") deferSecondaryKeys, _ = row.ToBool("defer_secondary_keys") rowsCopied = row.AsInt64("rows_copied", 0) - if err != nil { - return nil, "", err - } status := &ReplicationStatus{ Shard: primary.Shard, @@ -688,21 +691,18 @@ func (wr *Wrangler) getReplicationStatusFromRow(ctx context.Context, row sqltype deferSecondaryKeys: deferSecondaryKeys, RowsCopied: rowsCopied, } - status.CopyState, err = wr.getCopyState(ctx, primary, id) - if err != nil { - return nil, "", err - } + status.CopyState = copyStates status.State = updateState(message, binlogdatapb.VReplicationWorkflowState(binlogdatapb.VReplicationWorkflowState_value[state]), status.CopyState, timeUpdated) return status, bls.Keyspace, nil } -func (wr *Wrangler) getStreams(ctx context.Context, workflow, keyspace string) (*ReplicationStatusResult, error) { +func (wr *Wrangler) getStreams(ctx context.Context, workflow, keyspace string, shards []string) (*ReplicationStatusResult, error) { var rsr ReplicationStatusResult rsr.ShardStatuses = make(map[string]*ShardReplicationStatus) rsr.Workflow = workflow - var results map[*topo.TabletInfo]*querypb.QueryResult - query := `select + + const query = `select id, source, pos, @@ -722,7 +722,7 @@ func (wr *Wrangler) getStreams(ctx context.Context, workflow, keyspace string) ( defer_secondary_keys, rows_copied from _vt.vreplication` - results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, false) + results, err := wr.runVexec(ctx, workflow, keyspace, query, nil, false, shards) if err != nil { return nil, err } @@ -739,8 +739,27 @@ func (wr *Wrangler) getStreams(ctx context.Context, workflow, keyspace string) ( if len(nqr.Rows) == 0 { continue } + // Get all copy states for the shard. + vreplIDs := make([]int64, len(nqr.Rows)) + for i, row := range nqr.Rows { + vreplID, err := row.ToInt64("id") + if err != nil { + return nil, err + } + vreplIDs[i] = vreplID + } + copyStatesByVReplID, err := wr.getCopyStates(ctx, primary, vreplIDs) + if err != nil { + return nil, err + } for _, row := range nqr.Rows { - status, sk, err := wr.getReplicationStatusFromRow(ctx, row, primary) + vreplID, err := row.ToInt64("id") + if err != nil { + return nil, err + } + + copyStates := copyStatesByVReplID[vreplID] + status, sk, err := wr.getReplicationStatusFromRow(ctx, row, copyStates, primary) if err != nil { return nil, err } @@ -761,7 +780,7 @@ func (wr *Wrangler) getStreams(ctx context.Context, workflow, keyspace string) ( // Note: this is done here only because golang does // not currently support setting json tags in proto // declarations so that I could request it always be - // ommitted from marshalled JSON output: + // omitted from marshalled JSON output: // https://github.com/golang/protobuf/issues/52 status.Bls.OnDdl = 0 } @@ -838,7 +857,7 @@ func (wr *Wrangler) ListAllWorkflows(ctx context.Context, keyspace string, activ where = " where state <> 'Stopped'" } query := "select distinct workflow from _vt.vreplication" + where - vx := vtctldvexec.NewVExec(keyspace, "", wr.ts, wr.tmc) + vx := vtctldvexec.NewVExec(keyspace, "", wr.ts, wr.tmc, wr.SQLParser()) results, err := vx.QueryContext(ctx, query) if err != nil { return nil, err @@ -861,8 +880,9 @@ func (wr *Wrangler) ListAllWorkflows(ctx context.Context, keyspace string, activ } // ShowWorkflow will return all of the relevant replication related information for the given workflow. -func (wr *Wrangler) ShowWorkflow(ctx context.Context, workflow, keyspace string) (*ReplicationStatusResult, error) { - replStatus, err := wr.getStreams(ctx, workflow, keyspace) +// If shardSubset is nil, then all shards will be queried. +func (wr *Wrangler) ShowWorkflow(ctx context.Context, workflow, keyspace string, shardSubset []string) (*ReplicationStatusResult, error) { + replStatus, err := wr.getStreams(ctx, workflow, keyspace, shardSubset) if err != nil { return nil, err } @@ -902,27 +922,41 @@ func (wr *Wrangler) printWorkflowList(keyspace string, workflows []string) { wr.Logger().Printf("Following workflow(s) found in keyspace %s: %v\n", keyspace, list) } -func (wr *Wrangler) getCopyState(ctx context.Context, tablet *topo.TabletInfo, id int32) ([]copyState, error) { - var cs []copyState - query := fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d and id in (select max(id) from _vt.copy_state where vrepl_id = %d group by vrepl_id, table_name)", - id, id) - qr, err := wr.VReplicationExec(ctx, tablet.Alias, query) +func (wr *Wrangler) getCopyStates(ctx context.Context, tablet *topo.TabletInfo, ids []int64) (map[int64][]copyState, error) { + idsBV, err := sqltypes.BuildBindVariable(ids) + if err != nil { + return nil, err + } + query, err := sqlparser.ParseAndBind("select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in %a and id in (select max(id) from _vt.copy_state where vrepl_id in %a group by vrepl_id, table_name)", + idsBV, idsBV) + if err != nil { + return nil, err + } + qr, err := wr.tmc.VReplicationExec(ctx, tablet.Tablet, query) if err != nil { return nil, err } result := sqltypes.Proto3ToResult(qr) - if result != nil { - for _, row := range result.Rows { - // These fields are varbinary, but close enough - table := row[0].ToString() - lastPK := row[1].ToString() - copyState := copyState{ - Table: table, - LastPK: lastPK, - } - cs = append(cs, copyState) + if result == nil { + cs := make(map[int64][]copyState) + return cs, nil + } + + cs := make(map[int64][]copyState, len(result.Rows)) + for _, row := range result.Rows { + vreplID, err := row[0].ToInt64() + if err != nil { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to cast vrepl_id to int64: %v", err) + } + // These fields are varbinary, but close enough + table := row[1].ToString() + lastPK := row[2].ToString() + copyState := copyState{ + Table: table, + LastPK: lastPK, } + cs[vreplID] = append(cs[vreplID], copyState) } return cs, nil diff --git a/go/vt/wrangler/vexec_plan.go b/go/vt/wrangler/vexec_plan.go index 5b68d9ada5f..e9ea1daf5c3 100644 --- a/go/vt/wrangler/vexec_plan.go +++ b/go/vt/wrangler/vexec_plan.go @@ -21,12 +21,13 @@ import ( "fmt" "strings" + "github.com/olekukonko/tablewriter" + "vitess.io/vitess/go/vt/log" - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" - "github.com/olekukonko/tablewriter" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) // vexecPlan contains the final query to be sent to the tablets @@ -83,7 +84,7 @@ func (p vreplicationPlanner) exec( return qr, nil } func (p vreplicationPlanner) dryRun(ctx context.Context) error { - rsr, err := p.vx.wr.getStreams(p.vx.ctx, p.vx.workflow, p.vx.keyspace) + rsr, err := p.vx.wr.getStreams(p.vx.ctx, p.vx.workflow, p.vx.keyspace, nil) if err != nil { return err } @@ -119,9 +120,9 @@ const ( func extractTableName(stmt sqlparser.Statement) (string, error) { switch stmt := stmt.(type) { case *sqlparser.Update: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Delete: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Insert: return sqlparser.String(stmt.Table), nil case *sqlparser.Select: @@ -259,7 +260,7 @@ func (vx *vexec) buildUpdatePlan(ctx context.Context, planner vexecPlanner, upd } } if templates := plannerParams.updateTemplates; len(templates) > 0 { - match, err := sqlparser.QueryMatchesTemplates(vx.query, templates) + match, err := vx.wr.env.Parser().QueryMatchesTemplates(vx.query, templates) if err != nil { return nil, err } @@ -311,7 +312,7 @@ func (vx *vexec) buildInsertPlan(ctx context.Context, planner vexecPlanner, ins return nil, fmt.Errorf("query not supported by vexec: %s", sqlparser.String(ins)) } if len(templates) > 0 { - match, err := sqlparser.QueryMatchesTemplates(vx.query, templates) + match, err := vx.wr.env.Parser().QueryMatchesTemplates(vx.query, templates) if err != nil { return nil, err } diff --git a/go/vt/wrangler/vexec_test.go b/go/vt/wrangler/vexec_test.go index ead2be6a56f..27efbe61a9f 100644 --- a/go/vt/wrangler/vexec_test.go +++ b/go/vt/wrangler/vexec_test.go @@ -18,6 +18,7 @@ package wrangler import ( "context" + _ "embed" "fmt" "regexp" "sort" @@ -33,6 +34,16 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtenv" +) + +var ( + //go:embed testdata/show-all-shards.json + want_show_all_shards string + //go:embed testdata/show-dash80.json + want_show_dash_80 string + //go:embed testdata/show-80dash.json + want_show_80_dash string ) func TestVExec(t *testing.T) { @@ -41,13 +52,13 @@ func TestVExec(t *testing.T) { workflow := "wrWorkflow" keyspace := "target" query := "update _vt.vreplication set state = 'Running'" - env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, "", nil, time.Now().Unix()) + env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, nil, time.Now().Unix()) defer env.close() var logger = logutil.NewMemoryLogger() - wr := New(logger, env.topoServ, env.tmc) + wr := New(vtenv.NewTestEnv(), logger, env.topoServ, env.tmc) vx := newVExec(ctx, workflow, keyspace, query, wr) - err := vx.getPrimaries() + err := vx.getPrimaries(nil) require.Nil(t, err) primaries := vx.primaries require.NotNil(t, primaries) @@ -78,7 +89,7 @@ func TestVExec(t *testing.T) { vx.plannedQuery = plan.parsedQuery.Query vx.exec() - res, err := wr.getStreams(ctx, workflow, keyspace) + res, err := wr.getStreams(ctx, workflow, keyspace, nil) require.NoError(t, err) require.Less(t, res.MaxVReplicationLag, int64(3 /*seconds*/)) // lag should be very small @@ -94,7 +105,7 @@ func TestVExec(t *testing.T) { result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( "id|source|message|cell|tablet_types|workflow_type|workflow_sub_type|defer_secondary_keys", "int64|varchar|varchar|varchar|varchar|int64|int64|int64"), - "1|keyspace:\"source\" shard:\"0\" filter:{rules:{match:\"t1\"}}||||0|0|0", + "1|keyspace:\"source\" shard:\"0\" filter:{rules:{match:\"t1\"} rules:{match:\"t2\"}}||||0|0|0", ) testCases = append(testCases, &TestCase{ name: "select", @@ -163,10 +174,12 @@ func TestVExec(t *testing.T) { | TABLET | ID | BINLOGSOURCE | STATE | DBNAME | CURRENT GTID | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+ | -80/zone1-0000000200 | 1 | keyspace:"source" shard:"0" | Copying | vt_target | 14b68925-696a-11ea-aee7-fec597a91f5e:1-3 | -| | | filter:{rules:{match:"t1"}} | | | | +| | | filter:{rules:{match:"t1"} | | | | +| | | rules:{match:"t2"}} | | | | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+ | 80-/zone1-0000000210 | 1 | keyspace:"source" shard:"0" | Copying | vt_target | 14b68925-696a-11ea-aee7-fec597a91f5e:1-3 | -| | | filter:{rules:{match:"t1"}} | | | | +| | | filter:{rules:{match:"t1"} | | | | +| | | rules:{match:"t2"}} | | | | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+`, } require.Equal(t, strings.Join(dryRunResults, "\n")+"\n\n\n\n\n", logger.String()) @@ -186,140 +199,52 @@ func TestWorkflowListStreams(t *testing.T) { defer cancel() workflow := "wrWorkflow" keyspace := "target" - env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, "", nil, 1234) + env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, nil, 1234) defer env.close() logger := logutil.NewMemoryLogger() - wr := New(logger, env.topoServ, env.tmc) + wr := New(vtenv.NewTestEnv(), logger, env.topoServ, env.tmc) - _, err := wr.WorkflowAction(ctx, workflow, keyspace, "listall", false, nil) + _, err := wr.WorkflowAction(ctx, workflow, keyspace, "listall", false, nil, nil) require.NoError(t, err) - _, err = wr.WorkflowAction(ctx, workflow, "badks", "show", false, nil) + _, err = wr.WorkflowAction(ctx, workflow, "badks", "show", false, nil, nil) require.Errorf(t, err, "node doesn't exist: keyspaces/badks/shards") - _, err = wr.WorkflowAction(ctx, "badwf", keyspace, "show", false, nil) + _, err = wr.WorkflowAction(ctx, "badwf", keyspace, "show", false, nil, nil) require.Errorf(t, err, "no streams found for workflow badwf in keyspace target") logger.Clear() - _, err = wr.WorkflowAction(ctx, workflow, keyspace, "show", false, nil) - require.NoError(t, err) - want := `{ - "Workflow": "wrWorkflow", - "SourceLocation": { - "Keyspace": "source", - "Shards": [ - "0" - ] - }, - "TargetLocation": { - "Keyspace": "target", - "Shards": [ - "-80", - "80-" - ] - }, - "MaxVReplicationLag": 0, - "MaxVReplicationTransactionLag": 0, - "Frozen": false, - "ShardStatuses": { - "-80/zone1-0000000200": { - "PrimaryReplicationStatuses": [ - { - "Shard": "-80", - "Tablet": "zone1-0000000200", - "ID": 1, - "Bls": { - "keyspace": "source", - "shard": "0", - "filter": { - "rules": [ - { - "match": "t1" - } - ] - } - }, - "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", - "StopPos": "", - "State": "Copying", - "DBName": "vt_target", - "TransactionTimestamp": 0, - "TimeUpdated": 1234, - "TimeHeartbeat": 1234, - "TimeThrottled": 0, - "ComponentThrottled": "", - "Message": "", - "Tags": "", - "WorkflowType": "Materialize", - "WorkflowSubType": "None", - "CopyState": [ - { - "Table": "t1", - "LastPK": "pk1" - } - ], - "RowsCopied": 1000 - } - ], - "TabletControls": null, - "PrimaryIsServing": true - }, - "80-/zone1-0000000210": { - "PrimaryReplicationStatuses": [ - { - "Shard": "80-", - "Tablet": "zone1-0000000210", - "ID": 1, - "Bls": { - "keyspace": "source", - "shard": "0", - "filter": { - "rules": [ - { - "match": "t1" - } - ] - } - }, - "Pos": "14b68925-696a-11ea-aee7-fec597a91f5e:1-3", - "StopPos": "", - "State": "Copying", - "DBName": "vt_target", - "TransactionTimestamp": 0, - "TimeUpdated": 1234, - "TimeHeartbeat": 1234, - "TimeThrottled": 0, - "ComponentThrottled": "", - "Message": "", - "Tags": "", - "WorkflowType": "Materialize", - "WorkflowSubType": "None", - "CopyState": [ - { - "Table": "t1", - "LastPK": "pk1" - } - ], - "RowsCopied": 1000 - } - ], - "TabletControls": null, - "PrimaryIsServing": true - } - }, - "SourceTimeZone": "", - "TargetTimeZone": "" -} + var testCases = []struct { + shards []string + want string + }{ + {[]string{"-80", "80-"}, want_show_all_shards}, + {[]string{"-80"}, want_show_dash_80}, + {[]string{"80-"}, want_show_80_dash}, + } + scrub := func(s string) string { + s = strings.ReplaceAll(s, "\t", "") + s = strings.ReplaceAll(s, "\n", "") + s = strings.ReplaceAll(s, " ", "") + return s + } + for _, testCase := range testCases { + t.Run(fmt.Sprintf("%v", testCase.shards), func(t *testing.T) { + want := scrub(testCase.want) + _, err = wr.WorkflowAction(ctx, workflow, keyspace, "show", false, nil, testCase.shards) + require.NoError(t, err) + got := scrub(logger.String()) + // MaxVReplicationLag needs to be reset. This can't be determinable in this kind of a test because + // time.Now() is constantly shifting. + re := regexp.MustCompile(`"MaxVReplicationLag":\d+`) + got = re.ReplaceAllLiteralString(got, `"MaxVReplicationLag":0`) + re = regexp.MustCompile(`"MaxVReplicationTransactionLag":\d+`) + got = re.ReplaceAllLiteralString(got, `"MaxVReplicationTransactionLag":0`) + require.Equal(t, want, got) + logger.Clear() + }) + } -` - got := logger.String() - // MaxVReplicationLag needs to be reset. This can't be determinable in this kind of a test because time.Now() is constantly shifting. - re := regexp.MustCompile(`"MaxVReplicationLag": \d+`) - got = re.ReplaceAllLiteralString(got, `"MaxVReplicationLag": 0`) - re = regexp.MustCompile(`"MaxVReplicationTransactionLag": \d+`) - got = re.ReplaceAllLiteralString(got, `"MaxVReplicationTransactionLag": 0`) - require.Equal(t, want, got) - - results, err := wr.execWorkflowAction(ctx, workflow, keyspace, "stop", false, nil) + results, err := wr.execWorkflowAction(ctx, workflow, keyspace, "stop", false, nil, nil) require.Nil(t, err) // convert map to list and sort it for comparison @@ -333,7 +258,7 @@ func TestWorkflowListStreams(t *testing.T) { require.ElementsMatch(t, wantResults, gotResults) logger.Clear() - results, err = wr.execWorkflowAction(ctx, workflow, keyspace, "stop", true, nil) + results, err = wr.execWorkflowAction(ctx, workflow, keyspace, "stop", true, nil, nil) require.Nil(t, err) require.Equal(t, "map[]", fmt.Sprintf("%v", results)) dryRunResult := `Query: update _vt.vreplication set state = 'Stopped' where db_name = 'vt_target' and workflow = 'wrWorkflow' @@ -344,10 +269,12 @@ will be run on the following streams in keyspace target for workflow wrWorkflow: | TABLET | ID | BINLOGSOURCE | STATE | DBNAME | CURRENT GTID | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+ | -80/zone1-0000000200 | 1 | keyspace:"source" shard:"0" | Copying | vt_target | 14b68925-696a-11ea-aee7-fec597a91f5e:1-3 | -| | | filter:{rules:{match:"t1"}} | | | | +| | | filter:{rules:{match:"t1"} | | | | +| | | rules:{match:"t2"}} | | | | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+ | 80-/zone1-0000000210 | 1 | keyspace:"source" shard:"0" | Copying | vt_target | 14b68925-696a-11ea-aee7-fec597a91f5e:1-3 | -| | | filter:{rules:{match:"t1"}} | | | | +| | | filter:{rules:{match:"t1"} | | | | +| | | rules:{match:"t2"}} | | | | +----------------------+----+--------------------------------+---------+-----------+------------------------------------------+ @@ -362,10 +289,10 @@ func TestWorkflowListAll(t *testing.T) { defer cancel() keyspace := "target" workflow := "wrWorkflow" - env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, "", nil, 0) + env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, nil, 0) defer env.close() logger := logutil.NewMemoryLogger() - wr := New(logger, env.topoServ, env.tmc) + wr := New(vtenv.NewTestEnv(), logger, env.topoServ, env.tmc) workflows, err := wr.ListAllWorkflows(ctx, keyspace, true) require.Nil(t, err) @@ -383,10 +310,10 @@ func TestVExecValidations(t *testing.T) { workflow := "wf" keyspace := "ks" query := "" - env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, "", nil, 0) + env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, nil, 0) defer env.close() - wr := New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + wr := New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) vx := newVExec(ctx, workflow, keyspace, query, wr) @@ -469,10 +396,10 @@ func TestWorkflowUpdate(t *testing.T) { defer cancel() workflow := "wrWorkflow" keyspace := "target" - env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, "", nil, 1234) + env := newWranglerTestEnv(t, ctx, []string{"0"}, []string{"-80", "80-"}, nil, 1234) defer env.close() logger := logutil.NewMemoryLogger() - wr := New(logger, env.topoServ, env.tmc) + wr := New(vtenv.NewTestEnv(), logger, env.topoServ, env.tmc) nullSlice := textutil.SimulatedNullStringSlice // Used to represent a non-provided value nullOnDDL := binlogdatapb.OnDDLAction(textutil.SimulatedNullInt) // Used to represent a non-provided value tests := []struct { @@ -528,7 +455,7 @@ func TestWorkflowUpdate(t *testing.T) { OnDdl: tcase.onDDL, } - _, err := wr.WorkflowAction(ctx, workflow, keyspace, "update", true, rpcReq) + _, err := wr.WorkflowAction(ctx, workflow, keyspace, "update", true, rpcReq, nil) if tcase.wantErr != "" { require.Error(t, err) require.Equal(t, err.Error(), tcase.wantErr) diff --git a/go/vt/wrangler/workflow.go b/go/vt/wrangler/workflow.go index d9dbcee7291..6862f5f4d3f 100644 --- a/go/vt/wrangler/workflow.go +++ b/go/vt/wrangler/workflow.go @@ -77,6 +77,11 @@ type VReplicationWorkflowParams struct { // MoveTables only NoRoutingRules bool + + // Only these shards will be expected to participate in the workflow. Expects user to know what they are doing + // and provide the correct set of shards associated with the workflow. This is for reducing latency for workflows + // that only use a small set of shards in a keyspace with a large number of shards. + ShardSubset []string } // VReplicationWorkflow stores various internal objects for a workflow @@ -101,6 +106,7 @@ func (vrw *VReplicationWorkflow) String() string { func (wr *Wrangler) NewVReplicationWorkflow(ctx context.Context, workflowType VReplicationWorkflowType, params *VReplicationWorkflowParams) (*VReplicationWorkflow, error) { + wr.WorkflowParams = params log.Infof("NewVReplicationWorkflow with params %+v", params) vrw := &VReplicationWorkflow{wr: wr, ctx: ctx, params: params, workflowType: workflowType} ts, ws, err := wr.getWorkflowState(ctx, params.TargetKeyspace, params.Workflow) @@ -187,6 +193,7 @@ func (vrw *VReplicationWorkflow) stateAsString(ws *workflow.State) string { // at the shard level, so reads are effectively switched on the // shard when writes are switched. if len(ws.ShardsAlreadySwitched) > 0 && len(ws.ShardsNotYetSwitched) > 0 { + sort.Strings(ws.ShardsAlreadySwitched) stateInfo = append(stateInfo, fmt.Sprintf("Reads partially switched, for shards: %s", strings.Join(ws.ShardsAlreadySwitched, ","))) stateInfo = append(stateInfo, fmt.Sprintf("Writes partially switched, for shards: %s", strings.Join(ws.ShardsAlreadySwitched, ","))) } else { @@ -253,12 +260,19 @@ func NewWorkflowError(tablet string, id int32, description string) *WorkflowErro return wfErr } +func (vrw *VReplicationWorkflow) IsPartialMigration() bool { + if vrw.ws == nil { + return false + } + return vrw.ws.IsPartialMigration +} + // GetStreamCount returns a count of total streams and of streams that have started processing -func (vrw *VReplicationWorkflow) GetStreamCount() (int64, int64, []*WorkflowError, error) { +func (vrw *VReplicationWorkflow) GetStreamCount(shards []string) (int64, int64, []*WorkflowError, error) { var err error var workflowErrors []*WorkflowError var total, started int64 - res, err := vrw.wr.ShowWorkflow(vrw.ctx, vrw.params.Workflow, vrw.params.TargetKeyspace) + res, err := vrw.wr.ShowWorkflow(vrw.ctx, vrw.params.Workflow, vrw.params.TargetKeyspace, shards) if err != nil { return 0, 0, nil, err } @@ -525,7 +539,7 @@ func (vrw *VReplicationWorkflow) canSwitch(keyspace, workflowName string) (reaso return "", nil } log.Infof("state:%s, direction %d, switched %t", vrw.CachedState(), vrw.params.Direction, ws.WritesSwitched) - result, err := vrw.wr.getStreams(vrw.ctx, workflowName, keyspace) + result, err := vrw.wr.getStreams(vrw.ctx, workflowName, keyspace, vrw.params.ShardSubset) if err != nil { return "", err } diff --git a/go/vt/wrangler/workflow_test.go b/go/vt/wrangler/workflow_test.go index be3589a3f58..978883d5457 100644 --- a/go/vt/wrangler/workflow_test.go +++ b/go/vt/wrangler/workflow_test.go @@ -108,19 +108,19 @@ func expectCanSwitchQueries(t *testing.T, tme *testMigraterEnv, keyspace, state "id|source|pos|stop_pos|max_replication_lag|state|db_name|time_updated|transaction_timestamp|time_heartbeat|time_throttled|component_throttled|message|tags", "int64|varchar|int64|int64|int64|varchar|varchar|int64|int64|int64|int64|varchar|varchar|varchar"), row) - copyStateResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "table|lastpk", - "varchar|varchar"), - "t1|pk1", + copyStateResult := sqltypes.MakeTestResult( + sqltypes.MakeTestFields("vrepl_id|table|lastpk", "int64|varchar|varchar"), + "1|t1|pk1", + "1|t2|pk2", ) for _, db := range tme.dbTargetClients { db.addInvariant(streamExtInfoKs2, replicationResult) if state == "Copying" { - db.addInvariant(fmt.Sprintf(copyStateQuery, 1, 1), copyStateResult) + db.addInvariant(fmt.Sprintf(copyStateQuery, "1", "1"), copyStateResult) } else { - db.addInvariant(fmt.Sprintf(copyStateQuery, 1, 1), noResult) + db.addInvariant(fmt.Sprintf(copyStateQuery, "1", "1"), noResult) } } } @@ -378,6 +378,85 @@ func TestPartialMoveTables(t *testing.T) { require.Equal(t, WorkflowStateNotSwitched, wf.CurrentState()) } +// TestPartialMoveTablesShardSubset is a version of TestPartialMoveTables which uses the --shards option. +func TestPartialMoveTablesShardSubset(t *testing.T) { + ctx := context.Background() + shards := []string{"-40", "40-80", "80-c0", "c0-"} + shardsToMove := shards[0:2] + otherShards := shards[2:] + p := &VReplicationWorkflowParams{ + Workflow: "test", + WorkflowType: MoveTablesWorkflow, + SourceKeyspace: "ks1", + SourceShards: shardsToMove, // shard by shard + TargetShards: shardsToMove, // shard by shard + TargetKeyspace: "ks2", + Tables: "t1,t2", + Cells: "cell1,cell2", + TabletTypes: "REPLICA,RDONLY,PRIMARY", + Timeout: DefaultActionTimeout, + MaxAllowedTransactionLagSeconds: defaultMaxAllowedTransactionLagSeconds, + OnDDL: binlogdatapb.OnDDLAction_STOP.String(), + } + tme := newTestTablePartialMigrater(ctx, t, shards, shardsToMove, "select * %s") + defer tme.stopTablets(t) + + // Save some unrelated shard routing rules to be sure that + // they don't interfere in any way. + srr, err := tme.ts.GetShardRoutingRules(ctx) + require.NoError(t, err) + srr.Rules = append(srr.Rules, []*vschema.ShardRoutingRule{ + { + FromKeyspace: "wut", + Shard: "40-80", + ToKeyspace: "bloop", + }, + { + FromKeyspace: "haylo", + Shard: "-80", + ToKeyspace: "blarg", + }, + }...) + err = tme.ts.SaveShardRoutingRules(ctx, srr) + require.NoError(t, err) + + // Providing an incorrect shard should result in the workflow not being found. + p.ShardSubset = otherShards + wf, err := tme.wr.NewVReplicationWorkflow(ctx, MoveTablesWorkflow, p) + require.NoError(t, err) + require.Nil(t, wf.ts) + + p.ShardSubset = shardsToMove + wf, err = tme.wr.NewVReplicationWorkflow(ctx, MoveTablesWorkflow, p) + require.NoError(t, err) + require.NotNil(t, wf) + require.Equal(t, WorkflowStateNotSwitched, wf.CurrentState()) + require.True(t, wf.ts.isPartialMigration, "expected partial shard migration") + + srr, err = tme.ts.GetShardRoutingRules(ctx) + require.NoError(t, err) + srr.Rules = append(srr.Rules, &vschema.ShardRoutingRule{ + FromKeyspace: "ks2", + Shard: "80-", + ToKeyspace: "ks1", + }) + err = tme.ts.SaveShardRoutingRules(ctx, srr) + require.NoError(t, err) + + tme.expectNoPreviousJournals() + expectMoveTablesQueries(t, tme, p) + tme.expectNoPreviousJournals() + wf.params.ShardSubset = shardsToMove + require.NoError(t, testSwitchForward(t, wf)) + require.Equal(t, "Reads partially switched, for shards: -40,40-80. Writes partially switched, for shards: -40,40-80", wf.CurrentState()) + require.NoError(t, err) + + tme.expectNoPreviousJournals() + tme.expectNoPreviousReverseJournals() + require.NoError(t, testReverse(t, wf)) + require.Equal(t, WorkflowStateNotSwitched, wf.CurrentState()) +} + func validateRoutingRuleCount(ctx context.Context, t *testing.T, ts *topo.Server, cnt int) { rr, err := ts.GetRoutingRules(ctx) require.NoError(t, err) @@ -792,12 +871,14 @@ func expectMoveTablesQueries(t *testing.T, tme *testMigraterEnv, params *VReplic tme.dbSourceClients[0].addInvariant("select pos, state, message from _vt.vreplication where id=2", state) tme.dbSourceClients[1].addInvariant("select pos, state, message from _vt.vreplication where id=1", state) tme.dbSourceClients[1].addInvariant("select pos, state, message from _vt.vreplication where id=2", state) + tme.tmeDB.AddQuery("SET SESSION foreign_key_checks = OFF", &sqltypes.Result{}) tme.tmeDB.AddQuery("USE `vt_ks1`", noResult) tme.tmeDB.AddQuery("USE `vt_ks2`", noResult) tme.tmeDB.AddQuery("drop table `vt_ks1`.`t1`", noResult) tme.tmeDB.AddQuery("drop table `vt_ks1`.`t2`", noResult) tme.tmeDB.AddQuery("drop table `vt_ks2`.`t1`", noResult) tme.tmeDB.AddQuery("drop table `vt_ks2`.`t2`", noResult) + tme.tmeDB.AddQuery("SET SESSION foreign_key_checks = ON", &sqltypes.Result{}) tme.tmeDB.AddQuery("update _vt.vreplication set message='Picked source tablet: cell:\"cell1\" uid:10 ' where id=1", noResult) tme.tmeDB.AddQuery("lock tables `t1` read,`t2` read", &sqltypes.Result{}) tme.tmeDB.AddQuery("select distinct table_name from _vt.copy_state cs, _vt.vreplication vr where vr.id = cs.vrepl_id and vr.id = 1", noResult) diff --git a/go/vt/wrangler/wrangler.go b/go/vt/wrangler/wrangler.go index dbb046a36b3..ee18643cc78 100644 --- a/go/vt/wrangler/wrangler.go +++ b/go/vt/wrangler/wrangler.go @@ -25,8 +25,10 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/tmclient" vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" @@ -47,6 +49,7 @@ var ( // Multiple go routines can use the same Wrangler at the same time, // provided they want to share the same logger / topo server / lock timeout. type Wrangler struct { + env *vtenv.Environment logger logutil.Logger ts *topo.Server tmc tmclient.TabletManagerClient @@ -56,16 +59,18 @@ type Wrangler struct { // DO NOT USE in production code. VExecFunc func(ctx context.Context, workflow, keyspace, query string, dryRun bool) (map[*topo.TabletInfo]*sqltypes.Result, error) // Limt the number of concurrent background goroutines if needed. - sem *semaphore.Weighted + sem *semaphore.Weighted + WorkflowParams *VReplicationWorkflowParams } // New creates a new Wrangler object. -func New(logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClient) *Wrangler { +func New(env *vtenv.Environment, logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClient) *Wrangler { return &Wrangler{ + env: env, logger: logger, ts: ts, tmc: tmc, - vtctld: grpcvtctldserver.NewVtctldServer(ts), + vtctld: grpcvtctldserver.NewVtctldServer(env, ts), sourceTs: ts, } } @@ -74,6 +79,7 @@ func New(logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClien // in production. func NewTestWrangler(logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClient) *Wrangler { return &Wrangler{ + env: vtenv.NewTestEnv(), logger: logger, ts: ts, tmc: tmc, @@ -109,3 +115,8 @@ func (wr *Wrangler) SetLogger(logger logutil.Logger) { func (wr *Wrangler) Logger() logutil.Logger { return wr.logger } + +// SQLParser returns the parser this wrangler is using. +func (wr *Wrangler) SQLParser() *sqlparser.Parser { + return wr.env.Parser() +} diff --git a/go/vt/wrangler/wrangler_env_test.go b/go/vt/wrangler/wrangler_env_test.go index 4dd5e342c35..4bc20c0aad0 100644 --- a/go/vt/wrangler/wrangler_env_test.go +++ b/go/vt/wrangler/wrangler_env_test.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtenv" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" "vitess.io/vitess/go/vt/vttablet/tabletconn" @@ -60,7 +61,7 @@ type testWranglerEnv struct { //---------------------------------------------- // testWranglerEnv -func newWranglerTestEnv(t testing.TB, ctx context.Context, sourceShards, targetShards []string, query string, positions map[string]string, timeUpdated int64) *testWranglerEnv { +func newWranglerTestEnv(t testing.TB, ctx context.Context, sourceShards, targetShards []string, positions map[string]string, timeUpdated int64) *testWranglerEnv { env := &testWranglerEnv{ workflow: "wrWorkflow", topoServ: memorytopo.NewServer(ctx, "zone1"), @@ -68,7 +69,7 @@ func newWranglerTestEnv(t testing.TB, ctx context.Context, sourceShards, targetS tabletType: topodatapb.TabletType_REPLICA, tmc: newTestWranglerTMClient(), } - env.wr = New(logutil.NewConsoleLogger(), env.topoServ, env.tmc) + env.wr = New(vtenv.NewTestEnv(), logutil.NewConsoleLogger(), env.topoServ, env.tmc) env.tmc.tablets = make(map[int]*testWranglerTablet) // Generate a unique dialer name. @@ -106,10 +107,16 @@ func newWranglerTestEnv(t testing.TB, ctx context.Context, sourceShards, targetS Keyspace: "source", Shard: sourceShard, Filter: &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "t1", - Filter: query, - }}, + Rules: []*binlogdatapb.Rule{ + { + Match: "t1", + Filter: "", + }, + { + Match: "t2", + Filter: "", + }, + }, }, } rows = append(rows, fmt.Sprintf("%d|%v||||0|0|0", j+1, bls)) @@ -163,13 +170,13 @@ func newWranglerTestEnv(t testing.TB, ctx context.Context, sourceShards, targetS ) env.tmc.setVRResults(primary.tablet, "select distinct workflow from _vt.vreplication where state != 'Stopped' and db_name = 'vt_target'", result) - result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( - "table|lastpk", - "varchar|varchar"), - "t1|pk1", + result = sqltypes.MakeTestResult( + sqltypes.MakeTestFields("vrepl_id|table|lastpk", "int64|varchar|varchar"), + "1|t1|pk1", + "1|t2|pk2", ) - env.tmc.setVRResults(primary.tablet, "select table_name, lastpk from _vt.copy_state where vrepl_id = 1 and id in (select max(id) from _vt.copy_state where vrepl_id = 1 group by vrepl_id, table_name)", result) + env.tmc.setVRResults(primary.tablet, "select vrepl_id, table_name, lastpk from _vt.copy_state where vrepl_id in (1) and id in (select max(id) from _vt.copy_state where vrepl_id in (1) group by vrepl_id, table_name)", result) env.tmc.setVRResults(primary.tablet, "select id, source, pos, stop_pos, max_replication_lag, state, db_name, time_updated, transaction_timestamp, time_heartbeat, time_throttled, component_throttled, message, tags from _vt.vreplication where db_name = 'vt_target' and workflow = 'bad'", &sqltypes.Result{}) diff --git a/go/vt/zkctl/zkconf.go b/go/vt/zkctl/zkconf.go index 15a912231ff..92be0eb492e 100644 --- a/go/vt/zkctl/zkconf.go +++ b/go/vt/zkctl/zkconf.go @@ -24,7 +24,6 @@ limitations under the License. package zkctl import ( - "bytes" "fmt" "os" "path" @@ -94,29 +93,27 @@ func (cnf *ZkConfig) MyidFile() string { } func (cnf *ZkConfig) WriteMyid() error { - return os.WriteFile(cnf.MyidFile(), []byte(fmt.Sprintf("%v", cnf.ServerId)), 0664) + return os.WriteFile(cnf.MyidFile(), []byte(fmt.Sprintf("%v", cnf.ServerId)), 0o664) } /* Search for first existing file in cnfFiles and subsitute in the right values. */ func MakeZooCfg(cnfFiles []string, cnf *ZkConfig, header string) (string, error) { - myTemplateSource := new(bytes.Buffer) + var myTemplateSource strings.Builder for _, line := range strings.Split(header, "\n") { - fmt.Fprintf(myTemplateSource, "## %v\n", strings.TrimSpace(line)) + fmt.Fprintf(&myTemplateSource, "## %v\n", strings.TrimSpace(line)) } - var dataErr error + for _, path := range cnfFiles { - data, dataErr := os.ReadFile(path) - if dataErr != nil { + data, err := os.ReadFile(path) + if err != nil { continue } + myTemplateSource.WriteString("## " + path + "\n") myTemplateSource.Write(data) } - if dataErr != nil { - return "", dataErr - } myTemplateSource.WriteString("\n") // in case `data` did not end with a newline for _, extra := range cnf.Extra { @@ -127,9 +124,9 @@ func MakeZooCfg(cnfFiles []string, cnf *ZkConfig, header string) (string, error) if err != nil { return "", err } - cnfData := new(bytes.Buffer) - err = myTemplate.Execute(cnfData, cnf) - if err != nil { + + var cnfData strings.Builder + if err := myTemplate.Execute(&cnfData, cnf); err != nil { return "", err } return cnfData.String(), nil @@ -161,8 +158,10 @@ func MakeZkConfigFromString(cmdLine string, myID uint32) *ZkConfig { } myID = myID % 1000 - zkServer := zkServerAddr{ServerId: uint32(serverID), ClientPort: 2181, - LeaderPort: 2888, ElectionPort: 3888} + zkServer := zkServerAddr{ + ServerId: uint32(serverID), ClientPort: 2181, + LeaderPort: 2888, ElectionPort: 3888, + } switch len(zkAddrParts) { case 4: zkServer.ClientPort, _ = strconv.Atoi(zkAddrParts[3]) diff --git a/go/vt/zkctl/zkctl.go b/go/vt/zkctl/zkctl.go index acb8dba6356..60102d1bbf9 100644 --- a/go/vt/zkctl/zkctl.go +++ b/go/vt/zkctl/zkctl.go @@ -33,6 +33,7 @@ import ( zookeeper "github.com/z-division/go-zookeeper/zk" + "vitess.io/vitess/go/syscallutil" "vitess.io/vitess/go/vt/env" "vitess.io/vitess/go/vt/log" ) @@ -137,13 +138,13 @@ func (zkd *Zkd) Shutdown() error { if err != nil { return err } - err = syscall.Kill(pid, syscall.SIGKILL) + err = syscallutil.Kill(pid, syscall.SIGKILL) if err != nil && err != syscall.ESRCH { return err } timeout := time.Now().Add(shutdownWaitTime) for time.Now().Before(timeout) { - if syscall.Kill(pid, syscall.SIGKILL) == syscall.ESRCH { + if syscallutil.Kill(pid, syscall.SIGKILL) == syscall.ESRCH { return nil } time.Sleep(time.Second) diff --git a/java/client/pom.xml b/java/client/pom.xml index 60af9f72fde..9e0fa8fd2d1 100644 --- a/java/client/pom.xml +++ b/java/client/pom.xml @@ -5,7 +5,7 @@ io.vitess vitess-parent - 19.0.0-SNAPSHOT + 20.0.0-SNAPSHOT vitess-client diff --git a/java/example/pom.xml b/java/example/pom.xml index 7be8aa0cada..b0d013ac927 100644 --- a/java/example/pom.xml +++ b/java/example/pom.xml @@ -5,7 +5,7 @@ io.vitess vitess-parent - 19.0.0-SNAPSHOT + 20.0.0-SNAPSHOT vitess-example diff --git a/java/grpc-client/pom.xml b/java/grpc-client/pom.xml index cdd75855e74..20681619ca8 100644 --- a/java/grpc-client/pom.xml +++ b/java/grpc-client/pom.xml @@ -5,7 +5,7 @@ io.vitess vitess-parent - 19.0.0-SNAPSHOT + 20.0.0-SNAPSHOT vitess-grpc-client diff --git a/java/jdbc/pom.xml b/java/jdbc/pom.xml index 98a3f14ea0a..d8587f8e7b1 100644 --- a/java/jdbc/pom.xml +++ b/java/jdbc/pom.xml @@ -5,7 +5,7 @@ io.vitess vitess-parent - 19.0.0-SNAPSHOT + 20.0.0-SNAPSHOT vitess-jdbc diff --git a/java/pom.xml b/java/pom.xml index e424b304229..4737ea80a9c 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -11,7 +11,7 @@ io.vitessvitess-parent - 19.0.0-SNAPSHOT + 20.0.0-SNAPSHOTpomVitess Java Client libraries [Parent] @@ -69,7 +69,7 @@ 1.57.1 - 4.1.93.Final + 4.1.94.Final2.0.61.Final3.24.3 @@ -94,7 +94,7 @@ com.google.guava guava - 30.1.1-jre + 32.0.0-jre com.google.protobuf diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index b1f36261522..7dbfff5258d 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -176,6 +176,9 @@ message Rule { // such columns need to have special transofrmation of the data, from an integral format into a // string format. e.g. the value 0 needs to be converted to '0'. map convert_int_to_enum = 8; + + // ForceUniqueKey gives vtreamer a hint for `FORCE INDEX (...)` usage. + string force_unique_key = 9; } // Filter represents a list of ordered rules. The first diff --git a/proto/mysqlctl.proto b/proto/mysqlctl.proto index bc67cef07c1..7e5fe13b991 100644 --- a/proto/mysqlctl.proto +++ b/proto/mysqlctl.proto @@ -33,6 +33,7 @@ message StartResponse{} message ShutdownRequest{ bool wait_for_mysqld = 1; + vttime.Duration mysql_shutdown_timeout = 2; } message ShutdownResponse{} diff --git a/proto/tabletmanagerdata.proto b/proto/tabletmanagerdata.proto index fc9f6fa97b9..e1a0e97f03e 100644 --- a/proto/tabletmanagerdata.proto +++ b/proto/tabletmanagerdata.proto @@ -259,6 +259,7 @@ message ExecuteFetchAsDbaRequest { uint64 max_rows = 3; bool disable_binlogs = 4; bool reload_schema = 5; + bool disable_foreign_key_checks = 6; } message ExecuteFetchAsDbaResponse { @@ -483,7 +484,7 @@ message PromoteReplicaResponse { // Backup / Restore related messages message BackupRequest { - int64 concurrency = 1; + int32 concurrency = 1; bool allow_primary = 2; // IncrementalFromPos indicates a position of a previous backup. When this value is non-empty // then the backup becomes incremental and applies as of given position. @@ -609,17 +610,19 @@ message VDiffReportOptions { bool only_pks = 1; bool debug_query = 2; string format = 3; + int64 max_sample_rows = 4; } message VDiffCoreOptions { string tables = 1; bool auto_retry = 2; - int64 max_rows = 3; + int64 max_rows = 3; bool checksum = 4; int64 sample_pct = 5; int64 timeout_seconds = 6; int64 max_extra_rows_to_compare = 7; bool update_table_stats = 8; + int64 max_diff_seconds = 9; } message VDiffOptions { @@ -635,6 +638,7 @@ message UpdateVReplicationWorkflowRequest { TabletSelectionPreference tablet_selection_preference = 4; binlogdata.OnDDLAction on_ddl = 5; binlogdata.VReplicationWorkflowState state = 6; + repeated string shards = 7; } message UpdateVReplicationWorkflowResponse { diff --git a/proto/topodata.proto b/proto/topodata.proto index c921f72dfa4..364095be0ee 100644 --- a/proto/topodata.proto +++ b/proto/topodata.proto @@ -267,22 +267,8 @@ message Keyspace { // OBSOLETE int32 split_shard_count = 3; reserved 3; - // ServedFrom indicates a relationship between a TabletType and the - // keyspace name that's serving it. - message ServedFrom { - // the tablet type (key for the map) - TabletType tablet_type = 1; - - // the cells to limit this to - repeated string cells = 2; - - // the keyspace name that's serving it - string keyspace = 3; - } - - // ServedFrom will redirect the appropriate traffic to - // another keyspace. - repeated ServedFrom served_froms = 4; + // OBSOLETE ServedFrom served_froms = 4; + reserved 4; // keyspace_type will determine how this keyspace is treated by // vtgate / vschema. Normal keyspaces are routable by @@ -417,16 +403,6 @@ message SrvKeyspace { // The partitions this keyspace is serving, per tablet type. repeated KeyspacePartition partitions = 1; - // ServedFrom indicates a relationship between a TabletType and the - // keyspace name that's serving it. - message ServedFrom { - // the tablet type - TabletType tablet_type = 1; - - // the keyspace name that's serving it - string keyspace = 2; - } - // copied from Keyspace // OBSOLETE string sharding_column_name = 2; reserved 2; @@ -434,7 +410,8 @@ message SrvKeyspace { // OBSOLETE KeyspaceIdType sharding_column_type = 3; reserved 3; - repeated ServedFrom served_from = 4; + // OBSOLETE repeated ServedFrom served_from = 4; + reserved 4; // OBSOLETE int32 split_shard_count = 5; reserved 5; diff --git a/proto/vschema.proto b/proto/vschema.proto index 067be686db5..45c1b7a3aa9 100644 --- a/proto/vschema.proto +++ b/proto/vschema.proto @@ -127,6 +127,13 @@ message Column { string name = 1; query.Type type = 2; bool invisible = 3; + string default = 4; + string collation_name = 5; + int32 size = 6; + int32 scale = 7; + optional bool nullable = 8; + // values contains the list of values for an enum or set column. + repeated string values = 9; } // SrvVSchema is the roll-up of all the Keyspace schema for a cell. diff --git a/proto/vtadmin.proto b/proto/vtadmin.proto index 6a387466879..d6f1047fc1e 100644 --- a/proto/vtadmin.proto +++ b/proto/vtadmin.proto @@ -33,6 +33,17 @@ import "vtctldata.proto"; // VTAdmin is the Vitess Admin API service. It provides RPCs that operate on // across a range of Vitess clusters. service VTAdmin { + // ApplySchema applies a schema to a keyspace in the given cluster. + rpc ApplySchema(ApplySchemaRequest) returns (vtctldata.ApplySchemaResponse) {}; + // CancelSchemaMigration cancels one or all schema migrations in the given + // cluster, terminating any running ones as needed. + rpc CancelSchemaMigration(CancelSchemaMigrationRequest) returns (vtctldata.CancelSchemaMigrationResponse) {}; + // CleanupSchemaMigration marks a schema migration in the given cluster as + // ready for artifact cleanup. + rpc CleanupSchemaMigration(CleanupSchemaMigrationRequest) returns (vtctldata.CleanupSchemaMigrationResponse) {}; + // CompleteSchemaMigration completes one or all migrations in the given + // cluster executed with --postpone-completion. + rpc CompleteSchemaMigration(CompleteSchemaMigrationRequest) returns (vtctldata.CompleteSchemaMigrationResponse) {}; // CreateKeyspace creates a new keyspace in the given cluster. rpc CreateKeyspace(CreateKeyspaceRequest) returns (CreateKeyspaceResponse) {}; // CreateShard creates a new shard in the given cluster and keyspace. @@ -77,6 +88,13 @@ service VTAdmin { rpc GetSchema(GetSchemaRequest) returns (Schema) {}; // GetSchemas returns all schemas across the specified clusters. rpc GetSchemas(GetSchemasRequest) returns (GetSchemasResponse) {}; + // GetSchemaMigrations returns one or more online schema migrations for the + // set of keyspaces (or all keyspaces) in the given clusters, analagous to + // repeated executions of `SHOW VITESS_MIGRATIONS`. + // + // Different fields in the request message result in different behaviors. + // See the documentation on vtctldata.GetSchemaMigrationsRequest for details. + rpc GetSchemaMigrations(GetSchemaMigrationsRequest) returns (GetSchemaMigrationsResponse) {}; // GetShardReplicationPositions returns shard replication positions grouped // by cluster. rpc GetShardReplicationPositions(GetShardReplicationPositionsRequest) returns (GetShardReplicationPositionsResponse) {}; @@ -108,6 +126,9 @@ service VTAdmin { rpc GetWorkflow(GetWorkflowRequest) returns (Workflow) {}; // GetWorkflows returns the Workflows for all specified clusters. rpc GetWorkflows(GetWorkflowsRequest) returns (GetWorkflowsResponse) {}; + // LaunchSchemaMigration launches one or all migrations in the given + // cluster executed with --postpone-launch. + rpc LaunchSchemaMigration(LaunchSchemaMigrationRequest) returns (vtctldata.LaunchSchemaMigrationResponse) {}; // PingTablet checks that the specified tablet is awake and responding to // RPCs. This command can be blocked by other in-flight operations. rpc PingTablet(PingTabletRequest) returns (PingTabletResponse) {}; @@ -134,6 +155,9 @@ service VTAdmin { rpc ReloadSchemaShard(ReloadSchemaShardRequest) returns (ReloadSchemaShardResponse) {}; // RemoveKeyspaceCell removes the cell from the Cells list for all shards in the keyspace, and the SrvKeyspace for that keyspace in that cell. rpc RemoveKeyspaceCell(RemoveKeyspaceCellRequest) returns (RemoveKeyspaceCellResponse) {}; + // RetrySchemaMigration marks a given schema migration in the given cluster + // for retry. + rpc RetrySchemaMigration(RetrySchemaMigrationRequest) returns (vtctldata.RetrySchemaMigrationResponse) {}; // RunHealthCheck runs a healthcheck on the tablet. rpc RunHealthCheck(RunHealthCheckRequest) returns (RunHealthCheckResponse) {}; // SetReadOnly sets the tablet to read-only mode. @@ -249,6 +273,11 @@ message Schema { } } +message SchemaMigration { + Cluster cluster = 1; + vtctldata.SchemaMigration schema_migration = 2; +} + // Shard groups the vtctldata information about a shard record together with // the Vitess cluster it belongs to. message Shard { @@ -318,6 +347,26 @@ message Workflow { /* Request/Response types */ +message ApplySchemaRequest { + string cluster_id = 1; + vtctldata.ApplySchemaRequest request = 2; +} + +message CancelSchemaMigrationRequest { + string cluster_id = 1; + vtctldata.CancelSchemaMigrationRequest request = 2; +} + +message CleanupSchemaMigrationRequest { + string cluster_id = 1; + vtctldata.CleanupSchemaMigrationRequest request = 2; +} + +message CompleteSchemaMigrationRequest { + string cluster_id = 1; + vtctldata.CompleteSchemaMigrationRequest request = 2; +} + message CreateKeyspaceRequest { string cluster_id = 1; vtctldata.CreateKeyspaceRequest options = 2; @@ -472,6 +521,19 @@ message GetSchemasResponse { repeated Schema schemas = 1; } +message GetSchemaMigrationsRequest { + repeated ClusterRequest cluster_requests = 1; + + message ClusterRequest { + string cluster_id = 1; + vtctldata.GetSchemaMigrationsRequest request = 2; + } +} + +message GetSchemaMigrationsResponse { + repeated SchemaMigration schema_migrations = 1; +} + message GetShardReplicationPositionsRequest { repeated string cluster_ids = 1; // Keyspaces, if set, limits replication positions to just the specified @@ -605,6 +667,11 @@ message GetWorkflowsResponse { map workflows_by_cluster = 1; } +message LaunchSchemaMigrationRequest { + string cluster_id = 1; + vtctldata.LaunchSchemaMigrationRequest request = 2; +} + message PingTabletRequest { // Unique (per cluster) tablet alias of the standard form: "$cell-$uid" topodata.TabletAlias alias = 1; @@ -689,7 +756,7 @@ message ReloadSchemasRequest { // // In Tablets mode, Concurrency is the number of tablets to reload at once // *per cluster*. - uint32 concurrency = 5; + int32 concurrency = 5; // WaitPosition is the replication position that replicating tablets should // reach prior to reloading their schemas. // @@ -757,7 +824,7 @@ message ReloadSchemaShardRequest { string wait_position = 4; bool include_primary = 5; - uint32 concurrency = 6; + int32 concurrency = 6; } message ReloadSchemaShardResponse { @@ -788,6 +855,11 @@ message RemoveKeyspaceCellResponse { string status = 1; } +message RetrySchemaMigrationRequest { + string cluster_id = 1; + vtctldata.RetrySchemaMigrationRequest request = 2; +} + message RunHealthCheckRequest { topodata.TabletAlias alias = 1; repeated string cluster_ids = 2; diff --git a/proto/vtctldata.proto b/proto/vtctldata.proto index 3d59ea1bd5e..fd80b7ea69d 100644 --- a/proto/vtctldata.proto +++ b/proto/vtctldata.proto @@ -260,10 +260,14 @@ message Workflow { repeated string tags = 15; int64 rows_copied = 16; ThrottlerStatus throttler_status = 17; + repeated topodata.TabletType tablet_types = 18; + tabletmanagerdata.TabletSelectionPreference tablet_selection_preference = 19; + repeated string cells = 20; message CopyState { string table = 1; string last_pk = 2; + int64 stream_id = 3; } message Log { @@ -370,10 +374,25 @@ message ApplyVSchemaRequest { repeated string cells = 4; vschema.Keyspace v_schema = 5; string sql = 6; + // Strict returns an error if there are unknown vindex params. + bool strict = 7; } message ApplyVSchemaResponse { vschema.Keyspace v_schema = 1; + // UnknownVindexParams is a map of vindex name to params that were not recognized by the vindex + // type. E.g.: + // + // { + // "lookup_vdx": { + // "params": ["raed_lock", "not_verify"] + // } + // } + map unknown_vindex_params = 2; + + message ParamList { + repeated string params = 1; + } } message BackupRequest { @@ -385,7 +404,7 @@ message BackupRequest { bool allow_primary = 2; // Concurrency specifies the number of compression/checksum jobs to run // simultaneously. - uint64 concurrency = 3; + int32 concurrency = 3; // IncrementalFromPos indicates a position of a previous backup. When this value is non-empty // then the backup becomes incremental and applies as of given position. string incremental_from_pos = 4; @@ -410,7 +429,7 @@ message BackupShardRequest { bool allow_primary = 3; // Concurrency specifies the number of compression/checksum jobs to run // simultaneously. - uint64 concurrency = 4; + int32 concurrency = 4; // UpgradeSafe indicates if the backup should be taken with innodb_fast_shutdown=0 // so that it's a backup that can be used for an upgrade. bool upgrade_safe = 5; @@ -449,7 +468,6 @@ message CleanupSchemaMigrationResponse { map rows_affected_by_shard = 1; } - message CompleteSchemaMigrationRequest { string keyspace = 1; string uuid = 2; @@ -473,9 +491,8 @@ message CreateKeyspaceRequest { // OBSOLETE topodata.KeyspaceIdType sharding_column_type = 5; reserved 5; - // ServedFroms specifies a set of db_type:keyspace pairs used to serve - // traffic for the keyspace. - repeated topodata.Keyspace.ServedFrom served_froms = 6; + // OBSOLETE: repeated topodata.Keyspace.ServedFrom served_froms = 6; + reserved 6; // Type is the type of the keyspace to create. topodata.KeyspaceType type = 7; @@ -683,6 +700,15 @@ message FindAllShardsInKeyspaceResponse { map shards = 1; } +message ForceCutOverSchemaMigrationRequest { + string keyspace = 1; + string uuid = 2; +} + +message ForceCutOverSchemaMigrationResponse { + map rows_affected_by_shard = 1; +} + message GetBackupsRequest { string keyspace = 1; string shard = 2; @@ -985,6 +1011,7 @@ message GetWorkflowsRequest { // If you only want a specific workflow then set this field. string workflow = 4; bool include_logs = 5; + repeated string shards = 6; } message GetWorkflowsResponse { @@ -1174,6 +1201,7 @@ message MoveTablesCompleteRequest { bool keep_routing_rules = 5; bool rename_tables = 6; bool dry_run = 7; + repeated string shards = 8; } message MoveTablesCompleteResponse { @@ -1211,6 +1239,10 @@ message PlannedReparentShardRequest { // WaitReplicasTimeout time to catch up before the reparent, and an additional // WaitReplicasTimeout time to catch up after the reparent. vttime.Duration wait_replicas_timeout = 5; + // TolerableReplicationLag is the amount of replication lag that is considered + // acceptable for a tablet to be eligible for promotion when Vitess makes the choice of a new primary. + // A value of 0 indicates that Vitess shouldn't consider the replication lag at all. + vttime.Duration tolerable_replication_lag = 6; } message PlannedReparentShardResponse { @@ -1279,7 +1311,7 @@ message ReloadSchemaKeyspaceRequest { // Concurrency is the global concurrency across all shards in the keyspace // (so, at most this many tablets will be reloaded across the keyspace at any // given point). - uint32 concurrency = 4; + int32 concurrency = 4; } message ReloadSchemaKeyspaceResponse { @@ -1292,7 +1324,7 @@ message ReloadSchemaShardRequest { string wait_position = 3; bool include_primary = 4; // Concurrency is the maximum number of tablets to reload at one time. - uint32 concurrency = 5; + int32 concurrency = 5; } message ReloadSchemaShardResponse { @@ -1429,19 +1461,6 @@ message SetKeyspaceDurabilityPolicyResponse { topodata.Keyspace keyspace = 1; } -message SetKeyspaceServedFromRequest { - string keyspace = 1; - topodata.TabletType tablet_type = 2; - repeated string cells = 3; - bool remove = 4; - string source_keyspace = 5; -} - -message SetKeyspaceServedFromResponse { - // Keyspace is the updated keyspace record. - topodata.Keyspace keyspace = 1; -} - message SetKeyspaceShardingInfoRequest { string keyspace = 1; // OBSOLETE string column_name = 2; @@ -1725,6 +1744,8 @@ message VDiffCreateRequest { vttime.Duration wait_update_interval = 16; bool auto_retry = 17; bool verbose = 18; + int64 max_report_sample_rows = 19; + vttime.Duration max_diff_duration = 20; } message VDiffCreateResponse { @@ -1778,6 +1799,7 @@ message WorkflowDeleteRequest { string workflow = 2; bool keep_data = 3; bool keep_routing_rules = 4; + repeated string shards = 5; } message WorkflowDeleteResponse { @@ -1793,6 +1815,7 @@ message WorkflowDeleteResponse { message WorkflowStatusRequest { string keyspace = 1; string workflow = 2; + repeated string shards = 3; } message WorkflowStatusResponse { @@ -1832,6 +1855,7 @@ message WorkflowSwitchTrafficRequest { vttime.Duration timeout = 8; bool dry_run = 9; bool initialize_target_sequences = 10; + repeated string shards = 11; } message WorkflowSwitchTrafficResponse { diff --git a/proto/vtctlservice.proto b/proto/vtctlservice.proto index 59c24dc8445..3ce89af2995 100644 --- a/proto/vtctlservice.proto +++ b/proto/vtctlservice.proto @@ -54,7 +54,7 @@ service Vtctld { rpc Backup(vtctldata.BackupRequest) returns (stream vtctldata.BackupResponse) {}; // BackupShard chooses a tablet in the shard and uses it to create a backup. rpc BackupShard(vtctldata.BackupShardRequest) returns (stream vtctldata.BackupResponse) {}; - // CancelSchemaMigration cancels one or all migrations, terminating any runnign ones as needed. + // CancelSchemaMigration cancels one or all migrations, terminating any running ones as needed. rpc CancelSchemaMigration(vtctldata.CancelSchemaMigrationRequest) returns (vtctldata.CancelSchemaMigrationResponse) {}; // ChangeTabletType changes the db type for the specified tablet, if possible. // This is used primarily to arrange replicas, and it will not convert a @@ -103,6 +103,8 @@ service Vtctld { // FindAllShardsInKeyspace returns a map of shard names to shard references // for a given keyspace. rpc FindAllShardsInKeyspace(vtctldata.FindAllShardsInKeyspaceRequest) returns (vtctldata.FindAllShardsInKeyspaceResponse) {}; + // ForceCutOverSchemaMigration marks a schema migration for forced cut-over. + rpc ForceCutOverSchemaMigration(vtctldata.ForceCutOverSchemaMigrationRequest) returns (vtctldata.ForceCutOverSchemaMigrationResponse) {}; // GetBackups returns all the backups for a shard. rpc GetBackups(vtctldata.GetBackupsRequest) returns (vtctldata.GetBackupsResponse) {}; // GetCellInfo returns the information for a cell. diff --git a/proto/vttest.proto b/proto/vttest.proto index 3b3413979d4..b48107044d7 100644 --- a/proto/vttest.proto +++ b/proto/vttest.proto @@ -74,9 +74,9 @@ message Keyspace { // OBSOLETE string sharding_column_type = 4; reserved 4; - // redirects all traffic to another keyspace. If set, shards is ignored. - string served_from = 5; - + // OBSOLETE string served_from = 5; + reserved 5; + // number of replica tablets to instantiate. This includes the primary tablet. int32 replica_count = 6; diff --git a/test.go b/test.go index c7594557161..d7e40909927 100755 --- a/test.go +++ b/test.go @@ -77,7 +77,7 @@ For example: // Flags var ( flavor = flag.String("flavor", "mysql57", "comma-separated bootstrap flavor(s) to run against (when using Docker mode). Available flavors: all,"+flavors) - bootstrapVersion = flag.String("bootstrap-version", "24", "the version identifier to use for the docker images") + bootstrapVersion = flag.String("bootstrap-version", "28", "the version identifier to use for the docker images") runCount = flag.Int("runs", 1, "run each test this many times") retryMax = flag.Int("retry", 3, "max number of retries, to detect flaky tests") logPass = flag.Bool("log-pass", false, "log test output even if it passes") diff --git a/test/README.md b/test/README.md index 5fd5fadbedb..6579245ef45 100644 --- a/test/README.md +++ b/test/README.md @@ -1,4 +1,4 @@ -##Github CI Workflows +## Github CI Workflows This document has a short outline of how tests are run in CI, how to add new tests and where these are configured. diff --git a/test/ci_workflow_gen.go b/test/ci_workflow_gen.go index 5a3031d7307..d047d588f2a 100644 --- a/test/ci_workflow_gen.go +++ b/test/ci_workflow_gen.go @@ -80,16 +80,12 @@ var ( "21", "22", "mysql_server_vault", - "vstream_failover", - "vstream_stoponreshard_true", - "vstream_stoponreshard_false", - "vstream_with_keyspaces_to_watch", + "vstream", "onlineddl_ghost", "onlineddl_vrepl", "onlineddl_vrepl_stress", "onlineddl_vrepl_stress_suite", "onlineddl_vrepl_suite", - "vreplication_migrate_vdiff2_convert_tz", "onlineddl_revert", "onlineddl_scheduler", "tabletmanager_throttler_topo", @@ -116,12 +112,12 @@ var ( "xb_recovery", "mysql80", "vreplication_across_db_versions", - "vreplication_multicell", - "vreplication_cellalias", "vreplication_basic", + "vreplication_cellalias", "vreplication_v2", - "vreplication_partial_movetables_basic", - "vreplication_partial_movetables_sequences", + "vreplication_partial_movetables_and_materialize", + "vreplication_foreign_key_stress", + "vreplication_migrate_vdiff2_convert_tz", "schemadiff_vrepl", "topo_connection_cache", "vtgate_partial_keyspace", @@ -141,6 +137,9 @@ var ( "vtgate_topo_consul", "tabletmanager_consul", } + clustersRequiringMemoryCheck = []string{ + "vtorc", + } clusterRequiring16CoresMachines = []string{ "onlineddl_vrepl", "onlineddl_vrepl_stress", @@ -158,6 +157,7 @@ type unitTest struct { type clusterTest struct { Name, Shard, Platform string FileName string + MemoryCheck bool MakeTools, InstallXtraBackup bool Docker bool LimitResourceUsage bool @@ -355,6 +355,13 @@ func generateClusterWorkflows(list []string, tpl string) { break } } + memoryCheckClusters := canonnizeList(clustersRequiringMemoryCheck) + for _, memCheckCluster := range memoryCheckClusters { + if memCheckCluster == cluster { + test.MemoryCheck = true + break + } + } xtraBackupClusters := canonnizeList(clustersRequiringXtraBackup) for _, xtraBackupCluster := range xtraBackupClusters { if xtraBackupCluster == cluster { diff --git a/test/config.json b/test/config.json index 66657b4f37e..14c05cb8df6 100644 --- a/test/config.json +++ b/test/config.json @@ -507,7 +507,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_aggregation": { "File": "unused.go", @@ -516,7 +516,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_foundrows": { "File": "unused.go", @@ -525,7 +525,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_informationschema": { "File": "unused.go", @@ -534,7 +534,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_misc": { "File": "unused.go", @@ -543,7 +543,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_timeout": { "File": "unused.go", @@ -552,7 +552,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_normalize": { "File": "unused.go", @@ -561,7 +561,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_no_scatter": { "File": "unused.go", @@ -570,7 +570,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_orderby": { "File": "unused.go", @@ -606,7 +606,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_vexplain": { "File": "unused.go", @@ -615,7 +615,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 2, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_reference": { "File": "unused.go", @@ -624,7 +624,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_queries_random": { "File": "unused.go", @@ -633,7 +633,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_kill": { "File": "unused.go", @@ -642,7 +642,7 @@ "Manual": false, "Shard": "vtgate_queries", "RetryMax": 1, - "Tags": [] + "Tags": ["upgrade_downgrade_query_serving_queries"] }, "vtgate_concurrentdml": { "File": "unused.go", @@ -1004,21 +1004,12 @@ "RetryMax": 1, "Tags": [] }, - "vreplication_multicell": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "MultiCell"], - "Command": [], - "Manual": false, - "Shard": "vreplication_multicell", - "RetryMax": 2, - "Tags": [] - }, "vreplication_materialize": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestMaterialize"], "Command": [], "Manual": false, - "Shard": "vreplication_multicell", + "Shard": "vreplication_partial_movetables_and_materialize", "RetryMax": 0, "Tags": [] }, @@ -1027,7 +1018,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestMaterializeVtctldClient"], "Command": [], "Manual": false, - "Shard": "vreplication_multicell", + "Shard": "vreplication_partial_movetables_and_materialize", "RetryMax": 0, "Tags": [] }, @@ -1045,7 +1036,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "PartialMoveTablesBasic"], "Command": [], "Manual": false, - "Shard": "vreplication_partial_movetables_basic", + "Shard": "vreplication_partial_movetables_and_materialize", "RetryMax": 0, "Tags": [] }, @@ -1054,7 +1045,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestMultipleConcurrentVDiffs"], "Command": [], "Manual": false, - "Shard": "vreplication_partial_movetables_basic", + "Shard": "vreplication_partial_movetables_and_materialize", "RetryMax": 0, "Tags": [] }, @@ -1094,6 +1085,15 @@ "RetryMax": 1, "Tags": [] }, + "vreplication_vtctldclient_cli": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVtctldclientCLI", "-timeout", "20m"], + "Command": [], + "Manual": false, + "Shard": "vreplication_basic", + "RetryMax": 1, + "Tags": [] + }, "vreplication_copy_parallel": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVreplicationCopyParallel", "-timeout", "20m"], @@ -1108,7 +1108,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestPartialMoveTablesWithSequences"], "Command": [], "Manual": false, - "Shard": "vreplication_partial_movetables_sequences", + "Shard": "vreplication_partial_movetables_and_materialize", "RetryMax": 1, "Tags": [] }, @@ -1126,7 +1126,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "VStreamFailover"], "Command": [], "Manual": false, - "Shard": "vstream_failover", + "Shard": "vstream", "RetryMax": 3, "Tags": [] }, @@ -1135,7 +1135,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "VStreamStopOnReshardTrue"], "Command": [], "Manual": false, - "Shard": "vstream_stoponreshard_true", + "Shard": "vstream", "RetryMax": 1, "Tags": [] }, @@ -1144,7 +1144,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "VStreamStopOnReshardFalse"], "Command": [], "Manual": false, - "Shard": "vstream_stoponreshard_false", + "Shard": "vstream", "RetryMax": 1, "Tags": [] }, @@ -1153,7 +1153,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "VStreamWithKeyspacesToWatch"], "Command": [], "Manual": false, - "Shard": "vstream_with_keyspaces_to_watch", + "Shard": "vstream", "RetryMax": 1, "Tags": [] }, @@ -1220,6 +1220,15 @@ "RetryMax": 1, "Tags": [] }, + "vreplication_foreign_key_stress": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestFKExt"], + "Command": [], + "Manual": false, + "Shard": "vreplication_foreign_key_stress", + "RetryMax": 1, + "Tags": [] + }, "vreplication_across_db_versions": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestV2WorkflowsAcrossDBVersions", "-timeout", "20m"], @@ -1258,7 +1267,7 @@ }, "vdiff2": { "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVDiff2", "-timeout", "20m"], + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestVDiff2", "-timeout", "30m"], "Command": [], "Manual": false, "Shard": "vreplication_migrate_vdiff2_convert_tz", diff --git a/test/templates/cluster_endtoend_test.tpl b/test/templates/cluster_endtoend_test.tpl index b5c409d8f51..d75cdbe817d 100644 --- a/test/templates/cluster_endtoend_test.tpl +++ b/test/templates/cluster_endtoend_test.tpl @@ -41,19 +41,34 @@ jobs: draft=$(echo "$PR_DATA" | jq .draft -r) echo "is_draft=${draft}" >> $GITHUB_OUTPUT + {{if .MemoryCheck}} + + - name: Check Memory + run: | + totalMem=$(free -g | awk 'NR==2 {print $2}') + echo "total memory $totalMem GB" + if [[ "$totalMem" -lt 15 ]]; then + echo "Less memory than required" + exit 1 + fi + + {{end}} + - name: Check out code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -72,7 +87,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -107,9 +122,9 @@ jobs: {{else}} # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # Setup MySQL 8.0 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/test/templates/cluster_endtoend_test_docker.tpl b/test/templates/cluster_endtoend_test_docker.tpl index f53c705e2c1..9f06cb372c3 100644 --- a/test/templates/cluster_endtoend_test_docker.tpl +++ b/test/templates/cluster_endtoend_test_docker.tpl @@ -32,13 +32,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -54,7 +56,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Tune the OS if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' diff --git a/test/templates/cluster_endtoend_test_mysql57.tpl b/test/templates/cluster_endtoend_test_mysql57.tpl index f5bc482cda9..b4f528eeb2f 100644 --- a/test/templates/cluster_endtoend_test_mysql57.tpl +++ b/test/templates/cluster_endtoend_test_mysql57.tpl @@ -46,19 +46,34 @@ jobs: draft=$(echo "$PR_DATA" | jq .draft -r) echo "is_draft=${draft}" >> $GITHUB_OUTPUT + {{if .MemoryCheck}} + + - name: Check Memory + run: | + totalMem=$(free -g | awk 'NR==2 {print $2}') + echo "total memory $totalMem GB" + if [[ "$totalMem" -lt 15 ]]; then + echo "Less memory than required" + exit 1 + fi + + {{end}} + - name: Check out code if: steps.skip-workflow.outputs.skip-workflow == 'false' uses: actions/checkout@v3 - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' @@ -77,7 +92,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.end_to_end == 'true' @@ -109,9 +124,9 @@ jobs: sudo rm -rf /etc/mysql # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections diff --git a/test/templates/cluster_endtoend_test_self_hosted.tpl b/test/templates/cluster_endtoend_test_self_hosted.tpl index d9b48f6aecf..55c891ab95c 100644 --- a/test/templates/cluster_endtoend_test_self_hosted.tpl +++ b/test/templates/cluster_endtoend_test_self_hosted.tpl @@ -35,13 +35,15 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' filters: | end_to_end: - 'go/**/*.go' + - 'go/vt/sidecardb/**/*.sql' + - 'go/test/endtoend/onlineddl/vrepl_suite/**' - 'test.go' - 'Makefile' - 'build.env' diff --git a/test/templates/dockerfile.tpl b/test/templates/dockerfile.tpl index 16ea54e724b..170b7281219 100644 --- a/test/templates/dockerfile.tpl +++ b/test/templates/dockerfile.tpl @@ -1,4 +1,4 @@ -ARG bootstrap_version=24 +ARG bootstrap_version=28 ARG image="vitess/bootstrap:${bootstrap_version}-{{.Platform}}" FROM "${image}" diff --git a/test/templates/unit_test.tpl b/test/templates/unit_test.tpl index 7dffd83267b..bbd76dfa3a7 100644 --- a/test/templates/unit_test.tpl +++ b/test/templates/unit_test.tpl @@ -47,7 +47,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' @@ -69,7 +69,7 @@ jobs: if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' uses: actions/setup-go@v4 with: - go-version: 1.21.3 + go-version: 1.22.0 - name: Set up python if: steps.skip-workflow.outputs.skip-workflow == 'false' && steps.changes.outputs.unit_tests == 'true' @@ -100,10 +100,10 @@ jobs: {{if (eq .Platform "mysql57")}} # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # mysql57 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb # Bionic packages are still compatible for Jammy since there's no MySQL 5.7 # packages for Jammy. echo mysql-apt-config mysql-apt-config/repo-codename select bionic | sudo debconf-set-selections @@ -116,10 +116,10 @@ jobs: {{if (eq .Platform "mysql80")}} # Get key to latest MySQL repo - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C # mysql80 - wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb + wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update diff --git a/test/templates/unit_test_self_hosted.tpl b/test/templates/unit_test_self_hosted.tpl index 45d88392b9b..0e8c97d4907 100644 --- a/test/templates/unit_test_self_hosted.tpl +++ b/test/templates/unit_test_self_hosted.tpl @@ -34,7 +34,7 @@ jobs: - name: Check for changes in relevant files if: steps.skip-workflow.outputs.skip-workflow == 'false' - uses: frouioui/paths-filter@main + uses: dorny/paths-filter@v3.0.1 id: changes with: token: '' diff --git a/tools/create_release.sh b/tools/create_release.sh index 68e051f884e..17c8139dce2 100755 --- a/tools/create_release.sh +++ b/tools/create_release.sh @@ -52,7 +52,7 @@ function createRelease () { rm -f ./.github/workflows/code_freeze.yml.bak # Wait for release notes to be injected in the code base - echo -n Pausing so relase notes can be added. Press enter to continue + echo -n Pausing so release notes can be added. Press enter to continue read line git add --all diff --git a/tools/rowlog/rowlog.go b/tools/rowlog/rowlog.go index 475006b2b59..8092159c6b6 100644 --- a/tools/rowlog/rowlog.go +++ b/tools/rowlog/rowlog.go @@ -71,7 +71,6 @@ func usage() { func main() { usage() - defer log.Flush() ctx := context.Background() config := parseCommandLine() if !config.Validate() { diff --git a/vitess-mixin/e2e/vttablet-up.sh b/vitess-mixin/e2e/vttablet-up.sh index f41b31f025c..89709cf750f 100755 --- a/vitess-mixin/e2e/vttablet-up.sh +++ b/vitess-mixin/e2e/vttablet-up.sh @@ -133,7 +133,6 @@ if [ $tablet_role = "externalprimary" ]; then --enable_replication_reporter=false \ --enforce_strict_trans_tables=false \ --track_schema_versions=true \ - --vreplication_tablet_type=primary \ --watch_replication_stream=true" else external_db_args="--init_db_name_override $DB_NAME \ diff --git a/web/vtadmin/package-lock.json b/web/vtadmin/package-lock.json index 94044b24a27..6caa41e6011 100644 --- a/web/vtadmin/package-lock.json +++ b/web/vtadmin/package-lock.json @@ -61,7 +61,7 @@ "stylelint-config-standard-scss": "^3.0.0", "tailwindcss": "^3.0.18", "typescript": "^5.0.2", - "vite": "^4.2.3", + "vite": "^4.5.2", "vite-plugin-eslint": "^1.8.1", "vite-plugin-svgr": "^2.4.0", "vitest": "^0.29.8" @@ -72,9 +72,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", - "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -2704,9 +2704,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", - "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "cpu": [ "arm" ], @@ -2720,9 +2720,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", - "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "cpu": [ "arm64" ], @@ -2736,9 +2736,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", - "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "cpu": [ "x64" ], @@ -2752,9 +2752,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", - "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], @@ -2768,9 +2768,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", - "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], @@ -2784,9 +2784,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", - "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "cpu": [ "arm64" ], @@ -2800,9 +2800,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", - "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "cpu": [ "x64" ], @@ -2816,9 +2816,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", - "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "cpu": [ "arm" ], @@ -2832,9 +2832,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", - "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "cpu": [ "arm64" ], @@ -2848,9 +2848,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", - "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "cpu": [ "ia32" ], @@ -2864,9 +2864,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", - "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "cpu": [ "loong64" ], @@ -2880,9 +2880,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", - "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "cpu": [ "mips64el" ], @@ -2896,9 +2896,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", - "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "cpu": [ "ppc64" ], @@ -2912,9 +2912,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", - "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "cpu": [ "riscv64" ], @@ -2928,9 +2928,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", - "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "cpu": [ "s390x" ], @@ -2944,9 +2944,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", - "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "cpu": [ "x64" ], @@ -2960,9 +2960,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", - "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "cpu": [ "x64" ], @@ -2976,9 +2976,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", - "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "cpu": [ "x64" ], @@ -2992,9 +2992,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", - "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "cpu": [ "x64" ], @@ -3008,9 +3008,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", - "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "cpu": [ "arm64" ], @@ -3024,9 +3024,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", - "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "cpu": [ "ia32" ], @@ -3040,9 +3040,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", - "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "cpu": [ "x64" ], @@ -7125,9 +7125,9 @@ } }, "node_modules/esbuild": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", - "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, "hasInstallScript": true, "bin": { @@ -7137,28 +7137,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.15", - "@esbuild/android-arm64": "0.17.15", - "@esbuild/android-x64": "0.17.15", - "@esbuild/darwin-arm64": "0.17.15", - "@esbuild/darwin-x64": "0.17.15", - "@esbuild/freebsd-arm64": "0.17.15", - "@esbuild/freebsd-x64": "0.17.15", - "@esbuild/linux-arm": "0.17.15", - "@esbuild/linux-arm64": "0.17.15", - "@esbuild/linux-ia32": "0.17.15", - "@esbuild/linux-loong64": "0.17.15", - "@esbuild/linux-mips64el": "0.17.15", - "@esbuild/linux-ppc64": "0.17.15", - "@esbuild/linux-riscv64": "0.17.15", - "@esbuild/linux-s390x": "0.17.15", - "@esbuild/linux-x64": "0.17.15", - "@esbuild/netbsd-x64": "0.17.15", - "@esbuild/openbsd-x64": "0.17.15", - "@esbuild/sunos-x64": "0.17.15", - "@esbuild/win32-arm64": "0.17.15", - "@esbuild/win32-ia32": "0.17.15", - "@esbuild/win32-x64": "0.17.15" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "node_modules/escalade": { @@ -15512,9 +15512,9 @@ } }, "node_modules/rollup": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", - "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -16991,15 +16991,14 @@ } }, "node_modules/vite": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.3.tgz", - "integrity": "sha512-kLU+m2q0Y434Y1kCy3TchefAdtFso0ILi0dLyFV8Us3InXTU11H/B5ZTqCKIQHzSKNxVG/yEx813EA9f1imQ9A==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.21", - "resolve": "^1.22.1", - "rollup": "^3.18.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -17007,12 +17006,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -17025,6 +17028,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -17600,9 +17606,9 @@ }, "dependencies": { "@adobe/css-tools": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", - "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, "@ampproject/remapping": { @@ -19321,156 +19327,156 @@ "requires": {} }, "@esbuild/android-arm": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", - "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", - "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", - "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", - "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", - "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", - "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", - "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", - "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", - "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", - "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", - "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", - "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", - "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", - "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", - "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", - "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", - "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", - "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", - "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", - "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", - "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", - "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "dev": true, "optional": true }, @@ -22501,33 +22507,33 @@ } }, "esbuild": { - "version": "0.17.15", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", - "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.15", - "@esbuild/android-arm64": "0.17.15", - "@esbuild/android-x64": "0.17.15", - "@esbuild/darwin-arm64": "0.17.15", - "@esbuild/darwin-x64": "0.17.15", - "@esbuild/freebsd-arm64": "0.17.15", - "@esbuild/freebsd-x64": "0.17.15", - "@esbuild/linux-arm": "0.17.15", - "@esbuild/linux-arm64": "0.17.15", - "@esbuild/linux-ia32": "0.17.15", - "@esbuild/linux-loong64": "0.17.15", - "@esbuild/linux-mips64el": "0.17.15", - "@esbuild/linux-ppc64": "0.17.15", - "@esbuild/linux-riscv64": "0.17.15", - "@esbuild/linux-s390x": "0.17.15", - "@esbuild/linux-x64": "0.17.15", - "@esbuild/netbsd-x64": "0.17.15", - "@esbuild/openbsd-x64": "0.17.15", - "@esbuild/sunos-x64": "0.17.15", - "@esbuild/win32-arm64": "0.17.15", - "@esbuild/win32-ia32": "0.17.15", - "@esbuild/win32-x64": "0.17.15" + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "escalade": { @@ -28351,9 +28357,9 @@ } }, "rollup": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", - "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -29496,16 +29502,15 @@ "dev": true }, "vite": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.3.tgz", - "integrity": "sha512-kLU+m2q0Y434Y1kCy3TchefAdtFso0ILi0dLyFV8Us3InXTU11H/B5ZTqCKIQHzSKNxVG/yEx813EA9f1imQ9A==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "requires": { - "esbuild": "^0.17.5", + "esbuild": "^0.18.10", "fsevents": "~2.3.2", - "postcss": "^8.4.21", - "resolve": "^1.22.1", - "rollup": "^3.18.0" + "postcss": "^8.4.27", + "rollup": "^3.27.1" } }, "vite-node": { diff --git a/web/vtadmin/package.json b/web/vtadmin/package.json index bbfab0eab26..5a0f01e48c8 100644 --- a/web/vtadmin/package.json +++ b/web/vtadmin/package.json @@ -98,7 +98,7 @@ "stylelint-config-standard-scss": "^3.0.0", "tailwindcss": "^3.0.18", "typescript": "^5.0.2", - "vite": "^4.2.3", + "vite": "^4.5.2", "vite-plugin-eslint": "^1.8.1", "vite-plugin-svgr": "^2.4.0", "vitest": "^0.29.8" diff --git a/web/vtadmin/src/proto/vtadmin.d.ts b/web/vtadmin/src/proto/vtadmin.d.ts index 398d93080dc..20c1cbab8b8 100644 --- a/web/vtadmin/src/proto/vtadmin.d.ts +++ b/web/vtadmin/src/proto/vtadmin.d.ts @@ -23,6 +23,62 @@ export namespace vtadmin { */ public static create(rpcImpl: $protobuf.RPCImpl, requestDelimited?: boolean, responseDelimited?: boolean): VTAdmin; + /** + * Calls ApplySchema. + * @param request ApplySchemaRequest message or plain object + * @param callback Node-style callback called with the error, if any, and ApplySchemaResponse + */ + public applySchema(request: vtadmin.IApplySchemaRequest, callback: vtadmin.VTAdmin.ApplySchemaCallback): void; + + /** + * Calls ApplySchema. + * @param request ApplySchemaRequest message or plain object + * @returns Promise + */ + public applySchema(request: vtadmin.IApplySchemaRequest): Promise; + + /** + * Calls CancelSchemaMigration. + * @param request CancelSchemaMigrationRequest message or plain object + * @param callback Node-style callback called with the error, if any, and CancelSchemaMigrationResponse + */ + public cancelSchemaMigration(request: vtadmin.ICancelSchemaMigrationRequest, callback: vtadmin.VTAdmin.CancelSchemaMigrationCallback): void; + + /** + * Calls CancelSchemaMigration. + * @param request CancelSchemaMigrationRequest message or plain object + * @returns Promise + */ + public cancelSchemaMigration(request: vtadmin.ICancelSchemaMigrationRequest): Promise; + + /** + * Calls CleanupSchemaMigration. + * @param request CleanupSchemaMigrationRequest message or plain object + * @param callback Node-style callback called with the error, if any, and CleanupSchemaMigrationResponse + */ + public cleanupSchemaMigration(request: vtadmin.ICleanupSchemaMigrationRequest, callback: vtadmin.VTAdmin.CleanupSchemaMigrationCallback): void; + + /** + * Calls CleanupSchemaMigration. + * @param request CleanupSchemaMigrationRequest message or plain object + * @returns Promise + */ + public cleanupSchemaMigration(request: vtadmin.ICleanupSchemaMigrationRequest): Promise; + + /** + * Calls CompleteSchemaMigration. + * @param request CompleteSchemaMigrationRequest message or plain object + * @param callback Node-style callback called with the error, if any, and CompleteSchemaMigrationResponse + */ + public completeSchemaMigration(request: vtadmin.ICompleteSchemaMigrationRequest, callback: vtadmin.VTAdmin.CompleteSchemaMigrationCallback): void; + + /** + * Calls CompleteSchemaMigration. + * @param request CompleteSchemaMigrationRequest message or plain object + * @returns Promise + */ + public completeSchemaMigration(request: vtadmin.ICompleteSchemaMigrationRequest): Promise; + /** * Calls CreateKeyspace. * @param request CreateKeyspaceRequest message or plain object @@ -261,6 +317,20 @@ export namespace vtadmin { */ public getSchemas(request: vtadmin.IGetSchemasRequest): Promise; + /** + * Calls GetSchemaMigrations. + * @param request GetSchemaMigrationsRequest message or plain object + * @param callback Node-style callback called with the error, if any, and GetSchemaMigrationsResponse + */ + public getSchemaMigrations(request: vtadmin.IGetSchemaMigrationsRequest, callback: vtadmin.VTAdmin.GetSchemaMigrationsCallback): void; + + /** + * Calls GetSchemaMigrations. + * @param request GetSchemaMigrationsRequest message or plain object + * @returns Promise + */ + public getSchemaMigrations(request: vtadmin.IGetSchemaMigrationsRequest): Promise; + /** * Calls GetShardReplicationPositions. * @param request GetShardReplicationPositionsRequest message or plain object @@ -443,6 +513,20 @@ export namespace vtadmin { */ public getWorkflows(request: vtadmin.IGetWorkflowsRequest): Promise; + /** + * Calls LaunchSchemaMigration. + * @param request LaunchSchemaMigrationRequest message or plain object + * @param callback Node-style callback called with the error, if any, and LaunchSchemaMigrationResponse + */ + public launchSchemaMigration(request: vtadmin.ILaunchSchemaMigrationRequest, callback: vtadmin.VTAdmin.LaunchSchemaMigrationCallback): void; + + /** + * Calls LaunchSchemaMigration. + * @param request LaunchSchemaMigrationRequest message or plain object + * @returns Promise + */ + public launchSchemaMigration(request: vtadmin.ILaunchSchemaMigrationRequest): Promise; + /** * Calls PingTablet. * @param request PingTabletRequest message or plain object @@ -555,6 +639,20 @@ export namespace vtadmin { */ public removeKeyspaceCell(request: vtadmin.IRemoveKeyspaceCellRequest): Promise; + /** + * Calls RetrySchemaMigration. + * @param request RetrySchemaMigrationRequest message or plain object + * @param callback Node-style callback called with the error, if any, and RetrySchemaMigrationResponse + */ + public retrySchemaMigration(request: vtadmin.IRetrySchemaMigrationRequest, callback: vtadmin.VTAdmin.RetrySchemaMigrationCallback): void; + + /** + * Calls RetrySchemaMigration. + * @param request RetrySchemaMigrationRequest message or plain object + * @returns Promise + */ + public retrySchemaMigration(request: vtadmin.IRetrySchemaMigrationRequest): Promise; + /** * Calls RunHealthCheck. * @param request RunHealthCheckRequest message or plain object @@ -740,6 +838,34 @@ export namespace vtadmin { namespace VTAdmin { + /** + * Callback as used by {@link vtadmin.VTAdmin#applySchema}. + * @param error Error, if any + * @param [response] ApplySchemaResponse + */ + type ApplySchemaCallback = (error: (Error|null), response?: vtctldata.ApplySchemaResponse) => void; + + /** + * Callback as used by {@link vtadmin.VTAdmin#cancelSchemaMigration}. + * @param error Error, if any + * @param [response] CancelSchemaMigrationResponse + */ + type CancelSchemaMigrationCallback = (error: (Error|null), response?: vtctldata.CancelSchemaMigrationResponse) => void; + + /** + * Callback as used by {@link vtadmin.VTAdmin#cleanupSchemaMigration}. + * @param error Error, if any + * @param [response] CleanupSchemaMigrationResponse + */ + type CleanupSchemaMigrationCallback = (error: (Error|null), response?: vtctldata.CleanupSchemaMigrationResponse) => void; + + /** + * Callback as used by {@link vtadmin.VTAdmin#completeSchemaMigration}. + * @param error Error, if any + * @param [response] CompleteSchemaMigrationResponse + */ + type CompleteSchemaMigrationCallback = (error: (Error|null), response?: vtctldata.CompleteSchemaMigrationResponse) => void; + /** * Callback as used by {@link vtadmin.VTAdmin#createKeyspace}. * @param error Error, if any @@ -859,6 +985,13 @@ export namespace vtadmin { */ type GetSchemasCallback = (error: (Error|null), response?: vtadmin.GetSchemasResponse) => void; + /** + * Callback as used by {@link vtadmin.VTAdmin#getSchemaMigrations}. + * @param error Error, if any + * @param [response] GetSchemaMigrationsResponse + */ + type GetSchemaMigrationsCallback = (error: (Error|null), response?: vtadmin.GetSchemaMigrationsResponse) => void; + /** * Callback as used by {@link vtadmin.VTAdmin#getShardReplicationPositions}. * @param error Error, if any @@ -950,6 +1083,13 @@ export namespace vtadmin { */ type GetWorkflowsCallback = (error: (Error|null), response?: vtadmin.GetWorkflowsResponse) => void; + /** + * Callback as used by {@link vtadmin.VTAdmin#launchSchemaMigration}. + * @param error Error, if any + * @param [response] LaunchSchemaMigrationResponse + */ + type LaunchSchemaMigrationCallback = (error: (Error|null), response?: vtctldata.LaunchSchemaMigrationResponse) => void; + /** * Callback as used by {@link vtadmin.VTAdmin#pingTablet}. * @param error Error, if any @@ -1006,6 +1146,13 @@ export namespace vtadmin { */ type RemoveKeyspaceCellCallback = (error: (Error|null), response?: vtadmin.RemoveKeyspaceCellResponse) => void; + /** + * Callback as used by {@link vtadmin.VTAdmin#retrySchemaMigration}. + * @param error Error, if any + * @param [response] RetrySchemaMigrationResponse + */ + type RetrySchemaMigrationCallback = (error: (Error|null), response?: vtctldata.RetrySchemaMigrationResponse) => void; + /** * Callback as used by {@link vtadmin.VTAdmin#runHealthCheck}. * @param error Error, if any @@ -2173,6 +2320,109 @@ export namespace vtadmin { } } + /** Properties of a SchemaMigration. */ + interface ISchemaMigration { + + /** SchemaMigration cluster */ + cluster?: (vtadmin.ICluster|null); + + /** SchemaMigration schema_migration */ + schema_migration?: (vtctldata.ISchemaMigration|null); + } + + /** Represents a SchemaMigration. */ + class SchemaMigration implements ISchemaMigration { + + /** + * Constructs a new SchemaMigration. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.ISchemaMigration); + + /** SchemaMigration cluster. */ + public cluster?: (vtadmin.ICluster|null); + + /** SchemaMigration schema_migration. */ + public schema_migration?: (vtctldata.ISchemaMigration|null); + + /** + * Creates a new SchemaMigration instance using the specified properties. + * @param [properties] Properties to set + * @returns SchemaMigration instance + */ + public static create(properties?: vtadmin.ISchemaMigration): vtadmin.SchemaMigration; + + /** + * Encodes the specified SchemaMigration message. Does not implicitly {@link vtadmin.SchemaMigration.verify|verify} messages. + * @param message SchemaMigration message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.ISchemaMigration, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified SchemaMigration message, length delimited. Does not implicitly {@link vtadmin.SchemaMigration.verify|verify} messages. + * @param message SchemaMigration message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.ISchemaMigration, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a SchemaMigration message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns SchemaMigration + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.SchemaMigration; + + /** + * Decodes a SchemaMigration message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns SchemaMigration + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.SchemaMigration; + + /** + * Verifies a SchemaMigration message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a SchemaMigration message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns SchemaMigration + */ + public static fromObject(object: { [k: string]: any }): vtadmin.SchemaMigration; + + /** + * Creates a plain object from a SchemaMigration message. Also converts values to other types if specified. + * @param message SchemaMigration + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.SchemaMigration, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this SchemaMigration to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for SchemaMigration + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a Shard. */ interface IShard { @@ -2964,6 +3214,418 @@ export namespace vtadmin { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of an ApplySchemaRequest. */ + interface IApplySchemaRequest { + + /** ApplySchemaRequest cluster_id */ + cluster_id?: (string|null); + + /** ApplySchemaRequest request */ + request?: (vtctldata.IApplySchemaRequest|null); + } + + /** Represents an ApplySchemaRequest. */ + class ApplySchemaRequest implements IApplySchemaRequest { + + /** + * Constructs a new ApplySchemaRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.IApplySchemaRequest); + + /** ApplySchemaRequest cluster_id. */ + public cluster_id: string; + + /** ApplySchemaRequest request. */ + public request?: (vtctldata.IApplySchemaRequest|null); + + /** + * Creates a new ApplySchemaRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns ApplySchemaRequest instance + */ + public static create(properties?: vtadmin.IApplySchemaRequest): vtadmin.ApplySchemaRequest; + + /** + * Encodes the specified ApplySchemaRequest message. Does not implicitly {@link vtadmin.ApplySchemaRequest.verify|verify} messages. + * @param message ApplySchemaRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.IApplySchemaRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ApplySchemaRequest message, length delimited. Does not implicitly {@link vtadmin.ApplySchemaRequest.verify|verify} messages. + * @param message ApplySchemaRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.IApplySchemaRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes an ApplySchemaRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ApplySchemaRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.ApplySchemaRequest; + + /** + * Decodes an ApplySchemaRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ApplySchemaRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.ApplySchemaRequest; + + /** + * Verifies an ApplySchemaRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates an ApplySchemaRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ApplySchemaRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.ApplySchemaRequest; + + /** + * Creates a plain object from an ApplySchemaRequest message. Also converts values to other types if specified. + * @param message ApplySchemaRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.ApplySchemaRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ApplySchemaRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ApplySchemaRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a CancelSchemaMigrationRequest. */ + interface ICancelSchemaMigrationRequest { + + /** CancelSchemaMigrationRequest cluster_id */ + cluster_id?: (string|null); + + /** CancelSchemaMigrationRequest request */ + request?: (vtctldata.ICancelSchemaMigrationRequest|null); + } + + /** Represents a CancelSchemaMigrationRequest. */ + class CancelSchemaMigrationRequest implements ICancelSchemaMigrationRequest { + + /** + * Constructs a new CancelSchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.ICancelSchemaMigrationRequest); + + /** CancelSchemaMigrationRequest cluster_id. */ + public cluster_id: string; + + /** CancelSchemaMigrationRequest request. */ + public request?: (vtctldata.ICancelSchemaMigrationRequest|null); + + /** + * Creates a new CancelSchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns CancelSchemaMigrationRequest instance + */ + public static create(properties?: vtadmin.ICancelSchemaMigrationRequest): vtadmin.CancelSchemaMigrationRequest; + + /** + * Encodes the specified CancelSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CancelSchemaMigrationRequest.verify|verify} messages. + * @param message CancelSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.ICancelSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified CancelSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CancelSchemaMigrationRequest.verify|verify} messages. + * @param message CancelSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.ICancelSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a CancelSchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns CancelSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.CancelSchemaMigrationRequest; + + /** + * Decodes a CancelSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns CancelSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.CancelSchemaMigrationRequest; + + /** + * Verifies a CancelSchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a CancelSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns CancelSchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.CancelSchemaMigrationRequest; + + /** + * Creates a plain object from a CancelSchemaMigrationRequest message. Also converts values to other types if specified. + * @param message CancelSchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.CancelSchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this CancelSchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for CancelSchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a CleanupSchemaMigrationRequest. */ + interface ICleanupSchemaMigrationRequest { + + /** CleanupSchemaMigrationRequest cluster_id */ + cluster_id?: (string|null); + + /** CleanupSchemaMigrationRequest request */ + request?: (vtctldata.ICleanupSchemaMigrationRequest|null); + } + + /** Represents a CleanupSchemaMigrationRequest. */ + class CleanupSchemaMigrationRequest implements ICleanupSchemaMigrationRequest { + + /** + * Constructs a new CleanupSchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.ICleanupSchemaMigrationRequest); + + /** CleanupSchemaMigrationRequest cluster_id. */ + public cluster_id: string; + + /** CleanupSchemaMigrationRequest request. */ + public request?: (vtctldata.ICleanupSchemaMigrationRequest|null); + + /** + * Creates a new CleanupSchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns CleanupSchemaMigrationRequest instance + */ + public static create(properties?: vtadmin.ICleanupSchemaMigrationRequest): vtadmin.CleanupSchemaMigrationRequest; + + /** + * Encodes the specified CleanupSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CleanupSchemaMigrationRequest.verify|verify} messages. + * @param message CleanupSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.ICleanupSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified CleanupSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CleanupSchemaMigrationRequest.verify|verify} messages. + * @param message CleanupSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.ICleanupSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a CleanupSchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns CleanupSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.CleanupSchemaMigrationRequest; + + /** + * Decodes a CleanupSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns CleanupSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.CleanupSchemaMigrationRequest; + + /** + * Verifies a CleanupSchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a CleanupSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns CleanupSchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.CleanupSchemaMigrationRequest; + + /** + * Creates a plain object from a CleanupSchemaMigrationRequest message. Also converts values to other types if specified. + * @param message CleanupSchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.CleanupSchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this CleanupSchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for CleanupSchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a CompleteSchemaMigrationRequest. */ + interface ICompleteSchemaMigrationRequest { + + /** CompleteSchemaMigrationRequest cluster_id */ + cluster_id?: (string|null); + + /** CompleteSchemaMigrationRequest request */ + request?: (vtctldata.ICompleteSchemaMigrationRequest|null); + } + + /** Represents a CompleteSchemaMigrationRequest. */ + class CompleteSchemaMigrationRequest implements ICompleteSchemaMigrationRequest { + + /** + * Constructs a new CompleteSchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.ICompleteSchemaMigrationRequest); + + /** CompleteSchemaMigrationRequest cluster_id. */ + public cluster_id: string; + + /** CompleteSchemaMigrationRequest request. */ + public request?: (vtctldata.ICompleteSchemaMigrationRequest|null); + + /** + * Creates a new CompleteSchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns CompleteSchemaMigrationRequest instance + */ + public static create(properties?: vtadmin.ICompleteSchemaMigrationRequest): vtadmin.CompleteSchemaMigrationRequest; + + /** + * Encodes the specified CompleteSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CompleteSchemaMigrationRequest.verify|verify} messages. + * @param message CompleteSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.ICompleteSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified CompleteSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CompleteSchemaMigrationRequest.verify|verify} messages. + * @param message CompleteSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.ICompleteSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a CompleteSchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns CompleteSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.CompleteSchemaMigrationRequest; + + /** + * Decodes a CompleteSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns CompleteSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.CompleteSchemaMigrationRequest; + + /** + * Verifies a CompleteSchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a CompleteSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns CompleteSchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.CompleteSchemaMigrationRequest; + + /** + * Creates a plain object from a CompleteSchemaMigrationRequest message. Also converts values to other types if specified. + * @param message CompleteSchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.CompleteSchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this CompleteSchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for CompleteSchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a CreateKeyspaceRequest. */ interface ICreateKeyspaceRequest { @@ -5727,6 +6389,306 @@ export namespace vtadmin { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a GetSchemaMigrationsRequest. */ + interface IGetSchemaMigrationsRequest { + + /** GetSchemaMigrationsRequest cluster_requests */ + cluster_requests?: (vtadmin.GetSchemaMigrationsRequest.IClusterRequest[]|null); + } + + /** Represents a GetSchemaMigrationsRequest. */ + class GetSchemaMigrationsRequest implements IGetSchemaMigrationsRequest { + + /** + * Constructs a new GetSchemaMigrationsRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.IGetSchemaMigrationsRequest); + + /** GetSchemaMigrationsRequest cluster_requests. */ + public cluster_requests: vtadmin.GetSchemaMigrationsRequest.IClusterRequest[]; + + /** + * Creates a new GetSchemaMigrationsRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns GetSchemaMigrationsRequest instance + */ + public static create(properties?: vtadmin.IGetSchemaMigrationsRequest): vtadmin.GetSchemaMigrationsRequest; + + /** + * Encodes the specified GetSchemaMigrationsRequest message. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.verify|verify} messages. + * @param message GetSchemaMigrationsRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.IGetSchemaMigrationsRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified GetSchemaMigrationsRequest message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.verify|verify} messages. + * @param message GetSchemaMigrationsRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.IGetSchemaMigrationsRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a GetSchemaMigrationsRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns GetSchemaMigrationsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.GetSchemaMigrationsRequest; + + /** + * Decodes a GetSchemaMigrationsRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns GetSchemaMigrationsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.GetSchemaMigrationsRequest; + + /** + * Verifies a GetSchemaMigrationsRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a GetSchemaMigrationsRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns GetSchemaMigrationsRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.GetSchemaMigrationsRequest; + + /** + * Creates a plain object from a GetSchemaMigrationsRequest message. Also converts values to other types if specified. + * @param message GetSchemaMigrationsRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.GetSchemaMigrationsRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this GetSchemaMigrationsRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for GetSchemaMigrationsRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + namespace GetSchemaMigrationsRequest { + + /** Properties of a ClusterRequest. */ + interface IClusterRequest { + + /** ClusterRequest cluster_id */ + cluster_id?: (string|null); + + /** ClusterRequest request */ + request?: (vtctldata.IGetSchemaMigrationsRequest|null); + } + + /** Represents a ClusterRequest. */ + class ClusterRequest implements IClusterRequest { + + /** + * Constructs a new ClusterRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.GetSchemaMigrationsRequest.IClusterRequest); + + /** ClusterRequest cluster_id. */ + public cluster_id: string; + + /** ClusterRequest request. */ + public request?: (vtctldata.IGetSchemaMigrationsRequest|null); + + /** + * Creates a new ClusterRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns ClusterRequest instance + */ + public static create(properties?: vtadmin.GetSchemaMigrationsRequest.IClusterRequest): vtadmin.GetSchemaMigrationsRequest.ClusterRequest; + + /** + * Encodes the specified ClusterRequest message. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.ClusterRequest.verify|verify} messages. + * @param message ClusterRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.GetSchemaMigrationsRequest.IClusterRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ClusterRequest message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.ClusterRequest.verify|verify} messages. + * @param message ClusterRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.GetSchemaMigrationsRequest.IClusterRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ClusterRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ClusterRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.GetSchemaMigrationsRequest.ClusterRequest; + + /** + * Decodes a ClusterRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ClusterRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.GetSchemaMigrationsRequest.ClusterRequest; + + /** + * Verifies a ClusterRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ClusterRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ClusterRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.GetSchemaMigrationsRequest.ClusterRequest; + + /** + * Creates a plain object from a ClusterRequest message. Also converts values to other types if specified. + * @param message ClusterRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.GetSchemaMigrationsRequest.ClusterRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ClusterRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ClusterRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + } + + /** Properties of a GetSchemaMigrationsResponse. */ + interface IGetSchemaMigrationsResponse { + + /** GetSchemaMigrationsResponse schema_migrations */ + schema_migrations?: (vtadmin.ISchemaMigration[]|null); + } + + /** Represents a GetSchemaMigrationsResponse. */ + class GetSchemaMigrationsResponse implements IGetSchemaMigrationsResponse { + + /** + * Constructs a new GetSchemaMigrationsResponse. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.IGetSchemaMigrationsResponse); + + /** GetSchemaMigrationsResponse schema_migrations. */ + public schema_migrations: vtadmin.ISchemaMigration[]; + + /** + * Creates a new GetSchemaMigrationsResponse instance using the specified properties. + * @param [properties] Properties to set + * @returns GetSchemaMigrationsResponse instance + */ + public static create(properties?: vtadmin.IGetSchemaMigrationsResponse): vtadmin.GetSchemaMigrationsResponse; + + /** + * Encodes the specified GetSchemaMigrationsResponse message. Does not implicitly {@link vtadmin.GetSchemaMigrationsResponse.verify|verify} messages. + * @param message GetSchemaMigrationsResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.IGetSchemaMigrationsResponse, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified GetSchemaMigrationsResponse message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsResponse.verify|verify} messages. + * @param message GetSchemaMigrationsResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.IGetSchemaMigrationsResponse, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a GetSchemaMigrationsResponse message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns GetSchemaMigrationsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.GetSchemaMigrationsResponse; + + /** + * Decodes a GetSchemaMigrationsResponse message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns GetSchemaMigrationsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.GetSchemaMigrationsResponse; + + /** + * Verifies a GetSchemaMigrationsResponse message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a GetSchemaMigrationsResponse message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns GetSchemaMigrationsResponse + */ + public static fromObject(object: { [k: string]: any }): vtadmin.GetSchemaMigrationsResponse; + + /** + * Creates a plain object from a GetSchemaMigrationsResponse message. Also converts values to other types if specified. + * @param message GetSchemaMigrationsResponse + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.GetSchemaMigrationsResponse, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this GetSchemaMigrationsResponse to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for GetSchemaMigrationsResponse + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a GetShardReplicationPositionsRequest. */ interface IGetShardReplicationPositionsRequest { @@ -7866,6 +8828,109 @@ export namespace vtadmin { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a LaunchSchemaMigrationRequest. */ + interface ILaunchSchemaMigrationRequest { + + /** LaunchSchemaMigrationRequest cluster_id */ + cluster_id?: (string|null); + + /** LaunchSchemaMigrationRequest request */ + request?: (vtctldata.ILaunchSchemaMigrationRequest|null); + } + + /** Represents a LaunchSchemaMigrationRequest. */ + class LaunchSchemaMigrationRequest implements ILaunchSchemaMigrationRequest { + + /** + * Constructs a new LaunchSchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.ILaunchSchemaMigrationRequest); + + /** LaunchSchemaMigrationRequest cluster_id. */ + public cluster_id: string; + + /** LaunchSchemaMigrationRequest request. */ + public request?: (vtctldata.ILaunchSchemaMigrationRequest|null); + + /** + * Creates a new LaunchSchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns LaunchSchemaMigrationRequest instance + */ + public static create(properties?: vtadmin.ILaunchSchemaMigrationRequest): vtadmin.LaunchSchemaMigrationRequest; + + /** + * Encodes the specified LaunchSchemaMigrationRequest message. Does not implicitly {@link vtadmin.LaunchSchemaMigrationRequest.verify|verify} messages. + * @param message LaunchSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.ILaunchSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified LaunchSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.LaunchSchemaMigrationRequest.verify|verify} messages. + * @param message LaunchSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.ILaunchSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a LaunchSchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns LaunchSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.LaunchSchemaMigrationRequest; + + /** + * Decodes a LaunchSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns LaunchSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.LaunchSchemaMigrationRequest; + + /** + * Verifies a LaunchSchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a LaunchSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns LaunchSchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.LaunchSchemaMigrationRequest; + + /** + * Creates a plain object from a LaunchSchemaMigrationRequest message. Also converts values to other types if specified. + * @param message LaunchSchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.LaunchSchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this LaunchSchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for LaunchSchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a PingTabletRequest. */ interface IPingTabletRequest { @@ -9928,6 +10993,109 @@ export namespace vtadmin { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a RetrySchemaMigrationRequest. */ + interface IRetrySchemaMigrationRequest { + + /** RetrySchemaMigrationRequest cluster_id */ + cluster_id?: (string|null); + + /** RetrySchemaMigrationRequest request */ + request?: (vtctldata.IRetrySchemaMigrationRequest|null); + } + + /** Represents a RetrySchemaMigrationRequest. */ + class RetrySchemaMigrationRequest implements IRetrySchemaMigrationRequest { + + /** + * Constructs a new RetrySchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtadmin.IRetrySchemaMigrationRequest); + + /** RetrySchemaMigrationRequest cluster_id. */ + public cluster_id: string; + + /** RetrySchemaMigrationRequest request. */ + public request?: (vtctldata.IRetrySchemaMigrationRequest|null); + + /** + * Creates a new RetrySchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns RetrySchemaMigrationRequest instance + */ + public static create(properties?: vtadmin.IRetrySchemaMigrationRequest): vtadmin.RetrySchemaMigrationRequest; + + /** + * Encodes the specified RetrySchemaMigrationRequest message. Does not implicitly {@link vtadmin.RetrySchemaMigrationRequest.verify|verify} messages. + * @param message RetrySchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtadmin.IRetrySchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified RetrySchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.RetrySchemaMigrationRequest.verify|verify} messages. + * @param message RetrySchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtadmin.IRetrySchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a RetrySchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns RetrySchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.RetrySchemaMigrationRequest; + + /** + * Decodes a RetrySchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns RetrySchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.RetrySchemaMigrationRequest; + + /** + * Verifies a RetrySchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a RetrySchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns RetrySchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtadmin.RetrySchemaMigrationRequest; + + /** + * Creates a plain object from a RetrySchemaMigrationRequest message. Also converts values to other types if specified. + * @param message RetrySchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtadmin.RetrySchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this RetrySchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for RetrySchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a RunHealthCheckRequest. */ interface IRunHealthCheckRequest { @@ -12649,6 +13817,9 @@ export namespace mysqlctl { /** ShutdownRequest wait_for_mysqld */ wait_for_mysqld?: (boolean|null); + + /** ShutdownRequest mysql_shutdown_timeout */ + mysql_shutdown_timeout?: (vttime.IDuration|null); } /** Represents a ShutdownRequest. */ @@ -12663,6 +13834,9 @@ export namespace mysqlctl { /** ShutdownRequest wait_for_mysqld. */ public wait_for_mysqld: boolean; + /** ShutdownRequest mysql_shutdown_timeout. */ + public mysql_shutdown_timeout?: (vttime.IDuration|null); + /** * Creates a new ShutdownRequest instance using the specified properties. * @param [properties] Properties to set @@ -15090,9 +16264,6 @@ export namespace topodata { /** Properties of a Keyspace. */ interface IKeyspace { - /** Keyspace served_froms */ - served_froms?: (topodata.Keyspace.IServedFrom[]|null); - /** Keyspace keyspace_type */ keyspace_type?: (topodata.KeyspaceType|null); @@ -15121,9 +16292,6 @@ export namespace topodata { */ constructor(properties?: topodata.IKeyspace); - /** Keyspace served_froms. */ - public served_froms: topodata.Keyspace.IServedFrom[]; - /** Keyspace keyspace_type. */ public keyspace_type: topodata.KeyspaceType; @@ -15220,118 +16388,6 @@ export namespace topodata { public static getTypeUrl(typeUrlPrefix?: string): string; } - namespace Keyspace { - - /** Properties of a ServedFrom. */ - interface IServedFrom { - - /** ServedFrom tablet_type */ - tablet_type?: (topodata.TabletType|null); - - /** ServedFrom cells */ - cells?: (string[]|null); - - /** ServedFrom keyspace */ - keyspace?: (string|null); - } - - /** Represents a ServedFrom. */ - class ServedFrom implements IServedFrom { - - /** - * Constructs a new ServedFrom. - * @param [properties] Properties to set - */ - constructor(properties?: topodata.Keyspace.IServedFrom); - - /** ServedFrom tablet_type. */ - public tablet_type: topodata.TabletType; - - /** ServedFrom cells. */ - public cells: string[]; - - /** ServedFrom keyspace. */ - public keyspace: string; - - /** - * Creates a new ServedFrom instance using the specified properties. - * @param [properties] Properties to set - * @returns ServedFrom instance - */ - public static create(properties?: topodata.Keyspace.IServedFrom): topodata.Keyspace.ServedFrom; - - /** - * Encodes the specified ServedFrom message. Does not implicitly {@link topodata.Keyspace.ServedFrom.verify|verify} messages. - * @param message ServedFrom message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: topodata.Keyspace.IServedFrom, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified ServedFrom message, length delimited. Does not implicitly {@link topodata.Keyspace.ServedFrom.verify|verify} messages. - * @param message ServedFrom message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: topodata.Keyspace.IServedFrom, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a ServedFrom message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): topodata.Keyspace.ServedFrom; - - /** - * Decodes a ServedFrom message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): topodata.Keyspace.ServedFrom; - - /** - * Verifies a ServedFrom message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a ServedFrom message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns ServedFrom - */ - public static fromObject(object: { [k: string]: any }): topodata.Keyspace.ServedFrom; - - /** - * Creates a plain object from a ServedFrom message. Also converts values to other types if specified. - * @param message ServedFrom - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: topodata.Keyspace.ServedFrom, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this ServedFrom to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - - /** - * Gets the default type url for ServedFrom - * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns The default type url - */ - public static getTypeUrl(typeUrlPrefix?: string): string; - } - } - /** Properties of a ShardReplication. */ interface IShardReplication { @@ -16096,9 +17152,6 @@ export namespace topodata { /** SrvKeyspace partitions */ partitions?: (topodata.SrvKeyspace.IKeyspacePartition[]|null); - /** SrvKeyspace served_from */ - served_from?: (topodata.SrvKeyspace.IServedFrom[]|null); - /** SrvKeyspace throttler_config */ throttler_config?: (topodata.IThrottlerConfig|null); } @@ -16115,9 +17168,6 @@ export namespace topodata { /** SrvKeyspace partitions. */ public partitions: topodata.SrvKeyspace.IKeyspacePartition[]; - /** SrvKeyspace served_from. */ - public served_from: topodata.SrvKeyspace.IServedFrom[]; - /** SrvKeyspace throttler_config. */ public throttler_config?: (topodata.IThrottlerConfig|null); @@ -16309,109 +17359,6 @@ export namespace topodata { */ public static getTypeUrl(typeUrlPrefix?: string): string; } - - /** Properties of a ServedFrom. */ - interface IServedFrom { - - /** ServedFrom tablet_type */ - tablet_type?: (topodata.TabletType|null); - - /** ServedFrom keyspace */ - keyspace?: (string|null); - } - - /** Represents a ServedFrom. */ - class ServedFrom implements IServedFrom { - - /** - * Constructs a new ServedFrom. - * @param [properties] Properties to set - */ - constructor(properties?: topodata.SrvKeyspace.IServedFrom); - - /** ServedFrom tablet_type. */ - public tablet_type: topodata.TabletType; - - /** ServedFrom keyspace. */ - public keyspace: string; - - /** - * Creates a new ServedFrom instance using the specified properties. - * @param [properties] Properties to set - * @returns ServedFrom instance - */ - public static create(properties?: topodata.SrvKeyspace.IServedFrom): topodata.SrvKeyspace.ServedFrom; - - /** - * Encodes the specified ServedFrom message. Does not implicitly {@link topodata.SrvKeyspace.ServedFrom.verify|verify} messages. - * @param message ServedFrom message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: topodata.SrvKeyspace.IServedFrom, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified ServedFrom message, length delimited. Does not implicitly {@link topodata.SrvKeyspace.ServedFrom.verify|verify} messages. - * @param message ServedFrom message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: topodata.SrvKeyspace.IServedFrom, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a ServedFrom message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): topodata.SrvKeyspace.ServedFrom; - - /** - * Decodes a ServedFrom message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): topodata.SrvKeyspace.ServedFrom; - - /** - * Verifies a ServedFrom message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a ServedFrom message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns ServedFrom - */ - public static fromObject(object: { [k: string]: any }): topodata.SrvKeyspace.ServedFrom; - - /** - * Creates a plain object from a ServedFrom message. Also converts values to other types if specified. - * @param message ServedFrom - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: topodata.SrvKeyspace.ServedFrom, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this ServedFrom to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - - /** - * Gets the default type url for ServedFrom - * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns The default type url - */ - public static getTypeUrl(typeUrlPrefix?: string): string; - } } /** Properties of a CellInfo. */ @@ -20739,6 +21686,9 @@ export namespace tabletmanagerdata { /** ExecuteFetchAsDbaRequest reload_schema */ reload_schema?: (boolean|null); + + /** ExecuteFetchAsDbaRequest disable_foreign_key_checks */ + disable_foreign_key_checks?: (boolean|null); } /** Represents an ExecuteFetchAsDbaRequest. */ @@ -20765,6 +21715,9 @@ export namespace tabletmanagerdata { /** ExecuteFetchAsDbaRequest reload_schema. */ public reload_schema: boolean; + /** ExecuteFetchAsDbaRequest disable_foreign_key_checks. */ + public disable_foreign_key_checks: boolean; + /** * Creates a new ExecuteFetchAsDbaRequest instance using the specified properties. * @param [properties] Properties to set @@ -25952,7 +26905,7 @@ export namespace tabletmanagerdata { interface IBackupRequest { /** BackupRequest concurrency */ - concurrency?: (number|Long|null); + concurrency?: (number|null); /** BackupRequest allow_primary */ allow_primary?: (boolean|null); @@ -25974,7 +26927,7 @@ export namespace tabletmanagerdata { constructor(properties?: tabletmanagerdata.IBackupRequest); /** BackupRequest concurrency. */ - public concurrency: (number|Long); + public concurrency: number; /** BackupRequest allow_primary. */ public allow_primary: boolean; @@ -27596,6 +28549,9 @@ export namespace tabletmanagerdata { /** VDiffReportOptions format */ format?: (string|null); + + /** VDiffReportOptions max_sample_rows */ + max_sample_rows?: (number|Long|null); } /** Represents a VDiffReportOptions. */ @@ -27616,6 +28572,9 @@ export namespace tabletmanagerdata { /** VDiffReportOptions format. */ public format: string; + /** VDiffReportOptions max_sample_rows. */ + public max_sample_rows: (number|Long); + /** * Creates a new VDiffReportOptions instance using the specified properties. * @param [properties] Properties to set @@ -27720,6 +28679,9 @@ export namespace tabletmanagerdata { /** VDiffCoreOptions update_table_stats */ update_table_stats?: (boolean|null); + + /** VDiffCoreOptions max_diff_seconds */ + max_diff_seconds?: (number|Long|null); } /** Represents a VDiffCoreOptions. */ @@ -27755,6 +28717,9 @@ export namespace tabletmanagerdata { /** VDiffCoreOptions update_table_stats. */ public update_table_stats: boolean; + /** VDiffCoreOptions max_diff_seconds. */ + public max_diff_seconds: (number|Long); + /** * Creates a new VDiffCoreOptions instance using the specified properties. * @param [properties] Properties to set @@ -27962,6 +28927,9 @@ export namespace tabletmanagerdata { /** UpdateVReplicationWorkflowRequest state */ state?: (binlogdata.VReplicationWorkflowState|null); + + /** UpdateVReplicationWorkflowRequest shards */ + shards?: (string[]|null); } /** Represents an UpdateVReplicationWorkflowRequest. */ @@ -27991,6 +28959,9 @@ export namespace tabletmanagerdata { /** UpdateVReplicationWorkflowRequest state. */ public state: binlogdata.VReplicationWorkflowState; + /** UpdateVReplicationWorkflowRequest shards. */ + public shards: string[]; + /** * Creates a new UpdateVReplicationWorkflowRequest instance using the specified properties. * @param [properties] Properties to set @@ -29464,6 +30435,9 @@ export namespace binlogdata { /** Rule convert_int_to_enum */ convert_int_to_enum?: ({ [k: string]: boolean }|null); + + /** Rule force_unique_key */ + force_unique_key?: (string|null); } /** Represents a Rule. */ @@ -29499,6 +30473,9 @@ export namespace binlogdata { /** Rule convert_int_to_enum. */ public convert_int_to_enum: { [k: string]: boolean }; + /** Rule force_unique_key. */ + public force_unique_key: string; + /** * Creates a new Rule instance using the specified properties. * @param [properties] Properties to set @@ -41592,6 +42569,24 @@ export namespace vschema { /** Column invisible */ invisible?: (boolean|null); + + /** Column default */ + "default"?: (string|null); + + /** Column collation_name */ + collation_name?: (string|null); + + /** Column size */ + size?: (number|null); + + /** Column scale */ + scale?: (number|null); + + /** Column nullable */ + nullable?: (boolean|null); + + /** Column values */ + values?: (string[]|null); } /** Represents a Column. */ @@ -41612,6 +42607,27 @@ export namespace vschema { /** Column invisible. */ public invisible: boolean; + /** Column default. */ + public default: string; + + /** Column collation_name. */ + public collation_name: string; + + /** Column size. */ + public size: number; + + /** Column scale. */ + public scale: number; + + /** Column nullable. */ + public nullable?: (boolean|null); + + /** Column values. */ + public values: string[]; + + /** Column _nullable. */ + public _nullable?: "nullable"; + /** * Creates a new Column instance using the specified properties. * @param [properties] Properties to set @@ -43583,6 +44599,15 @@ export namespace vtctldata { /** Stream throttler_status */ throttler_status?: (vtctldata.Workflow.Stream.IThrottlerStatus|null); + + /** Stream tablet_types */ + tablet_types?: (topodata.TabletType[]|null); + + /** Stream tablet_selection_preference */ + tablet_selection_preference?: (tabletmanagerdata.TabletSelectionPreference|null); + + /** Stream cells */ + cells?: (string[]|null); } /** Represents a Stream. */ @@ -43645,6 +44670,15 @@ export namespace vtctldata { /** Stream throttler_status. */ public throttler_status?: (vtctldata.Workflow.Stream.IThrottlerStatus|null); + /** Stream tablet_types. */ + public tablet_types: topodata.TabletType[]; + + /** Stream tablet_selection_preference. */ + public tablet_selection_preference: tabletmanagerdata.TabletSelectionPreference; + + /** Stream cells. */ + public cells: string[]; + /** * Creates a new Stream instance using the specified properties. * @param [properties] Properties to set @@ -43733,6 +44767,9 @@ export namespace vtctldata { /** CopyState last_pk */ last_pk?: (string|null); + + /** CopyState stream_id */ + stream_id?: (number|Long|null); } /** Represents a CopyState. */ @@ -43750,6 +44787,9 @@ export namespace vtctldata { /** CopyState last_pk. */ public last_pk: string; + /** CopyState stream_id. */ + public stream_id: (number|Long); + /** * Creates a new CopyState instance using the specified properties. * @param [properties] Properties to set @@ -45122,6 +46162,9 @@ export namespace vtctldata { /** ApplyVSchemaRequest sql */ sql?: (string|null); + + /** ApplyVSchemaRequest strict */ + strict?: (boolean|null); } /** Represents an ApplyVSchemaRequest. */ @@ -45151,6 +46194,9 @@ export namespace vtctldata { /** ApplyVSchemaRequest sql. */ public sql: string; + /** ApplyVSchemaRequest strict. */ + public strict: boolean; + /** * Creates a new ApplyVSchemaRequest instance using the specified properties. * @param [properties] Properties to set @@ -45234,6 +46280,9 @@ export namespace vtctldata { /** ApplyVSchemaResponse v_schema */ v_schema?: (vschema.IKeyspace|null); + + /** ApplyVSchemaResponse unknown_vindex_params */ + unknown_vindex_params?: ({ [k: string]: vtctldata.ApplyVSchemaResponse.IParamList }|null); } /** Represents an ApplyVSchemaResponse. */ @@ -45248,6 +46297,9 @@ export namespace vtctldata { /** ApplyVSchemaResponse v_schema. */ public v_schema?: (vschema.IKeyspace|null); + /** ApplyVSchemaResponse unknown_vindex_params. */ + public unknown_vindex_params: { [k: string]: vtctldata.ApplyVSchemaResponse.IParamList }; + /** * Creates a new ApplyVSchemaResponse instance using the specified properties. * @param [properties] Properties to set @@ -45326,6 +46378,106 @@ export namespace vtctldata { public static getTypeUrl(typeUrlPrefix?: string): string; } + namespace ApplyVSchemaResponse { + + /** Properties of a ParamList. */ + interface IParamList { + + /** ParamList params */ + params?: (string[]|null); + } + + /** Represents a ParamList. */ + class ParamList implements IParamList { + + /** + * Constructs a new ParamList. + * @param [properties] Properties to set + */ + constructor(properties?: vtctldata.ApplyVSchemaResponse.IParamList); + + /** ParamList params. */ + public params: string[]; + + /** + * Creates a new ParamList instance using the specified properties. + * @param [properties] Properties to set + * @returns ParamList instance + */ + public static create(properties?: vtctldata.ApplyVSchemaResponse.IParamList): vtctldata.ApplyVSchemaResponse.ParamList; + + /** + * Encodes the specified ParamList message. Does not implicitly {@link vtctldata.ApplyVSchemaResponse.ParamList.verify|verify} messages. + * @param message ParamList message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtctldata.ApplyVSchemaResponse.IParamList, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ParamList message, length delimited. Does not implicitly {@link vtctldata.ApplyVSchemaResponse.ParamList.verify|verify} messages. + * @param message ParamList message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtctldata.ApplyVSchemaResponse.IParamList, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ParamList message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ParamList + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtctldata.ApplyVSchemaResponse.ParamList; + + /** + * Decodes a ParamList message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ParamList + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtctldata.ApplyVSchemaResponse.ParamList; + + /** + * Verifies a ParamList message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ParamList message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ParamList + */ + public static fromObject(object: { [k: string]: any }): vtctldata.ApplyVSchemaResponse.ParamList; + + /** + * Creates a plain object from a ParamList message. Also converts values to other types if specified. + * @param message ParamList + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtctldata.ApplyVSchemaResponse.ParamList, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ParamList to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ParamList + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + } + /** Properties of a BackupRequest. */ interface IBackupRequest { @@ -45336,7 +46488,7 @@ export namespace vtctldata { allow_primary?: (boolean|null); /** BackupRequest concurrency */ - concurrency?: (number|Long|null); + concurrency?: (number|null); /** BackupRequest incremental_from_pos */ incremental_from_pos?: (string|null); @@ -45361,7 +46513,7 @@ export namespace vtctldata { public allow_primary: boolean; /** BackupRequest concurrency. */ - public concurrency: (number|Long); + public concurrency: number; /** BackupRequest incremental_from_pos. */ public incremental_from_pos: string; @@ -45575,7 +46727,7 @@ export namespace vtctldata { allow_primary?: (boolean|null); /** BackupShardRequest concurrency */ - concurrency?: (number|Long|null); + concurrency?: (number|null); /** BackupShardRequest upgrade_safe */ upgrade_safe?: (boolean|null); @@ -45603,7 +46755,7 @@ export namespace vtctldata { public allow_primary: boolean; /** BackupShardRequest concurrency. */ - public concurrency: (number|Long); + public concurrency: number; /** BackupShardRequest upgrade_safe. */ public upgrade_safe: boolean; @@ -46519,9 +47671,6 @@ export namespace vtctldata { /** CreateKeyspaceRequest allow_empty_v_schema */ allow_empty_v_schema?: (boolean|null); - /** CreateKeyspaceRequest served_froms */ - served_froms?: (topodata.Keyspace.IServedFrom[]|null); - /** CreateKeyspaceRequest type */ type?: (topodata.KeyspaceType|null); @@ -46556,9 +47705,6 @@ export namespace vtctldata { /** CreateKeyspaceRequest allow_empty_v_schema. */ public allow_empty_v_schema: boolean; - /** CreateKeyspaceRequest served_froms. */ - public served_froms: topodata.Keyspace.IServedFrom[]; - /** CreateKeyspaceRequest type. */ public type: topodata.KeyspaceType; @@ -49215,6 +50361,206 @@ export namespace vtctldata { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a ForceCutOverSchemaMigrationRequest. */ + interface IForceCutOverSchemaMigrationRequest { + + /** ForceCutOverSchemaMigrationRequest keyspace */ + keyspace?: (string|null); + + /** ForceCutOverSchemaMigrationRequest uuid */ + uuid?: (string|null); + } + + /** Represents a ForceCutOverSchemaMigrationRequest. */ + class ForceCutOverSchemaMigrationRequest implements IForceCutOverSchemaMigrationRequest { + + /** + * Constructs a new ForceCutOverSchemaMigrationRequest. + * @param [properties] Properties to set + */ + constructor(properties?: vtctldata.IForceCutOverSchemaMigrationRequest); + + /** ForceCutOverSchemaMigrationRequest keyspace. */ + public keyspace: string; + + /** ForceCutOverSchemaMigrationRequest uuid. */ + public uuid: string; + + /** + * Creates a new ForceCutOverSchemaMigrationRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns ForceCutOverSchemaMigrationRequest instance + */ + public static create(properties?: vtctldata.IForceCutOverSchemaMigrationRequest): vtctldata.ForceCutOverSchemaMigrationRequest; + + /** + * Encodes the specified ForceCutOverSchemaMigrationRequest message. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationRequest.verify|verify} messages. + * @param message ForceCutOverSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtctldata.IForceCutOverSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ForceCutOverSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationRequest.verify|verify} messages. + * @param message ForceCutOverSchemaMigrationRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtctldata.IForceCutOverSchemaMigrationRequest, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ForceCutOverSchemaMigrationRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ForceCutOverSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtctldata.ForceCutOverSchemaMigrationRequest; + + /** + * Decodes a ForceCutOverSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ForceCutOverSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtctldata.ForceCutOverSchemaMigrationRequest; + + /** + * Verifies a ForceCutOverSchemaMigrationRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ForceCutOverSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ForceCutOverSchemaMigrationRequest + */ + public static fromObject(object: { [k: string]: any }): vtctldata.ForceCutOverSchemaMigrationRequest; + + /** + * Creates a plain object from a ForceCutOverSchemaMigrationRequest message. Also converts values to other types if specified. + * @param message ForceCutOverSchemaMigrationRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtctldata.ForceCutOverSchemaMigrationRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ForceCutOverSchemaMigrationRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ForceCutOverSchemaMigrationRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a ForceCutOverSchemaMigrationResponse. */ + interface IForceCutOverSchemaMigrationResponse { + + /** ForceCutOverSchemaMigrationResponse rows_affected_by_shard */ + rows_affected_by_shard?: ({ [k: string]: (number|Long) }|null); + } + + /** Represents a ForceCutOverSchemaMigrationResponse. */ + class ForceCutOverSchemaMigrationResponse implements IForceCutOverSchemaMigrationResponse { + + /** + * Constructs a new ForceCutOverSchemaMigrationResponse. + * @param [properties] Properties to set + */ + constructor(properties?: vtctldata.IForceCutOverSchemaMigrationResponse); + + /** ForceCutOverSchemaMigrationResponse rows_affected_by_shard. */ + public rows_affected_by_shard: { [k: string]: (number|Long) }; + + /** + * Creates a new ForceCutOverSchemaMigrationResponse instance using the specified properties. + * @param [properties] Properties to set + * @returns ForceCutOverSchemaMigrationResponse instance + */ + public static create(properties?: vtctldata.IForceCutOverSchemaMigrationResponse): vtctldata.ForceCutOverSchemaMigrationResponse; + + /** + * Encodes the specified ForceCutOverSchemaMigrationResponse message. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationResponse.verify|verify} messages. + * @param message ForceCutOverSchemaMigrationResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: vtctldata.IForceCutOverSchemaMigrationResponse, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified ForceCutOverSchemaMigrationResponse message, length delimited. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationResponse.verify|verify} messages. + * @param message ForceCutOverSchemaMigrationResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: vtctldata.IForceCutOverSchemaMigrationResponse, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a ForceCutOverSchemaMigrationResponse message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns ForceCutOverSchemaMigrationResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtctldata.ForceCutOverSchemaMigrationResponse; + + /** + * Decodes a ForceCutOverSchemaMigrationResponse message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns ForceCutOverSchemaMigrationResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtctldata.ForceCutOverSchemaMigrationResponse; + + /** + * Verifies a ForceCutOverSchemaMigrationResponse message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a ForceCutOverSchemaMigrationResponse message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns ForceCutOverSchemaMigrationResponse + */ + public static fromObject(object: { [k: string]: any }): vtctldata.ForceCutOverSchemaMigrationResponse; + + /** + * Creates a plain object from a ForceCutOverSchemaMigrationResponse message. Also converts values to other types if specified. + * @param message ForceCutOverSchemaMigrationResponse + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: vtctldata.ForceCutOverSchemaMigrationResponse, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this ForceCutOverSchemaMigrationResponse to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for ForceCutOverSchemaMigrationResponse + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a GetBackupsRequest. */ interface IGetBackupsRequest { @@ -54065,6 +55411,9 @@ export namespace vtctldata { /** GetWorkflowsRequest include_logs */ include_logs?: (boolean|null); + + /** GetWorkflowsRequest shards */ + shards?: (string[]|null); } /** Represents a GetWorkflowsRequest. */ @@ -54091,6 +55440,9 @@ export namespace vtctldata { /** GetWorkflowsRequest include_logs. */ public include_logs: boolean; + /** GetWorkflowsRequest shards. */ + public shards: string[]; + /** * Creates a new GetWorkflowsRequest instance using the specified properties. * @param [properties] Properties to set @@ -56953,6 +58305,9 @@ export namespace vtctldata { /** MoveTablesCompleteRequest dry_run */ dry_run?: (boolean|null); + + /** MoveTablesCompleteRequest shards */ + shards?: (string[]|null); } /** Represents a MoveTablesCompleteRequest. */ @@ -56982,6 +58337,9 @@ export namespace vtctldata { /** MoveTablesCompleteRequest dry_run. */ public dry_run: boolean; + /** MoveTablesCompleteRequest shards. */ + public shards: string[]; + /** * Creates a new MoveTablesCompleteRequest instance using the specified properties. * @param [properties] Properties to set @@ -57368,6 +58726,9 @@ export namespace vtctldata { /** PlannedReparentShardRequest wait_replicas_timeout */ wait_replicas_timeout?: (vttime.IDuration|null); + + /** PlannedReparentShardRequest tolerable_replication_lag */ + tolerable_replication_lag?: (vttime.IDuration|null); } /** Represents a PlannedReparentShardRequest. */ @@ -57394,6 +58755,9 @@ export namespace vtctldata { /** PlannedReparentShardRequest wait_replicas_timeout. */ public wait_replicas_timeout?: (vttime.IDuration|null); + /** PlannedReparentShardRequest tolerable_replication_lag. */ + public tolerable_replication_lag?: (vttime.IDuration|null); + /** * Creates a new PlannedReparentShardRequest instance using the specified properties. * @param [properties] Properties to set @@ -60804,224 +62168,6 @@ export namespace vtctldata { public static getTypeUrl(typeUrlPrefix?: string): string; } - /** Properties of a SetKeyspaceServedFromRequest. */ - interface ISetKeyspaceServedFromRequest { - - /** SetKeyspaceServedFromRequest keyspace */ - keyspace?: (string|null); - - /** SetKeyspaceServedFromRequest tablet_type */ - tablet_type?: (topodata.TabletType|null); - - /** SetKeyspaceServedFromRequest cells */ - cells?: (string[]|null); - - /** SetKeyspaceServedFromRequest remove */ - remove?: (boolean|null); - - /** SetKeyspaceServedFromRequest source_keyspace */ - source_keyspace?: (string|null); - } - - /** Represents a SetKeyspaceServedFromRequest. */ - class SetKeyspaceServedFromRequest implements ISetKeyspaceServedFromRequest { - - /** - * Constructs a new SetKeyspaceServedFromRequest. - * @param [properties] Properties to set - */ - constructor(properties?: vtctldata.ISetKeyspaceServedFromRequest); - - /** SetKeyspaceServedFromRequest keyspace. */ - public keyspace: string; - - /** SetKeyspaceServedFromRequest tablet_type. */ - public tablet_type: topodata.TabletType; - - /** SetKeyspaceServedFromRequest cells. */ - public cells: string[]; - - /** SetKeyspaceServedFromRequest remove. */ - public remove: boolean; - - /** SetKeyspaceServedFromRequest source_keyspace. */ - public source_keyspace: string; - - /** - * Creates a new SetKeyspaceServedFromRequest instance using the specified properties. - * @param [properties] Properties to set - * @returns SetKeyspaceServedFromRequest instance - */ - public static create(properties?: vtctldata.ISetKeyspaceServedFromRequest): vtctldata.SetKeyspaceServedFromRequest; - - /** - * Encodes the specified SetKeyspaceServedFromRequest message. Does not implicitly {@link vtctldata.SetKeyspaceServedFromRequest.verify|verify} messages. - * @param message SetKeyspaceServedFromRequest message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: vtctldata.ISetKeyspaceServedFromRequest, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified SetKeyspaceServedFromRequest message, length delimited. Does not implicitly {@link vtctldata.SetKeyspaceServedFromRequest.verify|verify} messages. - * @param message SetKeyspaceServedFromRequest message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: vtctldata.ISetKeyspaceServedFromRequest, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a SetKeyspaceServedFromRequest message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns SetKeyspaceServedFromRequest - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtctldata.SetKeyspaceServedFromRequest; - - /** - * Decodes a SetKeyspaceServedFromRequest message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns SetKeyspaceServedFromRequest - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtctldata.SetKeyspaceServedFromRequest; - - /** - * Verifies a SetKeyspaceServedFromRequest message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a SetKeyspaceServedFromRequest message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns SetKeyspaceServedFromRequest - */ - public static fromObject(object: { [k: string]: any }): vtctldata.SetKeyspaceServedFromRequest; - - /** - * Creates a plain object from a SetKeyspaceServedFromRequest message. Also converts values to other types if specified. - * @param message SetKeyspaceServedFromRequest - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: vtctldata.SetKeyspaceServedFromRequest, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this SetKeyspaceServedFromRequest to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - - /** - * Gets the default type url for SetKeyspaceServedFromRequest - * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns The default type url - */ - public static getTypeUrl(typeUrlPrefix?: string): string; - } - - /** Properties of a SetKeyspaceServedFromResponse. */ - interface ISetKeyspaceServedFromResponse { - - /** SetKeyspaceServedFromResponse keyspace */ - keyspace?: (topodata.IKeyspace|null); - } - - /** Represents a SetKeyspaceServedFromResponse. */ - class SetKeyspaceServedFromResponse implements ISetKeyspaceServedFromResponse { - - /** - * Constructs a new SetKeyspaceServedFromResponse. - * @param [properties] Properties to set - */ - constructor(properties?: vtctldata.ISetKeyspaceServedFromResponse); - - /** SetKeyspaceServedFromResponse keyspace. */ - public keyspace?: (topodata.IKeyspace|null); - - /** - * Creates a new SetKeyspaceServedFromResponse instance using the specified properties. - * @param [properties] Properties to set - * @returns SetKeyspaceServedFromResponse instance - */ - public static create(properties?: vtctldata.ISetKeyspaceServedFromResponse): vtctldata.SetKeyspaceServedFromResponse; - - /** - * Encodes the specified SetKeyspaceServedFromResponse message. Does not implicitly {@link vtctldata.SetKeyspaceServedFromResponse.verify|verify} messages. - * @param message SetKeyspaceServedFromResponse message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: vtctldata.ISetKeyspaceServedFromResponse, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified SetKeyspaceServedFromResponse message, length delimited. Does not implicitly {@link vtctldata.SetKeyspaceServedFromResponse.verify|verify} messages. - * @param message SetKeyspaceServedFromResponse message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: vtctldata.ISetKeyspaceServedFromResponse, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a SetKeyspaceServedFromResponse message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns SetKeyspaceServedFromResponse - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtctldata.SetKeyspaceServedFromResponse; - - /** - * Decodes a SetKeyspaceServedFromResponse message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns SetKeyspaceServedFromResponse - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtctldata.SetKeyspaceServedFromResponse; - - /** - * Verifies a SetKeyspaceServedFromResponse message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a SetKeyspaceServedFromResponse message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns SetKeyspaceServedFromResponse - */ - public static fromObject(object: { [k: string]: any }): vtctldata.SetKeyspaceServedFromResponse; - - /** - * Creates a plain object from a SetKeyspaceServedFromResponse message. Also converts values to other types if specified. - * @param message SetKeyspaceServedFromResponse - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: vtctldata.SetKeyspaceServedFromResponse, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this SetKeyspaceServedFromResponse to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - - /** - * Gets the default type url for SetKeyspaceServedFromResponse - * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns The default type url - */ - public static getTypeUrl(typeUrlPrefix?: string): string; - } - /** Properties of a SetKeyspaceShardingInfoRequest. */ interface ISetKeyspaceShardingInfoRequest { @@ -65804,6 +66950,12 @@ export namespace vtctldata { /** VDiffCreateRequest verbose */ verbose?: (boolean|null); + + /** VDiffCreateRequest max_report_sample_rows */ + max_report_sample_rows?: (number|Long|null); + + /** VDiffCreateRequest max_diff_duration */ + max_diff_duration?: (vttime.IDuration|null); } /** Represents a VDiffCreateRequest. */ @@ -65869,6 +67021,12 @@ export namespace vtctldata { /** VDiffCreateRequest verbose. */ public verbose: boolean; + /** VDiffCreateRequest max_report_sample_rows. */ + public max_report_sample_rows: (number|Long); + + /** VDiffCreateRequest max_diff_duration. */ + public max_diff_duration?: (vttime.IDuration|null); + /** * Creates a new VDiffCreateRequest instance using the specified properties. * @param [properties] Properties to set @@ -66864,6 +68022,9 @@ export namespace vtctldata { /** WorkflowDeleteRequest keep_routing_rules */ keep_routing_rules?: (boolean|null); + + /** WorkflowDeleteRequest shards */ + shards?: (string[]|null); } /** Represents a WorkflowDeleteRequest. */ @@ -66887,6 +68048,9 @@ export namespace vtctldata { /** WorkflowDeleteRequest keep_routing_rules. */ public keep_routing_rules: boolean; + /** WorkflowDeleteRequest shards. */ + public shards: string[]; + /** * Creates a new WorkflowDeleteRequest instance using the specified properties. * @param [properties] Properties to set @@ -67182,6 +68346,9 @@ export namespace vtctldata { /** WorkflowStatusRequest workflow */ workflow?: (string|null); + + /** WorkflowStatusRequest shards */ + shards?: (string[]|null); } /** Represents a WorkflowStatusRequest. */ @@ -67199,6 +68366,9 @@ export namespace vtctldata { /** WorkflowStatusRequest workflow. */ public workflow: string; + /** WorkflowStatusRequest shards. */ + public shards: string[]; + /** * Creates a new WorkflowStatusRequest instance using the specified properties. * @param [properties] Properties to set @@ -67772,6 +68942,9 @@ export namespace vtctldata { /** WorkflowSwitchTrafficRequest initialize_target_sequences */ initialize_target_sequences?: (boolean|null); + + /** WorkflowSwitchTrafficRequest shards */ + shards?: (string[]|null); } /** Represents a WorkflowSwitchTrafficRequest. */ @@ -67813,6 +68986,9 @@ export namespace vtctldata { /** WorkflowSwitchTrafficRequest initialize_target_sequences. */ public initialize_target_sequences: boolean; + /** WorkflowSwitchTrafficRequest shards. */ + public shards: string[]; + /** * Creates a new WorkflowSwitchTrafficRequest instance using the specified properties. * @param [properties] Properties to set diff --git a/web/vtadmin/src/proto/vtadmin.js b/web/vtadmin/src/proto/vtadmin.js index 9ddce1d0059..957efd73ed0 100644 --- a/web/vtadmin/src/proto/vtadmin.js +++ b/web/vtadmin/src/proto/vtadmin.js @@ -48,6 +48,138 @@ export const vtadmin = $root.vtadmin = (() => { return new this(rpcImpl, requestDelimited, responseDelimited); }; + /** + * Callback as used by {@link vtadmin.VTAdmin#applySchema}. + * @memberof vtadmin.VTAdmin + * @typedef ApplySchemaCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.ApplySchemaResponse} [response] ApplySchemaResponse + */ + + /** + * Calls ApplySchema. + * @function applySchema + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IApplySchemaRequest} request ApplySchemaRequest message or plain object + * @param {vtadmin.VTAdmin.ApplySchemaCallback} callback Node-style callback called with the error, if any, and ApplySchemaResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.applySchema = function applySchema(request, callback) { + return this.rpcCall(applySchema, $root.vtadmin.ApplySchemaRequest, $root.vtctldata.ApplySchemaResponse, request, callback); + }, "name", { value: "ApplySchema" }); + + /** + * Calls ApplySchema. + * @function applySchema + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IApplySchemaRequest} request ApplySchemaRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link vtadmin.VTAdmin#cancelSchemaMigration}. + * @memberof vtadmin.VTAdmin + * @typedef CancelSchemaMigrationCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.CancelSchemaMigrationResponse} [response] CancelSchemaMigrationResponse + */ + + /** + * Calls CancelSchemaMigration. + * @function cancelSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICancelSchemaMigrationRequest} request CancelSchemaMigrationRequest message or plain object + * @param {vtadmin.VTAdmin.CancelSchemaMigrationCallback} callback Node-style callback called with the error, if any, and CancelSchemaMigrationResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.cancelSchemaMigration = function cancelSchemaMigration(request, callback) { + return this.rpcCall(cancelSchemaMigration, $root.vtadmin.CancelSchemaMigrationRequest, $root.vtctldata.CancelSchemaMigrationResponse, request, callback); + }, "name", { value: "CancelSchemaMigration" }); + + /** + * Calls CancelSchemaMigration. + * @function cancelSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICancelSchemaMigrationRequest} request CancelSchemaMigrationRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link vtadmin.VTAdmin#cleanupSchemaMigration}. + * @memberof vtadmin.VTAdmin + * @typedef CleanupSchemaMigrationCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.CleanupSchemaMigrationResponse} [response] CleanupSchemaMigrationResponse + */ + + /** + * Calls CleanupSchemaMigration. + * @function cleanupSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICleanupSchemaMigrationRequest} request CleanupSchemaMigrationRequest message or plain object + * @param {vtadmin.VTAdmin.CleanupSchemaMigrationCallback} callback Node-style callback called with the error, if any, and CleanupSchemaMigrationResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.cleanupSchemaMigration = function cleanupSchemaMigration(request, callback) { + return this.rpcCall(cleanupSchemaMigration, $root.vtadmin.CleanupSchemaMigrationRequest, $root.vtctldata.CleanupSchemaMigrationResponse, request, callback); + }, "name", { value: "CleanupSchemaMigration" }); + + /** + * Calls CleanupSchemaMigration. + * @function cleanupSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICleanupSchemaMigrationRequest} request CleanupSchemaMigrationRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + + /** + * Callback as used by {@link vtadmin.VTAdmin#completeSchemaMigration}. + * @memberof vtadmin.VTAdmin + * @typedef CompleteSchemaMigrationCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.CompleteSchemaMigrationResponse} [response] CompleteSchemaMigrationResponse + */ + + /** + * Calls CompleteSchemaMigration. + * @function completeSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICompleteSchemaMigrationRequest} request CompleteSchemaMigrationRequest message or plain object + * @param {vtadmin.VTAdmin.CompleteSchemaMigrationCallback} callback Node-style callback called with the error, if any, and CompleteSchemaMigrationResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.completeSchemaMigration = function completeSchemaMigration(request, callback) { + return this.rpcCall(completeSchemaMigration, $root.vtadmin.CompleteSchemaMigrationRequest, $root.vtctldata.CompleteSchemaMigrationResponse, request, callback); + }, "name", { value: "CompleteSchemaMigration" }); + + /** + * Calls CompleteSchemaMigration. + * @function completeSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ICompleteSchemaMigrationRequest} request CompleteSchemaMigrationRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + /** * Callback as used by {@link vtadmin.VTAdmin#createKeyspace}. * @memberof vtadmin.VTAdmin @@ -609,6 +741,39 @@ export const vtadmin = $root.vtadmin = (() => { * @variation 2 */ + /** + * Callback as used by {@link vtadmin.VTAdmin#getSchemaMigrations}. + * @memberof vtadmin.VTAdmin + * @typedef GetSchemaMigrationsCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtadmin.GetSchemaMigrationsResponse} [response] GetSchemaMigrationsResponse + */ + + /** + * Calls GetSchemaMigrations. + * @function getSchemaMigrations + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IGetSchemaMigrationsRequest} request GetSchemaMigrationsRequest message or plain object + * @param {vtadmin.VTAdmin.GetSchemaMigrationsCallback} callback Node-style callback called with the error, if any, and GetSchemaMigrationsResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.getSchemaMigrations = function getSchemaMigrations(request, callback) { + return this.rpcCall(getSchemaMigrations, $root.vtadmin.GetSchemaMigrationsRequest, $root.vtadmin.GetSchemaMigrationsResponse, request, callback); + }, "name", { value: "GetSchemaMigrations" }); + + /** + * Calls GetSchemaMigrations. + * @function getSchemaMigrations + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IGetSchemaMigrationsRequest} request GetSchemaMigrationsRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + /** * Callback as used by {@link vtadmin.VTAdmin#getShardReplicationPositions}. * @memberof vtadmin.VTAdmin @@ -1038,6 +1203,39 @@ export const vtadmin = $root.vtadmin = (() => { * @variation 2 */ + /** + * Callback as used by {@link vtadmin.VTAdmin#launchSchemaMigration}. + * @memberof vtadmin.VTAdmin + * @typedef LaunchSchemaMigrationCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.LaunchSchemaMigrationResponse} [response] LaunchSchemaMigrationResponse + */ + + /** + * Calls LaunchSchemaMigration. + * @function launchSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ILaunchSchemaMigrationRequest} request LaunchSchemaMigrationRequest message or plain object + * @param {vtadmin.VTAdmin.LaunchSchemaMigrationCallback} callback Node-style callback called with the error, if any, and LaunchSchemaMigrationResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.launchSchemaMigration = function launchSchemaMigration(request, callback) { + return this.rpcCall(launchSchemaMigration, $root.vtadmin.LaunchSchemaMigrationRequest, $root.vtctldata.LaunchSchemaMigrationResponse, request, callback); + }, "name", { value: "LaunchSchemaMigration" }); + + /** + * Calls LaunchSchemaMigration. + * @function launchSchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.ILaunchSchemaMigrationRequest} request LaunchSchemaMigrationRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + /** * Callback as used by {@link vtadmin.VTAdmin#pingTablet}. * @memberof vtadmin.VTAdmin @@ -1302,6 +1500,39 @@ export const vtadmin = $root.vtadmin = (() => { * @variation 2 */ + /** + * Callback as used by {@link vtadmin.VTAdmin#retrySchemaMigration}. + * @memberof vtadmin.VTAdmin + * @typedef RetrySchemaMigrationCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {vtctldata.RetrySchemaMigrationResponse} [response] RetrySchemaMigrationResponse + */ + + /** + * Calls RetrySchemaMigration. + * @function retrySchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IRetrySchemaMigrationRequest} request RetrySchemaMigrationRequest message or plain object + * @param {vtadmin.VTAdmin.RetrySchemaMigrationCallback} callback Node-style callback called with the error, if any, and RetrySchemaMigrationResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(VTAdmin.prototype.retrySchemaMigration = function retrySchemaMigration(request, callback) { + return this.rpcCall(retrySchemaMigration, $root.vtadmin.RetrySchemaMigrationRequest, $root.vtctldata.RetrySchemaMigrationResponse, request, callback); + }, "name", { value: "RetrySchemaMigration" }); + + /** + * Calls RetrySchemaMigration. + * @function retrySchemaMigration + * @memberof vtadmin.VTAdmin + * @instance + * @param {vtadmin.IRetrySchemaMigrationRequest} request RetrySchemaMigrationRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + /** * Callback as used by {@link vtadmin.VTAdmin#runHealthCheck}. * @memberof vtadmin.VTAdmin @@ -4501,6 +4732,243 @@ export const vtadmin = $root.vtadmin = (() => { return Schema; })(); + vtadmin.SchemaMigration = (function() { + + /** + * Properties of a SchemaMigration. + * @memberof vtadmin + * @interface ISchemaMigration + * @property {vtadmin.ICluster|null} [cluster] SchemaMigration cluster + * @property {vtctldata.ISchemaMigration|null} [schema_migration] SchemaMigration schema_migration + */ + + /** + * Constructs a new SchemaMigration. + * @memberof vtadmin + * @classdesc Represents a SchemaMigration. + * @implements ISchemaMigration + * @constructor + * @param {vtadmin.ISchemaMigration=} [properties] Properties to set + */ + function SchemaMigration(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * SchemaMigration cluster. + * @member {vtadmin.ICluster|null|undefined} cluster + * @memberof vtadmin.SchemaMigration + * @instance + */ + SchemaMigration.prototype.cluster = null; + + /** + * SchemaMigration schema_migration. + * @member {vtctldata.ISchemaMigration|null|undefined} schema_migration + * @memberof vtadmin.SchemaMigration + * @instance + */ + SchemaMigration.prototype.schema_migration = null; + + /** + * Creates a new SchemaMigration instance using the specified properties. + * @function create + * @memberof vtadmin.SchemaMigration + * @static + * @param {vtadmin.ISchemaMigration=} [properties] Properties to set + * @returns {vtadmin.SchemaMigration} SchemaMigration instance + */ + SchemaMigration.create = function create(properties) { + return new SchemaMigration(properties); + }; + + /** + * Encodes the specified SchemaMigration message. Does not implicitly {@link vtadmin.SchemaMigration.verify|verify} messages. + * @function encode + * @memberof vtadmin.SchemaMigration + * @static + * @param {vtadmin.ISchemaMigration} message SchemaMigration message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SchemaMigration.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster != null && Object.hasOwnProperty.call(message, "cluster")) + $root.vtadmin.Cluster.encode(message.cluster, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.schema_migration != null && Object.hasOwnProperty.call(message, "schema_migration")) + $root.vtctldata.SchemaMigration.encode(message.schema_migration, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified SchemaMigration message, length delimited. Does not implicitly {@link vtadmin.SchemaMigration.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.SchemaMigration + * @static + * @param {vtadmin.ISchemaMigration} message SchemaMigration message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + SchemaMigration.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a SchemaMigration message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.SchemaMigration + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.SchemaMigration} SchemaMigration + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SchemaMigration.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.SchemaMigration(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster = $root.vtadmin.Cluster.decode(reader, reader.uint32()); + break; + } + case 2: { + message.schema_migration = $root.vtctldata.SchemaMigration.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a SchemaMigration message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.SchemaMigration + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.SchemaMigration} SchemaMigration + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + SchemaMigration.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a SchemaMigration message. + * @function verify + * @memberof vtadmin.SchemaMigration + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + SchemaMigration.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster != null && message.hasOwnProperty("cluster")) { + let error = $root.vtadmin.Cluster.verify(message.cluster); + if (error) + return "cluster." + error; + } + if (message.schema_migration != null && message.hasOwnProperty("schema_migration")) { + let error = $root.vtctldata.SchemaMigration.verify(message.schema_migration); + if (error) + return "schema_migration." + error; + } + return null; + }; + + /** + * Creates a SchemaMigration message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.SchemaMigration + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.SchemaMigration} SchemaMigration + */ + SchemaMigration.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.SchemaMigration) + return object; + let message = new $root.vtadmin.SchemaMigration(); + if (object.cluster != null) { + if (typeof object.cluster !== "object") + throw TypeError(".vtadmin.SchemaMigration.cluster: object expected"); + message.cluster = $root.vtadmin.Cluster.fromObject(object.cluster); + } + if (object.schema_migration != null) { + if (typeof object.schema_migration !== "object") + throw TypeError(".vtadmin.SchemaMigration.schema_migration: object expected"); + message.schema_migration = $root.vtctldata.SchemaMigration.fromObject(object.schema_migration); + } + return message; + }; + + /** + * Creates a plain object from a SchemaMigration message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.SchemaMigration + * @static + * @param {vtadmin.SchemaMigration} message SchemaMigration + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + SchemaMigration.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster = null; + object.schema_migration = null; + } + if (message.cluster != null && message.hasOwnProperty("cluster")) + object.cluster = $root.vtadmin.Cluster.toObject(message.cluster, options); + if (message.schema_migration != null && message.hasOwnProperty("schema_migration")) + object.schema_migration = $root.vtctldata.SchemaMigration.toObject(message.schema_migration, options); + return object; + }; + + /** + * Converts this SchemaMigration to JSON. + * @function toJSON + * @memberof vtadmin.SchemaMigration + * @instance + * @returns {Object.} JSON object + */ + SchemaMigration.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for SchemaMigration + * @function getTypeUrl + * @memberof vtadmin.SchemaMigration + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + SchemaMigration.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.SchemaMigration"; + }; + + return SchemaMigration; + })(); + vtadmin.Shard = (function() { /** @@ -6437,6 +6905,934 @@ export const vtadmin = $root.vtadmin = (() => { return Workflow; })(); + vtadmin.ApplySchemaRequest = (function() { + + /** + * Properties of an ApplySchemaRequest. + * @memberof vtadmin + * @interface IApplySchemaRequest + * @property {string|null} [cluster_id] ApplySchemaRequest cluster_id + * @property {vtctldata.IApplySchemaRequest|null} [request] ApplySchemaRequest request + */ + + /** + * Constructs a new ApplySchemaRequest. + * @memberof vtadmin + * @classdesc Represents an ApplySchemaRequest. + * @implements IApplySchemaRequest + * @constructor + * @param {vtadmin.IApplySchemaRequest=} [properties] Properties to set + */ + function ApplySchemaRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ApplySchemaRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.ApplySchemaRequest + * @instance + */ + ApplySchemaRequest.prototype.cluster_id = ""; + + /** + * ApplySchemaRequest request. + * @member {vtctldata.IApplySchemaRequest|null|undefined} request + * @memberof vtadmin.ApplySchemaRequest + * @instance + */ + ApplySchemaRequest.prototype.request = null; + + /** + * Creates a new ApplySchemaRequest instance using the specified properties. + * @function create + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {vtadmin.IApplySchemaRequest=} [properties] Properties to set + * @returns {vtadmin.ApplySchemaRequest} ApplySchemaRequest instance + */ + ApplySchemaRequest.create = function create(properties) { + return new ApplySchemaRequest(properties); + }; + + /** + * Encodes the specified ApplySchemaRequest message. Does not implicitly {@link vtadmin.ApplySchemaRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {vtadmin.IApplySchemaRequest} message ApplySchemaRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ApplySchemaRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.ApplySchemaRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified ApplySchemaRequest message, length delimited. Does not implicitly {@link vtadmin.ApplySchemaRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {vtadmin.IApplySchemaRequest} message ApplySchemaRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ApplySchemaRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes an ApplySchemaRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.ApplySchemaRequest} ApplySchemaRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ApplySchemaRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.ApplySchemaRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.ApplySchemaRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes an ApplySchemaRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.ApplySchemaRequest} ApplySchemaRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ApplySchemaRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies an ApplySchemaRequest message. + * @function verify + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ApplySchemaRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.ApplySchemaRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates an ApplySchemaRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.ApplySchemaRequest} ApplySchemaRequest + */ + ApplySchemaRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.ApplySchemaRequest) + return object; + let message = new $root.vtadmin.ApplySchemaRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.ApplySchemaRequest.request: object expected"); + message.request = $root.vtctldata.ApplySchemaRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from an ApplySchemaRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {vtadmin.ApplySchemaRequest} message ApplySchemaRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ApplySchemaRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.ApplySchemaRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this ApplySchemaRequest to JSON. + * @function toJSON + * @memberof vtadmin.ApplySchemaRequest + * @instance + * @returns {Object.} JSON object + */ + ApplySchemaRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ApplySchemaRequest + * @function getTypeUrl + * @memberof vtadmin.ApplySchemaRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ApplySchemaRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.ApplySchemaRequest"; + }; + + return ApplySchemaRequest; + })(); + + vtadmin.CancelSchemaMigrationRequest = (function() { + + /** + * Properties of a CancelSchemaMigrationRequest. + * @memberof vtadmin + * @interface ICancelSchemaMigrationRequest + * @property {string|null} [cluster_id] CancelSchemaMigrationRequest cluster_id + * @property {vtctldata.ICancelSchemaMigrationRequest|null} [request] CancelSchemaMigrationRequest request + */ + + /** + * Constructs a new CancelSchemaMigrationRequest. + * @memberof vtadmin + * @classdesc Represents a CancelSchemaMigrationRequest. + * @implements ICancelSchemaMigrationRequest + * @constructor + * @param {vtadmin.ICancelSchemaMigrationRequest=} [properties] Properties to set + */ + function CancelSchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * CancelSchemaMigrationRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.CancelSchemaMigrationRequest + * @instance + */ + CancelSchemaMigrationRequest.prototype.cluster_id = ""; + + /** + * CancelSchemaMigrationRequest request. + * @member {vtctldata.ICancelSchemaMigrationRequest|null|undefined} request + * @memberof vtadmin.CancelSchemaMigrationRequest + * @instance + */ + CancelSchemaMigrationRequest.prototype.request = null; + + /** + * Creates a new CancelSchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {vtadmin.ICancelSchemaMigrationRequest=} [properties] Properties to set + * @returns {vtadmin.CancelSchemaMigrationRequest} CancelSchemaMigrationRequest instance + */ + CancelSchemaMigrationRequest.create = function create(properties) { + return new CancelSchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified CancelSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CancelSchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {vtadmin.ICancelSchemaMigrationRequest} message CancelSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CancelSchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.CancelSchemaMigrationRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified CancelSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CancelSchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {vtadmin.ICancelSchemaMigrationRequest} message CancelSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CancelSchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a CancelSchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.CancelSchemaMigrationRequest} CancelSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CancelSchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.CancelSchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.CancelSchemaMigrationRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a CancelSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.CancelSchemaMigrationRequest} CancelSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CancelSchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a CancelSchemaMigrationRequest message. + * @function verify + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + CancelSchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.CancelSchemaMigrationRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a CancelSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.CancelSchemaMigrationRequest} CancelSchemaMigrationRequest + */ + CancelSchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.CancelSchemaMigrationRequest) + return object; + let message = new $root.vtadmin.CancelSchemaMigrationRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.CancelSchemaMigrationRequest.request: object expected"); + message.request = $root.vtctldata.CancelSchemaMigrationRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a CancelSchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {vtadmin.CancelSchemaMigrationRequest} message CancelSchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + CancelSchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.CancelSchemaMigrationRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this CancelSchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtadmin.CancelSchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + CancelSchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for CancelSchemaMigrationRequest + * @function getTypeUrl + * @memberof vtadmin.CancelSchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + CancelSchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.CancelSchemaMigrationRequest"; + }; + + return CancelSchemaMigrationRequest; + })(); + + vtadmin.CleanupSchemaMigrationRequest = (function() { + + /** + * Properties of a CleanupSchemaMigrationRequest. + * @memberof vtadmin + * @interface ICleanupSchemaMigrationRequest + * @property {string|null} [cluster_id] CleanupSchemaMigrationRequest cluster_id + * @property {vtctldata.ICleanupSchemaMigrationRequest|null} [request] CleanupSchemaMigrationRequest request + */ + + /** + * Constructs a new CleanupSchemaMigrationRequest. + * @memberof vtadmin + * @classdesc Represents a CleanupSchemaMigrationRequest. + * @implements ICleanupSchemaMigrationRequest + * @constructor + * @param {vtadmin.ICleanupSchemaMigrationRequest=} [properties] Properties to set + */ + function CleanupSchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * CleanupSchemaMigrationRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @instance + */ + CleanupSchemaMigrationRequest.prototype.cluster_id = ""; + + /** + * CleanupSchemaMigrationRequest request. + * @member {vtctldata.ICleanupSchemaMigrationRequest|null|undefined} request + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @instance + */ + CleanupSchemaMigrationRequest.prototype.request = null; + + /** + * Creates a new CleanupSchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {vtadmin.ICleanupSchemaMigrationRequest=} [properties] Properties to set + * @returns {vtadmin.CleanupSchemaMigrationRequest} CleanupSchemaMigrationRequest instance + */ + CleanupSchemaMigrationRequest.create = function create(properties) { + return new CleanupSchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified CleanupSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CleanupSchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {vtadmin.ICleanupSchemaMigrationRequest} message CleanupSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CleanupSchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.CleanupSchemaMigrationRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified CleanupSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CleanupSchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {vtadmin.ICleanupSchemaMigrationRequest} message CleanupSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CleanupSchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a CleanupSchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.CleanupSchemaMigrationRequest} CleanupSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CleanupSchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.CleanupSchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.CleanupSchemaMigrationRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a CleanupSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.CleanupSchemaMigrationRequest} CleanupSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CleanupSchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a CleanupSchemaMigrationRequest message. + * @function verify + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + CleanupSchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.CleanupSchemaMigrationRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a CleanupSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.CleanupSchemaMigrationRequest} CleanupSchemaMigrationRequest + */ + CleanupSchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.CleanupSchemaMigrationRequest) + return object; + let message = new $root.vtadmin.CleanupSchemaMigrationRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.CleanupSchemaMigrationRequest.request: object expected"); + message.request = $root.vtctldata.CleanupSchemaMigrationRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a CleanupSchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {vtadmin.CleanupSchemaMigrationRequest} message CleanupSchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + CleanupSchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.CleanupSchemaMigrationRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this CleanupSchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + CleanupSchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for CleanupSchemaMigrationRequest + * @function getTypeUrl + * @memberof vtadmin.CleanupSchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + CleanupSchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.CleanupSchemaMigrationRequest"; + }; + + return CleanupSchemaMigrationRequest; + })(); + + vtadmin.CompleteSchemaMigrationRequest = (function() { + + /** + * Properties of a CompleteSchemaMigrationRequest. + * @memberof vtadmin + * @interface ICompleteSchemaMigrationRequest + * @property {string|null} [cluster_id] CompleteSchemaMigrationRequest cluster_id + * @property {vtctldata.ICompleteSchemaMigrationRequest|null} [request] CompleteSchemaMigrationRequest request + */ + + /** + * Constructs a new CompleteSchemaMigrationRequest. + * @memberof vtadmin + * @classdesc Represents a CompleteSchemaMigrationRequest. + * @implements ICompleteSchemaMigrationRequest + * @constructor + * @param {vtadmin.ICompleteSchemaMigrationRequest=} [properties] Properties to set + */ + function CompleteSchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * CompleteSchemaMigrationRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @instance + */ + CompleteSchemaMigrationRequest.prototype.cluster_id = ""; + + /** + * CompleteSchemaMigrationRequest request. + * @member {vtctldata.ICompleteSchemaMigrationRequest|null|undefined} request + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @instance + */ + CompleteSchemaMigrationRequest.prototype.request = null; + + /** + * Creates a new CompleteSchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {vtadmin.ICompleteSchemaMigrationRequest=} [properties] Properties to set + * @returns {vtadmin.CompleteSchemaMigrationRequest} CompleteSchemaMigrationRequest instance + */ + CompleteSchemaMigrationRequest.create = function create(properties) { + return new CompleteSchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified CompleteSchemaMigrationRequest message. Does not implicitly {@link vtadmin.CompleteSchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {vtadmin.ICompleteSchemaMigrationRequest} message CompleteSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CompleteSchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.CompleteSchemaMigrationRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified CompleteSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.CompleteSchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {vtadmin.ICompleteSchemaMigrationRequest} message CompleteSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + CompleteSchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a CompleteSchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.CompleteSchemaMigrationRequest} CompleteSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CompleteSchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.CompleteSchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.CompleteSchemaMigrationRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a CompleteSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.CompleteSchemaMigrationRequest} CompleteSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + CompleteSchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a CompleteSchemaMigrationRequest message. + * @function verify + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + CompleteSchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.CompleteSchemaMigrationRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a CompleteSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.CompleteSchemaMigrationRequest} CompleteSchemaMigrationRequest + */ + CompleteSchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.CompleteSchemaMigrationRequest) + return object; + let message = new $root.vtadmin.CompleteSchemaMigrationRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.CompleteSchemaMigrationRequest.request: object expected"); + message.request = $root.vtctldata.CompleteSchemaMigrationRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a CompleteSchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {vtadmin.CompleteSchemaMigrationRequest} message CompleteSchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + CompleteSchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.CompleteSchemaMigrationRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this CompleteSchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + CompleteSchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for CompleteSchemaMigrationRequest + * @function getTypeUrl + * @memberof vtadmin.CompleteSchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + CompleteSchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.CompleteSchemaMigrationRequest"; + }; + + return CompleteSchemaMigrationRequest; + })(); + vtadmin.CreateKeyspaceRequest = (function() { /** @@ -12904,6 +14300,686 @@ export const vtadmin = $root.vtadmin = (() => { return GetSchemasResponse; })(); + vtadmin.GetSchemaMigrationsRequest = (function() { + + /** + * Properties of a GetSchemaMigrationsRequest. + * @memberof vtadmin + * @interface IGetSchemaMigrationsRequest + * @property {Array.|null} [cluster_requests] GetSchemaMigrationsRequest cluster_requests + */ + + /** + * Constructs a new GetSchemaMigrationsRequest. + * @memberof vtadmin + * @classdesc Represents a GetSchemaMigrationsRequest. + * @implements IGetSchemaMigrationsRequest + * @constructor + * @param {vtadmin.IGetSchemaMigrationsRequest=} [properties] Properties to set + */ + function GetSchemaMigrationsRequest(properties) { + this.cluster_requests = []; + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetSchemaMigrationsRequest cluster_requests. + * @member {Array.} cluster_requests + * @memberof vtadmin.GetSchemaMigrationsRequest + * @instance + */ + GetSchemaMigrationsRequest.prototype.cluster_requests = $util.emptyArray; + + /** + * Creates a new GetSchemaMigrationsRequest instance using the specified properties. + * @function create + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {vtadmin.IGetSchemaMigrationsRequest=} [properties] Properties to set + * @returns {vtadmin.GetSchemaMigrationsRequest} GetSchemaMigrationsRequest instance + */ + GetSchemaMigrationsRequest.create = function create(properties) { + return new GetSchemaMigrationsRequest(properties); + }; + + /** + * Encodes the specified GetSchemaMigrationsRequest message. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {vtadmin.IGetSchemaMigrationsRequest} message GetSchemaMigrationsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetSchemaMigrationsRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_requests != null && message.cluster_requests.length) + for (let i = 0; i < message.cluster_requests.length; ++i) + $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest.encode(message.cluster_requests[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetSchemaMigrationsRequest message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {vtadmin.IGetSchemaMigrationsRequest} message GetSchemaMigrationsRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetSchemaMigrationsRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetSchemaMigrationsRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.GetSchemaMigrationsRequest} GetSchemaMigrationsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetSchemaMigrationsRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.GetSchemaMigrationsRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (!(message.cluster_requests && message.cluster_requests.length)) + message.cluster_requests = []; + message.cluster_requests.push($root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest.decode(reader, reader.uint32())); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetSchemaMigrationsRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.GetSchemaMigrationsRequest} GetSchemaMigrationsRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetSchemaMigrationsRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetSchemaMigrationsRequest message. + * @function verify + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetSchemaMigrationsRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_requests != null && message.hasOwnProperty("cluster_requests")) { + if (!Array.isArray(message.cluster_requests)) + return "cluster_requests: array expected"; + for (let i = 0; i < message.cluster_requests.length; ++i) { + let error = $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest.verify(message.cluster_requests[i]); + if (error) + return "cluster_requests." + error; + } + } + return null; + }; + + /** + * Creates a GetSchemaMigrationsRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.GetSchemaMigrationsRequest} GetSchemaMigrationsRequest + */ + GetSchemaMigrationsRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.GetSchemaMigrationsRequest) + return object; + let message = new $root.vtadmin.GetSchemaMigrationsRequest(); + if (object.cluster_requests) { + if (!Array.isArray(object.cluster_requests)) + throw TypeError(".vtadmin.GetSchemaMigrationsRequest.cluster_requests: array expected"); + message.cluster_requests = []; + for (let i = 0; i < object.cluster_requests.length; ++i) { + if (typeof object.cluster_requests[i] !== "object") + throw TypeError(".vtadmin.GetSchemaMigrationsRequest.cluster_requests: object expected"); + message.cluster_requests[i] = $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest.fromObject(object.cluster_requests[i]); + } + } + return message; + }; + + /** + * Creates a plain object from a GetSchemaMigrationsRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {vtadmin.GetSchemaMigrationsRequest} message GetSchemaMigrationsRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetSchemaMigrationsRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.arrays || options.defaults) + object.cluster_requests = []; + if (message.cluster_requests && message.cluster_requests.length) { + object.cluster_requests = []; + for (let j = 0; j < message.cluster_requests.length; ++j) + object.cluster_requests[j] = $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest.toObject(message.cluster_requests[j], options); + } + return object; + }; + + /** + * Converts this GetSchemaMigrationsRequest to JSON. + * @function toJSON + * @memberof vtadmin.GetSchemaMigrationsRequest + * @instance + * @returns {Object.} JSON object + */ + GetSchemaMigrationsRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for GetSchemaMigrationsRequest + * @function getTypeUrl + * @memberof vtadmin.GetSchemaMigrationsRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + GetSchemaMigrationsRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.GetSchemaMigrationsRequest"; + }; + + GetSchemaMigrationsRequest.ClusterRequest = (function() { + + /** + * Properties of a ClusterRequest. + * @memberof vtadmin.GetSchemaMigrationsRequest + * @interface IClusterRequest + * @property {string|null} [cluster_id] ClusterRequest cluster_id + * @property {vtctldata.IGetSchemaMigrationsRequest|null} [request] ClusterRequest request + */ + + /** + * Constructs a new ClusterRequest. + * @memberof vtadmin.GetSchemaMigrationsRequest + * @classdesc Represents a ClusterRequest. + * @implements IClusterRequest + * @constructor + * @param {vtadmin.GetSchemaMigrationsRequest.IClusterRequest=} [properties] Properties to set + */ + function ClusterRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ClusterRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @instance + */ + ClusterRequest.prototype.cluster_id = ""; + + /** + * ClusterRequest request. + * @member {vtctldata.IGetSchemaMigrationsRequest|null|undefined} request + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @instance + */ + ClusterRequest.prototype.request = null; + + /** + * Creates a new ClusterRequest instance using the specified properties. + * @function create + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {vtadmin.GetSchemaMigrationsRequest.IClusterRequest=} [properties] Properties to set + * @returns {vtadmin.GetSchemaMigrationsRequest.ClusterRequest} ClusterRequest instance + */ + ClusterRequest.create = function create(properties) { + return new ClusterRequest(properties); + }; + + /** + * Encodes the specified ClusterRequest message. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.ClusterRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {vtadmin.GetSchemaMigrationsRequest.IClusterRequest} message ClusterRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ClusterRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.GetSchemaMigrationsRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified ClusterRequest message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsRequest.ClusterRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {vtadmin.GetSchemaMigrationsRequest.IClusterRequest} message ClusterRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ClusterRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ClusterRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.GetSchemaMigrationsRequest.ClusterRequest} ClusterRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ClusterRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.GetSchemaMigrationsRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ClusterRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.GetSchemaMigrationsRequest.ClusterRequest} ClusterRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ClusterRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ClusterRequest message. + * @function verify + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ClusterRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.GetSchemaMigrationsRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a ClusterRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.GetSchemaMigrationsRequest.ClusterRequest} ClusterRequest + */ + ClusterRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest) + return object; + let message = new $root.vtadmin.GetSchemaMigrationsRequest.ClusterRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.GetSchemaMigrationsRequest.ClusterRequest.request: object expected"); + message.request = $root.vtctldata.GetSchemaMigrationsRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a ClusterRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {vtadmin.GetSchemaMigrationsRequest.ClusterRequest} message ClusterRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ClusterRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.GetSchemaMigrationsRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this ClusterRequest to JSON. + * @function toJSON + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @instance + * @returns {Object.} JSON object + */ + ClusterRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ClusterRequest + * @function getTypeUrl + * @memberof vtadmin.GetSchemaMigrationsRequest.ClusterRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ClusterRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.GetSchemaMigrationsRequest.ClusterRequest"; + }; + + return ClusterRequest; + })(); + + return GetSchemaMigrationsRequest; + })(); + + vtadmin.GetSchemaMigrationsResponse = (function() { + + /** + * Properties of a GetSchemaMigrationsResponse. + * @memberof vtadmin + * @interface IGetSchemaMigrationsResponse + * @property {Array.|null} [schema_migrations] GetSchemaMigrationsResponse schema_migrations + */ + + /** + * Constructs a new GetSchemaMigrationsResponse. + * @memberof vtadmin + * @classdesc Represents a GetSchemaMigrationsResponse. + * @implements IGetSchemaMigrationsResponse + * @constructor + * @param {vtadmin.IGetSchemaMigrationsResponse=} [properties] Properties to set + */ + function GetSchemaMigrationsResponse(properties) { + this.schema_migrations = []; + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * GetSchemaMigrationsResponse schema_migrations. + * @member {Array.} schema_migrations + * @memberof vtadmin.GetSchemaMigrationsResponse + * @instance + */ + GetSchemaMigrationsResponse.prototype.schema_migrations = $util.emptyArray; + + /** + * Creates a new GetSchemaMigrationsResponse instance using the specified properties. + * @function create + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {vtadmin.IGetSchemaMigrationsResponse=} [properties] Properties to set + * @returns {vtadmin.GetSchemaMigrationsResponse} GetSchemaMigrationsResponse instance + */ + GetSchemaMigrationsResponse.create = function create(properties) { + return new GetSchemaMigrationsResponse(properties); + }; + + /** + * Encodes the specified GetSchemaMigrationsResponse message. Does not implicitly {@link vtadmin.GetSchemaMigrationsResponse.verify|verify} messages. + * @function encode + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {vtadmin.IGetSchemaMigrationsResponse} message GetSchemaMigrationsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetSchemaMigrationsResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.schema_migrations != null && message.schema_migrations.length) + for (let i = 0; i < message.schema_migrations.length; ++i) + $root.vtadmin.SchemaMigration.encode(message.schema_migrations[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified GetSchemaMigrationsResponse message, length delimited. Does not implicitly {@link vtadmin.GetSchemaMigrationsResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {vtadmin.IGetSchemaMigrationsResponse} message GetSchemaMigrationsResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + GetSchemaMigrationsResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a GetSchemaMigrationsResponse message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.GetSchemaMigrationsResponse} GetSchemaMigrationsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetSchemaMigrationsResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.GetSchemaMigrationsResponse(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (!(message.schema_migrations && message.schema_migrations.length)) + message.schema_migrations = []; + message.schema_migrations.push($root.vtadmin.SchemaMigration.decode(reader, reader.uint32())); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a GetSchemaMigrationsResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.GetSchemaMigrationsResponse} GetSchemaMigrationsResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + GetSchemaMigrationsResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a GetSchemaMigrationsResponse message. + * @function verify + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + GetSchemaMigrationsResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.schema_migrations != null && message.hasOwnProperty("schema_migrations")) { + if (!Array.isArray(message.schema_migrations)) + return "schema_migrations: array expected"; + for (let i = 0; i < message.schema_migrations.length; ++i) { + let error = $root.vtadmin.SchemaMigration.verify(message.schema_migrations[i]); + if (error) + return "schema_migrations." + error; + } + } + return null; + }; + + /** + * Creates a GetSchemaMigrationsResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.GetSchemaMigrationsResponse} GetSchemaMigrationsResponse + */ + GetSchemaMigrationsResponse.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.GetSchemaMigrationsResponse) + return object; + let message = new $root.vtadmin.GetSchemaMigrationsResponse(); + if (object.schema_migrations) { + if (!Array.isArray(object.schema_migrations)) + throw TypeError(".vtadmin.GetSchemaMigrationsResponse.schema_migrations: array expected"); + message.schema_migrations = []; + for (let i = 0; i < object.schema_migrations.length; ++i) { + if (typeof object.schema_migrations[i] !== "object") + throw TypeError(".vtadmin.GetSchemaMigrationsResponse.schema_migrations: object expected"); + message.schema_migrations[i] = $root.vtadmin.SchemaMigration.fromObject(object.schema_migrations[i]); + } + } + return message; + }; + + /** + * Creates a plain object from a GetSchemaMigrationsResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {vtadmin.GetSchemaMigrationsResponse} message GetSchemaMigrationsResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + GetSchemaMigrationsResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.arrays || options.defaults) + object.schema_migrations = []; + if (message.schema_migrations && message.schema_migrations.length) { + object.schema_migrations = []; + for (let j = 0; j < message.schema_migrations.length; ++j) + object.schema_migrations[j] = $root.vtadmin.SchemaMigration.toObject(message.schema_migrations[j], options); + } + return object; + }; + + /** + * Converts this GetSchemaMigrationsResponse to JSON. + * @function toJSON + * @memberof vtadmin.GetSchemaMigrationsResponse + * @instance + * @returns {Object.} JSON object + */ + GetSchemaMigrationsResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for GetSchemaMigrationsResponse + * @function getTypeUrl + * @memberof vtadmin.GetSchemaMigrationsResponse + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + GetSchemaMigrationsResponse.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.GetSchemaMigrationsResponse"; + }; + + return GetSchemaMigrationsResponse; + })(); + vtadmin.GetShardReplicationPositionsRequest = (function() { /** @@ -18005,6 +20081,238 @@ export const vtadmin = $root.vtadmin = (() => { return GetWorkflowsResponse; })(); + vtadmin.LaunchSchemaMigrationRequest = (function() { + + /** + * Properties of a LaunchSchemaMigrationRequest. + * @memberof vtadmin + * @interface ILaunchSchemaMigrationRequest + * @property {string|null} [cluster_id] LaunchSchemaMigrationRequest cluster_id + * @property {vtctldata.ILaunchSchemaMigrationRequest|null} [request] LaunchSchemaMigrationRequest request + */ + + /** + * Constructs a new LaunchSchemaMigrationRequest. + * @memberof vtadmin + * @classdesc Represents a LaunchSchemaMigrationRequest. + * @implements ILaunchSchemaMigrationRequest + * @constructor + * @param {vtadmin.ILaunchSchemaMigrationRequest=} [properties] Properties to set + */ + function LaunchSchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * LaunchSchemaMigrationRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @instance + */ + LaunchSchemaMigrationRequest.prototype.cluster_id = ""; + + /** + * LaunchSchemaMigrationRequest request. + * @member {vtctldata.ILaunchSchemaMigrationRequest|null|undefined} request + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @instance + */ + LaunchSchemaMigrationRequest.prototype.request = null; + + /** + * Creates a new LaunchSchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {vtadmin.ILaunchSchemaMigrationRequest=} [properties] Properties to set + * @returns {vtadmin.LaunchSchemaMigrationRequest} LaunchSchemaMigrationRequest instance + */ + LaunchSchemaMigrationRequest.create = function create(properties) { + return new LaunchSchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified LaunchSchemaMigrationRequest message. Does not implicitly {@link vtadmin.LaunchSchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {vtadmin.ILaunchSchemaMigrationRequest} message LaunchSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + LaunchSchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.LaunchSchemaMigrationRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified LaunchSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.LaunchSchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {vtadmin.ILaunchSchemaMigrationRequest} message LaunchSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + LaunchSchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a LaunchSchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.LaunchSchemaMigrationRequest} LaunchSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + LaunchSchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.LaunchSchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.LaunchSchemaMigrationRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a LaunchSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.LaunchSchemaMigrationRequest} LaunchSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + LaunchSchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a LaunchSchemaMigrationRequest message. + * @function verify + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + LaunchSchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.LaunchSchemaMigrationRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a LaunchSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.LaunchSchemaMigrationRequest} LaunchSchemaMigrationRequest + */ + LaunchSchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.LaunchSchemaMigrationRequest) + return object; + let message = new $root.vtadmin.LaunchSchemaMigrationRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.LaunchSchemaMigrationRequest.request: object expected"); + message.request = $root.vtctldata.LaunchSchemaMigrationRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a LaunchSchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {vtadmin.LaunchSchemaMigrationRequest} message LaunchSchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + LaunchSchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.LaunchSchemaMigrationRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this LaunchSchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + LaunchSchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for LaunchSchemaMigrationRequest + * @function getTypeUrl + * @memberof vtadmin.LaunchSchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + LaunchSchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.LaunchSchemaMigrationRequest"; + }; + + return LaunchSchemaMigrationRequest; + })(); + vtadmin.PingTabletRequest = (function() { /** @@ -20145,7 +22453,7 @@ export const vtadmin = $root.vtadmin = (() => { for (let i = 0; i < message.cluster_ids.length; ++i) writer.uint32(/* id 4, wireType 2 =*/34).string(message.cluster_ids[i]); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 5, wireType 0 =*/40).uint32(message.concurrency); + writer.uint32(/* id 5, wireType 0 =*/40).int32(message.concurrency); if (message.wait_position != null && Object.hasOwnProperty.call(message, "wait_position")) writer.uint32(/* id 6, wireType 2 =*/50).string(message.wait_position); if (message.include_primary != null && Object.hasOwnProperty.call(message, "include_primary")) @@ -20209,7 +22517,7 @@ export const vtadmin = $root.vtadmin = (() => { break; } case 5: { - message.concurrency = reader.uint32(); + message.concurrency = reader.int32(); break; } case 6: { @@ -20341,7 +22649,7 @@ export const vtadmin = $root.vtadmin = (() => { message.cluster_ids[i] = String(object.cluster_ids[i]); } if (object.concurrency != null) - message.concurrency = object.concurrency >>> 0; + message.concurrency = object.concurrency | 0; if (object.wait_position != null) message.wait_position = String(object.wait_position); if (object.include_primary != null) @@ -21594,7 +23902,7 @@ export const vtadmin = $root.vtadmin = (() => { if (message.include_primary != null && Object.hasOwnProperty.call(message, "include_primary")) writer.uint32(/* id 5, wireType 0 =*/40).bool(message.include_primary); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 6, wireType 0 =*/48).uint32(message.concurrency); + writer.uint32(/* id 6, wireType 0 =*/48).int32(message.concurrency); return writer; }; @@ -21650,7 +23958,7 @@ export const vtadmin = $root.vtadmin = (() => { break; } case 6: { - message.concurrency = reader.uint32(); + message.concurrency = reader.int32(); break; } default: @@ -21732,7 +24040,7 @@ export const vtadmin = $root.vtadmin = (() => { if (object.include_primary != null) message.include_primary = Boolean(object.include_primary); if (object.concurrency != null) - message.concurrency = object.concurrency >>> 0; + message.concurrency = object.concurrency | 0; return message; }; @@ -23055,6 +25363,238 @@ export const vtadmin = $root.vtadmin = (() => { return RemoveKeyspaceCellResponse; })(); + vtadmin.RetrySchemaMigrationRequest = (function() { + + /** + * Properties of a RetrySchemaMigrationRequest. + * @memberof vtadmin + * @interface IRetrySchemaMigrationRequest + * @property {string|null} [cluster_id] RetrySchemaMigrationRequest cluster_id + * @property {vtctldata.IRetrySchemaMigrationRequest|null} [request] RetrySchemaMigrationRequest request + */ + + /** + * Constructs a new RetrySchemaMigrationRequest. + * @memberof vtadmin + * @classdesc Represents a RetrySchemaMigrationRequest. + * @implements IRetrySchemaMigrationRequest + * @constructor + * @param {vtadmin.IRetrySchemaMigrationRequest=} [properties] Properties to set + */ + function RetrySchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * RetrySchemaMigrationRequest cluster_id. + * @member {string} cluster_id + * @memberof vtadmin.RetrySchemaMigrationRequest + * @instance + */ + RetrySchemaMigrationRequest.prototype.cluster_id = ""; + + /** + * RetrySchemaMigrationRequest request. + * @member {vtctldata.IRetrySchemaMigrationRequest|null|undefined} request + * @memberof vtadmin.RetrySchemaMigrationRequest + * @instance + */ + RetrySchemaMigrationRequest.prototype.request = null; + + /** + * Creates a new RetrySchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {vtadmin.IRetrySchemaMigrationRequest=} [properties] Properties to set + * @returns {vtadmin.RetrySchemaMigrationRequest} RetrySchemaMigrationRequest instance + */ + RetrySchemaMigrationRequest.create = function create(properties) { + return new RetrySchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified RetrySchemaMigrationRequest message. Does not implicitly {@link vtadmin.RetrySchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {vtadmin.IRetrySchemaMigrationRequest} message RetrySchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RetrySchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.cluster_id != null && Object.hasOwnProperty.call(message, "cluster_id")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.cluster_id); + if (message.request != null && Object.hasOwnProperty.call(message, "request")) + $root.vtctldata.RetrySchemaMigrationRequest.encode(message.request, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); + return writer; + }; + + /** + * Encodes the specified RetrySchemaMigrationRequest message, length delimited. Does not implicitly {@link vtadmin.RetrySchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {vtadmin.IRetrySchemaMigrationRequest} message RetrySchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + RetrySchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a RetrySchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtadmin.RetrySchemaMigrationRequest} RetrySchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RetrySchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtadmin.RetrySchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.cluster_id = reader.string(); + break; + } + case 2: { + message.request = $root.vtctldata.RetrySchemaMigrationRequest.decode(reader, reader.uint32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a RetrySchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtadmin.RetrySchemaMigrationRequest} RetrySchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + RetrySchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a RetrySchemaMigrationRequest message. + * @function verify + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + RetrySchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + if (!$util.isString(message.cluster_id)) + return "cluster_id: string expected"; + if (message.request != null && message.hasOwnProperty("request")) { + let error = $root.vtctldata.RetrySchemaMigrationRequest.verify(message.request); + if (error) + return "request." + error; + } + return null; + }; + + /** + * Creates a RetrySchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtadmin.RetrySchemaMigrationRequest} RetrySchemaMigrationRequest + */ + RetrySchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtadmin.RetrySchemaMigrationRequest) + return object; + let message = new $root.vtadmin.RetrySchemaMigrationRequest(); + if (object.cluster_id != null) + message.cluster_id = String(object.cluster_id); + if (object.request != null) { + if (typeof object.request !== "object") + throw TypeError(".vtadmin.RetrySchemaMigrationRequest.request: object expected"); + message.request = $root.vtctldata.RetrySchemaMigrationRequest.fromObject(object.request); + } + return message; + }; + + /** + * Creates a plain object from a RetrySchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {vtadmin.RetrySchemaMigrationRequest} message RetrySchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + RetrySchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.cluster_id = ""; + object.request = null; + } + if (message.cluster_id != null && message.hasOwnProperty("cluster_id")) + object.cluster_id = message.cluster_id; + if (message.request != null && message.hasOwnProperty("request")) + object.request = $root.vtctldata.RetrySchemaMigrationRequest.toObject(message.request, options); + return object; + }; + + /** + * Converts this RetrySchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtadmin.RetrySchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + RetrySchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for RetrySchemaMigrationRequest + * @function getTypeUrl + * @memberof vtadmin.RetrySchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + RetrySchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtadmin.RetrySchemaMigrationRequest"; + }; + + return RetrySchemaMigrationRequest; + })(); + vtadmin.RunHealthCheckRequest = (function() { /** @@ -29336,6 +31876,7 @@ export const mysqlctl = $root.mysqlctl = (() => { * @memberof mysqlctl * @interface IShutdownRequest * @property {boolean|null} [wait_for_mysqld] ShutdownRequest wait_for_mysqld + * @property {vttime.IDuration|null} [mysql_shutdown_timeout] ShutdownRequest mysql_shutdown_timeout */ /** @@ -29361,6 +31902,14 @@ export const mysqlctl = $root.mysqlctl = (() => { */ ShutdownRequest.prototype.wait_for_mysqld = false; + /** + * ShutdownRequest mysql_shutdown_timeout. + * @member {vttime.IDuration|null|undefined} mysql_shutdown_timeout + * @memberof mysqlctl.ShutdownRequest + * @instance + */ + ShutdownRequest.prototype.mysql_shutdown_timeout = null; + /** * Creates a new ShutdownRequest instance using the specified properties. * @function create @@ -29387,6 +31936,8 @@ export const mysqlctl = $root.mysqlctl = (() => { writer = $Writer.create(); if (message.wait_for_mysqld != null && Object.hasOwnProperty.call(message, "wait_for_mysqld")) writer.uint32(/* id 1, wireType 0 =*/8).bool(message.wait_for_mysqld); + if (message.mysql_shutdown_timeout != null && Object.hasOwnProperty.call(message, "mysql_shutdown_timeout")) + $root.vttime.Duration.encode(message.mysql_shutdown_timeout, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); return writer; }; @@ -29425,6 +31976,10 @@ export const mysqlctl = $root.mysqlctl = (() => { message.wait_for_mysqld = reader.bool(); break; } + case 2: { + message.mysql_shutdown_timeout = $root.vttime.Duration.decode(reader, reader.uint32()); + break; + } default: reader.skipType(tag & 7); break; @@ -29463,6 +32018,11 @@ export const mysqlctl = $root.mysqlctl = (() => { if (message.wait_for_mysqld != null && message.hasOwnProperty("wait_for_mysqld")) if (typeof message.wait_for_mysqld !== "boolean") return "wait_for_mysqld: boolean expected"; + if (message.mysql_shutdown_timeout != null && message.hasOwnProperty("mysql_shutdown_timeout")) { + let error = $root.vttime.Duration.verify(message.mysql_shutdown_timeout); + if (error) + return "mysql_shutdown_timeout." + error; + } return null; }; @@ -29480,6 +32040,11 @@ export const mysqlctl = $root.mysqlctl = (() => { let message = new $root.mysqlctl.ShutdownRequest(); if (object.wait_for_mysqld != null) message.wait_for_mysqld = Boolean(object.wait_for_mysqld); + if (object.mysql_shutdown_timeout != null) { + if (typeof object.mysql_shutdown_timeout !== "object") + throw TypeError(".mysqlctl.ShutdownRequest.mysql_shutdown_timeout: object expected"); + message.mysql_shutdown_timeout = $root.vttime.Duration.fromObject(object.mysql_shutdown_timeout); + } return message; }; @@ -29496,10 +32061,14 @@ export const mysqlctl = $root.mysqlctl = (() => { if (!options) options = {}; let object = {}; - if (options.defaults) + if (options.defaults) { object.wait_for_mysqld = false; + object.mysql_shutdown_timeout = null; + } if (message.wait_for_mysqld != null && message.hasOwnProperty("wait_for_mysqld")) object.wait_for_mysqld = message.wait_for_mysqld; + if (message.mysql_shutdown_timeout != null && message.hasOwnProperty("mysql_shutdown_timeout")) + object.mysql_shutdown_timeout = $root.vttime.Duration.toObject(message.mysql_shutdown_timeout, options); return object; }; @@ -35028,7 +37597,6 @@ export const topodata = $root.topodata = (() => { * Properties of a Keyspace. * @memberof topodata * @interface IKeyspace - * @property {Array.|null} [served_froms] Keyspace served_froms * @property {topodata.KeyspaceType|null} [keyspace_type] Keyspace keyspace_type * @property {string|null} [base_keyspace] Keyspace base_keyspace * @property {vttime.ITime|null} [snapshot_time] Keyspace snapshot_time @@ -35046,21 +37614,12 @@ export const topodata = $root.topodata = (() => { * @param {topodata.IKeyspace=} [properties] Properties to set */ function Keyspace(properties) { - this.served_froms = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) this[keys[i]] = properties[keys[i]]; } - /** - * Keyspace served_froms. - * @member {Array.} served_froms - * @memberof topodata.Keyspace - * @instance - */ - Keyspace.prototype.served_froms = $util.emptyArray; - /** * Keyspace keyspace_type. * @member {topodata.KeyspaceType} keyspace_type @@ -35133,9 +37692,6 @@ export const topodata = $root.topodata = (() => { Keyspace.encode = function encode(message, writer) { if (!writer) writer = $Writer.create(); - if (message.served_froms != null && message.served_froms.length) - for (let i = 0; i < message.served_froms.length; ++i) - $root.topodata.Keyspace.ServedFrom.encode(message.served_froms[i], writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); if (message.keyspace_type != null && Object.hasOwnProperty.call(message, "keyspace_type")) writer.uint32(/* id 5, wireType 0 =*/40).int32(message.keyspace_type); if (message.base_keyspace != null && Object.hasOwnProperty.call(message, "base_keyspace")) @@ -35182,12 +37738,6 @@ export const topodata = $root.topodata = (() => { while (reader.pos < end) { let tag = reader.uint32(); switch (tag >>> 3) { - case 4: { - if (!(message.served_froms && message.served_froms.length)) - message.served_froms = []; - message.served_froms.push($root.topodata.Keyspace.ServedFrom.decode(reader, reader.uint32())); - break; - } case 5: { message.keyspace_type = reader.int32(); break; @@ -35247,15 +37797,6 @@ export const topodata = $root.topodata = (() => { Keyspace.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; - if (message.served_froms != null && message.hasOwnProperty("served_froms")) { - if (!Array.isArray(message.served_froms)) - return "served_froms: array expected"; - for (let i = 0; i < message.served_froms.length; ++i) { - let error = $root.topodata.Keyspace.ServedFrom.verify(message.served_froms[i]); - if (error) - return "served_froms." + error; - } - } if (message.keyspace_type != null && message.hasOwnProperty("keyspace_type")) switch (message.keyspace_type) { default: @@ -35298,16 +37839,6 @@ export const topodata = $root.topodata = (() => { if (object instanceof $root.topodata.Keyspace) return object; let message = new $root.topodata.Keyspace(); - if (object.served_froms) { - if (!Array.isArray(object.served_froms)) - throw TypeError(".topodata.Keyspace.served_froms: array expected"); - message.served_froms = []; - for (let i = 0; i < object.served_froms.length; ++i) { - if (typeof object.served_froms[i] !== "object") - throw TypeError(".topodata.Keyspace.served_froms: object expected"); - message.served_froms[i] = $root.topodata.Keyspace.ServedFrom.fromObject(object.served_froms[i]); - } - } switch (object.keyspace_type) { default: if (typeof object.keyspace_type === "number") { @@ -35356,8 +37887,6 @@ export const topodata = $root.topodata = (() => { if (!options) options = {}; let object = {}; - if (options.arrays || options.defaults) - object.served_froms = []; if (options.defaults) { object.keyspace_type = options.enums === String ? "NORMAL" : 0; object.base_keyspace = ""; @@ -35366,11 +37895,6 @@ export const topodata = $root.topodata = (() => { object.throttler_config = null; object.sidecar_db_name = ""; } - if (message.served_froms && message.served_froms.length) { - object.served_froms = []; - for (let j = 0; j < message.served_froms.length; ++j) - object.served_froms[j] = $root.topodata.Keyspace.ServedFrom.toObject(message.served_froms[j], options); - } if (message.keyspace_type != null && message.hasOwnProperty("keyspace_type")) object.keyspace_type = options.enums === String ? $root.topodata.KeyspaceType[message.keyspace_type] === undefined ? message.keyspace_type : $root.topodata.KeyspaceType[message.keyspace_type] : message.keyspace_type; if (message.base_keyspace != null && message.hasOwnProperty("base_keyspace")) @@ -35412,337 +37936,6 @@ export const topodata = $root.topodata = (() => { return typeUrlPrefix + "/topodata.Keyspace"; }; - Keyspace.ServedFrom = (function() { - - /** - * Properties of a ServedFrom. - * @memberof topodata.Keyspace - * @interface IServedFrom - * @property {topodata.TabletType|null} [tablet_type] ServedFrom tablet_type - * @property {Array.|null} [cells] ServedFrom cells - * @property {string|null} [keyspace] ServedFrom keyspace - */ - - /** - * Constructs a new ServedFrom. - * @memberof topodata.Keyspace - * @classdesc Represents a ServedFrom. - * @implements IServedFrom - * @constructor - * @param {topodata.Keyspace.IServedFrom=} [properties] Properties to set - */ - function ServedFrom(properties) { - this.cells = []; - if (properties) - for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * ServedFrom tablet_type. - * @member {topodata.TabletType} tablet_type - * @memberof topodata.Keyspace.ServedFrom - * @instance - */ - ServedFrom.prototype.tablet_type = 0; - - /** - * ServedFrom cells. - * @member {Array.} cells - * @memberof topodata.Keyspace.ServedFrom - * @instance - */ - ServedFrom.prototype.cells = $util.emptyArray; - - /** - * ServedFrom keyspace. - * @member {string} keyspace - * @memberof topodata.Keyspace.ServedFrom - * @instance - */ - ServedFrom.prototype.keyspace = ""; - - /** - * Creates a new ServedFrom instance using the specified properties. - * @function create - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {topodata.Keyspace.IServedFrom=} [properties] Properties to set - * @returns {topodata.Keyspace.ServedFrom} ServedFrom instance - */ - ServedFrom.create = function create(properties) { - return new ServedFrom(properties); - }; - - /** - * Encodes the specified ServedFrom message. Does not implicitly {@link topodata.Keyspace.ServedFrom.verify|verify} messages. - * @function encode - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {topodata.Keyspace.IServedFrom} message ServedFrom message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - ServedFrom.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.tablet_type != null && Object.hasOwnProperty.call(message, "tablet_type")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.tablet_type); - if (message.cells != null && message.cells.length) - for (let i = 0; i < message.cells.length; ++i) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.cells[i]); - if (message.keyspace != null && Object.hasOwnProperty.call(message, "keyspace")) - writer.uint32(/* id 3, wireType 2 =*/26).string(message.keyspace); - return writer; - }; - - /** - * Encodes the specified ServedFrom message, length delimited. Does not implicitly {@link topodata.Keyspace.ServedFrom.verify|verify} messages. - * @function encodeDelimited - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {topodata.Keyspace.IServedFrom} message ServedFrom message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - ServedFrom.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a ServedFrom message from the specified reader or buffer. - * @function decode - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {topodata.Keyspace.ServedFrom} ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - ServedFrom.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.topodata.Keyspace.ServedFrom(); - while (reader.pos < end) { - let tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.tablet_type = reader.int32(); - break; - } - case 2: { - if (!(message.cells && message.cells.length)) - message.cells = []; - message.cells.push(reader.string()); - break; - } - case 3: { - message.keyspace = reader.string(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a ServedFrom message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {topodata.Keyspace.ServedFrom} ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - ServedFrom.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a ServedFrom message. - * @function verify - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - ServedFrom.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - switch (message.tablet_type) { - default: - return "tablet_type: enum value expected"; - case 0: - case 1: - case 1: - case 2: - case 3: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - break; - } - if (message.cells != null && message.hasOwnProperty("cells")) { - if (!Array.isArray(message.cells)) - return "cells: array expected"; - for (let i = 0; i < message.cells.length; ++i) - if (!$util.isString(message.cells[i])) - return "cells: string[] expected"; - } - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - if (!$util.isString(message.keyspace)) - return "keyspace: string expected"; - return null; - }; - - /** - * Creates a ServedFrom message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {Object.} object Plain object - * @returns {topodata.Keyspace.ServedFrom} ServedFrom - */ - ServedFrom.fromObject = function fromObject(object) { - if (object instanceof $root.topodata.Keyspace.ServedFrom) - return object; - let message = new $root.topodata.Keyspace.ServedFrom(); - switch (object.tablet_type) { - default: - if (typeof object.tablet_type === "number") { - message.tablet_type = object.tablet_type; - break; - } - break; - case "UNKNOWN": - case 0: - message.tablet_type = 0; - break; - case "PRIMARY": - case 1: - message.tablet_type = 1; - break; - case "MASTER": - case 1: - message.tablet_type = 1; - break; - case "REPLICA": - case 2: - message.tablet_type = 2; - break; - case "RDONLY": - case 3: - message.tablet_type = 3; - break; - case "BATCH": - case 3: - message.tablet_type = 3; - break; - case "SPARE": - case 4: - message.tablet_type = 4; - break; - case "EXPERIMENTAL": - case 5: - message.tablet_type = 5; - break; - case "BACKUP": - case 6: - message.tablet_type = 6; - break; - case "RESTORE": - case 7: - message.tablet_type = 7; - break; - case "DRAINED": - case 8: - message.tablet_type = 8; - break; - } - if (object.cells) { - if (!Array.isArray(object.cells)) - throw TypeError(".topodata.Keyspace.ServedFrom.cells: array expected"); - message.cells = []; - for (let i = 0; i < object.cells.length; ++i) - message.cells[i] = String(object.cells[i]); - } - if (object.keyspace != null) - message.keyspace = String(object.keyspace); - return message; - }; - - /** - * Creates a plain object from a ServedFrom message. Also converts values to other types if specified. - * @function toObject - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {topodata.Keyspace.ServedFrom} message ServedFrom - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - ServedFrom.toObject = function toObject(message, options) { - if (!options) - options = {}; - let object = {}; - if (options.arrays || options.defaults) - object.cells = []; - if (options.defaults) { - object.tablet_type = options.enums === String ? "UNKNOWN" : 0; - object.keyspace = ""; - } - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - object.tablet_type = options.enums === String ? $root.topodata.TabletType[message.tablet_type] === undefined ? message.tablet_type : $root.topodata.TabletType[message.tablet_type] : message.tablet_type; - if (message.cells && message.cells.length) { - object.cells = []; - for (let j = 0; j < message.cells.length; ++j) - object.cells[j] = message.cells[j]; - } - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - object.keyspace = message.keyspace; - return object; - }; - - /** - * Converts this ServedFrom to JSON. - * @function toJSON - * @memberof topodata.Keyspace.ServedFrom - * @instance - * @returns {Object.} JSON object - */ - ServedFrom.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for ServedFrom - * @function getTypeUrl - * @memberof topodata.Keyspace.ServedFrom - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - ServedFrom.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/topodata.Keyspace.ServedFrom"; - }; - - return ServedFrom; - })(); - return Keyspace; })(); @@ -37561,7 +39754,6 @@ export const topodata = $root.topodata = (() => { * @memberof topodata * @interface ISrvKeyspace * @property {Array.|null} [partitions] SrvKeyspace partitions - * @property {Array.|null} [served_from] SrvKeyspace served_from * @property {topodata.IThrottlerConfig|null} [throttler_config] SrvKeyspace throttler_config */ @@ -37575,7 +39767,6 @@ export const topodata = $root.topodata = (() => { */ function SrvKeyspace(properties) { this.partitions = []; - this.served_from = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -37590,14 +39781,6 @@ export const topodata = $root.topodata = (() => { */ SrvKeyspace.prototype.partitions = $util.emptyArray; - /** - * SrvKeyspace served_from. - * @member {Array.} served_from - * @memberof topodata.SrvKeyspace - * @instance - */ - SrvKeyspace.prototype.served_from = $util.emptyArray; - /** * SrvKeyspace throttler_config. * @member {topodata.IThrottlerConfig|null|undefined} throttler_config @@ -37633,9 +39816,6 @@ export const topodata = $root.topodata = (() => { if (message.partitions != null && message.partitions.length) for (let i = 0; i < message.partitions.length; ++i) $root.topodata.SrvKeyspace.KeyspacePartition.encode(message.partitions[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.served_from != null && message.served_from.length) - for (let i = 0; i < message.served_from.length; ++i) - $root.topodata.SrvKeyspace.ServedFrom.encode(message.served_from[i], writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); if (message.throttler_config != null && Object.hasOwnProperty.call(message, "throttler_config")) $root.topodata.ThrottlerConfig.encode(message.throttler_config, writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); return writer; @@ -37678,12 +39858,6 @@ export const topodata = $root.topodata = (() => { message.partitions.push($root.topodata.SrvKeyspace.KeyspacePartition.decode(reader, reader.uint32())); break; } - case 4: { - if (!(message.served_from && message.served_from.length)) - message.served_from = []; - message.served_from.push($root.topodata.SrvKeyspace.ServedFrom.decode(reader, reader.uint32())); - break; - } case 6: { message.throttler_config = $root.topodata.ThrottlerConfig.decode(reader, reader.uint32()); break; @@ -37732,15 +39906,6 @@ export const topodata = $root.topodata = (() => { return "partitions." + error; } } - if (message.served_from != null && message.hasOwnProperty("served_from")) { - if (!Array.isArray(message.served_from)) - return "served_from: array expected"; - for (let i = 0; i < message.served_from.length; ++i) { - let error = $root.topodata.SrvKeyspace.ServedFrom.verify(message.served_from[i]); - if (error) - return "served_from." + error; - } - } if (message.throttler_config != null && message.hasOwnProperty("throttler_config")) { let error = $root.topodata.ThrottlerConfig.verify(message.throttler_config); if (error) @@ -37771,16 +39936,6 @@ export const topodata = $root.topodata = (() => { message.partitions[i] = $root.topodata.SrvKeyspace.KeyspacePartition.fromObject(object.partitions[i]); } } - if (object.served_from) { - if (!Array.isArray(object.served_from)) - throw TypeError(".topodata.SrvKeyspace.served_from: array expected"); - message.served_from = []; - for (let i = 0; i < object.served_from.length; ++i) { - if (typeof object.served_from[i] !== "object") - throw TypeError(".topodata.SrvKeyspace.served_from: object expected"); - message.served_from[i] = $root.topodata.SrvKeyspace.ServedFrom.fromObject(object.served_from[i]); - } - } if (object.throttler_config != null) { if (typeof object.throttler_config !== "object") throw TypeError(".topodata.SrvKeyspace.throttler_config: object expected"); @@ -37802,10 +39957,8 @@ export const topodata = $root.topodata = (() => { if (!options) options = {}; let object = {}; - if (options.arrays || options.defaults) { + if (options.arrays || options.defaults) object.partitions = []; - object.served_from = []; - } if (options.defaults) object.throttler_config = null; if (message.partitions && message.partitions.length) { @@ -37813,11 +39966,6 @@ export const topodata = $root.topodata = (() => { for (let j = 0; j < message.partitions.length; ++j) object.partitions[j] = $root.topodata.SrvKeyspace.KeyspacePartition.toObject(message.partitions[j], options); } - if (message.served_from && message.served_from.length) { - object.served_from = []; - for (let j = 0; j < message.served_from.length; ++j) - object.served_from[j] = $root.topodata.SrvKeyspace.ServedFrom.toObject(message.served_from[j], options); - } if (message.throttler_config != null && message.hasOwnProperty("throttler_config")) object.throttler_config = $root.topodata.ThrottlerConfig.toObject(message.throttler_config, options); return object; @@ -38206,297 +40354,6 @@ export const topodata = $root.topodata = (() => { return KeyspacePartition; })(); - SrvKeyspace.ServedFrom = (function() { - - /** - * Properties of a ServedFrom. - * @memberof topodata.SrvKeyspace - * @interface IServedFrom - * @property {topodata.TabletType|null} [tablet_type] ServedFrom tablet_type - * @property {string|null} [keyspace] ServedFrom keyspace - */ - - /** - * Constructs a new ServedFrom. - * @memberof topodata.SrvKeyspace - * @classdesc Represents a ServedFrom. - * @implements IServedFrom - * @constructor - * @param {topodata.SrvKeyspace.IServedFrom=} [properties] Properties to set - */ - function ServedFrom(properties) { - if (properties) - for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * ServedFrom tablet_type. - * @member {topodata.TabletType} tablet_type - * @memberof topodata.SrvKeyspace.ServedFrom - * @instance - */ - ServedFrom.prototype.tablet_type = 0; - - /** - * ServedFrom keyspace. - * @member {string} keyspace - * @memberof topodata.SrvKeyspace.ServedFrom - * @instance - */ - ServedFrom.prototype.keyspace = ""; - - /** - * Creates a new ServedFrom instance using the specified properties. - * @function create - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {topodata.SrvKeyspace.IServedFrom=} [properties] Properties to set - * @returns {topodata.SrvKeyspace.ServedFrom} ServedFrom instance - */ - ServedFrom.create = function create(properties) { - return new ServedFrom(properties); - }; - - /** - * Encodes the specified ServedFrom message. Does not implicitly {@link topodata.SrvKeyspace.ServedFrom.verify|verify} messages. - * @function encode - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {topodata.SrvKeyspace.IServedFrom} message ServedFrom message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - ServedFrom.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.tablet_type != null && Object.hasOwnProperty.call(message, "tablet_type")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.tablet_type); - if (message.keyspace != null && Object.hasOwnProperty.call(message, "keyspace")) - writer.uint32(/* id 2, wireType 2 =*/18).string(message.keyspace); - return writer; - }; - - /** - * Encodes the specified ServedFrom message, length delimited. Does not implicitly {@link topodata.SrvKeyspace.ServedFrom.verify|verify} messages. - * @function encodeDelimited - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {topodata.SrvKeyspace.IServedFrom} message ServedFrom message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - ServedFrom.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a ServedFrom message from the specified reader or buffer. - * @function decode - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {topodata.SrvKeyspace.ServedFrom} ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - ServedFrom.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.topodata.SrvKeyspace.ServedFrom(); - while (reader.pos < end) { - let tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.tablet_type = reader.int32(); - break; - } - case 2: { - message.keyspace = reader.string(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a ServedFrom message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {topodata.SrvKeyspace.ServedFrom} ServedFrom - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - ServedFrom.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a ServedFrom message. - * @function verify - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - ServedFrom.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - switch (message.tablet_type) { - default: - return "tablet_type: enum value expected"; - case 0: - case 1: - case 1: - case 2: - case 3: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - break; - } - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - if (!$util.isString(message.keyspace)) - return "keyspace: string expected"; - return null; - }; - - /** - * Creates a ServedFrom message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {Object.} object Plain object - * @returns {topodata.SrvKeyspace.ServedFrom} ServedFrom - */ - ServedFrom.fromObject = function fromObject(object) { - if (object instanceof $root.topodata.SrvKeyspace.ServedFrom) - return object; - let message = new $root.topodata.SrvKeyspace.ServedFrom(); - switch (object.tablet_type) { - default: - if (typeof object.tablet_type === "number") { - message.tablet_type = object.tablet_type; - break; - } - break; - case "UNKNOWN": - case 0: - message.tablet_type = 0; - break; - case "PRIMARY": - case 1: - message.tablet_type = 1; - break; - case "MASTER": - case 1: - message.tablet_type = 1; - break; - case "REPLICA": - case 2: - message.tablet_type = 2; - break; - case "RDONLY": - case 3: - message.tablet_type = 3; - break; - case "BATCH": - case 3: - message.tablet_type = 3; - break; - case "SPARE": - case 4: - message.tablet_type = 4; - break; - case "EXPERIMENTAL": - case 5: - message.tablet_type = 5; - break; - case "BACKUP": - case 6: - message.tablet_type = 6; - break; - case "RESTORE": - case 7: - message.tablet_type = 7; - break; - case "DRAINED": - case 8: - message.tablet_type = 8; - break; - } - if (object.keyspace != null) - message.keyspace = String(object.keyspace); - return message; - }; - - /** - * Creates a plain object from a ServedFrom message. Also converts values to other types if specified. - * @function toObject - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {topodata.SrvKeyspace.ServedFrom} message ServedFrom - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - ServedFrom.toObject = function toObject(message, options) { - if (!options) - options = {}; - let object = {}; - if (options.defaults) { - object.tablet_type = options.enums === String ? "UNKNOWN" : 0; - object.keyspace = ""; - } - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - object.tablet_type = options.enums === String ? $root.topodata.TabletType[message.tablet_type] === undefined ? message.tablet_type : $root.topodata.TabletType[message.tablet_type] : message.tablet_type; - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - object.keyspace = message.keyspace; - return object; - }; - - /** - * Converts this ServedFrom to JSON. - * @function toJSON - * @memberof topodata.SrvKeyspace.ServedFrom - * @instance - * @returns {Object.} JSON object - */ - ServedFrom.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for ServedFrom - * @function getTypeUrl - * @memberof topodata.SrvKeyspace.ServedFrom - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - ServedFrom.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/topodata.SrvKeyspace.ServedFrom"; - }; - - return ServedFrom; - })(); - return SrvKeyspace; })(); @@ -48262,6 +50119,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { * @property {number|Long|null} [max_rows] ExecuteFetchAsDbaRequest max_rows * @property {boolean|null} [disable_binlogs] ExecuteFetchAsDbaRequest disable_binlogs * @property {boolean|null} [reload_schema] ExecuteFetchAsDbaRequest reload_schema + * @property {boolean|null} [disable_foreign_key_checks] ExecuteFetchAsDbaRequest disable_foreign_key_checks */ /** @@ -48319,6 +50177,14 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { */ ExecuteFetchAsDbaRequest.prototype.reload_schema = false; + /** + * ExecuteFetchAsDbaRequest disable_foreign_key_checks. + * @member {boolean} disable_foreign_key_checks + * @memberof tabletmanagerdata.ExecuteFetchAsDbaRequest + * @instance + */ + ExecuteFetchAsDbaRequest.prototype.disable_foreign_key_checks = false; + /** * Creates a new ExecuteFetchAsDbaRequest instance using the specified properties. * @function create @@ -48353,6 +50219,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { writer.uint32(/* id 4, wireType 0 =*/32).bool(message.disable_binlogs); if (message.reload_schema != null && Object.hasOwnProperty.call(message, "reload_schema")) writer.uint32(/* id 5, wireType 0 =*/40).bool(message.reload_schema); + if (message.disable_foreign_key_checks != null && Object.hasOwnProperty.call(message, "disable_foreign_key_checks")) + writer.uint32(/* id 6, wireType 0 =*/48).bool(message.disable_foreign_key_checks); return writer; }; @@ -48407,6 +50275,10 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.reload_schema = reader.bool(); break; } + case 6: { + message.disable_foreign_key_checks = reader.bool(); + break; + } default: reader.skipType(tag & 7); break; @@ -48457,6 +50329,9 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (message.reload_schema != null && message.hasOwnProperty("reload_schema")) if (typeof message.reload_schema !== "boolean") return "reload_schema: boolean expected"; + if (message.disable_foreign_key_checks != null && message.hasOwnProperty("disable_foreign_key_checks")) + if (typeof message.disable_foreign_key_checks !== "boolean") + return "disable_foreign_key_checks: boolean expected"; return null; }; @@ -48492,6 +50367,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.disable_binlogs = Boolean(object.disable_binlogs); if (object.reload_schema != null) message.reload_schema = Boolean(object.reload_schema); + if (object.disable_foreign_key_checks != null) + message.disable_foreign_key_checks = Boolean(object.disable_foreign_key_checks); return message; }; @@ -48524,6 +50401,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.max_rows = options.longs === String ? "0" : 0; object.disable_binlogs = false; object.reload_schema = false; + object.disable_foreign_key_checks = false; } if (message.query != null && message.hasOwnProperty("query")) object.query = options.bytes === String ? $util.base64.encode(message.query, 0, message.query.length) : options.bytes === Array ? Array.prototype.slice.call(message.query) : message.query; @@ -48538,6 +50416,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.disable_binlogs = message.disable_binlogs; if (message.reload_schema != null && message.hasOwnProperty("reload_schema")) object.reload_schema = message.reload_schema; + if (message.disable_foreign_key_checks != null && message.hasOwnProperty("disable_foreign_key_checks")) + object.disable_foreign_key_checks = message.disable_foreign_key_checks; return object; }; @@ -59306,7 +61186,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { * Properties of a BackupRequest. * @memberof tabletmanagerdata * @interface IBackupRequest - * @property {number|Long|null} [concurrency] BackupRequest concurrency + * @property {number|null} [concurrency] BackupRequest concurrency * @property {boolean|null} [allow_primary] BackupRequest allow_primary * @property {string|null} [incremental_from_pos] BackupRequest incremental_from_pos * @property {boolean|null} [upgrade_safe] BackupRequest upgrade_safe @@ -59329,11 +61209,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { /** * BackupRequest concurrency. - * @member {number|Long} concurrency + * @member {number} concurrency * @memberof tabletmanagerdata.BackupRequest * @instance */ - BackupRequest.prototype.concurrency = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + BackupRequest.prototype.concurrency = 0; /** * BackupRequest allow_primary. @@ -59384,7 +61264,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (!writer) writer = $Writer.create(); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 1, wireType 0 =*/8).int64(message.concurrency); + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.concurrency); if (message.allow_primary != null && Object.hasOwnProperty.call(message, "allow_primary")) writer.uint32(/* id 2, wireType 0 =*/16).bool(message.allow_primary); if (message.incremental_from_pos != null && Object.hasOwnProperty.call(message, "incremental_from_pos")) @@ -59426,7 +61306,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { let tag = reader.uint32(); switch (tag >>> 3) { case 1: { - message.concurrency = reader.int64(); + message.concurrency = reader.int32(); break; } case 2: { @@ -59477,8 +61357,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (typeof message !== "object" || message === null) return "object expected"; if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (!$util.isInteger(message.concurrency) && !(message.concurrency && $util.isInteger(message.concurrency.low) && $util.isInteger(message.concurrency.high))) - return "concurrency: integer|Long expected"; + if (!$util.isInteger(message.concurrency)) + return "concurrency: integer expected"; if (message.allow_primary != null && message.hasOwnProperty("allow_primary")) if (typeof message.allow_primary !== "boolean") return "allow_primary: boolean expected"; @@ -59504,14 +61384,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { return object; let message = new $root.tabletmanagerdata.BackupRequest(); if (object.concurrency != null) - if ($util.Long) - (message.concurrency = $util.Long.fromValue(object.concurrency)).unsigned = false; - else if (typeof object.concurrency === "string") - message.concurrency = parseInt(object.concurrency, 10); - else if (typeof object.concurrency === "number") - message.concurrency = object.concurrency; - else if (typeof object.concurrency === "object") - message.concurrency = new $util.LongBits(object.concurrency.low >>> 0, object.concurrency.high >>> 0).toNumber(); + message.concurrency = object.concurrency | 0; if (object.allow_primary != null) message.allow_primary = Boolean(object.allow_primary); if (object.incremental_from_pos != null) @@ -59535,20 +61408,13 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { options = {}; let object = {}; if (options.defaults) { - if ($util.Long) { - let long = new $util.Long(0, 0, false); - object.concurrency = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.concurrency = options.longs === String ? "0" : 0; + object.concurrency = 0; object.allow_primary = false; object.incremental_from_pos = ""; object.upgrade_safe = false; } if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (typeof message.concurrency === "number") - object.concurrency = options.longs === String ? String(message.concurrency) : message.concurrency; - else - object.concurrency = options.longs === String ? $util.Long.prototype.toString.call(message.concurrency) : options.longs === Number ? new $util.LongBits(message.concurrency.low >>> 0, message.concurrency.high >>> 0).toNumber() : message.concurrency; + object.concurrency = message.concurrency; if (message.allow_primary != null && message.hasOwnProperty("allow_primary")) object.allow_primary = message.allow_primary; if (message.incremental_from_pos != null && message.hasOwnProperty("incremental_from_pos")) @@ -63808,6 +65674,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { * @property {boolean|null} [only_pks] VDiffReportOptions only_pks * @property {boolean|null} [debug_query] VDiffReportOptions debug_query * @property {string|null} [format] VDiffReportOptions format + * @property {number|Long|null} [max_sample_rows] VDiffReportOptions max_sample_rows */ /** @@ -63849,6 +65716,14 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { */ VDiffReportOptions.prototype.format = ""; + /** + * VDiffReportOptions max_sample_rows. + * @member {number|Long} max_sample_rows + * @memberof tabletmanagerdata.VDiffReportOptions + * @instance + */ + VDiffReportOptions.prototype.max_sample_rows = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + /** * Creates a new VDiffReportOptions instance using the specified properties. * @function create @@ -63879,6 +65754,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { writer.uint32(/* id 2, wireType 0 =*/16).bool(message.debug_query); if (message.format != null && Object.hasOwnProperty.call(message, "format")) writer.uint32(/* id 3, wireType 2 =*/26).string(message.format); + if (message.max_sample_rows != null && Object.hasOwnProperty.call(message, "max_sample_rows")) + writer.uint32(/* id 4, wireType 0 =*/32).int64(message.max_sample_rows); return writer; }; @@ -63925,6 +65802,10 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.format = reader.string(); break; } + case 4: { + message.max_sample_rows = reader.int64(); + break; + } default: reader.skipType(tag & 7); break; @@ -63969,6 +65850,9 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (message.format != null && message.hasOwnProperty("format")) if (!$util.isString(message.format)) return "format: string expected"; + if (message.max_sample_rows != null && message.hasOwnProperty("max_sample_rows")) + if (!$util.isInteger(message.max_sample_rows) && !(message.max_sample_rows && $util.isInteger(message.max_sample_rows.low) && $util.isInteger(message.max_sample_rows.high))) + return "max_sample_rows: integer|Long expected"; return null; }; @@ -63990,6 +65874,15 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.debug_query = Boolean(object.debug_query); if (object.format != null) message.format = String(object.format); + if (object.max_sample_rows != null) + if ($util.Long) + (message.max_sample_rows = $util.Long.fromValue(object.max_sample_rows)).unsigned = false; + else if (typeof object.max_sample_rows === "string") + message.max_sample_rows = parseInt(object.max_sample_rows, 10); + else if (typeof object.max_sample_rows === "number") + message.max_sample_rows = object.max_sample_rows; + else if (typeof object.max_sample_rows === "object") + message.max_sample_rows = new $util.LongBits(object.max_sample_rows.low >>> 0, object.max_sample_rows.high >>> 0).toNumber(); return message; }; @@ -64010,6 +65903,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.only_pks = false; object.debug_query = false; object.format = ""; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.max_sample_rows = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.max_sample_rows = options.longs === String ? "0" : 0; } if (message.only_pks != null && message.hasOwnProperty("only_pks")) object.only_pks = message.only_pks; @@ -64017,6 +65915,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.debug_query = message.debug_query; if (message.format != null && message.hasOwnProperty("format")) object.format = message.format; + if (message.max_sample_rows != null && message.hasOwnProperty("max_sample_rows")) + if (typeof message.max_sample_rows === "number") + object.max_sample_rows = options.longs === String ? String(message.max_sample_rows) : message.max_sample_rows; + else + object.max_sample_rows = options.longs === String ? $util.Long.prototype.toString.call(message.max_sample_rows) : options.longs === Number ? new $util.LongBits(message.max_sample_rows.low >>> 0, message.max_sample_rows.high >>> 0).toNumber() : message.max_sample_rows; return object; }; @@ -64063,6 +65966,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { * @property {number|Long|null} [timeout_seconds] VDiffCoreOptions timeout_seconds * @property {number|Long|null} [max_extra_rows_to_compare] VDiffCoreOptions max_extra_rows_to_compare * @property {boolean|null} [update_table_stats] VDiffCoreOptions update_table_stats + * @property {number|Long|null} [max_diff_seconds] VDiffCoreOptions max_diff_seconds */ /** @@ -64144,6 +66048,14 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { */ VDiffCoreOptions.prototype.update_table_stats = false; + /** + * VDiffCoreOptions max_diff_seconds. + * @member {number|Long} max_diff_seconds + * @memberof tabletmanagerdata.VDiffCoreOptions + * @instance + */ + VDiffCoreOptions.prototype.max_diff_seconds = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + /** * Creates a new VDiffCoreOptions instance using the specified properties. * @function create @@ -64184,6 +66096,8 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { writer.uint32(/* id 7, wireType 0 =*/56).int64(message.max_extra_rows_to_compare); if (message.update_table_stats != null && Object.hasOwnProperty.call(message, "update_table_stats")) writer.uint32(/* id 8, wireType 0 =*/64).bool(message.update_table_stats); + if (message.max_diff_seconds != null && Object.hasOwnProperty.call(message, "max_diff_seconds")) + writer.uint32(/* id 9, wireType 0 =*/72).int64(message.max_diff_seconds); return writer; }; @@ -64250,6 +66164,10 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.update_table_stats = reader.bool(); break; } + case 9: { + message.max_diff_seconds = reader.int64(); + break; + } default: reader.skipType(tag & 7); break; @@ -64309,6 +66227,9 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (message.update_table_stats != null && message.hasOwnProperty("update_table_stats")) if (typeof message.update_table_stats !== "boolean") return "update_table_stats: boolean expected"; + if (message.max_diff_seconds != null && message.hasOwnProperty("max_diff_seconds")) + if (!$util.isInteger(message.max_diff_seconds) && !(message.max_diff_seconds && $util.isInteger(message.max_diff_seconds.low) && $util.isInteger(message.max_diff_seconds.high))) + return "max_diff_seconds: integer|Long expected"; return null; }; @@ -64368,6 +66289,15 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.max_extra_rows_to_compare = new $util.LongBits(object.max_extra_rows_to_compare.low >>> 0, object.max_extra_rows_to_compare.high >>> 0).toNumber(); if (object.update_table_stats != null) message.update_table_stats = Boolean(object.update_table_stats); + if (object.max_diff_seconds != null) + if ($util.Long) + (message.max_diff_seconds = $util.Long.fromValue(object.max_diff_seconds)).unsigned = false; + else if (typeof object.max_diff_seconds === "string") + message.max_diff_seconds = parseInt(object.max_diff_seconds, 10); + else if (typeof object.max_diff_seconds === "number") + message.max_diff_seconds = object.max_diff_seconds; + else if (typeof object.max_diff_seconds === "object") + message.max_diff_seconds = new $util.LongBits(object.max_diff_seconds.low >>> 0, object.max_diff_seconds.high >>> 0).toNumber(); return message; }; @@ -64409,6 +66339,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { } else object.max_extra_rows_to_compare = options.longs === String ? "0" : 0; object.update_table_stats = false; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.max_diff_seconds = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.max_diff_seconds = options.longs === String ? "0" : 0; } if (message.tables != null && message.hasOwnProperty("tables")) object.tables = message.tables; @@ -64438,6 +66373,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.max_extra_rows_to_compare = options.longs === String ? $util.Long.prototype.toString.call(message.max_extra_rows_to_compare) : options.longs === Number ? new $util.LongBits(message.max_extra_rows_to_compare.low >>> 0, message.max_extra_rows_to_compare.high >>> 0).toNumber() : message.max_extra_rows_to_compare; if (message.update_table_stats != null && message.hasOwnProperty("update_table_stats")) object.update_table_stats = message.update_table_stats; + if (message.max_diff_seconds != null && message.hasOwnProperty("max_diff_seconds")) + if (typeof message.max_diff_seconds === "number") + object.max_diff_seconds = options.longs === String ? String(message.max_diff_seconds) : message.max_diff_seconds; + else + object.max_diff_seconds = options.longs === String ? $util.Long.prototype.toString.call(message.max_diff_seconds) : options.longs === Number ? new $util.LongBits(message.max_diff_seconds.low >>> 0, message.max_diff_seconds.high >>> 0).toNumber() : message.max_diff_seconds; return object; }; @@ -64747,6 +66687,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { * @property {tabletmanagerdata.TabletSelectionPreference|null} [tablet_selection_preference] UpdateVReplicationWorkflowRequest tablet_selection_preference * @property {binlogdata.OnDDLAction|null} [on_ddl] UpdateVReplicationWorkflowRequest on_ddl * @property {binlogdata.VReplicationWorkflowState|null} [state] UpdateVReplicationWorkflowRequest state + * @property {Array.|null} [shards] UpdateVReplicationWorkflowRequest shards */ /** @@ -64760,6 +66701,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { function UpdateVReplicationWorkflowRequest(properties) { this.cells = []; this.tablet_types = []; + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -64814,6 +66756,14 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { */ UpdateVReplicationWorkflowRequest.prototype.state = 0; + /** + * UpdateVReplicationWorkflowRequest shards. + * @member {Array.} shards + * @memberof tabletmanagerdata.UpdateVReplicationWorkflowRequest + * @instance + */ + UpdateVReplicationWorkflowRequest.prototype.shards = $util.emptyArray; + /** * Creates a new UpdateVReplicationWorkflowRequest instance using the specified properties. * @function create @@ -64855,6 +66805,9 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { writer.uint32(/* id 5, wireType 0 =*/40).int32(message.on_ddl); if (message.state != null && Object.hasOwnProperty.call(message, "state")) writer.uint32(/* id 6, wireType 0 =*/48).int32(message.state); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 7, wireType 2 =*/58).string(message.shards[i]); return writer; }; @@ -64922,6 +66875,12 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.state = reader.int32(); break; } + case 7: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -65020,6 +66979,13 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { case 6: break; } + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -65181,6 +67147,13 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { message.state = 6; break; } + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".tabletmanagerdata.UpdateVReplicationWorkflowRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -65200,6 +67173,7 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { if (options.arrays || options.defaults) { object.cells = []; object.tablet_types = []; + object.shards = []; } if (options.defaults) { object.workflow = ""; @@ -65225,6 +67199,11 @@ export const tabletmanagerdata = $root.tabletmanagerdata = (() => { object.on_ddl = options.enums === String ? $root.binlogdata.OnDDLAction[message.on_ddl] === undefined ? message.on_ddl : $root.binlogdata.OnDDLAction[message.on_ddl] : message.on_ddl; if (message.state != null && message.hasOwnProperty("state")) object.state = options.enums === String ? $root.binlogdata.VReplicationWorkflowState[message.state] === undefined ? message.state : $root.binlogdata.VReplicationWorkflowState[message.state] : message.state; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; @@ -68438,6 +70417,7 @@ export const binlogdata = $root.binlogdata = (() => { * @property {string|null} [target_unique_key_columns] Rule target_unique_key_columns * @property {string|null} [source_unique_key_target_columns] Rule source_unique_key_target_columns * @property {Object.|null} [convert_int_to_enum] Rule convert_int_to_enum + * @property {string|null} [force_unique_key] Rule force_unique_key */ /** @@ -68522,6 +70502,14 @@ export const binlogdata = $root.binlogdata = (() => { */ Rule.prototype.convert_int_to_enum = $util.emptyObject; + /** + * Rule force_unique_key. + * @member {string} force_unique_key + * @memberof binlogdata.Rule + * @instance + */ + Rule.prototype.force_unique_key = ""; + /** * Creates a new Rule instance using the specified properties. * @function create @@ -68567,6 +70555,8 @@ export const binlogdata = $root.binlogdata = (() => { if (message.convert_int_to_enum != null && Object.hasOwnProperty.call(message, "convert_int_to_enum")) for (let keys = Object.keys(message.convert_int_to_enum), i = 0; i < keys.length; ++i) writer.uint32(/* id 8, wireType 2 =*/66).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]).uint32(/* id 2, wireType 0 =*/16).bool(message.convert_int_to_enum[keys[i]]).ldelim(); + if (message.force_unique_key != null && Object.hasOwnProperty.call(message, "force_unique_key")) + writer.uint32(/* id 9, wireType 2 =*/74).string(message.force_unique_key); return writer; }; @@ -68690,6 +70680,10 @@ export const binlogdata = $root.binlogdata = (() => { message.convert_int_to_enum[key] = value; break; } + case 9: { + message.force_unique_key = reader.string(); + break; + } default: reader.skipType(tag & 7); break; @@ -68766,6 +70760,9 @@ export const binlogdata = $root.binlogdata = (() => { if (typeof message.convert_int_to_enum[key[i]] !== "boolean") return "convert_int_to_enum: boolean{k:string} expected"; } + if (message.force_unique_key != null && message.hasOwnProperty("force_unique_key")) + if (!$util.isString(message.force_unique_key)) + return "force_unique_key: string expected"; return null; }; @@ -68815,6 +70812,8 @@ export const binlogdata = $root.binlogdata = (() => { for (let keys = Object.keys(object.convert_int_to_enum), i = 0; i < keys.length; ++i) message.convert_int_to_enum[keys[i]] = Boolean(object.convert_int_to_enum[keys[i]]); } + if (object.force_unique_key != null) + message.force_unique_key = String(object.force_unique_key); return message; }; @@ -68842,6 +70841,7 @@ export const binlogdata = $root.binlogdata = (() => { object.source_unique_key_columns = ""; object.target_unique_key_columns = ""; object.source_unique_key_target_columns = ""; + object.force_unique_key = ""; } if (message.match != null && message.hasOwnProperty("match")) object.match = message.match; @@ -68869,6 +70869,8 @@ export const binlogdata = $root.binlogdata = (() => { for (let j = 0; j < keys2.length; ++j) object.convert_int_to_enum[keys2[j]] = message.convert_int_to_enum[keys2[j]]; } + if (message.force_unique_key != null && message.hasOwnProperty("force_unique_key")) + object.force_unique_key = message.force_unique_key; return object; }; @@ -101273,6 +103275,12 @@ export const vschema = $root.vschema = (() => { * @property {string|null} [name] Column name * @property {query.Type|null} [type] Column type * @property {boolean|null} [invisible] Column invisible + * @property {string|null} ["default"] Column default + * @property {string|null} [collation_name] Column collation_name + * @property {number|null} [size] Column size + * @property {number|null} [scale] Column scale + * @property {boolean|null} [nullable] Column nullable + * @property {Array.|null} [values] Column values */ /** @@ -101284,6 +103292,7 @@ export const vschema = $root.vschema = (() => { * @param {vschema.IColumn=} [properties] Properties to set */ function Column(properties) { + this.values = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -101314,6 +103323,68 @@ export const vschema = $root.vschema = (() => { */ Column.prototype.invisible = false; + /** + * Column default. + * @member {string} default + * @memberof vschema.Column + * @instance + */ + Column.prototype["default"] = ""; + + /** + * Column collation_name. + * @member {string} collation_name + * @memberof vschema.Column + * @instance + */ + Column.prototype.collation_name = ""; + + /** + * Column size. + * @member {number} size + * @memberof vschema.Column + * @instance + */ + Column.prototype.size = 0; + + /** + * Column scale. + * @member {number} scale + * @memberof vschema.Column + * @instance + */ + Column.prototype.scale = 0; + + /** + * Column nullable. + * @member {boolean|null|undefined} nullable + * @memberof vschema.Column + * @instance + */ + Column.prototype.nullable = null; + + /** + * Column values. + * @member {Array.} values + * @memberof vschema.Column + * @instance + */ + Column.prototype.values = $util.emptyArray; + + // OneOf field names bound to virtual getters and setters + let $oneOfFields; + + /** + * Column _nullable. + * @member {"nullable"|undefined} _nullable + * @memberof vschema.Column + * @instance + */ + Object.defineProperty(Column.prototype, "_nullable", { + get: $util.oneOfGetter($oneOfFields = ["nullable"]), + set: $util.oneOfSetter($oneOfFields) + }); + /** * Creates a new Column instance using the specified properties. * @function create @@ -101344,6 +103415,19 @@ export const vschema = $root.vschema = (() => { writer.uint32(/* id 2, wireType 0 =*/16).int32(message.type); if (message.invisible != null && Object.hasOwnProperty.call(message, "invisible")) writer.uint32(/* id 3, wireType 0 =*/24).bool(message.invisible); + if (message["default"] != null && Object.hasOwnProperty.call(message, "default")) + writer.uint32(/* id 4, wireType 2 =*/34).string(message["default"]); + if (message.collation_name != null && Object.hasOwnProperty.call(message, "collation_name")) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.collation_name); + if (message.size != null && Object.hasOwnProperty.call(message, "size")) + writer.uint32(/* id 6, wireType 0 =*/48).int32(message.size); + if (message.scale != null && Object.hasOwnProperty.call(message, "scale")) + writer.uint32(/* id 7, wireType 0 =*/56).int32(message.scale); + if (message.nullable != null && Object.hasOwnProperty.call(message, "nullable")) + writer.uint32(/* id 8, wireType 0 =*/64).bool(message.nullable); + if (message.values != null && message.values.length) + for (let i = 0; i < message.values.length; ++i) + writer.uint32(/* id 9, wireType 2 =*/74).string(message.values[i]); return writer; }; @@ -101390,6 +103474,32 @@ export const vschema = $root.vschema = (() => { message.invisible = reader.bool(); break; } + case 4: { + message["default"] = reader.string(); + break; + } + case 5: { + message.collation_name = reader.string(); + break; + } + case 6: { + message.size = reader.int32(); + break; + } + case 7: { + message.scale = reader.int32(); + break; + } + case 8: { + message.nullable = reader.bool(); + break; + } + case 9: { + if (!(message.values && message.values.length)) + message.values = []; + message.values.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -101425,6 +103535,7 @@ export const vschema = $root.vschema = (() => { Column.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; + let properties = {}; if (message.name != null && message.hasOwnProperty("name")) if (!$util.isString(message.name)) return "name: string expected"; @@ -101472,6 +103583,30 @@ export const vschema = $root.vschema = (() => { if (message.invisible != null && message.hasOwnProperty("invisible")) if (typeof message.invisible !== "boolean") return "invisible: boolean expected"; + if (message["default"] != null && message.hasOwnProperty("default")) + if (!$util.isString(message["default"])) + return "default: string expected"; + if (message.collation_name != null && message.hasOwnProperty("collation_name")) + if (!$util.isString(message.collation_name)) + return "collation_name: string expected"; + if (message.size != null && message.hasOwnProperty("size")) + if (!$util.isInteger(message.size)) + return "size: integer expected"; + if (message.scale != null && message.hasOwnProperty("scale")) + if (!$util.isInteger(message.scale)) + return "scale: integer expected"; + if (message.nullable != null && message.hasOwnProperty("nullable")) { + properties._nullable = 1; + if (typeof message.nullable !== "boolean") + return "nullable: boolean expected"; + } + if (message.values != null && message.hasOwnProperty("values")) { + if (!Array.isArray(message.values)) + return "values: array expected"; + for (let i = 0; i < message.values.length; ++i) + if (!$util.isString(message.values[i])) + return "values: string[] expected"; + } return null; }; @@ -101639,6 +103774,23 @@ export const vschema = $root.vschema = (() => { } if (object.invisible != null) message.invisible = Boolean(object.invisible); + if (object["default"] != null) + message["default"] = String(object["default"]); + if (object.collation_name != null) + message.collation_name = String(object.collation_name); + if (object.size != null) + message.size = object.size | 0; + if (object.scale != null) + message.scale = object.scale | 0; + if (object.nullable != null) + message.nullable = Boolean(object.nullable); + if (object.values) { + if (!Array.isArray(object.values)) + throw TypeError(".vschema.Column.values: array expected"); + message.values = []; + for (let i = 0; i < object.values.length; ++i) + message.values[i] = String(object.values[i]); + } return message; }; @@ -101655,10 +103807,16 @@ export const vschema = $root.vschema = (() => { if (!options) options = {}; let object = {}; + if (options.arrays || options.defaults) + object.values = []; if (options.defaults) { object.name = ""; object.type = options.enums === String ? "NULL_TYPE" : 0; object.invisible = false; + object["default"] = ""; + object.collation_name = ""; + object.size = 0; + object.scale = 0; } if (message.name != null && message.hasOwnProperty("name")) object.name = message.name; @@ -101666,6 +103824,24 @@ export const vschema = $root.vschema = (() => { object.type = options.enums === String ? $root.query.Type[message.type] === undefined ? message.type : $root.query.Type[message.type] : message.type; if (message.invisible != null && message.hasOwnProperty("invisible")) object.invisible = message.invisible; + if (message["default"] != null && message.hasOwnProperty("default")) + object["default"] = message["default"]; + if (message.collation_name != null && message.hasOwnProperty("collation_name")) + object.collation_name = message.collation_name; + if (message.size != null && message.hasOwnProperty("size")) + object.size = message.size; + if (message.scale != null && message.hasOwnProperty("scale")) + object.scale = message.scale; + if (message.nullable != null && message.hasOwnProperty("nullable")) { + object.nullable = message.nullable; + if (options.oneofs) + object._nullable = "nullable"; + } + if (message.values && message.values.length) { + object.values = []; + for (let j = 0; j < message.values.length; ++j) + object.values[j] = message.values[j]; + } return object; }; @@ -107076,6 +109252,9 @@ export const vtctldata = $root.vtctldata = (() => { * @property {Array.|null} [tags] Stream tags * @property {number|Long|null} [rows_copied] Stream rows_copied * @property {vtctldata.Workflow.Stream.IThrottlerStatus|null} [throttler_status] Stream throttler_status + * @property {Array.|null} [tablet_types] Stream tablet_types + * @property {tabletmanagerdata.TabletSelectionPreference|null} [tablet_selection_preference] Stream tablet_selection_preference + * @property {Array.|null} [cells] Stream cells */ /** @@ -107090,6 +109269,8 @@ export const vtctldata = $root.vtctldata = (() => { this.copy_states = []; this.logs = []; this.tags = []; + this.tablet_types = []; + this.cells = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -107232,6 +109413,30 @@ export const vtctldata = $root.vtctldata = (() => { */ Stream.prototype.throttler_status = null; + /** + * Stream tablet_types. + * @member {Array.} tablet_types + * @memberof vtctldata.Workflow.Stream + * @instance + */ + Stream.prototype.tablet_types = $util.emptyArray; + + /** + * Stream tablet_selection_preference. + * @member {tabletmanagerdata.TabletSelectionPreference} tablet_selection_preference + * @memberof vtctldata.Workflow.Stream + * @instance + */ + Stream.prototype.tablet_selection_preference = 0; + + /** + * Stream cells. + * @member {Array.} cells + * @memberof vtctldata.Workflow.Stream + * @instance + */ + Stream.prototype.cells = $util.emptyArray; + /** * Creates a new Stream instance using the specified properties. * @function create @@ -107293,6 +109498,17 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 16, wireType 0 =*/128).int64(message.rows_copied); if (message.throttler_status != null && Object.hasOwnProperty.call(message, "throttler_status")) $root.vtctldata.Workflow.Stream.ThrottlerStatus.encode(message.throttler_status, writer.uint32(/* id 17, wireType 2 =*/138).fork()).ldelim(); + if (message.tablet_types != null && message.tablet_types.length) { + writer.uint32(/* id 18, wireType 2 =*/146).fork(); + for (let i = 0; i < message.tablet_types.length; ++i) + writer.int32(message.tablet_types[i]); + writer.ldelim(); + } + if (message.tablet_selection_preference != null && Object.hasOwnProperty.call(message, "tablet_selection_preference")) + writer.uint32(/* id 19, wireType 0 =*/152).int32(message.tablet_selection_preference); + if (message.cells != null && message.cells.length) + for (let i = 0; i < message.cells.length; ++i) + writer.uint32(/* id 20, wireType 2 =*/162).string(message.cells[i]); return writer; }; @@ -107401,6 +109617,27 @@ export const vtctldata = $root.vtctldata = (() => { message.throttler_status = $root.vtctldata.Workflow.Stream.ThrottlerStatus.decode(reader, reader.uint32()); break; } + case 18: { + if (!(message.tablet_types && message.tablet_types.length)) + message.tablet_types = []; + if ((tag & 7) === 2) { + let end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.tablet_types.push(reader.int32()); + } else + message.tablet_types.push(reader.int32()); + break; + } + case 19: { + message.tablet_selection_preference = reader.int32(); + break; + } + case 20: { + if (!(message.cells && message.cells.length)) + message.cells = []; + message.cells.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -107513,6 +109750,43 @@ export const vtctldata = $root.vtctldata = (() => { if (error) return "throttler_status." + error; } + if (message.tablet_types != null && message.hasOwnProperty("tablet_types")) { + if (!Array.isArray(message.tablet_types)) + return "tablet_types: array expected"; + for (let i = 0; i < message.tablet_types.length; ++i) + switch (message.tablet_types[i]) { + default: + return "tablet_types: enum value[] expected"; + case 0: + case 1: + case 1: + case 2: + case 3: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + break; + } + } + if (message.tablet_selection_preference != null && message.hasOwnProperty("tablet_selection_preference")) + switch (message.tablet_selection_preference) { + default: + return "tablet_selection_preference: enum value expected"; + case 0: + case 1: + case 3: + break; + } + if (message.cells != null && message.hasOwnProperty("cells")) { + if (!Array.isArray(message.cells)) + return "cells: array expected"; + for (let i = 0; i < message.cells.length; ++i) + if (!$util.isString(message.cells[i])) + return "cells: string[] expected"; + } return null; }; @@ -107612,6 +109886,90 @@ export const vtctldata = $root.vtctldata = (() => { throw TypeError(".vtctldata.Workflow.Stream.throttler_status: object expected"); message.throttler_status = $root.vtctldata.Workflow.Stream.ThrottlerStatus.fromObject(object.throttler_status); } + if (object.tablet_types) { + if (!Array.isArray(object.tablet_types)) + throw TypeError(".vtctldata.Workflow.Stream.tablet_types: array expected"); + message.tablet_types = []; + for (let i = 0; i < object.tablet_types.length; ++i) + switch (object.tablet_types[i]) { + default: + if (typeof object.tablet_types[i] === "number") { + message.tablet_types[i] = object.tablet_types[i]; + break; + } + case "UNKNOWN": + case 0: + message.tablet_types[i] = 0; + break; + case "PRIMARY": + case 1: + message.tablet_types[i] = 1; + break; + case "MASTER": + case 1: + message.tablet_types[i] = 1; + break; + case "REPLICA": + case 2: + message.tablet_types[i] = 2; + break; + case "RDONLY": + case 3: + message.tablet_types[i] = 3; + break; + case "BATCH": + case 3: + message.tablet_types[i] = 3; + break; + case "SPARE": + case 4: + message.tablet_types[i] = 4; + break; + case "EXPERIMENTAL": + case 5: + message.tablet_types[i] = 5; + break; + case "BACKUP": + case 6: + message.tablet_types[i] = 6; + break; + case "RESTORE": + case 7: + message.tablet_types[i] = 7; + break; + case "DRAINED": + case 8: + message.tablet_types[i] = 8; + break; + } + } + switch (object.tablet_selection_preference) { + default: + if (typeof object.tablet_selection_preference === "number") { + message.tablet_selection_preference = object.tablet_selection_preference; + break; + } + break; + case "ANY": + case 0: + message.tablet_selection_preference = 0; + break; + case "INORDER": + case 1: + message.tablet_selection_preference = 1; + break; + case "UNKNOWN": + case 3: + message.tablet_selection_preference = 3; + break; + } + if (object.cells) { + if (!Array.isArray(object.cells)) + throw TypeError(".vtctldata.Workflow.Stream.cells: array expected"); + message.cells = []; + for (let i = 0; i < object.cells.length; ++i) + message.cells[i] = String(object.cells[i]); + } return message; }; @@ -107632,6 +109990,8 @@ export const vtctldata = $root.vtctldata = (() => { object.copy_states = []; object.logs = []; object.tags = []; + object.tablet_types = []; + object.cells = []; } if (options.defaults) { if ($util.Long) { @@ -107656,6 +110016,7 @@ export const vtctldata = $root.vtctldata = (() => { } else object.rows_copied = options.longs === String ? "0" : 0; object.throttler_status = null; + object.tablet_selection_preference = options.enums === String ? "ANY" : 0; } if (message.id != null && message.hasOwnProperty("id")) if (typeof message.id === "number") @@ -107706,6 +110067,18 @@ export const vtctldata = $root.vtctldata = (() => { object.rows_copied = options.longs === String ? $util.Long.prototype.toString.call(message.rows_copied) : options.longs === Number ? new $util.LongBits(message.rows_copied.low >>> 0, message.rows_copied.high >>> 0).toNumber() : message.rows_copied; if (message.throttler_status != null && message.hasOwnProperty("throttler_status")) object.throttler_status = $root.vtctldata.Workflow.Stream.ThrottlerStatus.toObject(message.throttler_status, options); + if (message.tablet_types && message.tablet_types.length) { + object.tablet_types = []; + for (let j = 0; j < message.tablet_types.length; ++j) + object.tablet_types[j] = options.enums === String ? $root.topodata.TabletType[message.tablet_types[j]] === undefined ? message.tablet_types[j] : $root.topodata.TabletType[message.tablet_types[j]] : message.tablet_types[j]; + } + if (message.tablet_selection_preference != null && message.hasOwnProperty("tablet_selection_preference")) + object.tablet_selection_preference = options.enums === String ? $root.tabletmanagerdata.TabletSelectionPreference[message.tablet_selection_preference] === undefined ? message.tablet_selection_preference : $root.tabletmanagerdata.TabletSelectionPreference[message.tablet_selection_preference] : message.tablet_selection_preference; + if (message.cells && message.cells.length) { + object.cells = []; + for (let j = 0; j < message.cells.length; ++j) + object.cells[j] = message.cells[j]; + } return object; }; @@ -107743,6 +110116,7 @@ export const vtctldata = $root.vtctldata = (() => { * @interface ICopyState * @property {string|null} [table] CopyState table * @property {string|null} [last_pk] CopyState last_pk + * @property {number|Long|null} [stream_id] CopyState stream_id */ /** @@ -107776,6 +110150,14 @@ export const vtctldata = $root.vtctldata = (() => { */ CopyState.prototype.last_pk = ""; + /** + * CopyState stream_id. + * @member {number|Long} stream_id + * @memberof vtctldata.Workflow.Stream.CopyState + * @instance + */ + CopyState.prototype.stream_id = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + /** * Creates a new CopyState instance using the specified properties. * @function create @@ -107804,6 +110186,8 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 1, wireType 2 =*/10).string(message.table); if (message.last_pk != null && Object.hasOwnProperty.call(message, "last_pk")) writer.uint32(/* id 2, wireType 2 =*/18).string(message.last_pk); + if (message.stream_id != null && Object.hasOwnProperty.call(message, "stream_id")) + writer.uint32(/* id 3, wireType 0 =*/24).int64(message.stream_id); return writer; }; @@ -107846,6 +110230,10 @@ export const vtctldata = $root.vtctldata = (() => { message.last_pk = reader.string(); break; } + case 3: { + message.stream_id = reader.int64(); + break; + } default: reader.skipType(tag & 7); break; @@ -107887,6 +110275,9 @@ export const vtctldata = $root.vtctldata = (() => { if (message.last_pk != null && message.hasOwnProperty("last_pk")) if (!$util.isString(message.last_pk)) return "last_pk: string expected"; + if (message.stream_id != null && message.hasOwnProperty("stream_id")) + if (!$util.isInteger(message.stream_id) && !(message.stream_id && $util.isInteger(message.stream_id.low) && $util.isInteger(message.stream_id.high))) + return "stream_id: integer|Long expected"; return null; }; @@ -107906,6 +110297,15 @@ export const vtctldata = $root.vtctldata = (() => { message.table = String(object.table); if (object.last_pk != null) message.last_pk = String(object.last_pk); + if (object.stream_id != null) + if ($util.Long) + (message.stream_id = $util.Long.fromValue(object.stream_id)).unsigned = false; + else if (typeof object.stream_id === "string") + message.stream_id = parseInt(object.stream_id, 10); + else if (typeof object.stream_id === "number") + message.stream_id = object.stream_id; + else if (typeof object.stream_id === "object") + message.stream_id = new $util.LongBits(object.stream_id.low >>> 0, object.stream_id.high >>> 0).toNumber(); return message; }; @@ -107925,11 +110325,21 @@ export const vtctldata = $root.vtctldata = (() => { if (options.defaults) { object.table = ""; object.last_pk = ""; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.stream_id = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.stream_id = options.longs === String ? "0" : 0; } if (message.table != null && message.hasOwnProperty("table")) object.table = message.table; if (message.last_pk != null && message.hasOwnProperty("last_pk")) object.last_pk = message.last_pk; + if (message.stream_id != null && message.hasOwnProperty("stream_id")) + if (typeof message.stream_id === "number") + object.stream_id = options.longs === String ? String(message.stream_id) : message.stream_id; + else + object.stream_id = options.longs === String ? $util.Long.prototype.toString.call(message.stream_id) : options.longs === Number ? new $util.LongBits(message.stream_id.low >>> 0, message.stream_id.high >>> 0).toNumber() : message.stream_id; return object; }; @@ -111059,6 +113469,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {Array.|null} [cells] ApplyVSchemaRequest cells * @property {vschema.IKeyspace|null} [v_schema] ApplyVSchemaRequest v_schema * @property {string|null} [sql] ApplyVSchemaRequest sql + * @property {boolean|null} [strict] ApplyVSchemaRequest strict */ /** @@ -111125,6 +113536,14 @@ export const vtctldata = $root.vtctldata = (() => { */ ApplyVSchemaRequest.prototype.sql = ""; + /** + * ApplyVSchemaRequest strict. + * @member {boolean} strict + * @memberof vtctldata.ApplyVSchemaRequest + * @instance + */ + ApplyVSchemaRequest.prototype.strict = false; + /** * Creates a new ApplyVSchemaRequest instance using the specified properties. * @function create @@ -111162,6 +113581,8 @@ export const vtctldata = $root.vtctldata = (() => { $root.vschema.Keyspace.encode(message.v_schema, writer.uint32(/* id 5, wireType 2 =*/42).fork()).ldelim(); if (message.sql != null && Object.hasOwnProperty.call(message, "sql")) writer.uint32(/* id 6, wireType 2 =*/50).string(message.sql); + if (message.strict != null && Object.hasOwnProperty.call(message, "strict")) + writer.uint32(/* id 7, wireType 0 =*/56).bool(message.strict); return writer; }; @@ -111222,6 +113643,10 @@ export const vtctldata = $root.vtctldata = (() => { message.sql = reader.string(); break; } + case 7: { + message.strict = reader.bool(); + break; + } default: reader.skipType(tag & 7); break; @@ -111281,6 +113706,9 @@ export const vtctldata = $root.vtctldata = (() => { if (message.sql != null && message.hasOwnProperty("sql")) if (!$util.isString(message.sql)) return "sql: string expected"; + if (message.strict != null && message.hasOwnProperty("strict")) + if (typeof message.strict !== "boolean") + return "strict: boolean expected"; return null; }; @@ -111316,6 +113744,8 @@ export const vtctldata = $root.vtctldata = (() => { } if (object.sql != null) message.sql = String(object.sql); + if (object.strict != null) + message.strict = Boolean(object.strict); return message; }; @@ -111340,6 +113770,7 @@ export const vtctldata = $root.vtctldata = (() => { object.dry_run = false; object.v_schema = null; object.sql = ""; + object.strict = false; } if (message.keyspace != null && message.hasOwnProperty("keyspace")) object.keyspace = message.keyspace; @@ -111356,6 +113787,8 @@ export const vtctldata = $root.vtctldata = (() => { object.v_schema = $root.vschema.Keyspace.toObject(message.v_schema, options); if (message.sql != null && message.hasOwnProperty("sql")) object.sql = message.sql; + if (message.strict != null && message.hasOwnProperty("strict")) + object.strict = message.strict; return object; }; @@ -111395,6 +113828,7 @@ export const vtctldata = $root.vtctldata = (() => { * @memberof vtctldata * @interface IApplyVSchemaResponse * @property {vschema.IKeyspace|null} [v_schema] ApplyVSchemaResponse v_schema + * @property {Object.|null} [unknown_vindex_params] ApplyVSchemaResponse unknown_vindex_params */ /** @@ -111406,6 +113840,7 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.IApplyVSchemaResponse=} [properties] Properties to set */ function ApplyVSchemaResponse(properties) { + this.unknown_vindex_params = {}; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -111420,6 +113855,14 @@ export const vtctldata = $root.vtctldata = (() => { */ ApplyVSchemaResponse.prototype.v_schema = null; + /** + * ApplyVSchemaResponse unknown_vindex_params. + * @member {Object.} unknown_vindex_params + * @memberof vtctldata.ApplyVSchemaResponse + * @instance + */ + ApplyVSchemaResponse.prototype.unknown_vindex_params = $util.emptyObject; + /** * Creates a new ApplyVSchemaResponse instance using the specified properties. * @function create @@ -111446,6 +113889,11 @@ export const vtctldata = $root.vtctldata = (() => { writer = $Writer.create(); if (message.v_schema != null && Object.hasOwnProperty.call(message, "v_schema")) $root.vschema.Keyspace.encode(message.v_schema, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.unknown_vindex_params != null && Object.hasOwnProperty.call(message, "unknown_vindex_params")) + for (let keys = Object.keys(message.unknown_vindex_params), i = 0; i < keys.length; ++i) { + writer.uint32(/* id 2, wireType 2 =*/18).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]); + $root.vtctldata.ApplyVSchemaResponse.ParamList.encode(message.unknown_vindex_params[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); + } return writer; }; @@ -111476,7 +113924,7 @@ export const vtctldata = $root.vtctldata = (() => { ApplyVSchemaResponse.decode = function decode(reader, length) { if (!(reader instanceof $Reader)) reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.ApplyVSchemaResponse(); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.ApplyVSchemaResponse(), key, value; while (reader.pos < end) { let tag = reader.uint32(); switch (tag >>> 3) { @@ -111484,6 +113932,29 @@ export const vtctldata = $root.vtctldata = (() => { message.v_schema = $root.vschema.Keyspace.decode(reader, reader.uint32()); break; } + case 2: { + if (message.unknown_vindex_params === $util.emptyObject) + message.unknown_vindex_params = {}; + let end2 = reader.uint32() + reader.pos; + key = ""; + value = null; + while (reader.pos < end2) { + let tag2 = reader.uint32(); + switch (tag2 >>> 3) { + case 1: + key = reader.string(); + break; + case 2: + value = $root.vtctldata.ApplyVSchemaResponse.ParamList.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag2 & 7); + break; + } + } + message.unknown_vindex_params[key] = value; + break; + } default: reader.skipType(tag & 7); break; @@ -111524,6 +113995,16 @@ export const vtctldata = $root.vtctldata = (() => { if (error) return "v_schema." + error; } + if (message.unknown_vindex_params != null && message.hasOwnProperty("unknown_vindex_params")) { + if (!$util.isObject(message.unknown_vindex_params)) + return "unknown_vindex_params: object expected"; + let key = Object.keys(message.unknown_vindex_params); + for (let i = 0; i < key.length; ++i) { + let error = $root.vtctldata.ApplyVSchemaResponse.ParamList.verify(message.unknown_vindex_params[key[i]]); + if (error) + return "unknown_vindex_params." + error; + } + } return null; }; @@ -111544,6 +114025,16 @@ export const vtctldata = $root.vtctldata = (() => { throw TypeError(".vtctldata.ApplyVSchemaResponse.v_schema: object expected"); message.v_schema = $root.vschema.Keyspace.fromObject(object.v_schema); } + if (object.unknown_vindex_params) { + if (typeof object.unknown_vindex_params !== "object") + throw TypeError(".vtctldata.ApplyVSchemaResponse.unknown_vindex_params: object expected"); + message.unknown_vindex_params = {}; + for (let keys = Object.keys(object.unknown_vindex_params), i = 0; i < keys.length; ++i) { + if (typeof object.unknown_vindex_params[keys[i]] !== "object") + throw TypeError(".vtctldata.ApplyVSchemaResponse.unknown_vindex_params: object expected"); + message.unknown_vindex_params[keys[i]] = $root.vtctldata.ApplyVSchemaResponse.ParamList.fromObject(object.unknown_vindex_params[keys[i]]); + } + } return message; }; @@ -111560,10 +114051,18 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; + if (options.objects || options.defaults) + object.unknown_vindex_params = {}; if (options.defaults) object.v_schema = null; if (message.v_schema != null && message.hasOwnProperty("v_schema")) object.v_schema = $root.vschema.Keyspace.toObject(message.v_schema, options); + let keys2; + if (message.unknown_vindex_params && (keys2 = Object.keys(message.unknown_vindex_params)).length) { + object.unknown_vindex_params = {}; + for (let j = 0; j < keys2.length; ++j) + object.unknown_vindex_params[keys2[j]] = $root.vtctldata.ApplyVSchemaResponse.ParamList.toObject(message.unknown_vindex_params[keys2[j]], options); + } return object; }; @@ -111593,6 +114092,225 @@ export const vtctldata = $root.vtctldata = (() => { return typeUrlPrefix + "/vtctldata.ApplyVSchemaResponse"; }; + ApplyVSchemaResponse.ParamList = (function() { + + /** + * Properties of a ParamList. + * @memberof vtctldata.ApplyVSchemaResponse + * @interface IParamList + * @property {Array.|null} [params] ParamList params + */ + + /** + * Constructs a new ParamList. + * @memberof vtctldata.ApplyVSchemaResponse + * @classdesc Represents a ParamList. + * @implements IParamList + * @constructor + * @param {vtctldata.ApplyVSchemaResponse.IParamList=} [properties] Properties to set + */ + function ParamList(properties) { + this.params = []; + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ParamList params. + * @member {Array.} params + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @instance + */ + ParamList.prototype.params = $util.emptyArray; + + /** + * Creates a new ParamList instance using the specified properties. + * @function create + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {vtctldata.ApplyVSchemaResponse.IParamList=} [properties] Properties to set + * @returns {vtctldata.ApplyVSchemaResponse.ParamList} ParamList instance + */ + ParamList.create = function create(properties) { + return new ParamList(properties); + }; + + /** + * Encodes the specified ParamList message. Does not implicitly {@link vtctldata.ApplyVSchemaResponse.ParamList.verify|verify} messages. + * @function encode + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {vtctldata.ApplyVSchemaResponse.IParamList} message ParamList message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ParamList.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.params != null && message.params.length) + for (let i = 0; i < message.params.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.params[i]); + return writer; + }; + + /** + * Encodes the specified ParamList message, length delimited. Does not implicitly {@link vtctldata.ApplyVSchemaResponse.ParamList.verify|verify} messages. + * @function encodeDelimited + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {vtctldata.ApplyVSchemaResponse.IParamList} message ParamList message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ParamList.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ParamList message from the specified reader or buffer. + * @function decode + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtctldata.ApplyVSchemaResponse.ParamList} ParamList + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ParamList.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.ApplyVSchemaResponse.ParamList(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (!(message.params && message.params.length)) + message.params = []; + message.params.push(reader.string()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ParamList message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtctldata.ApplyVSchemaResponse.ParamList} ParamList + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ParamList.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ParamList message. + * @function verify + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ParamList.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.params != null && message.hasOwnProperty("params")) { + if (!Array.isArray(message.params)) + return "params: array expected"; + for (let i = 0; i < message.params.length; ++i) + if (!$util.isString(message.params[i])) + return "params: string[] expected"; + } + return null; + }; + + /** + * Creates a ParamList message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {Object.} object Plain object + * @returns {vtctldata.ApplyVSchemaResponse.ParamList} ParamList + */ + ParamList.fromObject = function fromObject(object) { + if (object instanceof $root.vtctldata.ApplyVSchemaResponse.ParamList) + return object; + let message = new $root.vtctldata.ApplyVSchemaResponse.ParamList(); + if (object.params) { + if (!Array.isArray(object.params)) + throw TypeError(".vtctldata.ApplyVSchemaResponse.ParamList.params: array expected"); + message.params = []; + for (let i = 0; i < object.params.length; ++i) + message.params[i] = String(object.params[i]); + } + return message; + }; + + /** + * Creates a plain object from a ParamList message. Also converts values to other types if specified. + * @function toObject + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {vtctldata.ApplyVSchemaResponse.ParamList} message ParamList + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ParamList.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.arrays || options.defaults) + object.params = []; + if (message.params && message.params.length) { + object.params = []; + for (let j = 0; j < message.params.length; ++j) + object.params[j] = message.params[j]; + } + return object; + }; + + /** + * Converts this ParamList to JSON. + * @function toJSON + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @instance + * @returns {Object.} JSON object + */ + ParamList.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ParamList + * @function getTypeUrl + * @memberof vtctldata.ApplyVSchemaResponse.ParamList + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ParamList.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtctldata.ApplyVSchemaResponse.ParamList"; + }; + + return ParamList; + })(); + return ApplyVSchemaResponse; })(); @@ -111604,7 +114322,7 @@ export const vtctldata = $root.vtctldata = (() => { * @interface IBackupRequest * @property {topodata.ITabletAlias|null} [tablet_alias] BackupRequest tablet_alias * @property {boolean|null} [allow_primary] BackupRequest allow_primary - * @property {number|Long|null} [concurrency] BackupRequest concurrency + * @property {number|null} [concurrency] BackupRequest concurrency * @property {string|null} [incremental_from_pos] BackupRequest incremental_from_pos * @property {boolean|null} [upgrade_safe] BackupRequest upgrade_safe */ @@ -111642,11 +114360,11 @@ export const vtctldata = $root.vtctldata = (() => { /** * BackupRequest concurrency. - * @member {number|Long} concurrency + * @member {number} concurrency * @memberof vtctldata.BackupRequest * @instance */ - BackupRequest.prototype.concurrency = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + BackupRequest.prototype.concurrency = 0; /** * BackupRequest incremental_from_pos. @@ -111693,7 +114411,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.allow_primary != null && Object.hasOwnProperty.call(message, "allow_primary")) writer.uint32(/* id 2, wireType 0 =*/16).bool(message.allow_primary); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 3, wireType 0 =*/24).uint64(message.concurrency); + writer.uint32(/* id 3, wireType 0 =*/24).int32(message.concurrency); if (message.incremental_from_pos != null && Object.hasOwnProperty.call(message, "incremental_from_pos")) writer.uint32(/* id 4, wireType 2 =*/34).string(message.incremental_from_pos); if (message.upgrade_safe != null && Object.hasOwnProperty.call(message, "upgrade_safe")) @@ -111741,7 +114459,7 @@ export const vtctldata = $root.vtctldata = (() => { break; } case 3: { - message.concurrency = reader.uint64(); + message.concurrency = reader.int32(); break; } case 4: { @@ -111796,8 +114514,8 @@ export const vtctldata = $root.vtctldata = (() => { if (typeof message.allow_primary !== "boolean") return "allow_primary: boolean expected"; if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (!$util.isInteger(message.concurrency) && !(message.concurrency && $util.isInteger(message.concurrency.low) && $util.isInteger(message.concurrency.high))) - return "concurrency: integer|Long expected"; + if (!$util.isInteger(message.concurrency)) + return "concurrency: integer expected"; if (message.incremental_from_pos != null && message.hasOwnProperty("incremental_from_pos")) if (!$util.isString(message.incremental_from_pos)) return "incremental_from_pos: string expected"; @@ -111827,14 +114545,7 @@ export const vtctldata = $root.vtctldata = (() => { if (object.allow_primary != null) message.allow_primary = Boolean(object.allow_primary); if (object.concurrency != null) - if ($util.Long) - (message.concurrency = $util.Long.fromValue(object.concurrency)).unsigned = true; - else if (typeof object.concurrency === "string") - message.concurrency = parseInt(object.concurrency, 10); - else if (typeof object.concurrency === "number") - message.concurrency = object.concurrency; - else if (typeof object.concurrency === "object") - message.concurrency = new $util.LongBits(object.concurrency.low >>> 0, object.concurrency.high >>> 0).toNumber(true); + message.concurrency = object.concurrency | 0; if (object.incremental_from_pos != null) message.incremental_from_pos = String(object.incremental_from_pos); if (object.upgrade_safe != null) @@ -111858,11 +114569,7 @@ export const vtctldata = $root.vtctldata = (() => { if (options.defaults) { object.tablet_alias = null; object.allow_primary = false; - if ($util.Long) { - let long = new $util.Long(0, 0, true); - object.concurrency = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.concurrency = options.longs === String ? "0" : 0; + object.concurrency = 0; object.incremental_from_pos = ""; object.upgrade_safe = false; } @@ -111871,10 +114578,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.allow_primary != null && message.hasOwnProperty("allow_primary")) object.allow_primary = message.allow_primary; if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (typeof message.concurrency === "number") - object.concurrency = options.longs === String ? String(message.concurrency) : message.concurrency; - else - object.concurrency = options.longs === String ? $util.Long.prototype.toString.call(message.concurrency) : options.longs === Number ? new $util.LongBits(message.concurrency.low >>> 0, message.concurrency.high >>> 0).toNumber(true) : message.concurrency; + object.concurrency = message.concurrency; if (message.incremental_from_pos != null && message.hasOwnProperty("incremental_from_pos")) object.incremental_from_pos = message.incremental_from_pos; if (message.upgrade_safe != null && message.hasOwnProperty("upgrade_safe")) @@ -112203,7 +114907,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {string|null} [keyspace] BackupShardRequest keyspace * @property {string|null} [shard] BackupShardRequest shard * @property {boolean|null} [allow_primary] BackupShardRequest allow_primary - * @property {number|Long|null} [concurrency] BackupShardRequest concurrency + * @property {number|null} [concurrency] BackupShardRequest concurrency * @property {boolean|null} [upgrade_safe] BackupShardRequest upgrade_safe * @property {string|null} [incremental_from_pos] BackupShardRequest incremental_from_pos */ @@ -112249,11 +114953,11 @@ export const vtctldata = $root.vtctldata = (() => { /** * BackupShardRequest concurrency. - * @member {number|Long} concurrency + * @member {number} concurrency * @memberof vtctldata.BackupShardRequest * @instance */ - BackupShardRequest.prototype.concurrency = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + BackupShardRequest.prototype.concurrency = 0; /** * BackupShardRequest upgrade_safe. @@ -112302,7 +115006,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.allow_primary != null && Object.hasOwnProperty.call(message, "allow_primary")) writer.uint32(/* id 3, wireType 0 =*/24).bool(message.allow_primary); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 4, wireType 0 =*/32).uint64(message.concurrency); + writer.uint32(/* id 4, wireType 0 =*/32).int32(message.concurrency); if (message.upgrade_safe != null && Object.hasOwnProperty.call(message, "upgrade_safe")) writer.uint32(/* id 5, wireType 0 =*/40).bool(message.upgrade_safe); if (message.incremental_from_pos != null && Object.hasOwnProperty.call(message, "incremental_from_pos")) @@ -112354,7 +115058,7 @@ export const vtctldata = $root.vtctldata = (() => { break; } case 4: { - message.concurrency = reader.uint64(); + message.concurrency = reader.int32(); break; } case 5: { @@ -112410,8 +115114,8 @@ export const vtctldata = $root.vtctldata = (() => { if (typeof message.allow_primary !== "boolean") return "allow_primary: boolean expected"; if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (!$util.isInteger(message.concurrency) && !(message.concurrency && $util.isInteger(message.concurrency.low) && $util.isInteger(message.concurrency.high))) - return "concurrency: integer|Long expected"; + if (!$util.isInteger(message.concurrency)) + return "concurrency: integer expected"; if (message.upgrade_safe != null && message.hasOwnProperty("upgrade_safe")) if (typeof message.upgrade_safe !== "boolean") return "upgrade_safe: boolean expected"; @@ -112440,14 +115144,7 @@ export const vtctldata = $root.vtctldata = (() => { if (object.allow_primary != null) message.allow_primary = Boolean(object.allow_primary); if (object.concurrency != null) - if ($util.Long) - (message.concurrency = $util.Long.fromValue(object.concurrency)).unsigned = true; - else if (typeof object.concurrency === "string") - message.concurrency = parseInt(object.concurrency, 10); - else if (typeof object.concurrency === "number") - message.concurrency = object.concurrency; - else if (typeof object.concurrency === "object") - message.concurrency = new $util.LongBits(object.concurrency.low >>> 0, object.concurrency.high >>> 0).toNumber(true); + message.concurrency = object.concurrency | 0; if (object.upgrade_safe != null) message.upgrade_safe = Boolean(object.upgrade_safe); if (object.incremental_from_pos != null) @@ -112472,11 +115169,7 @@ export const vtctldata = $root.vtctldata = (() => { object.keyspace = ""; object.shard = ""; object.allow_primary = false; - if ($util.Long) { - let long = new $util.Long(0, 0, true); - object.concurrency = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.concurrency = options.longs === String ? "0" : 0; + object.concurrency = 0; object.upgrade_safe = false; object.incremental_from_pos = ""; } @@ -112487,10 +115180,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.allow_primary != null && message.hasOwnProperty("allow_primary")) object.allow_primary = message.allow_primary; if (message.concurrency != null && message.hasOwnProperty("concurrency")) - if (typeof message.concurrency === "number") - object.concurrency = options.longs === String ? String(message.concurrency) : message.concurrency; - else - object.concurrency = options.longs === String ? $util.Long.prototype.toString.call(message.concurrency) : options.longs === Number ? new $util.LongBits(message.concurrency.low >>> 0, message.concurrency.high >>> 0).toNumber(true) : message.concurrency; + object.concurrency = message.concurrency; if (message.upgrade_safe != null && message.hasOwnProperty("upgrade_safe")) object.upgrade_safe = message.upgrade_safe; if (message.incremental_from_pos != null && message.hasOwnProperty("incremental_from_pos")) @@ -114540,7 +117230,6 @@ export const vtctldata = $root.vtctldata = (() => { * @property {string|null} [name] CreateKeyspaceRequest name * @property {boolean|null} [force] CreateKeyspaceRequest force * @property {boolean|null} [allow_empty_v_schema] CreateKeyspaceRequest allow_empty_v_schema - * @property {Array.|null} [served_froms] CreateKeyspaceRequest served_froms * @property {topodata.KeyspaceType|null} [type] CreateKeyspaceRequest type * @property {string|null} [base_keyspace] CreateKeyspaceRequest base_keyspace * @property {vttime.ITime|null} [snapshot_time] CreateKeyspaceRequest snapshot_time @@ -114557,7 +117246,6 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.ICreateKeyspaceRequest=} [properties] Properties to set */ function CreateKeyspaceRequest(properties) { - this.served_froms = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -114588,14 +117276,6 @@ export const vtctldata = $root.vtctldata = (() => { */ CreateKeyspaceRequest.prototype.allow_empty_v_schema = false; - /** - * CreateKeyspaceRequest served_froms. - * @member {Array.} served_froms - * @memberof vtctldata.CreateKeyspaceRequest - * @instance - */ - CreateKeyspaceRequest.prototype.served_froms = $util.emptyArray; - /** * CreateKeyspaceRequest type. * @member {topodata.KeyspaceType} type @@ -114666,9 +117346,6 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 2, wireType 0 =*/16).bool(message.force); if (message.allow_empty_v_schema != null && Object.hasOwnProperty.call(message, "allow_empty_v_schema")) writer.uint32(/* id 3, wireType 0 =*/24).bool(message.allow_empty_v_schema); - if (message.served_froms != null && message.served_froms.length) - for (let i = 0; i < message.served_froms.length; ++i) - $root.topodata.Keyspace.ServedFrom.encode(message.served_froms[i], writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); if (message.type != null && Object.hasOwnProperty.call(message, "type")) writer.uint32(/* id 7, wireType 0 =*/56).int32(message.type); if (message.base_keyspace != null && Object.hasOwnProperty.call(message, "base_keyspace")) @@ -114725,12 +117402,6 @@ export const vtctldata = $root.vtctldata = (() => { message.allow_empty_v_schema = reader.bool(); break; } - case 6: { - if (!(message.served_froms && message.served_froms.length)) - message.served_froms = []; - message.served_froms.push($root.topodata.Keyspace.ServedFrom.decode(reader, reader.uint32())); - break; - } case 7: { message.type = reader.int32(); break; @@ -114795,15 +117466,6 @@ export const vtctldata = $root.vtctldata = (() => { if (message.allow_empty_v_schema != null && message.hasOwnProperty("allow_empty_v_schema")) if (typeof message.allow_empty_v_schema !== "boolean") return "allow_empty_v_schema: boolean expected"; - if (message.served_froms != null && message.hasOwnProperty("served_froms")) { - if (!Array.isArray(message.served_froms)) - return "served_froms: array expected"; - for (let i = 0; i < message.served_froms.length; ++i) { - let error = $root.topodata.Keyspace.ServedFrom.verify(message.served_froms[i]); - if (error) - return "served_froms." + error; - } - } if (message.type != null && message.hasOwnProperty("type")) switch (message.type) { default: @@ -114847,16 +117509,6 @@ export const vtctldata = $root.vtctldata = (() => { message.force = Boolean(object.force); if (object.allow_empty_v_schema != null) message.allow_empty_v_schema = Boolean(object.allow_empty_v_schema); - if (object.served_froms) { - if (!Array.isArray(object.served_froms)) - throw TypeError(".vtctldata.CreateKeyspaceRequest.served_froms: array expected"); - message.served_froms = []; - for (let i = 0; i < object.served_froms.length; ++i) { - if (typeof object.served_froms[i] !== "object") - throw TypeError(".vtctldata.CreateKeyspaceRequest.served_froms: object expected"); - message.served_froms[i] = $root.topodata.Keyspace.ServedFrom.fromObject(object.served_froms[i]); - } - } switch (object.type) { default: if (typeof object.type === "number") { @@ -114900,8 +117552,6 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; - if (options.arrays || options.defaults) - object.served_froms = []; if (options.defaults) { object.name = ""; object.force = false; @@ -114918,11 +117568,6 @@ export const vtctldata = $root.vtctldata = (() => { object.force = message.force; if (message.allow_empty_v_schema != null && message.hasOwnProperty("allow_empty_v_schema")) object.allow_empty_v_schema = message.allow_empty_v_schema; - if (message.served_froms && message.served_froms.length) { - object.served_froms = []; - for (let j = 0; j < message.served_froms.length; ++j) - object.served_froms[j] = $root.topodata.Keyspace.ServedFrom.toObject(message.served_froms[j], options); - } if (message.type != null && message.hasOwnProperty("type")) object.type = options.enums === String ? $root.topodata.KeyspaceType[message.type] === undefined ? message.type : $root.topodata.KeyspaceType[message.type] : message.type; if (message.base_keyspace != null && message.hasOwnProperty("base_keyspace")) @@ -120772,6 +123417,481 @@ export const vtctldata = $root.vtctldata = (() => { return FindAllShardsInKeyspaceResponse; })(); + vtctldata.ForceCutOverSchemaMigrationRequest = (function() { + + /** + * Properties of a ForceCutOverSchemaMigrationRequest. + * @memberof vtctldata + * @interface IForceCutOverSchemaMigrationRequest + * @property {string|null} [keyspace] ForceCutOverSchemaMigrationRequest keyspace + * @property {string|null} [uuid] ForceCutOverSchemaMigrationRequest uuid + */ + + /** + * Constructs a new ForceCutOverSchemaMigrationRequest. + * @memberof vtctldata + * @classdesc Represents a ForceCutOverSchemaMigrationRequest. + * @implements IForceCutOverSchemaMigrationRequest + * @constructor + * @param {vtctldata.IForceCutOverSchemaMigrationRequest=} [properties] Properties to set + */ + function ForceCutOverSchemaMigrationRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ForceCutOverSchemaMigrationRequest keyspace. + * @member {string} keyspace + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @instance + */ + ForceCutOverSchemaMigrationRequest.prototype.keyspace = ""; + + /** + * ForceCutOverSchemaMigrationRequest uuid. + * @member {string} uuid + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @instance + */ + ForceCutOverSchemaMigrationRequest.prototype.uuid = ""; + + /** + * Creates a new ForceCutOverSchemaMigrationRequest instance using the specified properties. + * @function create + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationRequest=} [properties] Properties to set + * @returns {vtctldata.ForceCutOverSchemaMigrationRequest} ForceCutOverSchemaMigrationRequest instance + */ + ForceCutOverSchemaMigrationRequest.create = function create(properties) { + return new ForceCutOverSchemaMigrationRequest(properties); + }; + + /** + * Encodes the specified ForceCutOverSchemaMigrationRequest message. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationRequest.verify|verify} messages. + * @function encode + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationRequest} message ForceCutOverSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ForceCutOverSchemaMigrationRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.keyspace != null && Object.hasOwnProperty.call(message, "keyspace")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.keyspace); + if (message.uuid != null && Object.hasOwnProperty.call(message, "uuid")) + writer.uint32(/* id 2, wireType 2 =*/18).string(message.uuid); + return writer; + }; + + /** + * Encodes the specified ForceCutOverSchemaMigrationRequest message, length delimited. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationRequest} message ForceCutOverSchemaMigrationRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ForceCutOverSchemaMigrationRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ForceCutOverSchemaMigrationRequest message from the specified reader or buffer. + * @function decode + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtctldata.ForceCutOverSchemaMigrationRequest} ForceCutOverSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ForceCutOverSchemaMigrationRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.ForceCutOverSchemaMigrationRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.keyspace = reader.string(); + break; + } + case 2: { + message.uuid = reader.string(); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ForceCutOverSchemaMigrationRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtctldata.ForceCutOverSchemaMigrationRequest} ForceCutOverSchemaMigrationRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ForceCutOverSchemaMigrationRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ForceCutOverSchemaMigrationRequest message. + * @function verify + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ForceCutOverSchemaMigrationRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.keyspace != null && message.hasOwnProperty("keyspace")) + if (!$util.isString(message.keyspace)) + return "keyspace: string expected"; + if (message.uuid != null && message.hasOwnProperty("uuid")) + if (!$util.isString(message.uuid)) + return "uuid: string expected"; + return null; + }; + + /** + * Creates a ForceCutOverSchemaMigrationRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {Object.} object Plain object + * @returns {vtctldata.ForceCutOverSchemaMigrationRequest} ForceCutOverSchemaMigrationRequest + */ + ForceCutOverSchemaMigrationRequest.fromObject = function fromObject(object) { + if (object instanceof $root.vtctldata.ForceCutOverSchemaMigrationRequest) + return object; + let message = new $root.vtctldata.ForceCutOverSchemaMigrationRequest(); + if (object.keyspace != null) + message.keyspace = String(object.keyspace); + if (object.uuid != null) + message.uuid = String(object.uuid); + return message; + }; + + /** + * Creates a plain object from a ForceCutOverSchemaMigrationRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {vtctldata.ForceCutOverSchemaMigrationRequest} message ForceCutOverSchemaMigrationRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ForceCutOverSchemaMigrationRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.keyspace = ""; + object.uuid = ""; + } + if (message.keyspace != null && message.hasOwnProperty("keyspace")) + object.keyspace = message.keyspace; + if (message.uuid != null && message.hasOwnProperty("uuid")) + object.uuid = message.uuid; + return object; + }; + + /** + * Converts this ForceCutOverSchemaMigrationRequest to JSON. + * @function toJSON + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @instance + * @returns {Object.} JSON object + */ + ForceCutOverSchemaMigrationRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ForceCutOverSchemaMigrationRequest + * @function getTypeUrl + * @memberof vtctldata.ForceCutOverSchemaMigrationRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ForceCutOverSchemaMigrationRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtctldata.ForceCutOverSchemaMigrationRequest"; + }; + + return ForceCutOverSchemaMigrationRequest; + })(); + + vtctldata.ForceCutOverSchemaMigrationResponse = (function() { + + /** + * Properties of a ForceCutOverSchemaMigrationResponse. + * @memberof vtctldata + * @interface IForceCutOverSchemaMigrationResponse + * @property {Object.|null} [rows_affected_by_shard] ForceCutOverSchemaMigrationResponse rows_affected_by_shard + */ + + /** + * Constructs a new ForceCutOverSchemaMigrationResponse. + * @memberof vtctldata + * @classdesc Represents a ForceCutOverSchemaMigrationResponse. + * @implements IForceCutOverSchemaMigrationResponse + * @constructor + * @param {vtctldata.IForceCutOverSchemaMigrationResponse=} [properties] Properties to set + */ + function ForceCutOverSchemaMigrationResponse(properties) { + this.rows_affected_by_shard = {}; + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * ForceCutOverSchemaMigrationResponse rows_affected_by_shard. + * @member {Object.} rows_affected_by_shard + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @instance + */ + ForceCutOverSchemaMigrationResponse.prototype.rows_affected_by_shard = $util.emptyObject; + + /** + * Creates a new ForceCutOverSchemaMigrationResponse instance using the specified properties. + * @function create + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationResponse=} [properties] Properties to set + * @returns {vtctldata.ForceCutOverSchemaMigrationResponse} ForceCutOverSchemaMigrationResponse instance + */ + ForceCutOverSchemaMigrationResponse.create = function create(properties) { + return new ForceCutOverSchemaMigrationResponse(properties); + }; + + /** + * Encodes the specified ForceCutOverSchemaMigrationResponse message. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationResponse.verify|verify} messages. + * @function encode + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationResponse} message ForceCutOverSchemaMigrationResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ForceCutOverSchemaMigrationResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.rows_affected_by_shard != null && Object.hasOwnProperty.call(message, "rows_affected_by_shard")) + for (let keys = Object.keys(message.rows_affected_by_shard), i = 0; i < keys.length; ++i) + writer.uint32(/* id 1, wireType 2 =*/10).fork().uint32(/* id 1, wireType 2 =*/10).string(keys[i]).uint32(/* id 2, wireType 0 =*/16).uint64(message.rows_affected_by_shard[keys[i]]).ldelim(); + return writer; + }; + + /** + * Encodes the specified ForceCutOverSchemaMigrationResponse message, length delimited. Does not implicitly {@link vtctldata.ForceCutOverSchemaMigrationResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {vtctldata.IForceCutOverSchemaMigrationResponse} message ForceCutOverSchemaMigrationResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ForceCutOverSchemaMigrationResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ForceCutOverSchemaMigrationResponse message from the specified reader or buffer. + * @function decode + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {vtctldata.ForceCutOverSchemaMigrationResponse} ForceCutOverSchemaMigrationResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ForceCutOverSchemaMigrationResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.ForceCutOverSchemaMigrationResponse(), key, value; + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (message.rows_affected_by_shard === $util.emptyObject) + message.rows_affected_by_shard = {}; + let end2 = reader.uint32() + reader.pos; + key = ""; + value = 0; + while (reader.pos < end2) { + let tag2 = reader.uint32(); + switch (tag2 >>> 3) { + case 1: + key = reader.string(); + break; + case 2: + value = reader.uint64(); + break; + default: + reader.skipType(tag2 & 7); + break; + } + } + message.rows_affected_by_shard[key] = value; + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ForceCutOverSchemaMigrationResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {vtctldata.ForceCutOverSchemaMigrationResponse} ForceCutOverSchemaMigrationResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ForceCutOverSchemaMigrationResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ForceCutOverSchemaMigrationResponse message. + * @function verify + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ForceCutOverSchemaMigrationResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.rows_affected_by_shard != null && message.hasOwnProperty("rows_affected_by_shard")) { + if (!$util.isObject(message.rows_affected_by_shard)) + return "rows_affected_by_shard: object expected"; + let key = Object.keys(message.rows_affected_by_shard); + for (let i = 0; i < key.length; ++i) + if (!$util.isInteger(message.rows_affected_by_shard[key[i]]) && !(message.rows_affected_by_shard[key[i]] && $util.isInteger(message.rows_affected_by_shard[key[i]].low) && $util.isInteger(message.rows_affected_by_shard[key[i]].high))) + return "rows_affected_by_shard: integer|Long{k:string} expected"; + } + return null; + }; + + /** + * Creates a ForceCutOverSchemaMigrationResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {Object.} object Plain object + * @returns {vtctldata.ForceCutOverSchemaMigrationResponse} ForceCutOverSchemaMigrationResponse + */ + ForceCutOverSchemaMigrationResponse.fromObject = function fromObject(object) { + if (object instanceof $root.vtctldata.ForceCutOverSchemaMigrationResponse) + return object; + let message = new $root.vtctldata.ForceCutOverSchemaMigrationResponse(); + if (object.rows_affected_by_shard) { + if (typeof object.rows_affected_by_shard !== "object") + throw TypeError(".vtctldata.ForceCutOverSchemaMigrationResponse.rows_affected_by_shard: object expected"); + message.rows_affected_by_shard = {}; + for (let keys = Object.keys(object.rows_affected_by_shard), i = 0; i < keys.length; ++i) + if ($util.Long) + (message.rows_affected_by_shard[keys[i]] = $util.Long.fromValue(object.rows_affected_by_shard[keys[i]])).unsigned = true; + else if (typeof object.rows_affected_by_shard[keys[i]] === "string") + message.rows_affected_by_shard[keys[i]] = parseInt(object.rows_affected_by_shard[keys[i]], 10); + else if (typeof object.rows_affected_by_shard[keys[i]] === "number") + message.rows_affected_by_shard[keys[i]] = object.rows_affected_by_shard[keys[i]]; + else if (typeof object.rows_affected_by_shard[keys[i]] === "object") + message.rows_affected_by_shard[keys[i]] = new $util.LongBits(object.rows_affected_by_shard[keys[i]].low >>> 0, object.rows_affected_by_shard[keys[i]].high >>> 0).toNumber(true); + } + return message; + }; + + /** + * Creates a plain object from a ForceCutOverSchemaMigrationResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {vtctldata.ForceCutOverSchemaMigrationResponse} message ForceCutOverSchemaMigrationResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ForceCutOverSchemaMigrationResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.objects || options.defaults) + object.rows_affected_by_shard = {}; + let keys2; + if (message.rows_affected_by_shard && (keys2 = Object.keys(message.rows_affected_by_shard)).length) { + object.rows_affected_by_shard = {}; + for (let j = 0; j < keys2.length; ++j) + if (typeof message.rows_affected_by_shard[keys2[j]] === "number") + object.rows_affected_by_shard[keys2[j]] = options.longs === String ? String(message.rows_affected_by_shard[keys2[j]]) : message.rows_affected_by_shard[keys2[j]]; + else + object.rows_affected_by_shard[keys2[j]] = options.longs === String ? $util.Long.prototype.toString.call(message.rows_affected_by_shard[keys2[j]]) : options.longs === Number ? new $util.LongBits(message.rows_affected_by_shard[keys2[j]].low >>> 0, message.rows_affected_by_shard[keys2[j]].high >>> 0).toNumber(true) : message.rows_affected_by_shard[keys2[j]]; + } + return object; + }; + + /** + * Converts this ForceCutOverSchemaMigrationResponse to JSON. + * @function toJSON + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @instance + * @returns {Object.} JSON object + */ + ForceCutOverSchemaMigrationResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ForceCutOverSchemaMigrationResponse + * @function getTypeUrl + * @memberof vtctldata.ForceCutOverSchemaMigrationResponse + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ForceCutOverSchemaMigrationResponse.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/vtctldata.ForceCutOverSchemaMigrationResponse"; + }; + + return ForceCutOverSchemaMigrationResponse; + })(); + vtctldata.GetBackupsRequest = (function() { /** @@ -131854,6 +134974,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {boolean|null} [name_only] GetWorkflowsRequest name_only * @property {string|null} [workflow] GetWorkflowsRequest workflow * @property {boolean|null} [include_logs] GetWorkflowsRequest include_logs + * @property {Array.|null} [shards] GetWorkflowsRequest shards */ /** @@ -131865,6 +134986,7 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.IGetWorkflowsRequest=} [properties] Properties to set */ function GetWorkflowsRequest(properties) { + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -131911,6 +135033,14 @@ export const vtctldata = $root.vtctldata = (() => { */ GetWorkflowsRequest.prototype.include_logs = false; + /** + * GetWorkflowsRequest shards. + * @member {Array.} shards + * @memberof vtctldata.GetWorkflowsRequest + * @instance + */ + GetWorkflowsRequest.prototype.shards = $util.emptyArray; + /** * Creates a new GetWorkflowsRequest instance using the specified properties. * @function create @@ -131945,6 +135075,9 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 4, wireType 2 =*/34).string(message.workflow); if (message.include_logs != null && Object.hasOwnProperty.call(message, "include_logs")) writer.uint32(/* id 5, wireType 0 =*/40).bool(message.include_logs); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 6, wireType 2 =*/50).string(message.shards[i]); return writer; }; @@ -131999,6 +135132,12 @@ export const vtctldata = $root.vtctldata = (() => { message.include_logs = reader.bool(); break; } + case 6: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -132049,6 +135188,13 @@ export const vtctldata = $root.vtctldata = (() => { if (message.include_logs != null && message.hasOwnProperty("include_logs")) if (typeof message.include_logs !== "boolean") return "include_logs: boolean expected"; + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -132074,6 +135220,13 @@ export const vtctldata = $root.vtctldata = (() => { message.workflow = String(object.workflow); if (object.include_logs != null) message.include_logs = Boolean(object.include_logs); + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".vtctldata.GetWorkflowsRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -132090,6 +135243,8 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; + if (options.arrays || options.defaults) + object.shards = []; if (options.defaults) { object.keyspace = ""; object.active_only = false; @@ -132107,6 +135262,11 @@ export const vtctldata = $root.vtctldata = (() => { object.workflow = message.workflow; if (message.include_logs != null && message.hasOwnProperty("include_logs")) object.include_logs = message.include_logs; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; @@ -139136,6 +142296,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {boolean|null} [keep_routing_rules] MoveTablesCompleteRequest keep_routing_rules * @property {boolean|null} [rename_tables] MoveTablesCompleteRequest rename_tables * @property {boolean|null} [dry_run] MoveTablesCompleteRequest dry_run + * @property {Array.|null} [shards] MoveTablesCompleteRequest shards */ /** @@ -139147,6 +142308,7 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.IMoveTablesCompleteRequest=} [properties] Properties to set */ function MoveTablesCompleteRequest(properties) { + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -139201,6 +142363,14 @@ export const vtctldata = $root.vtctldata = (() => { */ MoveTablesCompleteRequest.prototype.dry_run = false; + /** + * MoveTablesCompleteRequest shards. + * @member {Array.} shards + * @memberof vtctldata.MoveTablesCompleteRequest + * @instance + */ + MoveTablesCompleteRequest.prototype.shards = $util.emptyArray; + /** * Creates a new MoveTablesCompleteRequest instance using the specified properties. * @function create @@ -139237,6 +142407,9 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 6, wireType 0 =*/48).bool(message.rename_tables); if (message.dry_run != null && Object.hasOwnProperty.call(message, "dry_run")) writer.uint32(/* id 7, wireType 0 =*/56).bool(message.dry_run); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 8, wireType 2 =*/66).string(message.shards[i]); return writer; }; @@ -139295,6 +142468,12 @@ export const vtctldata = $root.vtctldata = (() => { message.dry_run = reader.bool(); break; } + case 8: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -139348,6 +142527,13 @@ export const vtctldata = $root.vtctldata = (() => { if (message.dry_run != null && message.hasOwnProperty("dry_run")) if (typeof message.dry_run !== "boolean") return "dry_run: boolean expected"; + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -139375,6 +142561,13 @@ export const vtctldata = $root.vtctldata = (() => { message.rename_tables = Boolean(object.rename_tables); if (object.dry_run != null) message.dry_run = Boolean(object.dry_run); + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".vtctldata.MoveTablesCompleteRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -139391,6 +142584,8 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; + if (options.arrays || options.defaults) + object.shards = []; if (options.defaults) { object.workflow = ""; object.target_keyspace = ""; @@ -139411,6 +142606,11 @@ export const vtctldata = $root.vtctldata = (() => { object.rename_tables = message.rename_tables; if (message.dry_run != null && message.hasOwnProperty("dry_run")) object.dry_run = message.dry_run; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; @@ -140080,6 +143280,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {topodata.ITabletAlias|null} [new_primary] PlannedReparentShardRequest new_primary * @property {topodata.ITabletAlias|null} [avoid_primary] PlannedReparentShardRequest avoid_primary * @property {vttime.IDuration|null} [wait_replicas_timeout] PlannedReparentShardRequest wait_replicas_timeout + * @property {vttime.IDuration|null} [tolerable_replication_lag] PlannedReparentShardRequest tolerable_replication_lag */ /** @@ -140137,6 +143338,14 @@ export const vtctldata = $root.vtctldata = (() => { */ PlannedReparentShardRequest.prototype.wait_replicas_timeout = null; + /** + * PlannedReparentShardRequest tolerable_replication_lag. + * @member {vttime.IDuration|null|undefined} tolerable_replication_lag + * @memberof vtctldata.PlannedReparentShardRequest + * @instance + */ + PlannedReparentShardRequest.prototype.tolerable_replication_lag = null; + /** * Creates a new PlannedReparentShardRequest instance using the specified properties. * @function create @@ -140171,6 +143380,8 @@ export const vtctldata = $root.vtctldata = (() => { $root.topodata.TabletAlias.encode(message.avoid_primary, writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); if (message.wait_replicas_timeout != null && Object.hasOwnProperty.call(message, "wait_replicas_timeout")) $root.vttime.Duration.encode(message.wait_replicas_timeout, writer.uint32(/* id 5, wireType 2 =*/42).fork()).ldelim(); + if (message.tolerable_replication_lag != null && Object.hasOwnProperty.call(message, "tolerable_replication_lag")) + $root.vttime.Duration.encode(message.tolerable_replication_lag, writer.uint32(/* id 6, wireType 2 =*/50).fork()).ldelim(); return writer; }; @@ -140225,6 +143436,10 @@ export const vtctldata = $root.vtctldata = (() => { message.wait_replicas_timeout = $root.vttime.Duration.decode(reader, reader.uint32()); break; } + case 6: { + message.tolerable_replication_lag = $root.vttime.Duration.decode(reader, reader.uint32()); + break; + } default: reader.skipType(tag & 7); break; @@ -140281,6 +143496,11 @@ export const vtctldata = $root.vtctldata = (() => { if (error) return "wait_replicas_timeout." + error; } + if (message.tolerable_replication_lag != null && message.hasOwnProperty("tolerable_replication_lag")) { + let error = $root.vttime.Duration.verify(message.tolerable_replication_lag); + if (error) + return "tolerable_replication_lag." + error; + } return null; }; @@ -140315,6 +143535,11 @@ export const vtctldata = $root.vtctldata = (() => { throw TypeError(".vtctldata.PlannedReparentShardRequest.wait_replicas_timeout: object expected"); message.wait_replicas_timeout = $root.vttime.Duration.fromObject(object.wait_replicas_timeout); } + if (object.tolerable_replication_lag != null) { + if (typeof object.tolerable_replication_lag !== "object") + throw TypeError(".vtctldata.PlannedReparentShardRequest.tolerable_replication_lag: object expected"); + message.tolerable_replication_lag = $root.vttime.Duration.fromObject(object.tolerable_replication_lag); + } return message; }; @@ -140337,6 +143562,7 @@ export const vtctldata = $root.vtctldata = (() => { object.new_primary = null; object.avoid_primary = null; object.wait_replicas_timeout = null; + object.tolerable_replication_lag = null; } if (message.keyspace != null && message.hasOwnProperty("keyspace")) object.keyspace = message.keyspace; @@ -140348,6 +143574,8 @@ export const vtctldata = $root.vtctldata = (() => { object.avoid_primary = $root.topodata.TabletAlias.toObject(message.avoid_primary, options); if (message.wait_replicas_timeout != null && message.hasOwnProperty("wait_replicas_timeout")) object.wait_replicas_timeout = $root.vttime.Duration.toObject(message.wait_replicas_timeout, options); + if (message.tolerable_replication_lag != null && message.hasOwnProperty("tolerable_replication_lag")) + object.tolerable_replication_lag = $root.vttime.Duration.toObject(message.tolerable_replication_lag, options); return object; }; @@ -142866,7 +146094,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.include_primary != null && Object.hasOwnProperty.call(message, "include_primary")) writer.uint32(/* id 3, wireType 0 =*/24).bool(message.include_primary); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 4, wireType 0 =*/32).uint32(message.concurrency); + writer.uint32(/* id 4, wireType 0 =*/32).int32(message.concurrency); return writer; }; @@ -142914,7 +146142,7 @@ export const vtctldata = $root.vtctldata = (() => { break; } case 4: { - message.concurrency = reader.uint32(); + message.concurrency = reader.int32(); break; } default: @@ -142986,7 +146214,7 @@ export const vtctldata = $root.vtctldata = (() => { if (object.include_primary != null) message.include_primary = Boolean(object.include_primary); if (object.concurrency != null) - message.concurrency = object.concurrency >>> 0; + message.concurrency = object.concurrency | 0; return message; }; @@ -143374,7 +146602,7 @@ export const vtctldata = $root.vtctldata = (() => { if (message.include_primary != null && Object.hasOwnProperty.call(message, "include_primary")) writer.uint32(/* id 4, wireType 0 =*/32).bool(message.include_primary); if (message.concurrency != null && Object.hasOwnProperty.call(message, "concurrency")) - writer.uint32(/* id 5, wireType 0 =*/40).uint32(message.concurrency); + writer.uint32(/* id 5, wireType 0 =*/40).int32(message.concurrency); return writer; }; @@ -143426,7 +146654,7 @@ export const vtctldata = $root.vtctldata = (() => { break; } case 5: { - message.concurrency = reader.uint32(); + message.concurrency = reader.int32(); break; } default: @@ -143503,7 +146731,7 @@ export const vtctldata = $root.vtctldata = (() => { if (object.include_primary != null) message.include_primary = Boolean(object.include_primary); if (object.concurrency != null) - message.concurrency = object.concurrency >>> 0; + message.concurrency = object.concurrency | 0; return message; }; @@ -148106,591 +151334,6 @@ export const vtctldata = $root.vtctldata = (() => { return SetKeyspaceDurabilityPolicyResponse; })(); - vtctldata.SetKeyspaceServedFromRequest = (function() { - - /** - * Properties of a SetKeyspaceServedFromRequest. - * @memberof vtctldata - * @interface ISetKeyspaceServedFromRequest - * @property {string|null} [keyspace] SetKeyspaceServedFromRequest keyspace - * @property {topodata.TabletType|null} [tablet_type] SetKeyspaceServedFromRequest tablet_type - * @property {Array.|null} [cells] SetKeyspaceServedFromRequest cells - * @property {boolean|null} [remove] SetKeyspaceServedFromRequest remove - * @property {string|null} [source_keyspace] SetKeyspaceServedFromRequest source_keyspace - */ - - /** - * Constructs a new SetKeyspaceServedFromRequest. - * @memberof vtctldata - * @classdesc Represents a SetKeyspaceServedFromRequest. - * @implements ISetKeyspaceServedFromRequest - * @constructor - * @param {vtctldata.ISetKeyspaceServedFromRequest=} [properties] Properties to set - */ - function SetKeyspaceServedFromRequest(properties) { - this.cells = []; - if (properties) - for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SetKeyspaceServedFromRequest keyspace. - * @member {string} keyspace - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - */ - SetKeyspaceServedFromRequest.prototype.keyspace = ""; - - /** - * SetKeyspaceServedFromRequest tablet_type. - * @member {topodata.TabletType} tablet_type - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - */ - SetKeyspaceServedFromRequest.prototype.tablet_type = 0; - - /** - * SetKeyspaceServedFromRequest cells. - * @member {Array.} cells - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - */ - SetKeyspaceServedFromRequest.prototype.cells = $util.emptyArray; - - /** - * SetKeyspaceServedFromRequest remove. - * @member {boolean} remove - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - */ - SetKeyspaceServedFromRequest.prototype.remove = false; - - /** - * SetKeyspaceServedFromRequest source_keyspace. - * @member {string} source_keyspace - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - */ - SetKeyspaceServedFromRequest.prototype.source_keyspace = ""; - - /** - * Creates a new SetKeyspaceServedFromRequest instance using the specified properties. - * @function create - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {vtctldata.ISetKeyspaceServedFromRequest=} [properties] Properties to set - * @returns {vtctldata.SetKeyspaceServedFromRequest} SetKeyspaceServedFromRequest instance - */ - SetKeyspaceServedFromRequest.create = function create(properties) { - return new SetKeyspaceServedFromRequest(properties); - }; - - /** - * Encodes the specified SetKeyspaceServedFromRequest message. Does not implicitly {@link vtctldata.SetKeyspaceServedFromRequest.verify|verify} messages. - * @function encode - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {vtctldata.ISetKeyspaceServedFromRequest} message SetKeyspaceServedFromRequest message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SetKeyspaceServedFromRequest.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.keyspace != null && Object.hasOwnProperty.call(message, "keyspace")) - writer.uint32(/* id 1, wireType 2 =*/10).string(message.keyspace); - if (message.tablet_type != null && Object.hasOwnProperty.call(message, "tablet_type")) - writer.uint32(/* id 2, wireType 0 =*/16).int32(message.tablet_type); - if (message.cells != null && message.cells.length) - for (let i = 0; i < message.cells.length; ++i) - writer.uint32(/* id 3, wireType 2 =*/26).string(message.cells[i]); - if (message.remove != null && Object.hasOwnProperty.call(message, "remove")) - writer.uint32(/* id 4, wireType 0 =*/32).bool(message.remove); - if (message.source_keyspace != null && Object.hasOwnProperty.call(message, "source_keyspace")) - writer.uint32(/* id 5, wireType 2 =*/42).string(message.source_keyspace); - return writer; - }; - - /** - * Encodes the specified SetKeyspaceServedFromRequest message, length delimited. Does not implicitly {@link vtctldata.SetKeyspaceServedFromRequest.verify|verify} messages. - * @function encodeDelimited - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {vtctldata.ISetKeyspaceServedFromRequest} message SetKeyspaceServedFromRequest message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SetKeyspaceServedFromRequest.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SetKeyspaceServedFromRequest message from the specified reader or buffer. - * @function decode - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {vtctldata.SetKeyspaceServedFromRequest} SetKeyspaceServedFromRequest - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SetKeyspaceServedFromRequest.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.SetKeyspaceServedFromRequest(); - while (reader.pos < end) { - let tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.keyspace = reader.string(); - break; - } - case 2: { - message.tablet_type = reader.int32(); - break; - } - case 3: { - if (!(message.cells && message.cells.length)) - message.cells = []; - message.cells.push(reader.string()); - break; - } - case 4: { - message.remove = reader.bool(); - break; - } - case 5: { - message.source_keyspace = reader.string(); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SetKeyspaceServedFromRequest message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {vtctldata.SetKeyspaceServedFromRequest} SetKeyspaceServedFromRequest - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SetKeyspaceServedFromRequest.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SetKeyspaceServedFromRequest message. - * @function verify - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SetKeyspaceServedFromRequest.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - if (!$util.isString(message.keyspace)) - return "keyspace: string expected"; - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - switch (message.tablet_type) { - default: - return "tablet_type: enum value expected"; - case 0: - case 1: - case 1: - case 2: - case 3: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - break; - } - if (message.cells != null && message.hasOwnProperty("cells")) { - if (!Array.isArray(message.cells)) - return "cells: array expected"; - for (let i = 0; i < message.cells.length; ++i) - if (!$util.isString(message.cells[i])) - return "cells: string[] expected"; - } - if (message.remove != null && message.hasOwnProperty("remove")) - if (typeof message.remove !== "boolean") - return "remove: boolean expected"; - if (message.source_keyspace != null && message.hasOwnProperty("source_keyspace")) - if (!$util.isString(message.source_keyspace)) - return "source_keyspace: string expected"; - return null; - }; - - /** - * Creates a SetKeyspaceServedFromRequest message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {Object.} object Plain object - * @returns {vtctldata.SetKeyspaceServedFromRequest} SetKeyspaceServedFromRequest - */ - SetKeyspaceServedFromRequest.fromObject = function fromObject(object) { - if (object instanceof $root.vtctldata.SetKeyspaceServedFromRequest) - return object; - let message = new $root.vtctldata.SetKeyspaceServedFromRequest(); - if (object.keyspace != null) - message.keyspace = String(object.keyspace); - switch (object.tablet_type) { - default: - if (typeof object.tablet_type === "number") { - message.tablet_type = object.tablet_type; - break; - } - break; - case "UNKNOWN": - case 0: - message.tablet_type = 0; - break; - case "PRIMARY": - case 1: - message.tablet_type = 1; - break; - case "MASTER": - case 1: - message.tablet_type = 1; - break; - case "REPLICA": - case 2: - message.tablet_type = 2; - break; - case "RDONLY": - case 3: - message.tablet_type = 3; - break; - case "BATCH": - case 3: - message.tablet_type = 3; - break; - case "SPARE": - case 4: - message.tablet_type = 4; - break; - case "EXPERIMENTAL": - case 5: - message.tablet_type = 5; - break; - case "BACKUP": - case 6: - message.tablet_type = 6; - break; - case "RESTORE": - case 7: - message.tablet_type = 7; - break; - case "DRAINED": - case 8: - message.tablet_type = 8; - break; - } - if (object.cells) { - if (!Array.isArray(object.cells)) - throw TypeError(".vtctldata.SetKeyspaceServedFromRequest.cells: array expected"); - message.cells = []; - for (let i = 0; i < object.cells.length; ++i) - message.cells[i] = String(object.cells[i]); - } - if (object.remove != null) - message.remove = Boolean(object.remove); - if (object.source_keyspace != null) - message.source_keyspace = String(object.source_keyspace); - return message; - }; - - /** - * Creates a plain object from a SetKeyspaceServedFromRequest message. Also converts values to other types if specified. - * @function toObject - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {vtctldata.SetKeyspaceServedFromRequest} message SetKeyspaceServedFromRequest - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SetKeyspaceServedFromRequest.toObject = function toObject(message, options) { - if (!options) - options = {}; - let object = {}; - if (options.arrays || options.defaults) - object.cells = []; - if (options.defaults) { - object.keyspace = ""; - object.tablet_type = options.enums === String ? "UNKNOWN" : 0; - object.remove = false; - object.source_keyspace = ""; - } - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - object.keyspace = message.keyspace; - if (message.tablet_type != null && message.hasOwnProperty("tablet_type")) - object.tablet_type = options.enums === String ? $root.topodata.TabletType[message.tablet_type] === undefined ? message.tablet_type : $root.topodata.TabletType[message.tablet_type] : message.tablet_type; - if (message.cells && message.cells.length) { - object.cells = []; - for (let j = 0; j < message.cells.length; ++j) - object.cells[j] = message.cells[j]; - } - if (message.remove != null && message.hasOwnProperty("remove")) - object.remove = message.remove; - if (message.source_keyspace != null && message.hasOwnProperty("source_keyspace")) - object.source_keyspace = message.source_keyspace; - return object; - }; - - /** - * Converts this SetKeyspaceServedFromRequest to JSON. - * @function toJSON - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @instance - * @returns {Object.} JSON object - */ - SetKeyspaceServedFromRequest.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for SetKeyspaceServedFromRequest - * @function getTypeUrl - * @memberof vtctldata.SetKeyspaceServedFromRequest - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - SetKeyspaceServedFromRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/vtctldata.SetKeyspaceServedFromRequest"; - }; - - return SetKeyspaceServedFromRequest; - })(); - - vtctldata.SetKeyspaceServedFromResponse = (function() { - - /** - * Properties of a SetKeyspaceServedFromResponse. - * @memberof vtctldata - * @interface ISetKeyspaceServedFromResponse - * @property {topodata.IKeyspace|null} [keyspace] SetKeyspaceServedFromResponse keyspace - */ - - /** - * Constructs a new SetKeyspaceServedFromResponse. - * @memberof vtctldata - * @classdesc Represents a SetKeyspaceServedFromResponse. - * @implements ISetKeyspaceServedFromResponse - * @constructor - * @param {vtctldata.ISetKeyspaceServedFromResponse=} [properties] Properties to set - */ - function SetKeyspaceServedFromResponse(properties) { - if (properties) - for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SetKeyspaceServedFromResponse keyspace. - * @member {topodata.IKeyspace|null|undefined} keyspace - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @instance - */ - SetKeyspaceServedFromResponse.prototype.keyspace = null; - - /** - * Creates a new SetKeyspaceServedFromResponse instance using the specified properties. - * @function create - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {vtctldata.ISetKeyspaceServedFromResponse=} [properties] Properties to set - * @returns {vtctldata.SetKeyspaceServedFromResponse} SetKeyspaceServedFromResponse instance - */ - SetKeyspaceServedFromResponse.create = function create(properties) { - return new SetKeyspaceServedFromResponse(properties); - }; - - /** - * Encodes the specified SetKeyspaceServedFromResponse message. Does not implicitly {@link vtctldata.SetKeyspaceServedFromResponse.verify|verify} messages. - * @function encode - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {vtctldata.ISetKeyspaceServedFromResponse} message SetKeyspaceServedFromResponse message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SetKeyspaceServedFromResponse.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.keyspace != null && Object.hasOwnProperty.call(message, "keyspace")) - $root.topodata.Keyspace.encode(message.keyspace, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified SetKeyspaceServedFromResponse message, length delimited. Does not implicitly {@link vtctldata.SetKeyspaceServedFromResponse.verify|verify} messages. - * @function encodeDelimited - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {vtctldata.ISetKeyspaceServedFromResponse} message SetKeyspaceServedFromResponse message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SetKeyspaceServedFromResponse.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SetKeyspaceServedFromResponse message from the specified reader or buffer. - * @function decode - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {vtctldata.SetKeyspaceServedFromResponse} SetKeyspaceServedFromResponse - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SetKeyspaceServedFromResponse.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.vtctldata.SetKeyspaceServedFromResponse(); - while (reader.pos < end) { - let tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.keyspace = $root.topodata.Keyspace.decode(reader, reader.uint32()); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SetKeyspaceServedFromResponse message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {vtctldata.SetKeyspaceServedFromResponse} SetKeyspaceServedFromResponse - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SetKeyspaceServedFromResponse.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SetKeyspaceServedFromResponse message. - * @function verify - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SetKeyspaceServedFromResponse.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.keyspace != null && message.hasOwnProperty("keyspace")) { - let error = $root.topodata.Keyspace.verify(message.keyspace); - if (error) - return "keyspace." + error; - } - return null; - }; - - /** - * Creates a SetKeyspaceServedFromResponse message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {Object.} object Plain object - * @returns {vtctldata.SetKeyspaceServedFromResponse} SetKeyspaceServedFromResponse - */ - SetKeyspaceServedFromResponse.fromObject = function fromObject(object) { - if (object instanceof $root.vtctldata.SetKeyspaceServedFromResponse) - return object; - let message = new $root.vtctldata.SetKeyspaceServedFromResponse(); - if (object.keyspace != null) { - if (typeof object.keyspace !== "object") - throw TypeError(".vtctldata.SetKeyspaceServedFromResponse.keyspace: object expected"); - message.keyspace = $root.topodata.Keyspace.fromObject(object.keyspace); - } - return message; - }; - - /** - * Creates a plain object from a SetKeyspaceServedFromResponse message. Also converts values to other types if specified. - * @function toObject - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {vtctldata.SetKeyspaceServedFromResponse} message SetKeyspaceServedFromResponse - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SetKeyspaceServedFromResponse.toObject = function toObject(message, options) { - if (!options) - options = {}; - let object = {}; - if (options.defaults) - object.keyspace = null; - if (message.keyspace != null && message.hasOwnProperty("keyspace")) - object.keyspace = $root.topodata.Keyspace.toObject(message.keyspace, options); - return object; - }; - - /** - * Converts this SetKeyspaceServedFromResponse to JSON. - * @function toJSON - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @instance - * @returns {Object.} JSON object - */ - SetKeyspaceServedFromResponse.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for SetKeyspaceServedFromResponse - * @function getTypeUrl - * @memberof vtctldata.SetKeyspaceServedFromResponse - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - SetKeyspaceServedFromResponse.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/vtctldata.SetKeyspaceServedFromResponse"; - }; - - return SetKeyspaceServedFromResponse; - })(); - vtctldata.SetKeyspaceShardingInfoRequest = (function() { /** @@ -159738,6 +162381,8 @@ export const vtctldata = $root.vtctldata = (() => { * @property {vttime.IDuration|null} [wait_update_interval] VDiffCreateRequest wait_update_interval * @property {boolean|null} [auto_retry] VDiffCreateRequest auto_retry * @property {boolean|null} [verbose] VDiffCreateRequest verbose + * @property {number|Long|null} [max_report_sample_rows] VDiffCreateRequest max_report_sample_rows + * @property {vttime.IDuration|null} [max_diff_duration] VDiffCreateRequest max_diff_duration */ /** @@ -159903,6 +162548,22 @@ export const vtctldata = $root.vtctldata = (() => { */ VDiffCreateRequest.prototype.verbose = false; + /** + * VDiffCreateRequest max_report_sample_rows. + * @member {number|Long} max_report_sample_rows + * @memberof vtctldata.VDiffCreateRequest + * @instance + */ + VDiffCreateRequest.prototype.max_report_sample_rows = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * VDiffCreateRequest max_diff_duration. + * @member {vttime.IDuration|null|undefined} max_diff_duration + * @memberof vtctldata.VDiffCreateRequest + * @instance + */ + VDiffCreateRequest.prototype.max_diff_duration = null; + /** * Creates a new VDiffCreateRequest instance using the specified properties. * @function create @@ -159970,6 +162631,10 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 17, wireType 0 =*/136).bool(message.auto_retry); if (message.verbose != null && Object.hasOwnProperty.call(message, "verbose")) writer.uint32(/* id 18, wireType 0 =*/144).bool(message.verbose); + if (message.max_report_sample_rows != null && Object.hasOwnProperty.call(message, "max_report_sample_rows")) + writer.uint32(/* id 19, wireType 0 =*/152).int64(message.max_report_sample_rows); + if (message.max_diff_duration != null && Object.hasOwnProperty.call(message, "max_diff_duration")) + $root.vttime.Duration.encode(message.max_diff_duration, writer.uint32(/* id 20, wireType 2 =*/162).fork()).ldelim(); return writer; }; @@ -160089,6 +162754,14 @@ export const vtctldata = $root.vtctldata = (() => { message.verbose = reader.bool(); break; } + case 19: { + message.max_report_sample_rows = reader.int64(); + break; + } + case 20: { + message.max_diff_duration = $root.vttime.Duration.decode(reader, reader.uint32()); + break; + } default: reader.skipType(tag & 7); break; @@ -160218,6 +162891,14 @@ export const vtctldata = $root.vtctldata = (() => { if (message.verbose != null && message.hasOwnProperty("verbose")) if (typeof message.verbose !== "boolean") return "verbose: boolean expected"; + if (message.max_report_sample_rows != null && message.hasOwnProperty("max_report_sample_rows")) + if (!$util.isInteger(message.max_report_sample_rows) && !(message.max_report_sample_rows && $util.isInteger(message.max_report_sample_rows.low) && $util.isInteger(message.max_report_sample_rows.high))) + return "max_report_sample_rows: integer|Long expected"; + if (message.max_diff_duration != null && message.hasOwnProperty("max_diff_duration")) { + let error = $root.vttime.Duration.verify(message.max_diff_duration); + if (error) + return "max_diff_duration." + error; + } return null; }; @@ -160377,6 +163058,20 @@ export const vtctldata = $root.vtctldata = (() => { message.auto_retry = Boolean(object.auto_retry); if (object.verbose != null) message.verbose = Boolean(object.verbose); + if (object.max_report_sample_rows != null) + if ($util.Long) + (message.max_report_sample_rows = $util.Long.fromValue(object.max_report_sample_rows)).unsigned = false; + else if (typeof object.max_report_sample_rows === "string") + message.max_report_sample_rows = parseInt(object.max_report_sample_rows, 10); + else if (typeof object.max_report_sample_rows === "number") + message.max_report_sample_rows = object.max_report_sample_rows; + else if (typeof object.max_report_sample_rows === "object") + message.max_report_sample_rows = new $util.LongBits(object.max_report_sample_rows.low >>> 0, object.max_report_sample_rows.high >>> 0).toNumber(); + if (object.max_diff_duration != null) { + if (typeof object.max_diff_duration !== "object") + throw TypeError(".vtctldata.VDiffCreateRequest.max_diff_duration: object expected"); + message.max_diff_duration = $root.vttime.Duration.fromObject(object.max_diff_duration); + } return message; }; @@ -160422,6 +163117,12 @@ export const vtctldata = $root.vtctldata = (() => { object.wait_update_interval = null; object.auto_retry = false; object.verbose = false; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.max_report_sample_rows = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.max_report_sample_rows = options.longs === String ? "0" : 0; + object.max_diff_duration = null; } if (message.workflow != null && message.hasOwnProperty("workflow")) object.workflow = message.workflow; @@ -160477,6 +163178,13 @@ export const vtctldata = $root.vtctldata = (() => { object.auto_retry = message.auto_retry; if (message.verbose != null && message.hasOwnProperty("verbose")) object.verbose = message.verbose; + if (message.max_report_sample_rows != null && message.hasOwnProperty("max_report_sample_rows")) + if (typeof message.max_report_sample_rows === "number") + object.max_report_sample_rows = options.longs === String ? String(message.max_report_sample_rows) : message.max_report_sample_rows; + else + object.max_report_sample_rows = options.longs === String ? $util.Long.prototype.toString.call(message.max_report_sample_rows) : options.longs === Number ? new $util.LongBits(message.max_report_sample_rows.low >>> 0, message.max_report_sample_rows.high >>> 0).toNumber() : message.max_report_sample_rows; + if (message.max_diff_duration != null && message.hasOwnProperty("max_diff_duration")) + object.max_diff_duration = $root.vttime.Duration.toObject(message.max_diff_duration, options); return object; }; @@ -162492,6 +165200,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {string|null} [workflow] WorkflowDeleteRequest workflow * @property {boolean|null} [keep_data] WorkflowDeleteRequest keep_data * @property {boolean|null} [keep_routing_rules] WorkflowDeleteRequest keep_routing_rules + * @property {Array.|null} [shards] WorkflowDeleteRequest shards */ /** @@ -162503,6 +165212,7 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.IWorkflowDeleteRequest=} [properties] Properties to set */ function WorkflowDeleteRequest(properties) { + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -162541,6 +165251,14 @@ export const vtctldata = $root.vtctldata = (() => { */ WorkflowDeleteRequest.prototype.keep_routing_rules = false; + /** + * WorkflowDeleteRequest shards. + * @member {Array.} shards + * @memberof vtctldata.WorkflowDeleteRequest + * @instance + */ + WorkflowDeleteRequest.prototype.shards = $util.emptyArray; + /** * Creates a new WorkflowDeleteRequest instance using the specified properties. * @function create @@ -162573,6 +165291,9 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 3, wireType 0 =*/24).bool(message.keep_data); if (message.keep_routing_rules != null && Object.hasOwnProperty.call(message, "keep_routing_rules")) writer.uint32(/* id 4, wireType 0 =*/32).bool(message.keep_routing_rules); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 5, wireType 2 =*/42).string(message.shards[i]); return writer; }; @@ -162623,6 +165344,12 @@ export const vtctldata = $root.vtctldata = (() => { message.keep_routing_rules = reader.bool(); break; } + case 5: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -162670,6 +165397,13 @@ export const vtctldata = $root.vtctldata = (() => { if (message.keep_routing_rules != null && message.hasOwnProperty("keep_routing_rules")) if (typeof message.keep_routing_rules !== "boolean") return "keep_routing_rules: boolean expected"; + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -162693,6 +165427,13 @@ export const vtctldata = $root.vtctldata = (() => { message.keep_data = Boolean(object.keep_data); if (object.keep_routing_rules != null) message.keep_routing_rules = Boolean(object.keep_routing_rules); + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".vtctldata.WorkflowDeleteRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -162709,6 +165450,8 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; + if (options.arrays || options.defaults) + object.shards = []; if (options.defaults) { object.keyspace = ""; object.workflow = ""; @@ -162723,6 +165466,11 @@ export const vtctldata = $root.vtctldata = (() => { object.keep_data = message.keep_data; if (message.keep_routing_rules != null && message.hasOwnProperty("keep_routing_rules")) object.keep_routing_rules = message.keep_routing_rules; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; @@ -163243,6 +165991,7 @@ export const vtctldata = $root.vtctldata = (() => { * @interface IWorkflowStatusRequest * @property {string|null} [keyspace] WorkflowStatusRequest keyspace * @property {string|null} [workflow] WorkflowStatusRequest workflow + * @property {Array.|null} [shards] WorkflowStatusRequest shards */ /** @@ -163254,6 +166003,7 @@ export const vtctldata = $root.vtctldata = (() => { * @param {vtctldata.IWorkflowStatusRequest=} [properties] Properties to set */ function WorkflowStatusRequest(properties) { + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -163276,6 +166026,14 @@ export const vtctldata = $root.vtctldata = (() => { */ WorkflowStatusRequest.prototype.workflow = ""; + /** + * WorkflowStatusRequest shards. + * @member {Array.} shards + * @memberof vtctldata.WorkflowStatusRequest + * @instance + */ + WorkflowStatusRequest.prototype.shards = $util.emptyArray; + /** * Creates a new WorkflowStatusRequest instance using the specified properties. * @function create @@ -163304,6 +166062,9 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 1, wireType 2 =*/10).string(message.keyspace); if (message.workflow != null && Object.hasOwnProperty.call(message, "workflow")) writer.uint32(/* id 2, wireType 2 =*/18).string(message.workflow); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 3, wireType 2 =*/26).string(message.shards[i]); return writer; }; @@ -163346,6 +166107,12 @@ export const vtctldata = $root.vtctldata = (() => { message.workflow = reader.string(); break; } + case 3: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -163387,6 +166154,13 @@ export const vtctldata = $root.vtctldata = (() => { if (message.workflow != null && message.hasOwnProperty("workflow")) if (!$util.isString(message.workflow)) return "workflow: string expected"; + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -163406,6 +166180,13 @@ export const vtctldata = $root.vtctldata = (() => { message.keyspace = String(object.keyspace); if (object.workflow != null) message.workflow = String(object.workflow); + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".vtctldata.WorkflowStatusRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -163422,6 +166203,8 @@ export const vtctldata = $root.vtctldata = (() => { if (!options) options = {}; let object = {}; + if (options.arrays || options.defaults) + object.shards = []; if (options.defaults) { object.keyspace = ""; object.workflow = ""; @@ -163430,6 +166213,11 @@ export const vtctldata = $root.vtctldata = (() => { object.keyspace = message.keyspace; if (message.workflow != null && message.hasOwnProperty("workflow")) object.workflow = message.workflow; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; @@ -164735,6 +167523,7 @@ export const vtctldata = $root.vtctldata = (() => { * @property {vttime.IDuration|null} [timeout] WorkflowSwitchTrafficRequest timeout * @property {boolean|null} [dry_run] WorkflowSwitchTrafficRequest dry_run * @property {boolean|null} [initialize_target_sequences] WorkflowSwitchTrafficRequest initialize_target_sequences + * @property {Array.|null} [shards] WorkflowSwitchTrafficRequest shards */ /** @@ -164748,6 +167537,7 @@ export const vtctldata = $root.vtctldata = (() => { function WorkflowSwitchTrafficRequest(properties) { this.cells = []; this.tablet_types = []; + this.shards = []; if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -164834,6 +167624,14 @@ export const vtctldata = $root.vtctldata = (() => { */ WorkflowSwitchTrafficRequest.prototype.initialize_target_sequences = false; + /** + * WorkflowSwitchTrafficRequest shards. + * @member {Array.} shards + * @memberof vtctldata.WorkflowSwitchTrafficRequest + * @instance + */ + WorkflowSwitchTrafficRequest.prototype.shards = $util.emptyArray; + /** * Creates a new WorkflowSwitchTrafficRequest instance using the specified properties. * @function create @@ -164883,6 +167681,9 @@ export const vtctldata = $root.vtctldata = (() => { writer.uint32(/* id 9, wireType 0 =*/72).bool(message.dry_run); if (message.initialize_target_sequences != null && Object.hasOwnProperty.call(message, "initialize_target_sequences")) writer.uint32(/* id 10, wireType 0 =*/80).bool(message.initialize_target_sequences); + if (message.shards != null && message.shards.length) + for (let i = 0; i < message.shards.length; ++i) + writer.uint32(/* id 11, wireType 2 =*/90).string(message.shards[i]); return writer; }; @@ -164966,6 +167767,12 @@ export const vtctldata = $root.vtctldata = (() => { message.initialize_target_sequences = reader.bool(); break; } + case 11: { + if (!(message.shards && message.shards.length)) + message.shards = []; + message.shards.push(reader.string()); + break; + } default: reader.skipType(tag & 7); break; @@ -165057,6 +167864,13 @@ export const vtctldata = $root.vtctldata = (() => { if (message.initialize_target_sequences != null && message.hasOwnProperty("initialize_target_sequences")) if (typeof message.initialize_target_sequences !== "boolean") return "initialize_target_sequences: boolean expected"; + if (message.shards != null && message.hasOwnProperty("shards")) { + if (!Array.isArray(message.shards)) + return "shards: array expected"; + for (let i = 0; i < message.shards.length; ++i) + if (!$util.isString(message.shards[i])) + return "shards: string[] expected"; + } return null; }; @@ -165158,6 +167972,13 @@ export const vtctldata = $root.vtctldata = (() => { message.dry_run = Boolean(object.dry_run); if (object.initialize_target_sequences != null) message.initialize_target_sequences = Boolean(object.initialize_target_sequences); + if (object.shards) { + if (!Array.isArray(object.shards)) + throw TypeError(".vtctldata.WorkflowSwitchTrafficRequest.shards: array expected"); + message.shards = []; + for (let i = 0; i < object.shards.length; ++i) + message.shards[i] = String(object.shards[i]); + } return message; }; @@ -165177,6 +167998,7 @@ export const vtctldata = $root.vtctldata = (() => { if (options.arrays || options.defaults) { object.cells = []; object.tablet_types = []; + object.shards = []; } if (options.defaults) { object.keyspace = ""; @@ -165214,6 +168036,11 @@ export const vtctldata = $root.vtctldata = (() => { object.dry_run = message.dry_run; if (message.initialize_target_sequences != null && message.hasOwnProperty("initialize_target_sequences")) object.initialize_target_sequences = message.initialize_target_sequences; + if (message.shards && message.shards.length) { + object.shards = []; + for (let j = 0; j < message.shards.length; ++j) + object.shards[j] = message.shards[j]; + } return object; }; diff --git a/web/vtadmin/src/util/tabletDebugVars.ts b/web/vtadmin/src/util/tabletDebugVars.ts index 37ea08e49b1..1d01ec16bee 100644 --- a/web/vtadmin/src/util/tabletDebugVars.ts +++ b/web/vtadmin/src/util/tabletDebugVars.ts @@ -33,6 +33,7 @@ export type TabletDebugVars = Partial<{ BuildNumber: string; BuildTimestamp: string; BuildUser: string; + BuildVersion: string; QPS: { [k: string]: number[] };